| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package state_test |
| |
| import ( |
| "testing" |
| |
| fidlcommon "fidl-lsp/third_party/common" |
| |
| "fidl-lsp/state" |
| ) |
| |
| func TestLibraryOfFile(t *testing.T) { |
| library, err := state.LibraryOfFile(`library example;`) |
| if err != nil { |
| t.Errorf("error getting library of file: %s", err) |
| } |
| expLibrary := "example" |
| if library.FullyQualifiedName() != expLibrary { |
| t.Errorf("expected library `%s`, got `%s`", expLibrary, library) |
| } |
| |
| library, err = state.LibraryOfFile(` |
| /// Doc comments |
| library fuchsia.foo.bar; |
| struct S {}; |
| `) |
| if err != nil { |
| t.Errorf("error getting library of file: %s", err) |
| } |
| expLibrary = "fuchsia.foo.bar" |
| if library.FullyQualifiedName() != expLibrary { |
| t.Errorf("expected library `%s`, got `%s`", expLibrary, library) |
| } |
| |
| library, err = state.LibraryOfFile(`library .invalid.name;`) |
| if err == nil { |
| t.Error("expect error getting library of file") |
| } |
| |
| library, err = state.LibraryOfFile(`library exampl // Missing semicolon`) |
| if err == nil { |
| t.Error("expect error getting library of file") |
| } |
| } |
| |
| func TestParseLibraryMatch(t *testing.T) { |
| library, ok := state.ParseLibraryMatch(` |
| library example; |
| // ~~~~~~~ expected range |
| `) |
| if !ok { |
| t.Errorf("error getting library of file") |
| } |
| expLibrary := state.LibraryMatch{ |
| Lib: fidlcommon.MustReadLibraryName("example"), |
| Range: state.Range{ |
| Start: state.Position{Line: 1, Character: 8}, |
| End: state.Position{Line: 1, Character: 15}, |
| }, |
| } |
| if library != expLibrary { |
| t.Errorf("expected library `%v`, got `%v`", expLibrary, library) |
| } |
| |
| library, ok = state.ParseLibraryMatch(` |
| /// Doc comments |
| library fuchsia.foo.bar; |
| // ~~~~~~~~~~~~~~~ expected range |
| struct S {}; |
| `) |
| if !ok { |
| t.Errorf("error getting library of file") |
| } |
| expLibrary = state.LibraryMatch{ |
| Lib: fidlcommon.MustReadLibraryName("fuchsia.foo.bar"), |
| Range: state.Range{ |
| Start: state.Position{Line: 2, Character: 8}, |
| End: state.Position{Line: 2, Character: 23}, |
| }, |
| } |
| if library != expLibrary { |
| t.Errorf("expected library `%v`, got `%v`", expLibrary, library) |
| } |
| |
| library, ok = state.ParseLibraryMatch(`library .invalid.name;`) |
| if ok { |
| t.Error("expect error getting library of file") |
| } |
| |
| library, ok = state.ParseLibraryMatch(`library exampl // Missing semicolon`) |
| if ok { |
| t.Error("expect error getting library of file") |
| } |
| } |
| |
| func TestParsePlatformImportsMatch(t *testing.T) { |
| imports := state.ParsePlatformImportsMatch(` |
| library example; |
| using fuchsia.foo.bar; |
| using fuchsia.baz.qux; |
| using non.platform.import; |
| using TypeAlias = uint8; |
| `) |
| |
| expImports := []state.LibraryMatch{ |
| { |
| Lib: fidlcommon.MustReadLibraryName("fuchsia.foo.bar"), |
| Range: state.Range{ |
| Start: state.Position{Line: 2, Character: 6}, |
| End: state.Position{Line: 2, Character: 21}, |
| }, |
| }, |
| { |
| Lib: fidlcommon.MustReadLibraryName("fuchsia.baz.qux"), |
| Range: state.Range{ |
| Start: state.Position{Line: 3, Character: 6}, |
| End: state.Position{Line: 3, Character: 21}, |
| }, |
| }, |
| } |
| if len(imports) != len(expImports) { |
| t.Errorf("expected %d imports, found %d", len(expImports), len(imports)) |
| } |
| for i, expImport := range expImports { |
| if imports[i] != expImport { |
| t.Errorf("unexpected import `%v`, expected `%v`", imports[i], expImport) |
| } |
| } |
| } |
| |
| func TestSymbolAtPos(t *testing.T) { |
| fs := state.NewFileSystem() |
| fs.NewFile("test.fidl", ` |
| library example; |
| using test.import; |
| |
| struct Foo { |
| uint8 bar_baz; |
| }; |
| `) |
| |
| cases := []struct { |
| pos state.Position |
| sym state.Symbol |
| }{ |
| { |
| pos: state.Position{Line: 1, Character: 0}, |
| sym: state.Symbol{ |
| Name: "library", |
| Location: state.Location{ |
| FileID: state.FileID("test.fidl"), |
| Range: state.Range{ |
| Start: state.Position{Line: 1, Character: 0}, |
| End: state.Position{Line: 1, Character: 7}, |
| }, |
| }, |
| }, |
| }, |
| { |
| pos: state.Position{Line: 1, Character: 15}, |
| sym: state.Symbol{ |
| Name: "example", |
| Location: state.Location{ |
| FileID: state.FileID("test.fidl"), |
| Range: state.Range{ |
| Start: state.Position{Line: 1, Character: 8}, |
| End: state.Position{Line: 1, Character: 15}, |
| }, |
| }, |
| }, |
| }, |
| { |
| pos: state.Position{Line: 2, Character: 11}, |
| sym: state.Symbol{ |
| Name: "test.import", |
| Location: state.Location{ |
| FileID: state.FileID("test.fidl"), |
| Range: state.Range{ |
| Start: state.Position{Line: 2, Character: 6}, |
| End: state.Position{Line: 2, Character: 17}, |
| }, |
| }, |
| }, |
| }, |
| { |
| pos: state.Position{Line: 4, Character: 10}, |
| sym: state.Symbol{ |
| Name: "Foo", |
| Location: state.Location{ |
| FileID: state.FileID("test.fidl"), |
| Range: state.Range{ |
| Start: state.Position{Line: 4, Character: 7}, |
| End: state.Position{Line: 4, Character: 10}, |
| }, |
| }, |
| }, |
| }, |
| { |
| pos: state.Position{Line: 5, Character: 13}, |
| sym: state.Symbol{ |
| Name: "bar_baz", |
| Location: state.Location{ |
| FileID: state.FileID("test.fidl"), |
| Range: state.Range{ |
| Start: state.Position{Line: 5, Character: 10}, |
| End: state.Position{Line: 5, Character: 17}, |
| }, |
| }, |
| }, |
| }, |
| } |
| |
| for _, ex := range cases { |
| sym, err := fs.SymbolAtPos( |
| state.FileID("test.fidl"), |
| ex.pos, |
| ) |
| if err != nil { |
| t.Fatalf("error finding symbol: %s", err) |
| } |
| if sym.Name != ex.sym.Name { |
| t.Fatalf("did not find correct symbol; expected %s, found %s", ex.sym.Name, sym.Name) |
| } |
| if sym.Location.FileID != ex.sym.Location.FileID { |
| t.Fatalf( |
| "incorrect file; expected %s, found %s", |
| ex.sym.Location.FileID, |
| sym.Location.FileID, |
| ) |
| } |
| if sym.Location.Range != ex.sym.Location.Range { |
| t.Fatalf( |
| "incorrect range; expected %#v, found %#v", |
| ex.sym.Location.Range, |
| sym.Location.Range, |
| ) |
| } |
| } |
| } |
| |
| func TestNoSymbolAtPos(t *testing.T) { |
| fs := state.NewFileSystem() |
| fs.NewFile("test.fidl", ` |
| // ^ should not be a symbol |
| library example; |
| // ^ should not be a symbol |
| |
| struct Foo { |
| // ^ should not be a symbol |
| uint8 foo; |
| //^ should not be a symbol |
| }; |
| |
| .prepended.dot |
| ^ should not be a symbol ('prepended.dot' is a valid symbol) |
| trailing.dot. |
| ^ should not be a symbol ('trailing.dot' is a valid symbol) |
| 0starts_with_a_number |
| ^ should not be a symbol |
| ends_with_a_semicolon_ |
| ^ should not be a symbol |
| `) |
| |
| cases := []state.Position{ |
| {Line: 0, Character: 0}, |
| {Line: 2, Character: 16}, |
| {Line: 5, Character: 11}, |
| {Line: 7, Character: 2}, |
| {Line: 11, Character: 0}, |
| {Line: 13, Character: 13}, |
| {Line: 15, Character: 3}, |
| {Line: 17, Character: 3}, |
| } |
| |
| for _, ex := range cases { |
| sym, err := fs.SymbolAtPos( |
| state.FileID("test.fidl"), |
| ex, |
| ) |
| if err == nil { |
| t.Errorf("expected not to find symbol at position %v, but found %s", ex, sym.Name) |
| } |
| } |
| } |