TestBuildPackageGraph tests the BuildPackageGraph constructor, which uses the reference analysis of the Refs function to build a graph of relationships between packages. It simulates the operation of gopls at startup: packages are loaded via go/packages, and their syntax+metadata analyzed to determ
(t *testing.T)
| 64 | // - verify toggles the verification w.r.t. the load graph (which may be |
| 65 | // prohibitively expensive with large queries). |
| 66 | func TestBuildPackageGraph(t *testing.T) { |
| 67 | if testing.Short() { |
| 68 | t.Skip("skipping with -short: loading the packages can take a long time with a cold cache") |
| 69 | } |
| 70 | testenv.NeedsGoBuild(t) // for go/packages |
| 71 | |
| 72 | t0 := time.Now() |
| 73 | exports, meta, err := loadPackages(*query, *verify) |
| 74 | if err != nil { |
| 75 | t.Fatalf("loading failed: %v", err) |
| 76 | } |
| 77 | t.Logf("loaded %d packages in %v", len(exports), time.Since(t0)) |
| 78 | |
| 79 | ctx := context.Background() |
| 80 | var ids []PackageID |
| 81 | for id := range exports { |
| 82 | ids = append(ids, id) |
| 83 | } |
| 84 | slices.Sort(ids) |
| 85 | |
| 86 | t0 = time.Now() |
| 87 | g, err := BuildPackageGraph(ctx, meta, ids, newParser().parse) |
| 88 | if err != nil { |
| 89 | t.Fatal(err) |
| 90 | } |
| 91 | t.Logf("building package graph took %v", time.Since(t0)) |
| 92 | |
| 93 | // Collect information about the edges between packages for later analysis. |
| 94 | // |
| 95 | // We compare the following package graphs: |
| 96 | // - the imports graph: edges are transitive imports |
| 97 | // - the reaches graph: edges are reachability relationships through syntax |
| 98 | // of imports (as defined in the package doc) |
| 99 | // - the loads graph: edges are packages loaded through the export data of |
| 100 | // imports |
| 101 | // |
| 102 | // By definition, loads < reaches < imports. |
| 103 | type edgeSet map[PackageID]map[PackageID]bool |
| 104 | var ( |
| 105 | imports = make(edgeSet) // A imports B transitively |
| 106 | importedBy = make(edgeSet) // A is imported by B transitively |
| 107 | reaches = make(edgeSet) // A reaches B through top-level declaration syntax |
| 108 | reachedBy = make(edgeSet) // A is reached by B through top-level declaration syntax |
| 109 | loads = make(edgeSet) // A loads B through export data of its direct dependencies |
| 110 | loadedBy = make(edgeSet) // A is loaded by B through export data of B's direct dependencies |
| 111 | ) |
| 112 | recordEdge := func(from, to PackageID, fwd, rev edgeSet) { |
| 113 | if fwd[from] == nil { |
| 114 | fwd[from] = make(map[PackageID]bool) |
| 115 | } |
| 116 | fwd[from][to] = true |
| 117 | if rev[to] == nil { |
| 118 | rev[to] = make(map[PackageID]bool) |
| 119 | } |
| 120 | rev[to][from] = true |
| 121 | } |
| 122 | |
| 123 | exportedPackages := make(map[PackageID]*types.Package) |
nothing calls this directly
no test coverage detected
searching dependent graphs…