func TestAddTarget(t *testing.T) { pkg := core.NewPackage("src/parse") addTargetTest1 := func(name string, binary, container, test bool, testCmd string) *core.BuildTarget { return addTarget(uintptr(unsafe.Pointer(pkg)), name, "true", testCmd, binary, test, false, false, container, false, false, false, 0, 0, 0, "Building...") } addTargetTest := func(name string, binary, container bool) *core.BuildTarget { return addTargetTest1(name, binary, container, false, "") } // Test that labels are correctly applied target1 := addTargetTest("target1", false, false) assert.False(t, target1.HasLabel("bin")) assert.False(t, target1.HasLabel("container")) target2 := addTargetTest("target2", true, false) assert.True(t, target2.HasLabel("bin")) assert.False(t, target2.HasLabel("container")) target3 := addTargetTest("target3", true, true) assert.True(t, target3.HasLabel("bin")) assert.True(t, target3.HasLabel("container")) assert.Panics(t, func() { addTargetTest(":target1", false, false) }, "Should return nil attempting to add a target with an illegal name") assert.Nil(t, addTargetTest("target1", false, false), "Should return nil attempting to add a new target with the same name") assert.Nil(t, core.State.Graph.Target(core.ParseBuildLabel("//src/parse:target1", "")), "Shouldn't have added target to the graph yet") core.State.Graph.AddPackage(pkg) addTargetTest("target6", true, false) target6 := core.State.Graph.Target(core.ParseBuildLabel("//src/parse:target6", "")) assert.NotNil(t, target6, "Should have been added to the graph since the package is added") assert.True(t, target6.HasLabel("bin")) }
func makeTarget(label string, deps ...string) *core.BuildTarget { target := core.NewBuildTarget(core.ParseBuildLabel(label, "")) for _, dep := range deps { target.AddDependency(core.ParseBuildLabel(dep, "")) } return target }
func TestDiffGraphsChangedHash(t *testing.T) { changes := readAndDiffGraphs("src/misc/test_data/before.json", "src/misc/test_data/changed_hash.json", nil, nil, nil) expected := []core.BuildLabel{ core.ParseBuildLabel("//:all_tools", ""), core.ParseBuildLabel("//src/cache/server:http_cache_server_bin", ""), } assert.Equal(t, expected, changes) }
func TestDiffGraphsSimple(t *testing.T) { changes := readAndDiffGraphs("src/misc/test_data/before.json", "src/misc/test_data/after.json", nil, nil, nil) expected := []core.BuildLabel{ core.ParseBuildLabel("//src/misc:plz_diff_graphs", ""), core.ParseBuildLabel("//src/misc:plz_diff_graphs_test", ""), } assert.Equal(t, expected, changes) }
func TestDiffGraphsRemovedPackage2(t *testing.T) { changes := readAndDiffGraphs("src/misc/test_data/removed_package.json", "src/misc/test_data/before.json", nil, nil, nil) expected := []core.BuildLabel{ core.ParseBuildLabel("//:all_tools", ""), core.ParseBuildLabel("//src/cache/tools:cache_cleaner", ""), core.ParseBuildLabel("//src/cache/tools:cache_cleaner_platform", ""), } assert.Equal(t, expected, changes) }
func TestDiffGraphsIncludeLabels(t *testing.T) { changes := readAndDiffGraphs("src/misc/test_data/before.json", "src/misc/test_data/labels2.json", nil, []string{"py"}, nil) expected := []core.BuildLabel{ core.ParseBuildLabel("//src/build/python:pex_import_test", ""), } assert.Equal(t, expected, changes) }
func TestQuerySingleTarget(t *testing.T) { graph := makeJSONGraph(makeGraph(), []core.BuildLabel{core.ParseBuildLabel("//package1:target2", "")}) assert.Equal(t, 1, len(graph.Packages)) pkg1 := graph.Packages["package1"] assert.Equal(t, 2, len(pkg1.Targets)) assert.Equal(t, []string{"//package1:target1"}, pkg1.Targets["target2"].Deps) }
func getSubincludeFile(pkg *core.Package, labelStr string) string { label := core.ParseBuildLabel(labelStr, pkg.Name) if label.PackageName == pkg.Name { return fmt.Sprintf("__Can't subinclude :%s in %s; can't subinclude local targets.", label.Name, pkg.Name) } pkgLabel := core.BuildLabel{PackageName: pkg.Name, Name: "all"} target := core.State.Graph.Target(label) if target == nil { // Might not have been parsed yet. Check for that first. if subincludePackage := core.State.Graph.Package(label.PackageName); subincludePackage == nil { if deferParse(label, pkg) { return pyDeferParse // Not an error, they'll just have to wait. } target = core.State.Graph.TargetOrDie(label) // Should be there now. } else { return fmt.Sprintf("__Failed to subinclude %s; package %s has no target by that name", label, label.PackageName) } } else if tmp := core.NewBuildTarget(pkgLabel); !tmp.CanSee(target) { return fmt.Sprintf("__Can't subinclude %s from %s due to visibility constraints", label, pkg.Name) } else if len(target.Outputs()) != 1 { return fmt.Sprintf("__Can't subinclude %s, subinclude targets must have exactly one output", label) } else if target.State() < core.Built { if deferParse(label, pkg) { return pyDeferParse // Again, they'll have to wait for this guy to build. } } pkg.RegisterSubinclude(target.Label) // Well if we made it to here it's actually ready to go, so tell them where to get it. return path.Join(target.OutDir(), target.Outputs()[0]) }
func newState(label string) (*core.BuildState, *core.BuildTarget) { config, _ := core.ReadConfigFiles(nil) state := core.NewBuildState(1, nil, 4, config) target := core.NewBuildTarget(core.ParseBuildLabel(label, "")) target.Command = fmt.Sprintf("echo 'output of %s' > $OUT", target.Label) state.Graph.AddTarget(target) return state, target }
func makeGraph() *core.BuildGraph { core.State = &core.BuildState{} graph := core.NewGraph() pkg1 := core.NewPackage("package1") pkg1.Targets["target1"] = makeTarget("//package1:target1") pkg1.Targets["target2"] = makeTarget("//package1:target2", "//package1:target1") graph.AddPackage(pkg1) graph.AddTarget(pkg1.Targets["target1"]) graph.AddTarget(pkg1.Targets["target2"]) pkg2 := core.NewPackage("package2") pkg2.Targets["target3"] = makeTarget("//package2:target3", "//package1:target2") graph.AddPackage(pkg2) graph.AddTarget(pkg2.Targets["target3"]) graph.AddDependency(core.ParseBuildLabel("//package1:target2", ""), core.ParseBuildLabel("//package1:target1", "")) graph.AddDependency(core.ParseBuildLabel("//package2:target3", ""), core.ParseBuildLabel("//package1:target2", "")) return graph }
func newPyFilegroup(state *core.BuildState, label, filename string) *core.BuildTarget { target := core.NewBuildTarget(core.ParseBuildLabel(label, "")) target.AddSource(core.FileLabel{File: filename, Package: target.Label.PackageName}) target.AddOutput(filename) target.AddLabel("py") target.Command = "__FILEGROUP__" // magic state.Graph.AddTarget(target) return target }
func TestMapKeysContainFullPathFromProjectRoot(t *testing.T) { core.State = &core.BuildState{} graph := core.NewGraph() makeTarget(graph, "package1", "target1", []string{"out1", "out2"}) makeTarget(graph, "package1", "target2", []string{"out3"}) makeTarget(graph, "package2", "target1", []string{"out4"}) m := filesToLabelMap(graph) label1 := core.ParseBuildLabel("//package1:target1", "") label2 := core.ParseBuildLabel("//package1:target2", "") label3 := core.ParseBuildLabel("//package2:target1", "") p1 := graph.PackageOrDie("package1") p2 := graph.PackageOrDie("package2") assert.Equal(t, m[path.Join(p1.Targets["target1"].OutDir(), "out1")].String(), label1.String()) assert.Equal(t, m[path.Join(p1.Targets["target1"].OutDir(), "out2")].String(), label1.String()) assert.Equal(t, m[path.Join(p1.Targets["target2"].OutDir(), "out3")].String(), label2.String()) assert.Equal(t, m[path.Join(p2.Targets["target1"].OutDir(), "out4")].String(), label3.String()) }
func TestGetSubincludeFile(t *testing.T) { assertError := func(t *testing.T, ret, msg string) { assert.True(t, strings.HasPrefix(ret, "__"), msg) } state := core.NewBuildState(10, nil, 2, core.DefaultConfiguration()) pkg := core.NewPackage("src/parse") pkg2 := core.NewPackage("src/core") assert.Equal(t, pyDeferParse, getSubincludeFile(pkg, "//src/core:target"), "Package not loaded yet, should defer") assertError(t, getSubincludeFile(pkg, "//src/parse:target"), "Should produce an error on attempts for local subincludes.") assertError(t, getSubincludeFile(pkg, ":target"), "Should produce an error on attempts for local subincludes.") state.Graph.AddPackage(pkg) state.Graph.AddPackage(pkg2) assertError(t, getSubincludeFile(pkg, "//src/core:target"), "Produces an error, target does not exist in package.") target := core.NewBuildTarget(core.ParseBuildLabel("//src/core:target", "")) state.Graph.AddTarget(target) assertError(t, getSubincludeFile(pkg, "//src/core:target"), "Errors, target is not visible to subincluding package.") target.Visibility = []core.BuildLabel{core.ParseBuildLabel("//src/parse:all", "")} assertError(t, getSubincludeFile(pkg, "//src/core:target"), "Errors, target doesn't have any outputs to include.") target.AddOutput("test.py") assert.Equal(t, pyDeferParse, getSubincludeFile(pkg, "//src/core:target"), "Target isn't built yet, so still deferred") target.SetState(core.Built) assert.Equal(t, "plz-out/gen/src/core/test.py", getSubincludeFile(pkg, "//src/core:target"), "Success at last") }
func TestGetLabels(t *testing.T) { state := core.NewBuildState(10, nil, 2, core.DefaultConfiguration()) target1 := core.NewBuildTarget(core.ParseBuildLabel("//src/parse:target1", "")) target2 := core.NewBuildTarget(core.ParseBuildLabel("//src/parse:target2", "")) target3 := core.NewBuildTarget(core.ParseBuildLabel("//src/parse:target3", "")) target1.AddLabel("go") target2.AddLabel("py") target3.AddLabel("cc") target1.AddDependency(target2.Label) target1.AddDependency(target3.Label) target2.AddDependency(target3.Label) state.Graph.AddTarget(target1) state.Graph.AddTarget(target2) state.Graph.AddTarget(target3) state.Graph.AddDependency(target1.Label, target2.Label) state.Graph.AddDependency(target1.Label, target3.Label) state.Graph.AddDependency(target2.Label, target3.Label) // Note labels always come out in sorted order. assert.Equal(t, []string{"cc", "go", "py"}, getLabels(target1, "", core.Inactive)) assert.Equal(t, []string{"cc", "py"}, getLabels(target2, "", core.Inactive)) assert.Equal(t, []string{"cc"}, getLabels(target3, "", core.Inactive)) assert.Equal(t, []string{"y"}, getLabels(target1, "p", core.Inactive)) }
func TestConstructsMapFromGraph(t *testing.T) { core.State = &core.BuildState{} graph := core.NewGraph() m := filesToLabelMap(graph) assert.Equal(t, 0, len(m)) label := core.ParseBuildLabel("//package1:target1", "") makeTarget(graph, "package1", "target1", []string{"out1", "out2"}) m = filesToLabelMap(graph) assert.Equal(t, 2, len(m)) for _, l := range m { assert.Equal(t, label.String(), l.String()) } }
func makeTarget(name string, command string, dep *core.BuildTarget) *core.BuildTarget { target := core.NewBuildTarget(core.ParseBuildLabel(name, "")) target.Command = command target.AddOutput(target.Label.Name + ".py") if dep != nil { target.AddDependency(dep.Label) // This is a bit awkward but I don't want to add a public interface just for a test. graph := core.NewGraph() graph.AddTarget(target) graph.AddTarget(dep) graph.AddDependency(target.Label, dep.Label) } return target }
// Replaces a single escape sequence in a command. func replaceSequence(target *core.BuildTarget, in string, runnable, multiple, dir, outPrefix, hash, test bool) string { if core.LooksLikeABuildLabel(in) { label := core.ParseBuildLabel(in, target.Label.PackageName) return replaceSequenceLabel(target, label, in, runnable, multiple, dir, outPrefix, hash, test, true) } for _, src := range target.AllSources() { if label := src.Label(); label != nil && src.String() == in { return replaceSequenceLabel(target, *label, in, runnable, multiple, dir, outPrefix, hash, test, false) } } if hash { return base64.RawURLEncoding.EncodeToString(mustPathHash(path.Join(target.Label.PackageName, in))) } return quote(path.Join(target.Label.PackageName, in)) }
func makeTarget(g *core.BuildGraph, packageName string, labelName string, outputs []string) *core.BuildTarget { l := core.ParseBuildLabel(fmt.Sprintf("//%s:%s", packageName, labelName), "") t := core.NewBuildTarget(l) p := g.Package(packageName) if p == nil { p = core.NewPackage(packageName) g.AddPackage(p) } for _, out := range outputs { t.AddOutput(out) p.MustRegisterOutput(out, t) } p.Targets[labelName] = t g.AddTarget(t) return t }
func assertPendingBuilds(t *testing.T, state *core.BuildState, targets ...string) { state.Stop(1) pending := []core.BuildLabel{} for { label, _, typ := state.NextTask() if typ == core.Stop { break } else if typ != core.Build && typ != core.SubincludeBuild { t.Errorf("Unexpected non-build task") } else { pending = append(pending, label) } } expected := []core.BuildLabel{} for _, target := range targets { expected = append(expected, core.ParseBuildLabel(target, "")) } assert.Equal(t, expected, pending) }
// depsChanged returns true if any of the transitive dependencies of this target have changed. // It marks any changes in allChanges for efficiency. func depsChanged(graph *query.JSONGraph, allChanges map[string]bool, pkgName, targetName string, recurse bool) bool { label := fmt.Sprintf("//%s:%s", pkgName, targetName) changed, present := allChanges[label] if present { return changed } target := graph.Packages[pkgName].Targets[targetName] if !recurse { return false } for _, dep := range target.Deps { depLabel := core.ParseBuildLabel(dep, "") if depsChanged(graph, allChanges, depLabel.PackageName, depLabel.Name, recurse) { allChanges[label] = true return true } } allChanges[label] = false return false }
func TestFindGraphCycle(t *testing.T) { graph := core.NewGraph() graph.AddTarget(makeTarget("//src/output:target1", "//src/output:target2", "//src/output:target3", "//src/core:target2")) graph.AddTarget(makeTarget("//src/output:target2", "//src/output:target3", "//src/core:target1")) graph.AddTarget(makeTarget("//src/output:target3")) graph.AddTarget(makeTarget("//src/core:target1", "//third_party/go:target2", "//third_party/go:target3", "//src/core:target3")) graph.AddTarget(makeTarget("//src/core:target2", "//third_party/go:target3", "//src/output:target2")) graph.AddTarget(makeTarget("//src/core:target3", "//third_party/go:target2", "//src/output:target2")) graph.AddTarget(makeTarget("//third_party/go:target2", "//third_party/go:target1")) graph.AddTarget(makeTarget("//third_party/go:target3", "//third_party/go:target1")) graph.AddTarget(makeTarget("//third_party/go:target1")) updateDependencies(graph) cycle := findGraphCycle(graph, graph.TargetOrDie(core.ParseBuildLabel("//src/output:target1", ""))) if len(cycle) == 0 { t.Fatalf("Failed to find cycle") } else if len(cycle) != 3 { t.Errorf("Found unexpected cycle of length %d", len(cycle)) } assertTarget(t, cycle[0], "//src/output:target2") assertTarget(t, cycle[1], "//src/core:target1") assertTarget(t, cycle[2], "//src/core:target3") }
func makeTarget(label string) *core.BuildTarget { return core.NewBuildTarget(core.ParseBuildLabel(label, "")) }
func assertTarget(t *testing.T, target *core.BuildTarget, label string) { if target.Label != core.ParseBuildLabel(label, "") { t.Errorf("Unexpected target in detected cycle; expected %s, was %s", label, target.Label) } }
func bl(label string) core.BuildLabel { return core.ParseBuildLabel(label, "") }
func label(i int) core.BuildLabel { return core.ParseBuildLabel(fmt.Sprintf("//pkg:target%d", i), "") }
func buildLabel(bl string) core.BuildLabel { return core.ParseBuildLabel(bl, "") }
func TestDiffGraphsChangedFile(t *testing.T) { changedFile := []string{"src/build/java/net/thoughtmachine/please/test/TestCoverage.java"} changes := readAndDiffGraphs("src/misc/test_data/before.json", "src/misc/test_data/before.json", changedFile, nil, nil) expected := []core.BuildLabel{ core.ParseBuildLabel("//:all_tools", ""), core.ParseBuildLabel("//src/build/java:_junit_runner#jar", ""), core.ParseBuildLabel("//src/build/java:junit_runner", ""), core.ParseBuildLabel("//src/build/java/net/thoughtmachine/please/test:_junit_runner_parameterized_test#jar", ""), core.ParseBuildLabel("//src/build/java/net/thoughtmachine/please/test:_junit_runner_parameterized_test#lib", ""), core.ParseBuildLabel("//src/build/java/net/thoughtmachine/please/test:_junit_runner_test#jar", ""), core.ParseBuildLabel("//src/build/java/net/thoughtmachine/please/test:_junit_runner_test#lib", ""), core.ParseBuildLabel("//src/build/java/net/thoughtmachine/please/test:_please_coverage_class_loader_test#jar", ""), core.ParseBuildLabel("//src/build/java/net/thoughtmachine/please/test:_please_coverage_class_loader_test#lib", ""), core.ParseBuildLabel("//src/build/java/net/thoughtmachine/please/test:_resources_root_test#jar", ""), core.ParseBuildLabel("//src/build/java/net/thoughtmachine/please/test:_resources_root_test#lib", ""), core.ParseBuildLabel("//src/build/java/net/thoughtmachine/please/test:_test_coverage_test#jar", ""), core.ParseBuildLabel("//src/build/java/net/thoughtmachine/please/test:_test_coverage_test#lib", ""), core.ParseBuildLabel("//src/build/java/net/thoughtmachine/please/test:junit_runner", ""), core.ParseBuildLabel("//src/build/java/net/thoughtmachine/please/test:junit_runner_parameterized_test", ""), core.ParseBuildLabel("//src/build/java/net/thoughtmachine/please/test:junit_runner_test", ""), core.ParseBuildLabel("//src/build/java/net/thoughtmachine/please/test:please_coverage_class_loader_test", ""), core.ParseBuildLabel("//src/build/java/net/thoughtmachine/please/test:resources_root_test", ""), core.ParseBuildLabel("//src/build/java/net/thoughtmachine/please/test:test_coverage_test", ""), } assert.Equal(t, expected, changes) }