github.com/tiagovtristao/plz@v13.4.0+incompatible/tools/build_langserver/langserver/analyzer_test.go (about)

     1  package langserver
     2  
     3  import (
     4  	"context"
     5  
     6  	"path"
     7  	"path/filepath"
     8  	"testing"
     9  
    10  	"github.com/thought-machine/please/src/core"
    11  	"github.com/thought-machine/please/src/parse/asp"
    12  	"github.com/thought-machine/please/src/parse/rules"
    13  	"github.com/thought-machine/please/tools/build_langserver/lsp"
    14  
    15  	"github.com/stretchr/testify/assert"
    16  	"strings"
    17  )
    18  
    19  func TestNewAnalyzer(t *testing.T) {
    20  	a, err := newAnalyzer()
    21  	assert.Equal(t, err, nil)
    22  
    23  	assert.NotEqual(t, nil, a.BuiltIns)
    24  
    25  	goLibrary := a.BuiltIns["go_library"]
    26  	assert.Equal(t, 15, len(goLibrary.ArgMap))
    27  	assert.Equal(t, true, goLibrary.ArgMap["name"].Required)
    28  
    29  	// check preloadBuildDefs has being loaded
    30  	goBinData := a.BuiltIns["go_bindata"]
    31  	assert.Equal(t, 10, len(goBinData.ArgMap))
    32  	assert.Equal(t, true, goBinData.ArgMap["name"].Required)
    33  	assert.Equal(t, "input_dir=None", goBinData.ArgMap["input_dir"].Repr)
    34  
    35  	// Ensure private funcDefs are not loaded
    36  	for name := range a.BuiltIns {
    37  		assert.False(t, strings.HasPrefix(name, "_"))
    38  	}
    39  
    40  	// Check for methods map
    41  	_, ok := a.Attributes["str"]
    42  	assert.True(t, ok)
    43  }
    44  
    45  func TestAspStatementFromFile(t *testing.T) {
    46  	a, err := newAnalyzer()
    47  	assert.Equal(t, err, nil)
    48  
    49  	filePath := "tools/build_langserver/langserver/test_data/example.build"
    50  	a.State.Config.Parse.BuildFileName = append(a.State.Config.Parse.BuildFileName, "example.build")
    51  	uri := lsp.DocumentURI("file://" + filePath)
    52  
    53  	stmts, err := a.AspStatementFromFile(uri)
    54  	assert.Equal(t, err, nil)
    55  	assert.Equal(t, stmts[0].Ident.Name, "go_library")
    56  
    57  	assert.Equal(t, stmts[1].Ident.Name, "go_test")
    58  }
    59  
    60  func TestNewRuleDef(t *testing.T) {
    61  	a, err := newAnalyzer()
    62  	assert.Equal(t, err, nil)
    63  
    64  	// Test header the definition for build_rule
    65  	expected := "def go_library(name:str, srcs:list, asm_srcs:list=None, hdrs:list=None, out:str=None, deps:list=[],\n" +
    66  		"               visibility:list=None, test_only:bool&testonly=False, complete:bool=True, cover:bool=True,\n" +
    67  		"               filter_srcs:bool=True)"
    68  
    69  	ruleContent := rules.MustAsset("go_rules.build_defs")
    70  
    71  	statements, err := a.parser.ParseData(ruleContent, "go_rules.build_defs")
    72  	assert.Equal(t, err, nil)
    73  
    74  	stmt := getStatementByName(statements, "go_library")
    75  	ruleDef := newRuleDef(string(ruleContent), stmt)
    76  
    77  	assert.Equal(t, ruleDef.Header, expected)
    78  	assert.Equal(t, len(ruleDef.Arguments), len(ruleDef.ArgMap))
    79  	assert.Equal(t, false, ruleDef.ArgMap["_link_private"].Required)
    80  	assert.Equal(t, true, ruleDef.ArgMap["name"].Required)
    81  	assert.Equal(t, ruleDef.ArgMap["visibility"].Definition,
    82  		"visibility required:false, type:list")
    83  	assert.Equal(t, ruleDef.ArgMap["visibility"].Argument.Name, "visibility")
    84  	assert.Equal(t, ruleDef.ArgMap["name"].Argument.Name, "name")
    85  	assert.Equal(t, ruleDef.ArgMap["_link_private"].Argument.Name, "_link_private")
    86  
    87  	// Test header for len()
    88  	ruleContent = rules.MustAsset("builtins.build_defs")
    89  
    90  	statements, err = a.parser.ParseData(ruleContent, "builtins.build_defs")
    91  	assert.Equal(t, err, nil)
    92  
    93  	stmt = getStatementByName(statements, "len")
    94  	ruleDef = newRuleDef(string(ruleContent), stmt)
    95  
    96  	assert.Equal(t, ruleDef.Header, "def len(obj)")
    97  	assert.Equal(t, len(ruleDef.ArgMap), 1)
    98  	assert.Equal(t, true, ruleDef.ArgMap["obj"].Required)
    99  	assert.Equal(t, ruleDef.ArgMap["obj"].Definition, "obj required:true")
   100  	assert.Equal(t, "obj", ruleDef.ArgMap["obj"].Repr)
   101  	assert.Equal(t, ruleDef.ArgMap["obj"].Argument.Name, "obj")
   102  
   103  	// Test header for a string function, startswith()
   104  	stmt = getStatementByName(statements, "startswith")
   105  	ruleDef = newRuleDef(string(ruleContent), stmt)
   106  
   107  	assert.Equal(t, ruleDef.Header, "str.startswith(s:str)")
   108  	assert.Equal(t, len(ruleDef.ArgMap), 1)
   109  	assert.Equal(t, true, ruleDef.ArgMap["s"].Required)
   110  	assert.Equal(t, "s:str", ruleDef.ArgMap["s"].Repr)
   111  
   112  	// Test header for a string function, format()
   113  	stmt = getStatementByName(statements, "format")
   114  	ruleDef = newRuleDef(string(ruleContent), stmt)
   115  
   116  	assert.Equal(t, ruleDef.Header, "str.format()")
   117  	assert.Equal(t, len(ruleDef.ArgMap), 0)
   118  	assert.Equal(t, ruleDef.Object, "str")
   119  
   120  	// Test header for a config function, setdefault()
   121  	stmt = getStatementByName(statements, "setdefault")
   122  	ruleDef = newRuleDef(string(ruleContent), stmt)
   123  
   124  	assert.Equal(t, ruleDef.Header, "config.setdefault(key:str, default=None)")
   125  	assert.Equal(t, 2, len(ruleDef.ArgMap))
   126  	assert.Equal(t, false, ruleDef.ArgMap["default"].Required)
   127  }
   128  
   129  func TestGetArgString(t *testing.T) {
   130  	argWithVal := asp.Argument{
   131  		Name: "mystring",
   132  		Type: []string{"string", "list"},
   133  		Value: &asp.Expression{
   134  			Optimised: &asp.OptimisedExpression{
   135  				Local: "None",
   136  			},
   137  		},
   138  	}
   139  	assert.Equal(t, getArgString(argWithVal), "mystring required:false, type:string|list")
   140  
   141  	argWithoutVal := asp.Argument{
   142  		Name: "name",
   143  		Type: []string{"string"},
   144  	}
   145  	assert.Equal(t, getArgString(argWithoutVal), "name required:true, type:string")
   146  }
   147  
   148  func TestBuildLabelFromString(t *testing.T) {
   149  	a, err := newAnalyzer()
   150  	assert.Equal(t, err, nil)
   151  
   152  	ctx := context.Background()
   153  	filePath := "tools/build_langserver/langserver/test_data/example.build"
   154  	uri := lsp.DocumentURI("file://" + filePath)
   155  
   156  	a.State.Config.Parse.BuildFileName = append(a.State.Config.Parse.BuildFileName, "example.build")
   157  
   158  	// Test case for regular and complete BuildLabel path
   159  	label, err := a.BuildLabelFromString(ctx, uri, "//third_party/go:jsonrpc2")
   160  	expectedContent := "go_get(\n" +
   161  		"    name = \"jsonrpc2\",\n" +
   162  		"    get = \"github.com/sourcegraph/jsonrpc2\",\n" +
   163  		"    revision = \"549eb959f029d014d623104d40ab966d159a92de\",\n" +
   164  		")"
   165  	assert.Equal(t, err, nil)
   166  	assert.Equal(t, path.Join(core.RepoRoot, "third_party/go/BUILD"), label.Path)
   167  	assert.Equal(t, "jsonrpc2", label.Name)
   168  	assert.Equal(t, expectedContent, label.BuildDef.Content)
   169  	assert.Equal(t, `BUILD Label: //third_party/go:jsonrpc2`, label.Definition)
   170  
   171  	// Test case for relative BuildLabel path
   172  	label, err = a.BuildLabelFromString(ctx, uri, ":langserver")
   173  	expectedContent = "go_library(\n" +
   174  		"    name = \"langserver\",\n" +
   175  		"    srcs = glob(\n" +
   176  		"        [\"*.go\"],\n" +
   177  		"        exclude = [\"*_test.go\"],\n" +
   178  		"    ),\n" +
   179  		"    visibility = [\"//tools/build_langserver/...\", \"//src/core\"],\n" +
   180  		"    deps = [\n" +
   181  		"        \"//src/core\",\n" +
   182  		"        \"//src/fs\",\n" +
   183  		"        \"//src/parse\",\n" +
   184  		"        \"//src/parse/asp\",\n" +
   185  		"        \"//src/parse/rules\",\n" +
   186  		"        \"//third_party/go:jsonrpc2\",\n" +
   187  		"        \"//third_party/go:logging\",\n" +
   188  		"        \"//tools/build_langserver/lsp\",\n" +
   189  		"    ],\n" +
   190  		")"
   191  	absPath, err := filepath.Abs(filePath)
   192  	assert.Equal(t, err, nil)
   193  	assert.Equal(t, absPath, label.Path)
   194  	assert.Equal(t, "langserver", label.Name)
   195  	assert.Equal(t, expectedContent, label.BuildDef.Content)
   196  
   197  	// Test case for Allsubpackage BuildLabels: "//src/parse/..."
   198  	label, err = a.BuildLabelFromString(ctx,
   199  		uri, "//src/parse/...")
   200  	assert.Equal(t, err, nil)
   201  	assert.True(t, nil == label.BuildDef)
   202  	assert.Equal(t, "BuildLabel includes all subpackages in path: "+path.Join(core.RepoRoot, "src/parse"),
   203  		label.Definition)
   204  
   205  	// Test case for All targets in a BUILD file: "//src/parse:all"
   206  	label, err = a.BuildLabelFromString(ctx,
   207  		uri, "//src/parse:all")
   208  	assert.Equal(t, err, nil)
   209  	assert.True(t, nil == label.BuildDef)
   210  	assert.Equal(t, "BuildLabel includes all BuildTargets in BUILD file: "+path.Join(core.RepoRoot, "src/parse/BUILD"),
   211  		label.Definition)
   212  
   213  	// Test case for shortended BuildLabel
   214  	label, err = a.BuildLabelFromString(ctx, uri, "//src/core")
   215  	assert.Equal(t, err, nil)
   216  
   217  	label2, err := a.BuildLabelFromString(ctx, uri, "//src/core:core")
   218  	assert.Equal(t, err, nil)
   219  
   220  	assert.Equal(t, label.Definition, label2.Definition)
   221  
   222  	// Test case for subrepo
   223  	label, err = a.BuildLabelFromString(ctx, uri, "@mysubrepo//spam/eggs:ham")
   224  	assert.Equal(t, err, nil)
   225  	assert.True(t, nil == label.BuildDef)
   226  	assert.Equal(t, "Subrepo label: @mysubrepo//spam/eggs:ham", label.Definition)
   227  }
   228  
   229  func TestAnalyzer_BuildLabelFromStringBogusLabel(t *testing.T) {
   230  	a, err := newAnalyzer()
   231  	assert.Equal(t, err, nil)
   232  
   233  	ctx := context.Background()
   234  
   235  	// Ensure we get an error when we pass in a bogus label
   236  	label, err := a.BuildLabelFromString(ctx, exampleBuildURI, "//blah/foo")
   237  	assert.NotEqual(t, err, nil)
   238  	assert.True(t, nil == label)
   239  
   240  	label, err = a.BuildLabelFromString(ctx, exampleBuildURI, "//src/core:blah")
   241  	assert.NotEqual(t, err, nil)
   242  	assert.True(t, nil == label)
   243  }
   244  
   245  func TestAnalyzer_BuildDefFromUri(t *testing.T) {
   246  	ctx := context.Background()
   247  
   248  	buildDefs, err := analyzer.BuildDefsFromURI(ctx, exampleBuildURI)
   249  	assert.Equal(t, err, nil)
   250  	assert.Equal(t, 6, len(buildDefs))
   251  	assert.True(t, StringInSlice(buildDefs["langserver"].Visibility, "//tools/build_langserver/..."))
   252  	assert.True(t, StringInSlice(buildDefs["langserver"].Visibility, "//src/core"))
   253  	t.Log(buildDefs["langserver_test"].Visibility)
   254  
   255  	exampleBuildURI2 := lsp.DocumentURI("file://tools/build_langserver/langserver/test_data/example2.build")
   256  	buildDefs, err = analyzer.BuildDefsFromURI(ctx, exampleBuildURI2)
   257  	assert.Equal(t, 2, len(buildDefs))
   258  	assert.True(t, StringInSlice(buildDefs["langserver_test"].Visibility, "PUBLIC"))
   259  }
   260  
   261  func TestAnalyzer_IsBuildFile(t *testing.T) {
   262  	a, err := newAnalyzer()
   263  	assert.Equal(t, err, nil)
   264  
   265  	uri := lsp.DocumentURI("file://tools/build_langserver/langserver/test_data/example.build")
   266  
   267  	assert.False(t, a.IsBuildFile(uri))
   268  
   269  	a.State.Config.Parse.BuildFileName = append(a.State.Config.Parse.BuildFileName, "example.build")
   270  	assert.True(t, a.IsBuildFile(uri))
   271  }
   272  
   273  func TestAnalyzer_VariableFromContentGLOBAL(t *testing.T) {
   274  	a, err := newAnalyzer()
   275  	assert.Equal(t, err, nil)
   276  	pos := &lsp.Position{Line: 5, Character: 0}
   277  
   278  	// Tests for string variables
   279  	vars := a.VariablesFromContent(`my_str = "my str"`+"   \n"+
   280  		`another_str = ""`+"\n   "+`more_empty = ''`, pos)
   281  	assert.Equal(t, "my_str", vars["my_str"].Name)
   282  	assert.Equal(t, "another_str", vars["another_str"].Name)
   283  	assert.Equal(t, "more_empty", vars["more_empty"].Name)
   284  	for _, v := range vars {
   285  		assert.Equal(t, "str", v.Type)
   286  	}
   287  
   288  	vars = a.VariablesFromContent(`fstr = f"blah"`, pos)
   289  	assert.Equal(t, "str", vars["fstr"].Type)
   290  
   291  	// Tests for int variables
   292  	vars = a.VariablesFromContent(`my_int = 34`, pos)
   293  	assert.Equal(t, "my_int", vars["my_int"].Name)
   294  	assert.Equal(t, "int", vars["my_int"].Type)
   295  
   296  	// Tests for list variables
   297  	vars = a.VariablesFromContent(`my_list = []`, pos)
   298  	assert.Equal(t, "my_list", vars["my_list"].Name)
   299  	assert.Equal(t, "list", vars["my_list"].Type)
   300  
   301  	vars = a.VariablesFromContent(`my_list2 = [1, 2, 3]`, pos)
   302  	assert.Equal(t, "my_list2", vars["my_list2"].Name)
   303  	assert.Equal(t, "list", vars["my_list2"].Type)
   304  
   305  	// Tests for dict variables
   306  	vars = a.VariablesFromContent(`my_dict = {'foo': 1, 'bar': 3}`, pos)
   307  	assert.Equal(t, "my_dict", vars["my_dict"].Name)
   308  	assert.Equal(t, "dict", vars["my_dict"].Type)
   309  
   310  	// Test for calls
   311  	vars = a.VariablesFromContent(`my_call = go_library()`, pos)
   312  	assert.Equal(t, "", vars["my_call"].Type)
   313  
   314  	// Test for reassigning variable
   315  	vars = a.VariablesFromContent(`foo = "hello"`+"\n"+`foo = 90`, pos)
   316  	assert.Equal(t, "int", vars["foo"].Type)
   317  }
   318  
   319  func TestAnalyzer_GetSubinclude(t *testing.T) {
   320  	a, err := newAnalyzer()
   321  	assert.Equal(t, err, nil)
   322  	ctx := context.Background()
   323  
   324  	stmts, err := a.AspStatementFromFile(subincludeURI)
   325  	assert.NoError(t, err)
   326  
   327  	subinclude := a.GetSubinclude(ctx, stmts, subincludeURI)
   328  	assert.Equal(t, len(subinclude), 1)
   329  	_, ok := subinclude["plz_e2e_test"]
   330  	assert.True(t, ok)
   331  }
   332  
   333  /************************
   334   * Helper functions
   335   ************************/
   336  func getStatementByName(statements []*asp.Statement, name string) *asp.Statement {
   337  	for _, stmt := range statements {
   338  		if stmt.FuncDef != nil && stmt.FuncDef.Name == name {
   339  			return stmt
   340  		}
   341  	}
   342  	return nil
   343  }