github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/unit/generate_unit.go (about)

     1  // Copyright ©2014 The Gonum Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // +build ignore
     6  
     7  package main
     8  
     9  import (
    10  	"bytes"
    11  	"go/format"
    12  	"log"
    13  	"os"
    14  	"strings"
    15  	"text/template"
    16  
    17  	"github.com/jingcheng-WU/gonum/unit"
    18  )
    19  
    20  type Unit struct {
    21  	DimensionName string
    22  	Receiver      string
    23  	PowerOffset   int    // from normal (for example, mass base unit is kg, not g)
    24  	PrintString   string // print string for the unit (kg for mass)
    25  	ExtraConstant []Constant
    26  	Name          string
    27  	TypeComment   string // text to comment the type
    28  	Dimensions    []Dimension
    29  	ErForm        string // for Xxxer interface
    30  }
    31  
    32  type Dimension struct {
    33  	Name  string
    34  	Power int
    35  }
    36  
    37  func (u Unit) Units() string {
    38  	dims := make(unit.Dimensions)
    39  	for _, d := range u.Dimensions {
    40  		dims[dimOf[d.Name]] = d.Power
    41  	}
    42  	return dims.String()
    43  }
    44  
    45  const (
    46  	AngleName             string = "AngleDim"
    47  	CurrentName           string = "CurrentDim"
    48  	LengthName            string = "LengthDim"
    49  	LuminousIntensityName string = "LuminousIntensityDim"
    50  	MassName              string = "MassDim"
    51  	MoleName              string = "MoleDim"
    52  	TemperatureName       string = "TemperatureDim"
    53  	TimeName              string = "TimeDim"
    54  )
    55  
    56  var dimOf = map[string]unit.Dimension{
    57  	"AngleDim":             unit.AngleDim,
    58  	"CurrentDim":           unit.CurrentDim,
    59  	"LengthDim":            unit.LengthDim,
    60  	"LuminousIntensityDim": unit.LuminousIntensityDim,
    61  	"MassDim":              unit.MassDim,
    62  	"MoleDim":              unit.MoleDim,
    63  	"TemperatureDim":       unit.TemperatureDim,
    64  	"TimeDim":              unit.TimeDim,
    65  }
    66  
    67  type Constant struct {
    68  	Name  string
    69  	Value string
    70  }
    71  
    72  type Prefix struct {
    73  	Name  string
    74  	Power int
    75  }
    76  
    77  var Units = []Unit{
    78  	// Base units.
    79  	{
    80  		DimensionName: "Dimless",
    81  		Receiver:      "d",
    82  		TypeComment:   "Dimless represents a dimensionless constant",
    83  		Dimensions:    []Dimension{},
    84  	},
    85  	{
    86  		DimensionName: "Angle",
    87  		Receiver:      "a",
    88  		PrintString:   "rad",
    89  		Name:          "Rad",
    90  		TypeComment:   "Angle represents an angle in radians",
    91  		Dimensions: []Dimension{
    92  			{Name: AngleName, Power: 1},
    93  		},
    94  	},
    95  	{
    96  		DimensionName: "Current",
    97  		Receiver:      "i",
    98  		PrintString:   "A",
    99  		Name:          "Ampere",
   100  		TypeComment:   "Current represents a current in Amperes",
   101  		Dimensions: []Dimension{
   102  			{Name: CurrentName, Power: 1},
   103  		},
   104  	},
   105  	{
   106  		DimensionName: "Length",
   107  		Receiver:      "l",
   108  		PrintString:   "m",
   109  		Name:          "Metre",
   110  		TypeComment:   "Length represents a length in metres",
   111  		Dimensions: []Dimension{
   112  			{Name: LengthName, Power: 1},
   113  		},
   114  	},
   115  	{
   116  		DimensionName: "LuminousIntensity",
   117  		Receiver:      "j",
   118  		PrintString:   "cd",
   119  		Name:          "Candela",
   120  		TypeComment:   "Candela represents a luminous intensity in candela",
   121  		Dimensions: []Dimension{
   122  			{Name: LuminousIntensityName, Power: 1},
   123  		},
   124  	},
   125  	{
   126  		DimensionName: "Mass",
   127  		Receiver:      "m",
   128  		PowerOffset:   -3,
   129  		PrintString:   "kg",
   130  		Name:          "Gram",
   131  		TypeComment:   "Mass represents a mass in kilograms",
   132  		ExtraConstant: []Constant{
   133  			{Name: "Kilogram", Value: "Kilo * Gram"},
   134  		},
   135  		Dimensions: []Dimension{
   136  			{Name: MassName, Power: 1},
   137  		},
   138  	},
   139  	{
   140  		DimensionName: "Mole",
   141  		Receiver:      "n",
   142  		PrintString:   "mol",
   143  		Name:          "Mol",
   144  		TypeComment:   "Mole represents an amount in moles",
   145  		Dimensions: []Dimension{
   146  			{Name: MoleName, Power: 1},
   147  		},
   148  	},
   149  	{
   150  		DimensionName: "Temperature",
   151  		Receiver:      "t",
   152  		PrintString:   "K",
   153  		Name:          "Kelvin",
   154  		TypeComment:   "Temperature represents a temperature in Kelvin",
   155  		Dimensions: []Dimension{
   156  			{Name: TemperatureName, Power: 1},
   157  		},
   158  		ErForm: "Temperaturer",
   159  	},
   160  	{
   161  		DimensionName: "Time",
   162  		Receiver:      "t",
   163  		PrintString:   "s",
   164  		Name:          "Second",
   165  		TypeComment:   "Time represents a duration in seconds",
   166  		ExtraConstant: []Constant{
   167  			{Name: "Minute", Value: "60 * Second"},
   168  			{Name: "Hour", Value: "60 * Minute"},
   169  		},
   170  		Dimensions: []Dimension{
   171  			{Name: TimeName, Power: 1},
   172  		},
   173  		ErForm: "Timer",
   174  	},
   175  
   176  	// Derived units.
   177  	{
   178  		DimensionName: "AbsorbedRadioactiveDose",
   179  		Receiver:      "a",
   180  		PrintString:   "Gy",
   181  		Name:          "Gray",
   182  		TypeComment:   "AbsorbedRadioactiveDose is a measure of absorbed dose of ionizing radiation in grays",
   183  		Dimensions: []Dimension{
   184  			{Name: LengthName, Power: 2},
   185  			{Name: TimeName, Power: -2},
   186  		},
   187  	},
   188  	{
   189  		DimensionName: "Acceleration",
   190  		Receiver:      "a",
   191  		PrintString:   "m s^-2",
   192  		TypeComment:   "Acceleration represents an acceleration in metres per second squared",
   193  		Dimensions: []Dimension{
   194  			{Name: LengthName, Power: 1},
   195  			{Name: TimeName, Power: -2},
   196  		},
   197  	},
   198  	{
   199  		DimensionName: "Area",
   200  		Receiver:      "a",
   201  		PrintString:   "m^2",
   202  		TypeComment:   "Area represents an area in square metres",
   203  		Dimensions: []Dimension{
   204  			{Name: LengthName, Power: 2},
   205  		},
   206  	},
   207  	{
   208  		DimensionName: "Radioactivity",
   209  		Receiver:      "r",
   210  		PrintString:   "Bq",
   211  		Name:          "Becquerel",
   212  		TypeComment:   "Radioactivity represents a rate of radioactive decay in becquerels",
   213  		Dimensions: []Dimension{
   214  			{Name: TimeName, Power: -1},
   215  		},
   216  	},
   217  	{
   218  		DimensionName: "Capacitance",
   219  		Receiver:      "cp",
   220  		PrintString:   "F",
   221  		Name:          "Farad",
   222  		TypeComment:   "Capacitance represents an electrical capacitance in Farads",
   223  		Dimensions: []Dimension{
   224  			{Name: CurrentName, Power: 2},
   225  			{Name: LengthName, Power: -2},
   226  			{Name: MassName, Power: -1},
   227  			{Name: TimeName, Power: 4},
   228  		},
   229  		ErForm: "Capacitancer",
   230  	},
   231  	{
   232  		DimensionName: "Charge",
   233  		Receiver:      "ch",
   234  		PrintString:   "C",
   235  		Name:          "Coulomb",
   236  		TypeComment:   "Charge represents an electric charge in Coulombs",
   237  		Dimensions: []Dimension{
   238  			{Name: CurrentName, Power: 1},
   239  			{Name: TimeName, Power: 1},
   240  		},
   241  		ErForm: "Charger",
   242  	},
   243  	{
   244  		DimensionName: "Conductance",
   245  		Receiver:      "co",
   246  		PrintString:   "S",
   247  		Name:          "Siemens",
   248  		TypeComment:   "Conductance represents an electrical conductance in Siemens",
   249  		Dimensions: []Dimension{
   250  			{Name: CurrentName, Power: 2},
   251  			{Name: LengthName, Power: -2},
   252  			{Name: MassName, Power: -1},
   253  			{Name: TimeName, Power: 3},
   254  		},
   255  		ErForm: "Conductancer",
   256  	},
   257  	{
   258  		DimensionName: "EquivalentRadioactiveDose",
   259  		Receiver:      "a",
   260  		PrintString:   "Sy",
   261  		Name:          "Sievert",
   262  		TypeComment:   "EquivalentRadioactiveDose is a measure of equivalent dose of ionizing radiation in sieverts",
   263  		Dimensions: []Dimension{
   264  			{Name: LengthName, Power: 2},
   265  			{Name: TimeName, Power: -2},
   266  		},
   267  	},
   268  	{
   269  		DimensionName: "Energy",
   270  		Receiver:      "e",
   271  		PrintString:   "J",
   272  		Name:          "Joule",
   273  		TypeComment:   "Energy represents a quantity of energy in Joules",
   274  		Dimensions: []Dimension{
   275  			{Name: LengthName, Power: 2},
   276  			{Name: MassName, Power: 1},
   277  			{Name: TimeName, Power: -2},
   278  		},
   279  	},
   280  	{
   281  		DimensionName: "Frequency",
   282  		Receiver:      "f",
   283  		PrintString:   "Hz",
   284  		Name:          "Hertz",
   285  		TypeComment:   "Frequency represents a frequency in Hertz",
   286  		Dimensions: []Dimension{
   287  			{Name: TimeName, Power: -1},
   288  		},
   289  	},
   290  	{
   291  		DimensionName: "Force",
   292  		Receiver:      "f",
   293  		PrintString:   "N",
   294  		Name:          "Newton",
   295  		TypeComment:   "Force represents a force in Newtons",
   296  		Dimensions: []Dimension{
   297  			{Name: LengthName, Power: 1},
   298  			{Name: MassName, Power: 1},
   299  			{Name: TimeName, Power: -2},
   300  		},
   301  		ErForm: "Forcer",
   302  	},
   303  	{
   304  		DimensionName: "Inductance",
   305  		Receiver:      "i",
   306  		PrintString:   "H",
   307  		Name:          "Henry",
   308  		TypeComment:   "Inductance represents an electrical inductance in Henry",
   309  		Dimensions: []Dimension{
   310  			{Name: CurrentName, Power: -2},
   311  			{Name: LengthName, Power: 2},
   312  			{Name: MassName, Power: 1},
   313  			{Name: TimeName, Power: -2},
   314  		},
   315  		ErForm: "Inductancer",
   316  	},
   317  	{
   318  		DimensionName: "Power",
   319  		Receiver:      "pw",
   320  		PrintString:   "W",
   321  		Name:          "Watt",
   322  		TypeComment:   "Power represents a power in Watts",
   323  		Dimensions: []Dimension{
   324  			{Name: LengthName, Power: 2},
   325  			{Name: MassName, Power: 1},
   326  			{Name: TimeName, Power: -3},
   327  		},
   328  	},
   329  	{
   330  		DimensionName: "Resistance",
   331  		Receiver:      "r",
   332  		PrintString:   "Ω",
   333  		Name:          "Ohm",
   334  		TypeComment:   "Resistance represents an electrical resistance, impedance or reactance in Ohms",
   335  		Dimensions: []Dimension{
   336  			{Name: CurrentName, Power: -2},
   337  			{Name: LengthName, Power: 2},
   338  			{Name: MassName, Power: 1},
   339  			{Name: TimeName, Power: -3},
   340  		},
   341  		ErForm: "Resistancer",
   342  	},
   343  	{
   344  		DimensionName: "MagneticFlux",
   345  		Receiver:      "m",
   346  		PrintString:   "Wb",
   347  		Name:          "Weber",
   348  		TypeComment:   "MagneticFlux represents a magnetic flux in Weber",
   349  		Dimensions: []Dimension{
   350  			{Name: CurrentName, Power: -1},
   351  			{Name: LengthName, Power: 2},
   352  			{Name: MassName, Power: 1},
   353  			{Name: TimeName, Power: -2},
   354  		},
   355  	},
   356  	{
   357  		DimensionName: "MagneticFluxDensity",
   358  		Receiver:      "m",
   359  		PrintString:   "T",
   360  		Name:          "Tesla",
   361  		TypeComment:   "MagneticFluxDensity represents a magnetic flux density in Tesla",
   362  		Dimensions: []Dimension{
   363  			{Name: CurrentName, Power: -1},
   364  			{Name: MassName, Power: 1},
   365  			{Name: TimeName, Power: -2},
   366  		},
   367  	},
   368  	{
   369  		DimensionName: "Pressure",
   370  		Receiver:      "pr",
   371  		PrintString:   "Pa",
   372  		Name:          "Pascal",
   373  		TypeComment:   "Pressure represents a pressure in Pascals",
   374  		Dimensions: []Dimension{
   375  			{Name: LengthName, Power: -1},
   376  			{Name: MassName, Power: 1},
   377  			{Name: TimeName, Power: -2},
   378  		},
   379  		ErForm: "Pressurer",
   380  	},
   381  	{
   382  		DimensionName: "Torque",
   383  		Receiver:      "t",
   384  		PrintString:   "N m",
   385  		Name:          "Newtonmetre",
   386  		TypeComment:   "Torque represents a torque in Newton metres",
   387  		Dimensions: []Dimension{
   388  			{Name: LengthName, Power: 2},
   389  			{Name: MassName, Power: 1},
   390  			{Name: TimeName, Power: -2},
   391  		},
   392  		ErForm: "Torquer",
   393  	},
   394  	{
   395  		DimensionName: "Velocity",
   396  		Receiver:      "v",
   397  		PrintString:   "m s^-1",
   398  		TypeComment:   "Velocity represents a velocity in metres per second",
   399  		Dimensions: []Dimension{
   400  			{Name: LengthName, Power: 1},
   401  			{Name: TimeName, Power: -1},
   402  		},
   403  	},
   404  	{
   405  		DimensionName: "Voltage",
   406  		Receiver:      "v",
   407  		PrintString:   "V",
   408  		Name:          "Volt",
   409  		TypeComment:   "Voltage represents a voltage in Volts",
   410  		Dimensions: []Dimension{
   411  			{Name: CurrentName, Power: -1},
   412  			{Name: LengthName, Power: 2},
   413  			{Name: MassName, Power: 1},
   414  			{Name: TimeName, Power: -3},
   415  		},
   416  		ErForm: "Voltager",
   417  	},
   418  	{
   419  		DimensionName: "Volume",
   420  		Receiver:      "v",
   421  		PowerOffset:   -3,
   422  		PrintString:   "m^3",
   423  		Name:          "Litre",
   424  		TypeComment:   "Volume represents a volume in cubic metres",
   425  		Dimensions: []Dimension{
   426  			{Name: LengthName, Power: 3},
   427  		},
   428  	},
   429  }
   430  
   431  // Generate generates a file for each of the units
   432  func main() {
   433  	for _, unit := range Units {
   434  		generate(unit)
   435  		generateTest(unit)
   436  	}
   437  }
   438  
   439  const headerTemplate = `// Code generated by "go generate github.com/jingcheng-WU/gonum/unit”; DO NOT EDIT.
   440  
   441  // Copyright ©2014 The Gonum Authors. All rights reserved.
   442  // Use of this source code is governed by a BSD-style
   443  // license that can be found in the LICENSE file.
   444  
   445  package unit
   446  
   447  import (
   448  	"errors"
   449  	"fmt"
   450  	"math"
   451  	{{if .PrintString}}"unicode/utf8"{{end}}
   452  )
   453  
   454  // {{.TypeComment}}.
   455  type {{.DimensionName}} float64
   456  `
   457  
   458  var header = template.Must(template.New("header").Parse(headerTemplate))
   459  
   460  const constTemplate = `
   461  const {{if .ExtraConstant}}({{end}}
   462  	{{.Name}} {{.DimensionName}} = {{if .PowerOffset}} 1e{{.PowerOffset}} {{else}} 1 {{end}}
   463  	{{$name := .Name}}
   464  	{{range .ExtraConstant}} {{.Name}} = {{.Value}}
   465  	{{end}}
   466  {{if .ExtraConstant}}){{end}}
   467  `
   468  
   469  var prefix = template.Must(template.New("prefix").Parse(constTemplate))
   470  
   471  const methodTemplate = `
   472  // Unit converts the {{.DimensionName}} to a *Unit.
   473  func ({{.Receiver}} {{.DimensionName}}) Unit() *Unit {
   474  	return New(float64({{.Receiver}}), Dimensions{
   475  		{{range .Dimensions}} {{.Name}}: {{.Power}},
   476  		{{end}}
   477  		})
   478  }
   479  
   480  // {{.DimensionName}} allows {{.DimensionName}} to implement a {{if .ErForm}}{{.ErForm}}{{else}}{{.DimensionName}}er{{end}} interface.
   481  func ({{.Receiver}} {{.DimensionName}}) {{.DimensionName}}() {{.DimensionName}} {
   482  	return {{.Receiver}}
   483  }
   484  
   485  // From converts the unit into the receiver. From returns an
   486  // error if there is a mismatch in dimension.
   487  func ({{.Receiver}} *{{.DimensionName}}) From(u Uniter) error {
   488  	if !DimensionsMatch(u, {{if .Name}}{{.Name}}{{else}}{{.DimensionName}}(0){{end}}){
   489  		*{{.Receiver}} = {{.DimensionName}}(math.NaN())
   490  		return errors.New("unit: dimension mismatch")
   491  	}
   492  	*{{.Receiver}} = {{.DimensionName}}(u.Unit().Value())
   493  	return nil
   494  }
   495  `
   496  
   497  var methods = template.Must(template.New("methods").Parse(methodTemplate))
   498  
   499  const formatTemplate = `
   500  func ({{.Receiver}} {{.DimensionName}}) Format(fs fmt.State, c rune) {
   501  	switch c {
   502  	case 'v':
   503  		if fs.Flag('#') {
   504  			fmt.Fprintf(fs, "%T(%v)", {{.Receiver}}, float64({{.Receiver}}))
   505  			return
   506  		}
   507  		fallthrough
   508  	case 'e', 'E', 'f', 'F', 'g', 'G':
   509  		p, pOk := fs.Precision()
   510  		w, wOk := fs.Width()
   511  		{{if .PrintString}}const unit = " {{.PrintString}}"
   512  		switch {
   513  		case pOk && wOk:
   514  			fmt.Fprintf(fs, "%*.*"+string(c), pos(w-utf8.RuneCount([]byte(unit))), p, float64({{.Receiver}}))
   515  		case pOk:
   516  			fmt.Fprintf(fs, "%.*"+string(c), p, float64({{.Receiver}}))
   517  		case wOk:
   518  			fmt.Fprintf(fs, "%*"+string(c), pos(w-utf8.RuneCount([]byte(unit))), float64({{.Receiver}}))
   519  		default:
   520  			fmt.Fprintf(fs, "%"+string(c), float64({{.Receiver}}))
   521  		}
   522  		fmt.Fprint(fs, unit)
   523  	default:
   524  		fmt.Fprintf(fs, "%%!%c(%T=%g {{.PrintString}})", c, {{.Receiver}}, float64({{.Receiver}})) {{else}} switch {
   525  		case pOk && wOk:
   526  			fmt.Fprintf(fs, "%*.*"+string(c), w, p, float64({{.Receiver}}))
   527  		case pOk:
   528  			fmt.Fprintf(fs, "%.*"+string(c), p, float64({{.Receiver}}))
   529  		case wOk:
   530  			fmt.Fprintf(fs, "%*"+string(c), w, float64({{.Receiver}}))
   531  		default:
   532  			fmt.Fprintf(fs, "%"+string(c), float64({{.Receiver}}))
   533  		}
   534  	default:
   535  		fmt.Fprintf(fs, "%%!%c(%T=%g)", c, {{.Receiver}}, float64({{.Receiver}})) {{end}}
   536  	}
   537  }
   538  `
   539  
   540  var form = template.Must(template.New("format").Parse(formatTemplate))
   541  
   542  func generate(unit Unit) {
   543  	lowerName := strings.ToLower(unit.DimensionName)
   544  	filename := lowerName + ".go"
   545  	f, err := os.Create(filename)
   546  	if err != nil {
   547  		log.Fatal(err)
   548  	}
   549  	defer f.Close()
   550  
   551  	var buf bytes.Buffer
   552  
   553  	err = header.Execute(&buf, unit)
   554  	if err != nil {
   555  		log.Fatal(err)
   556  	}
   557  
   558  	if unit.Name != "" {
   559  		err = prefix.Execute(&buf, unit)
   560  		if err != nil {
   561  			log.Fatal(err)
   562  		}
   563  	}
   564  
   565  	err = methods.Execute(&buf, unit)
   566  	if err != nil {
   567  		log.Fatal(err)
   568  	}
   569  
   570  	err = form.Execute(&buf, unit)
   571  	if err != nil {
   572  		log.Fatal(err)
   573  	}
   574  
   575  	b, err := format.Source(buf.Bytes())
   576  	if err != nil {
   577  		f.Write(buf.Bytes()) // This is here to debug bad format
   578  		log.Fatalf("error formatting %q: %s", unit.DimensionName, err)
   579  	}
   580  
   581  	f.Write(b)
   582  }
   583  
   584  const testTemplate = `// Code generated by "go generate github.com/jingcheng-WU/gonum/unit; DO NOT EDIT.
   585  
   586  // Copyright ©2019 The Gonum Authors. All rights reserved.
   587  // Use of this source code is governed by a BSD-style
   588  // license that can be found in the LICENSE file.
   589  
   590  package unit
   591  
   592  import (
   593  	"fmt"
   594  	"testing"
   595  )
   596  
   597  func Test{{.DimensionName}}(t *testing.T) {
   598  	t.Parallel()
   599  	for _, value := range []float64{-1, 0, 1} {
   600  		var got {{.DimensionName}}
   601  		err := got.From({{.DimensionName}}(value).Unit())
   602  		if err != nil {
   603  			t.Errorf("unexpected error for %T conversion: %v", got, err)
   604  		}
   605  		if got != {{.DimensionName}}(value) {
   606  			t.Errorf("unexpected result from round trip of %T(%v): got: %v want: %v", got, float64(value), got, value)
   607  		}
   608  		if got != got.{{.DimensionName}}() {
   609  			t.Errorf("unexpected result from self interface method call: got: %#v want: %#v", got, value)
   610  		}
   611  		err = got.From(ether(1))
   612  		if err == nil {
   613  			t.Errorf("expected error for ether to %T conversion", got)
   614  		}
   615  	}
   616  }
   617  
   618  func Test{{.DimensionName}}Format(t *testing.T) {
   619  	t.Parallel()
   620  	for _, test := range []struct{
   621  		value  {{.DimensionName}}
   622  		format string
   623  		want   string
   624  	}{
   625  		{1.23456789, "%v", "1.23456789{{with .PrintString}} {{.}}{{end}}"},
   626  		{1.23456789, "%.1v", "1{{with .PrintString}} {{.}}{{end}}"},
   627  		{1.23456789, "%20.1v", "{{if .PrintString}}{{$s := printf "1 %s" .PrintString}}{{printf "%20s" $s}}{{else}}{{printf "%20s" "1"}}{{end}}"},
   628  		{1.23456789, "%20v", "{{if .PrintString}}{{$s := printf "1.23456789 %s" .PrintString}}{{printf "%20s" $s}}{{else}}{{printf "%20s" "1.23456789"}}{{end}}"},
   629  		{1.23456789, "%1v", "1.23456789{{with .PrintString}} {{.}}{{end}}"},
   630  		{1.23456789, "%#v", "unit.{{.DimensionName}}(1.23456789)"},
   631  		{1.23456789, "%s", "%!s(unit.{{.DimensionName}}=1.23456789{{with .PrintString}} {{.}}{{end}})"},
   632  	} {
   633  		got := fmt.Sprintf(test.format, test.value)
   634  		if got != test.want {
   635  			t.Errorf("Format %q %v: got: %q want: %q", test.format, float64(test.value), got, test.want)
   636  		}
   637  	}
   638  }
   639  `
   640  
   641  var tests = template.Must(template.New("test").Parse(testTemplate))
   642  
   643  func generateTest(unit Unit) {
   644  	lowerName := strings.ToLower(unit.DimensionName)
   645  	filename := lowerName + "_test.go"
   646  	f, err := os.Create(filename)
   647  	if err != nil {
   648  		log.Fatal(err)
   649  	}
   650  	defer f.Close()
   651  
   652  	var buf bytes.Buffer
   653  	err = tests.Execute(&buf, unit)
   654  	if err != nil {
   655  		log.Fatal(err)
   656  	}
   657  
   658  	b, err := format.Source(buf.Bytes())
   659  	if err != nil {
   660  		f.Write(buf.Bytes()) // This is here to debug bad format.
   661  		log.Fatalf("error formatting test for %q: %s", unit.DimensionName, err)
   662  	}
   663  
   664  	f.Write(b)
   665  }