...

Source file src/oss.terrastruct.com/d2/d2ir/compile_test.go

Documentation: oss.terrastruct.com/d2/d2ir

     1  package d2ir_test
     2  
     3  import (
     4  	"fmt"
     5  	"math/big"
     6  	"path/filepath"
     7  	"strings"
     8  	"testing"
     9  
    10  	"oss.terrastruct.com/util-go/assert"
    11  	"oss.terrastruct.com/util-go/diff"
    12  	"oss.terrastruct.com/util-go/mapfs"
    13  
    14  	"oss.terrastruct.com/d2/d2ast"
    15  	"oss.terrastruct.com/d2/d2ir"
    16  	"oss.terrastruct.com/d2/d2parser"
    17  )
    18  
    19  func TestCompile(t *testing.T) {
    20  	t.Parallel()
    21  
    22  	t.Run("fields", testCompileFields)
    23  	t.Run("classes", testCompileClasses)
    24  	t.Run("edges", testCompileEdges)
    25  	t.Run("layers", testCompileLayers)
    26  	t.Run("scenarios", testCompileScenarios)
    27  	t.Run("steps", testCompileSteps)
    28  	t.Run("imports", testCompileImports)
    29  	t.Run("patterns", testCompilePatterns)
    30  	t.Run("filters", testCompileFilters)
    31  }
    32  
    33  type testCase struct {
    34  	name string
    35  	run  func(testing.TB)
    36  }
    37  
    38  func runa(t *testing.T, tca []testCase) {
    39  	for _, tc := range tca {
    40  		tc := tc
    41  		t.Run(tc.name, func(t *testing.T) {
    42  			t.Parallel()
    43  			tc.run(t)
    44  		})
    45  	}
    46  }
    47  
    48  func compile(t testing.TB, text string) (*d2ir.Map, error) {
    49  	t.Helper()
    50  
    51  	d2Path := fmt.Sprintf("%v.d2", t.Name())
    52  	return compileFS(t, d2Path, map[string]string{d2Path: text})
    53  }
    54  
    55  func compileFS(t testing.TB, path string, mfs map[string]string) (*d2ir.Map, error) {
    56  	t.Helper()
    57  
    58  	ast, err := d2parser.Parse(path, strings.NewReader(mfs[path]), nil)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  
    63  	fs, err := mapfs.New(mfs)
    64  	assert.Success(t, err)
    65  	t.Cleanup(func() {
    66  		err = fs.Close()
    67  		assert.Success(t, err)
    68  	})
    69  	m, err := d2ir.Compile(ast, &d2ir.CompileOptions{
    70  		FS: fs,
    71  	})
    72  	if err != nil {
    73  		return nil, err
    74  	}
    75  
    76  	err = diff.TestdataJSON(filepath.Join("..", "testdata", "d2ir", t.Name()), m)
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  	return m, nil
    81  }
    82  
    83  func assertQuery(t testing.TB, n d2ir.Node, nfields, nedges int, primary interface{}, idStr string) d2ir.Node {
    84  	t.Helper()
    85  
    86  	m := n.Map()
    87  	p := n.Primary()
    88  
    89  	var na []d2ir.Node
    90  	if idStr != "" {
    91  		var err error
    92  		na, err = m.QueryAll(idStr)
    93  		assert.Success(t, err)
    94  		assert.NotEqual(t, n, nil)
    95  	} else {
    96  		na = append(na, n)
    97  	}
    98  
    99  	for _, n := range na {
   100  		m = n.Map()
   101  		p = n.Primary()
   102  		assert.Equal(t, nfields, m.FieldCountRecursive())
   103  		assert.Equal(t, nedges, m.EdgeCountRecursive())
   104  		if !makeScalar(p).Equal(makeScalar(primary)) {
   105  			t.Fatalf("expected primary %#v but got %s", primary, p)
   106  		}
   107  	}
   108  
   109  	if len(na) == 0 {
   110  		t.Fatalf("query didn't match anything")
   111  	}
   112  
   113  	return na[0]
   114  }
   115  
   116  func makeScalar(v interface{}) *d2ir.Scalar {
   117  	s := &d2ir.Scalar{}
   118  	switch v := v.(type) {
   119  	case *d2ir.Scalar:
   120  		if v == nil {
   121  			s.Value = &d2ast.Null{}
   122  			return s
   123  		}
   124  		return v
   125  	case bool:
   126  		s.Value = &d2ast.Boolean{
   127  			Value: v,
   128  		}
   129  	case float64:
   130  		bv := &big.Rat{}
   131  		bv.SetFloat64(v)
   132  		s.Value = &d2ast.Number{
   133  			Raw:   fmt.Sprint(v),
   134  			Value: bv,
   135  		}
   136  	case int:
   137  		s.Value = &d2ast.Number{
   138  			Raw:   fmt.Sprint(v),
   139  			Value: big.NewRat(int64(v), 1),
   140  		}
   141  	case string:
   142  		s.Value = d2ast.FlatDoubleQuotedString(v)
   143  	default:
   144  		if v != nil {
   145  			panic(fmt.Sprintf("d2ir: unexpected type to makeScalar: %#v", v))
   146  		}
   147  		s.Value = &d2ast.Null{}
   148  	}
   149  	return s
   150  }
   151  
   152  func testCompileFields(t *testing.T) {
   153  	t.Parallel()
   154  	tca := []testCase{
   155  		{
   156  			name: "root",
   157  			run: func(t testing.TB) {
   158  				m, err := compile(t, `x`)
   159  				assert.Success(t, err)
   160  				assertQuery(t, m, 1, 0, nil, "")
   161  
   162  				assertQuery(t, m, 0, 0, nil, "x")
   163  			},
   164  		},
   165  		{
   166  			name: "label",
   167  			run: func(t testing.TB) {
   168  				m, err := compile(t, `x: yes`)
   169  				assert.Success(t, err)
   170  				assertQuery(t, m, 1, 0, nil, "")
   171  
   172  				assertQuery(t, m, 0, 0, "yes", "x")
   173  			},
   174  		},
   175  		{
   176  			name: "nested",
   177  			run: func(t testing.TB) {
   178  				m, err := compile(t, `x.y: yes`)
   179  				assert.Success(t, err)
   180  				assertQuery(t, m, 2, 0, nil, "")
   181  
   182  				assertQuery(t, m, 1, 0, nil, "x")
   183  				assertQuery(t, m, 0, 0, "yes", "x.y")
   184  			},
   185  		},
   186  		{
   187  			name: "array",
   188  			run: func(t testing.TB) {
   189  				m, err := compile(t, `x: [1;2;3;4]`)
   190  				assert.Success(t, err)
   191  				assertQuery(t, m, 1, 0, nil, "")
   192  
   193  				f := assertQuery(t, m, 0, 0, nil, "x").(*d2ir.Field)
   194  				assert.String(t, `[1; 2; 3; 4]`, f.Composite.String())
   195  			},
   196  		},
   197  		{
   198  			name: "null",
   199  			run: func(t testing.TB) {
   200  				m, err := compile(t, `pq: pq
   201  pq: null`)
   202  				assert.Success(t, err)
   203  				assertQuery(t, m, 0, 0, nil, "")
   204  			},
   205  		},
   206  	}
   207  	runa(t, tca)
   208  	t.Run("primary", func(t *testing.T) {
   209  		t.Parallel()
   210  		tca := []testCase{
   211  			{
   212  				name: "root",
   213  				run: func(t testing.TB) {
   214  					m, err := compile(t, `x: yes { pqrs }`)
   215  					assert.Success(t, err)
   216  					assertQuery(t, m, 2, 0, nil, "")
   217  
   218  					assertQuery(t, m, 1, 0, "yes", "x")
   219  					assertQuery(t, m, 0, 0, nil, "x.pqrs")
   220  				},
   221  			},
   222  			{
   223  				name: "nested",
   224  				run: func(t testing.TB) {
   225  					m, err := compile(t, `x.y: yes { pqrs }`)
   226  					assert.Success(t, err)
   227  					assertQuery(t, m, 3, 0, nil, "")
   228  
   229  					assertQuery(t, m, 2, 0, nil, "x")
   230  					assertQuery(t, m, 1, 0, "yes", "x.y")
   231  					assertQuery(t, m, 0, 0, nil, "x.y.pqrs")
   232  				},
   233  			},
   234  		}
   235  		runa(t, tca)
   236  	})
   237  }
   238  
   239  func testCompileEdges(t *testing.T) {
   240  	t.Parallel()
   241  	tca := []testCase{
   242  		{
   243  			name: "root",
   244  			run: func(t testing.TB) {
   245  				m, err := compile(t, `x -> y`)
   246  				assert.Success(t, err)
   247  				assertQuery(t, m, 2, 1, nil, "")
   248  				assertQuery(t, m, 0, 0, nil, `(x -> y)[0]`)
   249  
   250  				assertQuery(t, m, 0, 0, nil, "x")
   251  				assertQuery(t, m, 0, 0, nil, "y")
   252  			},
   253  		},
   254  		{
   255  			name: "nested",
   256  			run: func(t testing.TB) {
   257  				m, err := compile(t, `x.y -> z.p`)
   258  				assert.Success(t, err)
   259  				assertQuery(t, m, 4, 1, nil, "")
   260  
   261  				assertQuery(t, m, 1, 0, nil, "x")
   262  				assertQuery(t, m, 0, 0, nil, "x.y")
   263  
   264  				assertQuery(t, m, 1, 0, nil, "z")
   265  				assertQuery(t, m, 0, 0, nil, "z.p")
   266  
   267  				assertQuery(t, m, 0, 0, nil, "(x.y -> z.p)[0]")
   268  			},
   269  		},
   270  		{
   271  			name: "underscore",
   272  			run: func(t testing.TB) {
   273  				m, err := compile(t, `p: { _.x -> z }`)
   274  				assert.Success(t, err)
   275  				assertQuery(t, m, 3, 1, nil, "")
   276  
   277  				assertQuery(t, m, 0, 0, nil, "x")
   278  				assertQuery(t, m, 1, 0, nil, "p")
   279  
   280  				assertQuery(t, m, 0, 0, nil, "(x -> p.z)[0]")
   281  			},
   282  		},
   283  		{
   284  			name: "chain",
   285  			run: func(t testing.TB) {
   286  				m, err := compile(t, `a -> b -> c -> d`)
   287  				assert.Success(t, err)
   288  				assertQuery(t, m, 4, 3, nil, "")
   289  
   290  				assertQuery(t, m, 0, 0, nil, "a")
   291  				assertQuery(t, m, 0, 0, nil, "b")
   292  				assertQuery(t, m, 0, 0, nil, "c")
   293  				assertQuery(t, m, 0, 0, nil, "d")
   294  				assertQuery(t, m, 0, 0, nil, "(a -> b)[0]")
   295  				assertQuery(t, m, 0, 0, nil, "(b -> c)[0]")
   296  				assertQuery(t, m, 0, 0, nil, "(c -> d)[0]")
   297  			},
   298  		},
   299  	}
   300  	runa(t, tca)
   301  	t.Run("errs", func(t *testing.T) {
   302  		t.Parallel()
   303  		tca := []testCase{
   304  			{
   305  				name: "bad_edge",
   306  				run: func(t testing.TB) {
   307  					_, err := compile(t, `(x -> y): { p -> q }`)
   308  					assert.ErrorString(t, err, `TestCompile/edges/errs/bad_edge.d2:1:13: cannot create edge inside edge`)
   309  				},
   310  			},
   311  		}
   312  		runa(t, tca)
   313  	})
   314  }
   315  
   316  func testCompileLayers(t *testing.T) {
   317  	t.Parallel()
   318  	tca := []testCase{
   319  		{
   320  			name: "root",
   321  			run: func(t testing.TB) {
   322  				m, err := compile(t, `x -> y
   323  layers: {
   324  	bingo: { p.q.z }
   325  }`)
   326  				assert.Success(t, err)
   327  
   328  				assertQuery(t, m, 7, 1, nil, "")
   329  				assertQuery(t, m, 0, 0, nil, `(x -> y)[0]`)
   330  
   331  				assertQuery(t, m, 0, 0, nil, "x")
   332  				assertQuery(t, m, 0, 0, nil, "y")
   333  
   334  				assertQuery(t, m, 3, 0, nil, "layers.bingo")
   335  			},
   336  		},
   337  	}
   338  	runa(t, tca)
   339  	t.Run("errs", func(t *testing.T) {
   340  		t.Parallel()
   341  		tca := []testCase{
   342  			{
   343  				name: "1/bad_edge",
   344  				run: func(t testing.TB) {
   345  					_, err := compile(t, `layers.x -> layers.y`)
   346  					assert.ErrorString(t, err, `TestCompile/layers/errs/1/bad_edge.d2:1:1: cannot create edges between boards`)
   347  				},
   348  			},
   349  			{
   350  				name: "2/bad_edge",
   351  				run: func(t testing.TB) {
   352  					_, err := compile(t, `layers -> scenarios`)
   353  					assert.ErrorString(t, err, `TestCompile/layers/errs/2/bad_edge.d2:1:1: edge with board keyword alone doesn't make sense`)
   354  				},
   355  			},
   356  			{
   357  				name: "3/bad_edge",
   358  				run: func(t testing.TB) {
   359  					_, err := compile(t, `layers.x.y -> steps.z.p`)
   360  					assert.ErrorString(t, err, `TestCompile/layers/errs/3/bad_edge.d2:1:1: cannot create edges between boards`)
   361  				},
   362  			},
   363  			{
   364  				name: "4/good_edge",
   365  				run: func(t testing.TB) {
   366  					_, err := compile(t, `layers.x.y -> layers.x.y`)
   367  					assert.Success(t, err)
   368  				},
   369  			},
   370  		}
   371  		runa(t, tca)
   372  	})
   373  }
   374  
   375  func testCompileScenarios(t *testing.T) {
   376  	t.Parallel()
   377  	tca := []testCase{
   378  		{
   379  			name: "root",
   380  			run: func(t testing.TB) {
   381  				m, err := compile(t, `x -> y
   382  scenarios: {
   383  	bingo: { p.q.z }
   384  	nuclear: { quiche }
   385  }`)
   386  				assert.Success(t, err)
   387  
   388  				assertQuery(t, m, 13, 3, nil, "")
   389  
   390  				assertQuery(t, m, 0, 0, nil, "x")
   391  				assertQuery(t, m, 0, 0, nil, "y")
   392  				assertQuery(t, m, 0, 0, nil, `(x -> y)[0]`)
   393  
   394  				assertQuery(t, m, 5, 1, nil, "scenarios.bingo")
   395  				assertQuery(t, m, 0, 0, nil, "scenarios.bingo.x")
   396  				assertQuery(t, m, 0, 0, nil, "scenarios.bingo.y")
   397  				assertQuery(t, m, 0, 0, nil, `scenarios.bingo.(x -> y)[0]`)
   398  				assertQuery(t, m, 2, 0, nil, "scenarios.bingo.p")
   399  				assertQuery(t, m, 1, 0, nil, "scenarios.bingo.p.q")
   400  				assertQuery(t, m, 0, 0, nil, "scenarios.bingo.p.q.z")
   401  
   402  				assertQuery(t, m, 3, 1, nil, "scenarios.nuclear")
   403  				assertQuery(t, m, 0, 0, nil, "scenarios.nuclear.x")
   404  				assertQuery(t, m, 0, 0, nil, "scenarios.nuclear.y")
   405  				assertQuery(t, m, 0, 0, nil, `scenarios.nuclear.(x -> y)[0]`)
   406  				assertQuery(t, m, 0, 0, nil, "scenarios.nuclear.quiche")
   407  			},
   408  		},
   409  		{
   410  			name: "edge",
   411  			run: func(t testing.TB) {
   412  				m, err := compile(t, `a -> b
   413  scenarios: {
   414    1: {
   415      (a -> b)[0].style.opacity: 0.1
   416    }
   417  }`)
   418  				assert.Success(t, err)
   419  				assertQuery(t, m, 8, 2, nil, "")
   420  				assertQuery(t, m, 0, 0, nil, "(a -> b)[0]")
   421  			},
   422  		},
   423  		{
   424  			name: "multiple-scenario-map",
   425  			run: func(t testing.TB) {
   426  				m, err := compile(t, `a -> b: { style.opacity: 0.3 }
   427  scenarios: {
   428    1: {
   429      (a -> b)[0].style.opacity: 0.1
   430    }
   431    1: {
   432  	z
   433    }
   434  }`)
   435  				assert.Success(t, err)
   436  				assertQuery(t, m, 11, 2, nil, "")
   437  				assertQuery(t, m, 0, 0, 0.1, "scenarios.1.(a -> b)[0].style.opacity")
   438  			},
   439  		},
   440  	}
   441  	runa(t, tca)
   442  }
   443  
   444  func testCompileSteps(t *testing.T) {
   445  	t.Parallel()
   446  	tca := []testCase{
   447  		{
   448  			name: "root",
   449  			run: func(t testing.TB) {
   450  				m, err := compile(t, `x -> y
   451  steps: {
   452  	bingo: { p.q.z }
   453  	nuclear: { quiche }
   454  }`)
   455  				assert.Success(t, err)
   456  
   457  				assertQuery(t, m, 16, 3, nil, "")
   458  
   459  				assertQuery(t, m, 0, 0, nil, "x")
   460  				assertQuery(t, m, 0, 0, nil, "y")
   461  				assertQuery(t, m, 0, 0, nil, `(x -> y)[0]`)
   462  
   463  				assertQuery(t, m, 5, 1, nil, "steps.bingo")
   464  				assertQuery(t, m, 0, 0, nil, "steps.bingo.x")
   465  				assertQuery(t, m, 0, 0, nil, "steps.bingo.y")
   466  				assertQuery(t, m, 0, 0, nil, `steps.bingo.(x -> y)[0]`)
   467  				assertQuery(t, m, 2, 0, nil, "steps.bingo.p")
   468  				assertQuery(t, m, 1, 0, nil, "steps.bingo.p.q")
   469  				assertQuery(t, m, 0, 0, nil, "steps.bingo.p.q.z")
   470  
   471  				assertQuery(t, m, 6, 1, nil, "steps.nuclear")
   472  				assertQuery(t, m, 0, 0, nil, "steps.nuclear.x")
   473  				assertQuery(t, m, 0, 0, nil, "steps.nuclear.y")
   474  				assertQuery(t, m, 0, 0, nil, `steps.nuclear.(x -> y)[0]`)
   475  				assertQuery(t, m, 2, 0, nil, "steps.nuclear.p")
   476  				assertQuery(t, m, 1, 0, nil, "steps.nuclear.p.q")
   477  				assertQuery(t, m, 0, 0, nil, "steps.nuclear.p.q.z")
   478  				assertQuery(t, m, 0, 0, nil, "steps.nuclear.quiche")
   479  			},
   480  		},
   481  		{
   482  			name: "steps_panic",
   483  			run: func(t testing.TB) {
   484  				_, err := compile(t, `steps: {
   485    shape: sql_table
   486    id: int {constraint: primary_key}
   487  }
   488  scenarios: {
   489    shape: sql_table
   490    hey: int {constraint: primary_key}
   491  }`)
   492  				assert.ErrorString(t, err, `TestCompile/steps/steps_panic.d2:3:3: invalid step
   493  TestCompile/steps/steps_panic.d2:7:3: invalid scenario`)
   494  			},
   495  		},
   496  		{
   497  			name: "recursive",
   498  			run: func(t testing.TB) {
   499  				m, err := compile(t, `x -> y
   500  steps: {
   501  	bingo: { p.q.z }
   502  	nuclear: {
   503  		quiche
   504  		scenarios: {
   505  			bavarian: {
   506  				perseverance
   507  			}
   508  		}
   509  	}
   510  }`)
   511  				assert.Success(t, err)
   512  
   513  				assertQuery(t, m, 25, 4, nil, "")
   514  
   515  				assertQuery(t, m, 0, 0, nil, "x")
   516  				assertQuery(t, m, 0, 0, nil, "y")
   517  				assertQuery(t, m, 0, 0, nil, `(x -> y)[0]`)
   518  
   519  				assertQuery(t, m, 5, 1, nil, "steps.bingo")
   520  				assertQuery(t, m, 0, 0, nil, "steps.bingo.x")
   521  				assertQuery(t, m, 0, 0, nil, "steps.bingo.y")
   522  				assertQuery(t, m, 0, 0, nil, `steps.bingo.(x -> y)[0]`)
   523  				assertQuery(t, m, 2, 0, nil, "steps.bingo.p")
   524  				assertQuery(t, m, 1, 0, nil, "steps.bingo.p.q")
   525  				assertQuery(t, m, 0, 0, nil, "steps.bingo.p.q.z")
   526  
   527  				assertQuery(t, m, 15, 2, nil, "steps.nuclear")
   528  				assertQuery(t, m, 0, 0, nil, "steps.nuclear.x")
   529  				assertQuery(t, m, 0, 0, nil, "steps.nuclear.y")
   530  				assertQuery(t, m, 0, 0, nil, `steps.nuclear.(x -> y)[0]`)
   531  				assertQuery(t, m, 2, 0, nil, "steps.nuclear.p")
   532  				assertQuery(t, m, 1, 0, nil, "steps.nuclear.p.q")
   533  				assertQuery(t, m, 0, 0, nil, "steps.nuclear.p.q.z")
   534  				assertQuery(t, m, 0, 0, nil, "steps.nuclear.quiche")
   535  
   536  				assertQuery(t, m, 7, 1, nil, "steps.nuclear.scenarios.bavarian")
   537  				assertQuery(t, m, 0, 0, nil, "steps.nuclear.scenarios.bavarian.x")
   538  				assertQuery(t, m, 0, 0, nil, "steps.nuclear.scenarios.bavarian.y")
   539  				assertQuery(t, m, 0, 0, nil, `steps.nuclear.scenarios.bavarian.(x -> y)[0]`)
   540  				assertQuery(t, m, 2, 0, nil, "steps.nuclear.scenarios.bavarian.p")
   541  				assertQuery(t, m, 1, 0, nil, "steps.nuclear.scenarios.bavarian.p.q")
   542  				assertQuery(t, m, 0, 0, nil, "steps.nuclear.scenarios.bavarian.p.q.z")
   543  				assertQuery(t, m, 0, 0, nil, "steps.nuclear.scenarios.bavarian.quiche")
   544  				assertQuery(t, m, 0, 0, nil, "steps.nuclear.scenarios.bavarian.perseverance")
   545  			},
   546  		},
   547  	}
   548  	runa(t, tca)
   549  }
   550  
   551  func testCompileClasses(t *testing.T) {
   552  	t.Parallel()
   553  	tca := []testCase{
   554  		{
   555  			name: "basic",
   556  			run: func(t testing.TB) {
   557  				_, err := compile(t, `x
   558  classes: {
   559    mango: {
   560      style.fill: orange
   561    }
   562  }
   563  `)
   564  				assert.Success(t, err)
   565  			},
   566  		},
   567  		{
   568  			name: "nonroot",
   569  			run: func(t testing.TB) {
   570  				_, err := compile(t, `x: {
   571    classes: {
   572      mango: {
   573        style.fill: orange
   574      }
   575    }
   576  }
   577  `)
   578  				assert.ErrorString(t, err, `TestCompile/classes/nonroot.d2:2:3: classes is only allowed at a board root`)
   579  			},
   580  		},
   581  		{
   582  			name: "merge",
   583  			run: func(t testing.TB) {
   584  				m, err := compile(t, `classes: {
   585    mango: {
   586      style.fill: orange
   587  		width: 10
   588    }
   589  }
   590  layers: {
   591    hawaii: {
   592      classes: {
   593        mango: {
   594          width: 9000
   595        }
   596      }
   597    }
   598  }
   599  `)
   600  				assert.Success(t, err)
   601  				assertQuery(t, m, 3, 0, nil, "layers.hawaii.classes.mango")
   602  				assertQuery(t, m, 0, 0, "orange", "layers.hawaii.classes.mango.style.fill")
   603  				assertQuery(t, m, 0, 0, 9000, "layers.hawaii.classes.mango.width")
   604  			},
   605  		},
   606  		{
   607  			name: "nested",
   608  			run: func(t testing.TB) {
   609  				m, err := compile(t, `classes: {
   610    mango: {
   611      style.fill: orange
   612    }
   613  }
   614  layers: {
   615    hawaii: {
   616  		layers: {
   617        maui: {
   618          x
   619        }
   620      }
   621    }
   622  }
   623  `)
   624  				assert.Success(t, err)
   625  				assertQuery(t, m, 3, 0, nil, "layers.hawaii.classes")
   626  				assertQuery(t, m, 3, 0, nil, "layers.hawaii.layers.maui.classes")
   627  			},
   628  		},
   629  		{
   630  			name: "inherited",
   631  			run: func(t testing.TB) {
   632  				m, err := compile(t, `classes: {
   633    mango: {
   634      style.fill: orange
   635    }
   636  }
   637  scenarios: {
   638    hawaii: {
   639  		steps: {
   640        1: {
   641          classes: {
   642            cherry: {
   643              style.fill: red
   644            }
   645          }
   646          x
   647        }
   648        2: {
   649          y
   650        }
   651        3: {
   652          classes: {
   653            cherry: {
   654              style.fill: blue
   655            }
   656          }
   657          y
   658        }
   659        4: {
   660          layers: {
   661            deep: {
   662              x
   663            }
   664          }
   665          x
   666        }
   667      }
   668    }
   669  }
   670  `)
   671  				assert.Success(t, err)
   672  				assertQuery(t, m, 3, 0, nil, "scenarios.hawaii.classes")
   673  				assertQuery(t, m, 2, 0, nil, "scenarios.hawaii.steps.2.classes.mango")
   674  				assertQuery(t, m, 2, 0, nil, "scenarios.hawaii.steps.2.classes.cherry")
   675  				assertQuery(t, m, 0, 0, "blue", "scenarios.hawaii.steps.4.classes.cherry.style.fill")
   676  				assertQuery(t, m, 0, 0, "blue", "scenarios.hawaii.steps.4.layers.deep.classes.cherry.style.fill")
   677  			},
   678  		},
   679  		{
   680  			name: "layer-modify",
   681  			run: func(t testing.TB) {
   682  				m, err := compile(t, `classes: {
   683    orb: {
   684      style.fill: yellow
   685    }
   686  }
   687  layers: {
   688    x: {
   689      classes.orb.style.stroke: red
   690    }
   691  }
   692  `)
   693  				assert.Success(t, err)
   694  				assertQuery(t, m, 0, 0, "yellow", "layers.x.classes.orb.style.fill")
   695  				assertQuery(t, m, 0, 0, "red", "layers.x.classes.orb.style.stroke")
   696  			},
   697  		},
   698  	}
   699  	runa(t, tca)
   700  }
   701  

View as plain text