github.com/joomcode/cue@v0.4.4-0.20221111115225-539fe3512047/cue/ast/astutil/apply_test.go (about)

     1  // Copyright 2019 CUE Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package astutil_test
    16  
    17  import (
    18  	"strings"
    19  	"testing"
    20  
    21  	"github.com/stretchr/testify/assert"
    22  	"github.com/stretchr/testify/require"
    23  
    24  	"github.com/joomcode/cue/cue/ast"
    25  	"github.com/joomcode/cue/cue/ast/astutil"
    26  	"github.com/joomcode/cue/cue/format"
    27  	"github.com/joomcode/cue/cue/parser"
    28  	"github.com/joomcode/cue/cue/token"
    29  )
    30  
    31  func TestApply(t *testing.T) {
    32  	testCases := []struct {
    33  		name   string
    34  		in     string
    35  		out    string
    36  		before func(astutil.Cursor) bool
    37  		after  func(astutil.Cursor) bool
    38  	}{{
    39  		// This should pass
    40  	}, {
    41  		name: "insert before",
    42  		in: `
    43  		// foo is a
    44  		foo: {
    45  			a: 3
    46  		}
    47  		`,
    48  		out: `
    49  iam: new
    50  
    51  // foo is a
    52  foo: {
    53  	iam: new
    54  	a:   3
    55  }
    56  `,
    57  		before: func(c astutil.Cursor) bool {
    58  			switch c.Node().(type) {
    59  			case *ast.Field:
    60  				c.InsertBefore(&ast.Field{
    61  					Label: ast.NewIdent("iam"),
    62  					Value: ast.NewIdent("new"),
    63  				})
    64  			}
    65  			return true
    66  		},
    67  	}, {
    68  		name: "insert after",
    69  		in: `
    70  			foo: {
    71  				a: 3 @test()
    72  			}
    73  			`,
    74  		out: `
    75  foo: {
    76  	a:   3 @test()
    77  	iam: new
    78  }
    79  iam: new
    80  `,
    81  		before: func(c astutil.Cursor) bool {
    82  			switch c.Node().(type) {
    83  			case *ast.Field:
    84  				c.InsertAfter(&ast.Field{
    85  					Label: ast.NewIdent("iam"),
    86  					Value: ast.NewIdent("new"),
    87  				})
    88  			}
    89  			return true
    90  		},
    91  	}, {
    92  		name: "insert after recursive",
    93  		in: `
    94  			foo: {
    95  				a: 3 @test()
    96  			}
    97  			`,
    98  		out: `
    99  foo: {
   100  	a: 3 @test()
   101  	iam: {
   102  		here:  new
   103  		there: new
   104  	}
   105  	everywhere: new
   106  }
   107  iam: {
   108  	here:  new
   109  	there: new
   110  }
   111  everywhere: new
   112  	`,
   113  		before: func(c astutil.Cursor) bool {
   114  			switch x := c.Node().(type) {
   115  			case *ast.Field:
   116  				switch x.Label.(*ast.Ident).Name {
   117  				default:
   118  					c.InsertAfter(astutil.ApplyRecursively(&ast.Field{
   119  						Label: ast.NewIdent("iam"),
   120  						Value: ast.NewStruct(ast.NewIdent("here"), ast.NewIdent("new")),
   121  					}))
   122  				case "iam":
   123  					c.InsertAfter(&ast.Field{
   124  						Label: ast.NewIdent("everywhere"),
   125  						Value: ast.NewIdent("new"),
   126  					})
   127  				case "here":
   128  					c.InsertAfter(&ast.Field{
   129  						Label: ast.NewIdent("there"),
   130  						Value: ast.NewIdent("new"),
   131  					})
   132  				case "everywhere":
   133  				}
   134  			}
   135  			return true
   136  		}}, {
   137  		name: "templates",
   138  		in: `
   139  				foo: {
   140  					a: [string]: c: 3
   141  				}
   142  				`,
   143  		out: `
   144  foo: {
   145  	a: [string]: {
   146  		c:   3
   147  		iam: new
   148  	}
   149  }
   150  	`,
   151  		before: func(c astutil.Cursor) bool {
   152  			switch x := c.Node().(type) {
   153  			case *ast.Field:
   154  				if _, ok := x.Value.(*ast.StructLit); !ok {
   155  					c.InsertAfter(&ast.Field{
   156  						Label: ast.NewIdent("iam"),
   157  						Value: ast.NewIdent("new"),
   158  					})
   159  				}
   160  			}
   161  			return true
   162  		},
   163  	}, {
   164  		name: "replace",
   165  		in: `
   166  		// keep comment
   167  		a: "string" // and this one
   168  		b: 3
   169  		c: [ 1, 2, 8, 4 ]
   170  		d: "\(foo) is \(0)"
   171  		`,
   172  		out: `
   173  // keep comment
   174  a: s // and this one
   175  b: 4
   176  c: [4, 4, 4, 4]
   177  d: "\(foo) is \(4)"
   178  `,
   179  		before: func(c astutil.Cursor) bool {
   180  			switch x := c.Node().(type) {
   181  			case *ast.BasicLit:
   182  				switch x.Kind {
   183  				case token.STRING:
   184  					if c.Index() < 0 {
   185  						c.Replace(ast.NewIdent("s"))
   186  					}
   187  				case token.INT:
   188  					c.Replace(ast.NewLit(token.INT, "4"))
   189  				}
   190  			}
   191  			return true
   192  		},
   193  	}, {
   194  		name: "delete",
   195  		in: `
   196  		z: 0
   197  		a: "foo"
   198  		b: 3
   199  		b: "bar"
   200  		c: 2
   201  		`,
   202  		out: `
   203  a: "foo"
   204  b: "bar"
   205  	`,
   206  		before: func(c astutil.Cursor) bool {
   207  			f, ok := c.Node().(*ast.Field)
   208  			if !ok {
   209  				return true
   210  			}
   211  			switch x := f.Value.(type) {
   212  			case *ast.BasicLit:
   213  				switch x.Kind {
   214  				case token.INT:
   215  					c.Delete()
   216  				}
   217  			}
   218  			return true
   219  		},
   220  	}, {
   221  		name: "comments",
   222  		in: `
   223  		// test
   224  		a: "string"
   225  		`,
   226  		out: `
   227  // 1, 2, 3
   228  a: "string"
   229  	`,
   230  		before: func(c astutil.Cursor) bool {
   231  			switch c.Node().(type) {
   232  			case *ast.Comment:
   233  				c.Replace(&ast.Comment{Text: "// 1, 2, 3"})
   234  			}
   235  			return true
   236  		},
   237  	}, {
   238  		name: "comments after",
   239  		in: `
   240  	// test
   241  	a: "string"
   242  			`,
   243  		out: `
   244  // 1, 2, 3
   245  a: "string"
   246  		`,
   247  		after: func(c astutil.Cursor) bool {
   248  			switch c.Node().(type) {
   249  			case *ast.Comment:
   250  				c.Replace(&ast.Comment{Text: "// 1, 2, 3"})
   251  			}
   252  			return true
   253  		},
   254  	}, {
   255  		name: "imports add",
   256  		in: `
   257  a: "string"
   258  			`,
   259  		out: `
   260  import list6c6973 "list"
   261  
   262  a: list6c6973
   263  		`,
   264  		after: func(c astutil.Cursor) bool {
   265  			switch c.Node().(type) {
   266  			case *ast.BasicLit:
   267  				c.Replace(c.Import("list"))
   268  			}
   269  			return true
   270  		},
   271  	}, {
   272  		name: "imports add to",
   273  		in: `package foo
   274  
   275  import "math"
   276  
   277  a: 3
   278  				`,
   279  		out: `package foo
   280  
   281  import (
   282  	"math"
   283  	list6c6973 "list"
   284  )
   285  
   286  a: list6c6973
   287  			`,
   288  		after: func(c astutil.Cursor) bool {
   289  			switch x := c.Node().(type) {
   290  			case *ast.BasicLit:
   291  				if x.Kind == token.INT {
   292  					c.Replace(c.Import("list"))
   293  				}
   294  			}
   295  			return true
   296  		},
   297  	}, {
   298  		name: "imports duplicate",
   299  		in: `package foo
   300  
   301  import "list"
   302  
   303  a: 3
   304  				`,
   305  		out: `package foo
   306  
   307  import (
   308  	"list"
   309  	list6c6973 "list"
   310  )
   311  
   312  a: list6c6973
   313  					`,
   314  		after: func(c astutil.Cursor) bool {
   315  			switch x := c.Node().(type) {
   316  			case *ast.BasicLit:
   317  				if x.Kind == token.INT {
   318  					c.Replace(c.Import("list"))
   319  				}
   320  			}
   321  			return true
   322  		},
   323  	}}
   324  	for _, tc := range testCases {
   325  		t.Run(tc.name, func(t *testing.T) {
   326  			f, err := parser.ParseFile(tc.name, tc.in, parser.ParseComments)
   327  			if err != nil {
   328  				t.Fatal(err)
   329  			}
   330  
   331  			n := astutil.Apply(f, tc.before, tc.after)
   332  
   333  			b, err := format.Node(n)
   334  			require.NoError(t, err)
   335  			got := strings.TrimSpace(string(b))
   336  			want := strings.TrimSpace(tc.out)
   337  			assert.Equal(t, want, got)
   338  		})
   339  	}
   340  }