github.com/nats-io/nats-server/v2@v2.11.0-preview.2/conf/parse_test.go (about)

     1  package conf
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  	"reflect"
     8  	"strings"
     9  	"testing"
    10  	"time"
    11  )
    12  
    13  // Test to make sure we get what we expect.
    14  
    15  func test(t *testing.T, data string, ex map[string]any) {
    16  	t.Helper()
    17  	m, err := Parse(data)
    18  	if err != nil {
    19  		t.Fatalf("Received err: %v\n", err)
    20  	}
    21  	if m == nil {
    22  		t.Fatal("Received nil map")
    23  	}
    24  
    25  	if !reflect.DeepEqual(m, ex) {
    26  		t.Fatalf("Not Equal:\nReceived: '%+v'\nExpected: '%+v'\n", m, ex)
    27  	}
    28  }
    29  
    30  func TestSimpleTopLevel(t *testing.T) {
    31  	ex := map[string]any{
    32  		"foo": "1",
    33  		"bar": float64(2.2),
    34  		"baz": true,
    35  		"boo": int64(22),
    36  	}
    37  	test(t, "foo='1'; bar=2.2; baz=true; boo=22", ex)
    38  }
    39  
    40  func TestBools(t *testing.T) {
    41  	ex := map[string]any{
    42  		"foo": true,
    43  	}
    44  	test(t, "foo=true", ex)
    45  	test(t, "foo=TRUE", ex)
    46  	test(t, "foo=true", ex)
    47  	test(t, "foo=yes", ex)
    48  	test(t, "foo=on", ex)
    49  }
    50  
    51  var varSample = `
    52    index = 22
    53    foo = $index
    54  `
    55  
    56  func TestSimpleVariable(t *testing.T) {
    57  	ex := map[string]any{
    58  		"index": int64(22),
    59  		"foo":   int64(22),
    60  	}
    61  	test(t, varSample, ex)
    62  }
    63  
    64  var varNestedSample = `
    65    index = 22
    66    nest {
    67      index = 11
    68      foo = $index
    69    }
    70    bar = $index
    71  `
    72  
    73  func TestNestedVariable(t *testing.T) {
    74  	ex := map[string]any{
    75  		"index": int64(22),
    76  		"nest": map[string]any{
    77  			"index": int64(11),
    78  			"foo":   int64(11),
    79  		},
    80  		"bar": int64(22),
    81  	}
    82  	test(t, varNestedSample, ex)
    83  }
    84  
    85  func TestMissingVariable(t *testing.T) {
    86  	_, err := Parse("foo=$index")
    87  	if err == nil {
    88  		t.Fatalf("Expected an error for a missing variable, got none")
    89  	}
    90  	if !strings.HasPrefix(err.Error(), "variable reference") {
    91  		t.Fatalf("Wanted a variable reference err, got %q\n", err)
    92  	}
    93  }
    94  
    95  func TestEnvVariable(t *testing.T) {
    96  	ex := map[string]any{
    97  		"foo": int64(22),
    98  	}
    99  	evar := "__UNIQ22__"
   100  	os.Setenv(evar, "22")
   101  	defer os.Unsetenv(evar)
   102  	test(t, fmt.Sprintf("foo = $%s", evar), ex)
   103  }
   104  
   105  func TestEnvVariableString(t *testing.T) {
   106  	ex := map[string]any{
   107  		"foo": "xyz",
   108  	}
   109  	evar := "__UNIQ22__"
   110  	os.Setenv(evar, "xyz")
   111  	defer os.Unsetenv(evar)
   112  	test(t, fmt.Sprintf("foo = $%s", evar), ex)
   113  }
   114  
   115  func TestEnvVariableStringStartingWithNumber(t *testing.T) {
   116  	evar := "__UNIQ22__"
   117  	os.Setenv(evar, "3xyz")
   118  	defer os.Unsetenv(evar)
   119  
   120  	_, err := Parse("foo = $%s")
   121  	if err == nil {
   122  		t.Fatalf("Expected err not being able to process string: %v\n", err)
   123  	}
   124  }
   125  
   126  func TestEnvVariableStringStartingWithNumberAndSizeUnit(t *testing.T) {
   127  	ex := map[string]any{
   128  		"foo": "3Gyz",
   129  	}
   130  	evar := "__UNIQ22__"
   131  	os.Setenv(evar, "3Gyz")
   132  	defer os.Unsetenv(evar)
   133  	test(t, fmt.Sprintf("foo = $%s", evar), ex)
   134  }
   135  
   136  func TestEnvVariableStringStartingWithNumberUsingQuotes(t *testing.T) {
   137  	ex := map[string]any{
   138  		"foo": "3xyz",
   139  	}
   140  	evar := "__UNIQ22__"
   141  	os.Setenv(evar, "'3xyz'")
   142  	defer os.Unsetenv(evar)
   143  	test(t, fmt.Sprintf("foo = $%s", evar), ex)
   144  }
   145  
   146  func TestBcryptVariable(t *testing.T) {
   147  	ex := map[string]any{
   148  		"password": "$2a$11$ooo",
   149  	}
   150  	test(t, "password: $2a$11$ooo", ex)
   151  }
   152  
   153  var easynum = `
   154  k = 8k
   155  kb = 4kb
   156  ki = 3ki
   157  kib = 4ki
   158  m = 1m
   159  mb = 2MB
   160  mi = 2Mi
   161  mib = 64MiB
   162  g = 2g
   163  gb = 22GB
   164  gi = 22Gi
   165  gib = 22GiB
   166  tb = 22TB
   167  ti = 22Ti
   168  tib = 22TiB
   169  pb = 22PB
   170  pi = 22Pi
   171  pib = 22PiB
   172  `
   173  
   174  func TestConvenientNumbers(t *testing.T) {
   175  	ex := map[string]any{
   176  		"k":   int64(8 * 1000),
   177  		"kb":  int64(4 * 1024),
   178  		"ki":  int64(3 * 1024),
   179  		"kib": int64(4 * 1024),
   180  		"m":   int64(1000 * 1000),
   181  		"mb":  int64(2 * 1024 * 1024),
   182  		"mi":  int64(2 * 1024 * 1024),
   183  		"mib": int64(64 * 1024 * 1024),
   184  		"g":   int64(2 * 1000 * 1000 * 1000),
   185  		"gb":  int64(22 * 1024 * 1024 * 1024),
   186  		"gi":  int64(22 * 1024 * 1024 * 1024),
   187  		"gib": int64(22 * 1024 * 1024 * 1024),
   188  		"tb":  int64(22 * 1024 * 1024 * 1024 * 1024),
   189  		"ti":  int64(22 * 1024 * 1024 * 1024 * 1024),
   190  		"tib": int64(22 * 1024 * 1024 * 1024 * 1024),
   191  		"pb":  int64(22 * 1024 * 1024 * 1024 * 1024 * 1024),
   192  		"pi":  int64(22 * 1024 * 1024 * 1024 * 1024 * 1024),
   193  		"pib": int64(22 * 1024 * 1024 * 1024 * 1024 * 1024),
   194  	}
   195  	test(t, easynum, ex)
   196  }
   197  
   198  var sample1 = `
   199  foo  {
   200    host {
   201      ip   = '127.0.0.1'
   202      port = 4242
   203    }
   204    servers = [ "a.com", "b.com", "c.com"]
   205  }
   206  `
   207  
   208  func TestSample1(t *testing.T) {
   209  	ex := map[string]any{
   210  		"foo": map[string]any{
   211  			"host": map[string]any{
   212  				"ip":   "127.0.0.1",
   213  				"port": int64(4242),
   214  			},
   215  			"servers": []any{"a.com", "b.com", "c.com"},
   216  		},
   217  	}
   218  	test(t, sample1, ex)
   219  }
   220  
   221  var cluster = `
   222  cluster {
   223    port: 4244
   224  
   225    authorization {
   226      user: route_user
   227      password: top_secret
   228      timeout: 1
   229    }
   230  
   231    # Routes are actively solicited and connected to from this server.
   232    # Other servers can connect to us if they supply the correct credentials
   233    # in their routes definitions from above.
   234  
   235    // Test both styles of comments
   236  
   237    routes = [
   238      nats-route://foo:bar@apcera.me:4245
   239      nats-route://foo:bar@apcera.me:4246
   240    ]
   241  }
   242  `
   243  
   244  func TestSample2(t *testing.T) {
   245  	ex := map[string]any{
   246  		"cluster": map[string]any{
   247  			"port": int64(4244),
   248  			"authorization": map[string]any{
   249  				"user":     "route_user",
   250  				"password": "top_secret",
   251  				"timeout":  int64(1),
   252  			},
   253  			"routes": []any{
   254  				"nats-route://foo:bar@apcera.me:4245",
   255  				"nats-route://foo:bar@apcera.me:4246",
   256  			},
   257  		},
   258  	}
   259  
   260  	test(t, cluster, ex)
   261  }
   262  
   263  var sample3 = `
   264  foo  {
   265    expr = '(true == "false")'
   266    text = 'This is a multi-line
   267  text block.'
   268  }
   269  `
   270  
   271  func TestSample3(t *testing.T) {
   272  	ex := map[string]any{
   273  		"foo": map[string]any{
   274  			"expr": "(true == \"false\")",
   275  			"text": "This is a multi-line\ntext block.",
   276  		},
   277  	}
   278  	test(t, sample3, ex)
   279  }
   280  
   281  var sample4 = `
   282    array [
   283      { abc: 123 }
   284      { xyz: "word" }
   285    ]
   286  `
   287  
   288  func TestSample4(t *testing.T) {
   289  	ex := map[string]any{
   290  		"array": []any{
   291  			map[string]any{"abc": int64(123)},
   292  			map[string]any{"xyz": "word"},
   293  		},
   294  	}
   295  	test(t, sample4, ex)
   296  }
   297  
   298  var sample5 = `
   299    now = 2016-05-04T18:53:41Z
   300    gmt = false
   301  
   302  `
   303  
   304  func TestSample5(t *testing.T) {
   305  	dt, _ := time.Parse("2006-01-02T15:04:05Z", "2016-05-04T18:53:41Z")
   306  	ex := map[string]any{
   307  		"now": dt,
   308  		"gmt": false,
   309  	}
   310  	test(t, sample5, ex)
   311  }
   312  
   313  func TestIncludes(t *testing.T) {
   314  	ex := map[string]any{
   315  		"listen": "127.0.0.1:4222",
   316  		"authorization": map[string]any{
   317  			"ALICE_PASS": "$2a$10$UHR6GhotWhpLsKtVP0/i6.Nh9.fuY73cWjLoJjb2sKT8KISBcUW5q",
   318  			"BOB_PASS":   "$2a$11$dZM98SpGeI7dCFFGSpt.JObQcix8YHml4TBUZoge9R1uxnMIln5ly",
   319  			"users": []any{
   320  				map[string]any{
   321  					"user":     "alice",
   322  					"password": "$2a$10$UHR6GhotWhpLsKtVP0/i6.Nh9.fuY73cWjLoJjb2sKT8KISBcUW5q"},
   323  				map[string]any{
   324  					"user":     "bob",
   325  					"password": "$2a$11$dZM98SpGeI7dCFFGSpt.JObQcix8YHml4TBUZoge9R1uxnMIln5ly"},
   326  			},
   327  			"timeout": float64(0.5),
   328  		},
   329  	}
   330  
   331  	m, err := ParseFile("simple.conf")
   332  	if err != nil {
   333  		t.Fatalf("Received err: %v\n", err)
   334  	}
   335  	if m == nil {
   336  		t.Fatal("Received nil map")
   337  	}
   338  
   339  	if !reflect.DeepEqual(m, ex) {
   340  		t.Fatalf("Not Equal:\nReceived: '%+v'\nExpected: '%+v'\n", m, ex)
   341  	}
   342  }
   343  
   344  var varIncludedVariablesSample = `
   345  authorization {
   346  
   347    include "./includes/passwords.conf"
   348  
   349    CAROL_PASS: foo
   350  
   351    users = [
   352     {user: alice, password: $ALICE_PASS}
   353     {user: bob,   password: $BOB_PASS}
   354     {user: carol, password: $CAROL_PASS}
   355    ]
   356  }
   357  `
   358  
   359  func TestIncludeVariablesWithChecks(t *testing.T) {
   360  	p, err := parse(varIncludedVariablesSample, "", true)
   361  	if err != nil {
   362  		t.Fatalf("Received err: %v\n", err)
   363  	}
   364  	key := "authorization"
   365  	m, ok := p.mapping[key]
   366  	if !ok {
   367  		t.Errorf("Expected %q to be in the config", key)
   368  	}
   369  	expectKeyVal := func(t *testing.T, m any, expectedKey string, expectedVal string, expectedLine, expectedPos int) {
   370  		t.Helper()
   371  		tk := m.(*token)
   372  		v := tk.Value()
   373  		vv := v.(map[string]any)
   374  		value, ok := vv[expectedKey]
   375  		if !ok {
   376  			t.Errorf("Expected key %q", expectedKey)
   377  		}
   378  		tk, ok = value.(*token)
   379  		if !ok {
   380  			t.Fatalf("Expected token %v", value)
   381  		}
   382  		if tk.Line() != expectedLine {
   383  			t.Errorf("Expected token to be at line %d, got: %d", expectedLine, tk.Line())
   384  		}
   385  		if tk.Position() != expectedPos {
   386  			t.Errorf("Expected token to be at position %d, got: %d", expectedPos, tk.Position())
   387  		}
   388  		v = tk.Value()
   389  		if v != expectedVal {
   390  			t.Errorf("Expected %q, got: %s", expectedVal, v)
   391  		}
   392  	}
   393  	expectKeyVal(t, m, "ALICE_PASS", "$2a$10$UHR6GhotWhpLsKtVP0/i6.Nh9.fuY73cWjLoJjb2sKT8KISBcUW5q", 2, 1)
   394  	expectKeyVal(t, m, "BOB_PASS", "$2a$11$dZM98SpGeI7dCFFGSpt.JObQcix8YHml4TBUZoge9R1uxnMIln5ly", 3, 1)
   395  	expectKeyVal(t, m, "CAROL_PASS", "foo", 6, 3)
   396  }
   397  
   398  func TestParserNoInfiniteLoop(t *testing.T) {
   399  	for _, test := range []string{`A@@Føøøø?˛ø:{øøøø˙˙`, `include "9/�`} {
   400  		if _, err := Parse(test); err == nil {
   401  			t.Fatal("expected an error")
   402  		} else if !strings.Contains(err.Error(), "Unexpected EOF") {
   403  			t.Fatal("expected unexpected eof error")
   404  		}
   405  	}
   406  }
   407  
   408  func TestParseWithNoValuesAreInvalid(t *testing.T) {
   409  	for _, test := range []struct {
   410  		name string
   411  		conf string
   412  		err  string
   413  	}{
   414  		{
   415  			"invalid key without values",
   416  			`aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`,
   417  			"config is invalid (:1:41)",
   418  		},
   419  		{
   420  			"invalid untrimmed key without values",
   421  			`              aaaaaaaaaaaaaaaaaaaaaaaaaaa`,
   422  			"config is invalid (:1:41)",
   423  		},
   424  		{
   425  			"invalid untrimmed key without values",
   426  			`     aaaaaaaaaaaaaaaaaaaaaaaaaaa         `,
   427  			"config is invalid (:1:41)",
   428  		},
   429  		{
   430  			"invalid keys after comments",
   431  			`
   432            		# with comments and no spaces to create key values
   433           		# is also an invalid config.
   434           		aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
   435                          `,
   436  			"config is invalid (:5:25)",
   437  		},
   438  		{
   439  			"comma separated without values are invalid",
   440  			`
   441                          a,a,a,a,a,a,a,a,a,a,a
   442                          `,
   443  			"config is invalid (:3:25)",
   444  		},
   445  	} {
   446  		t.Run(test.name, func(t *testing.T) {
   447  			if _, err := parse(test.conf, "", true); err == nil {
   448  				t.Error("expected an error")
   449  			} else if !strings.Contains(err.Error(), test.err) {
   450  				t.Errorf("expected invalid conf error, got: %v", err)
   451  			}
   452  		})
   453  	}
   454  }
   455  
   456  func TestParseWithNoValuesEmptyConfigsAreValid(t *testing.T) {
   457  	for _, test := range []struct {
   458  		name string
   459  		conf string
   460  	}{
   461  		{
   462  			"empty conf",
   463  			"",
   464  		},
   465  		{
   466  			"empty conf with line breaks",
   467  			`
   468  
   469  
   470                          `,
   471  		},
   472  		{
   473  			"just comments with no values",
   474  			`
   475                          # just comments with no values
   476                          # is still valid.
   477                          `,
   478  		},
   479  	} {
   480  		t.Run(test.name, func(t *testing.T) {
   481  			if _, err := parse(test.conf, "", true); err != nil {
   482  				t.Errorf("unexpected error: %v", err)
   483  			}
   484  		})
   485  	}
   486  }
   487  
   488  func TestParseWithTrailingBracketsAreValid(t *testing.T) {
   489  	for _, test := range []struct {
   490  		name string
   491  		conf string
   492  	}{
   493  		{
   494  			"empty conf",
   495  			"{}",
   496  		},
   497  		{
   498  			"just comments with no values",
   499  			`
   500                          {
   501                          # comments in the body
   502                          }
   503                          `,
   504  		},
   505  		{
   506  			// trailing brackets accidentally can become keys,
   507  			// this is valid since needed to support JSON like configs..
   508  			"trailing brackets after config",
   509  			`
   510                          accounts { users = [{}]}
   511                          }
   512                          `,
   513  		},
   514  		{
   515  			"wrapped in brackets",
   516  			`{
   517                            accounts { users = [{}]}
   518                          }
   519                          `,
   520  		},
   521  	} {
   522  		t.Run(test.name, func(t *testing.T) {
   523  			if _, err := parse(test.conf, "", true); err != nil {
   524  				t.Errorf("unexpected error: %v", err)
   525  			}
   526  		})
   527  	}
   528  }
   529  
   530  func TestParseWithNoValuesIncludes(t *testing.T) {
   531  	for _, test := range []struct {
   532  		input    string
   533  		includes map[string]string
   534  		err      string
   535  		linepos  string
   536  	}{
   537  		{
   538  			`# includes
   539  			accounts {
   540                            foo { include 'foo.conf'}
   541                            bar { users = [{user = "bar"}] }
   542                            quux { include 'quux.conf'}
   543                          }
   544                          `,
   545  			map[string]string{
   546  				"foo.conf":  ``,
   547  				"quux.conf": `?????????????`,
   548  			},
   549  			"error parsing include file 'quux.conf', config is invalid",
   550  			"quux.conf:1:1",
   551  		},
   552  		{
   553  			`# includes
   554  			accounts {
   555                            foo { include 'foo.conf'}
   556                            bar { include 'bar.conf'}
   557                            quux { include 'quux.conf'}
   558                          }
   559                          `,
   560  			map[string]string{
   561  				"foo.conf": ``, // Empty configs are ok
   562  				"bar.conf": `AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`,
   563  				"quux.conf": `
   564                                     # just some comments,
   565                                     # and no key values also ok.
   566                                  `,
   567  			},
   568  			"error parsing include file 'bar.conf', config is invalid",
   569  			"bar.conf:1:34",
   570  		},
   571  	} {
   572  		t.Run("", func(t *testing.T) {
   573  			sdir := t.TempDir()
   574  			f, err := os.CreateTemp(sdir, "nats.conf-")
   575  			if err != nil {
   576  				t.Fatal(err)
   577  			}
   578  			if err := os.WriteFile(f.Name(), []byte(test.input), 066); err != nil {
   579  				t.Error(err)
   580  			}
   581  			if test.includes != nil {
   582  				for includeFile, contents := range test.includes {
   583  					inf, err := os.Create(filepath.Join(sdir, includeFile))
   584  					if err != nil {
   585  						t.Fatal(err)
   586  					}
   587  					if err := os.WriteFile(inf.Name(), []byte(contents), 066); err != nil {
   588  						t.Error(err)
   589  					}
   590  				}
   591  			}
   592  			if _, err := parse(test.input, f.Name(), true); err == nil {
   593  				t.Error("expected an error")
   594  			} else if !strings.Contains(err.Error(), test.err) || !strings.Contains(err.Error(), test.linepos) {
   595  				t.Errorf("expected invalid conf error, got: %v", err)
   596  			}
   597  		})
   598  	}
   599  }
   600  
   601  func TestJSONParseCompat(t *testing.T) {
   602  	for _, test := range []struct {
   603  		name     string
   604  		input    string
   605  		includes map[string]string
   606  		expected map[string]any
   607  	}{
   608  		{
   609  			"JSON with nested blocks",
   610  			`
   611                          {
   612                            "http_port": 8227,
   613                            "port": 4227,
   614                            "write_deadline": "1h",
   615                            "cluster": {
   616                              "port": 6222,
   617                              "routes": [
   618                                "nats://127.0.0.1:4222",
   619                                "nats://127.0.0.1:4223",
   620                                "nats://127.0.0.1:4224"
   621                              ]
   622                            }
   623                          }
   624                          `,
   625  			nil,
   626  			map[string]any{
   627  				"http_port":      int64(8227),
   628  				"port":           int64(4227),
   629  				"write_deadline": "1h",
   630  				"cluster": map[string]any{
   631  					"port": int64(6222),
   632  					"routes": []any{
   633  						"nats://127.0.0.1:4222",
   634  						"nats://127.0.0.1:4223",
   635  						"nats://127.0.0.1:4224",
   636  					},
   637  				},
   638  			},
   639  		},
   640  		{
   641  			"JSON with nested blocks",
   642  			`{
   643                            "jetstream": {
   644                              "store_dir": "/tmp/nats"
   645                              "max_mem": 1000000,
   646                            },
   647                            "port": 4222,
   648                            "server_name": "nats1"
   649                          }
   650                          `,
   651  			nil,
   652  			map[string]any{
   653  				"jetstream": map[string]any{
   654  					"store_dir": "/tmp/nats",
   655  					"max_mem":   int64(1_000_000),
   656  				},
   657  				"port":        int64(4222),
   658  				"server_name": "nats1",
   659  			},
   660  		},
   661  		{
   662  			"JSON empty object in one line",
   663  			`{}`,
   664  			nil,
   665  			map[string]any{},
   666  		},
   667  		{
   668  			"JSON empty object with line breaks",
   669  			`
   670                          {
   671                          }
   672                          `,
   673  			nil,
   674  			map[string]any{},
   675  		},
   676  		{
   677  			"JSON includes",
   678  			`
   679                          accounts {
   680                            foo  { include 'foo.json'  }
   681                            bar  { include 'bar.json'  }
   682                            quux { include 'quux.json' }
   683                          }
   684                          `,
   685  			map[string]string{
   686  				"foo.json": `{ "users": [ {"user": "foo"} ] }`,
   687  				"bar.json": `{
   688                                    "users": [ {"user": "bar"} ]
   689                                  }`,
   690  				"quux.json": `{}`,
   691  			},
   692  			map[string]any{
   693  				"accounts": map[string]any{
   694  					"foo": map[string]any{
   695  						"users": []any{
   696  							map[string]any{
   697  								"user": "foo",
   698  							},
   699  						},
   700  					},
   701  					"bar": map[string]any{
   702  						"users": []any{
   703  							map[string]any{
   704  								"user": "bar",
   705  							},
   706  						},
   707  					},
   708  					"quux": map[string]any{},
   709  				},
   710  			},
   711  		},
   712  	} {
   713  		t.Run(test.name, func(t *testing.T) {
   714  			sdir := t.TempDir()
   715  			f, err := os.CreateTemp(sdir, "nats.conf-")
   716  			if err != nil {
   717  				t.Fatal(err)
   718  			}
   719  			if err := os.WriteFile(f.Name(), []byte(test.input), 066); err != nil {
   720  				t.Error(err)
   721  			}
   722  			if test.includes != nil {
   723  				for includeFile, contents := range test.includes {
   724  					inf, err := os.Create(filepath.Join(sdir, includeFile))
   725  					if err != nil {
   726  						t.Fatal(err)
   727  					}
   728  					if err := os.WriteFile(inf.Name(), []byte(contents), 066); err != nil {
   729  						t.Error(err)
   730  					}
   731  				}
   732  			}
   733  			m, err := ParseFile(f.Name())
   734  			if err != nil {
   735  				t.Fatalf("Unexpected error: %v", err)
   736  			}
   737  			if !reflect.DeepEqual(m, test.expected) {
   738  				t.Fatalf("Not Equal:\nReceived: '%+v'\nExpected: '%+v'\n", m, test.expected)
   739  			}
   740  		})
   741  	}
   742  }