github.phpd.cn/thought-machine/please@v12.2.0+incompatible/src/build/command_replacements_test.go (about) 1 // Tests the command replacement functionality. 2 3 package build 4 5 import ( 6 "io/ioutil" 7 "os" 8 "path" 9 "testing" 10 11 "github.com/stretchr/testify/assert" 12 13 "core" 14 ) 15 16 var wd string 17 var state *core.BuildState 18 19 func init() { 20 state = core.NewDefaultBuildState() 21 wd, _ = os.Getwd() 22 } 23 24 func TestLocation(t *testing.T) { 25 target2 := makeTarget("//path/to:target2", "", nil) 26 target1 := makeTarget("//path/to:target1", "ln -s $(location //path/to:target2) ${OUT}", target2) 27 28 expected := "ln -s path/to/target2.py ${OUT}" 29 cmd := replaceSequences(state, target1) 30 if cmd != expected { 31 t.Errorf("Replacement sequence not as expected; is %s, should be %s", cmd, expected) 32 } 33 } 34 35 func TestLocations(t *testing.T) { 36 target2 := makeTarget("//path/to:target2", "", nil) 37 target2.AddOutput("target2_other.py") 38 target1 := makeTarget("//path/to:target1", "cat $(locations //path/to:target2) > ${OUT}", target2) 39 40 expected := "cat path/to/target2.py path/to/target2_other.py > ${OUT}" 41 cmd := replaceSequences(state, target1) 42 if cmd != expected { 43 t.Errorf("Replacement sequence not as expected; is %s, should be %s", cmd, expected) 44 } 45 } 46 47 func TestExe(t *testing.T) { 48 target2 := makeTarget("//path/to:target2", "", nil) 49 target2.IsBinary = true 50 target1 := makeTarget("//path/to:target1", "$(exe //path/to:target2) -o ${OUT}", target2) 51 52 expected := "path/to/target2.py -o ${OUT}" 53 cmd := replaceSequences(state, target1) 54 if cmd != expected { 55 t.Errorf("Replacement sequence not as expected; is %s, should be %s", cmd, expected) 56 } 57 } 58 59 func TestOutExe(t *testing.T) { 60 target2 := makeTarget("//path/to:target2", "", nil) 61 target2.IsBinary = true 62 target1 := makeTarget("//path/to:target1", "$(out_exe //path/to:target2) -o ${OUT}", target2) 63 64 expected := "plz-out/bin/path/to/target2.py -o ${OUT}" 65 cmd := replaceSequences(state, target1) 66 if cmd != expected { 67 t.Errorf("Replacement sequence not as expected; is %s, should be %s", cmd, expected) 68 } 69 } 70 71 func TestJavaExe(t *testing.T) { 72 target2 := makeTarget("//path/to:target2", "", nil) 73 target2.IsBinary = true 74 target2.AddLabel("java_non_exe") // This label tells us to prefix it with java -jar. 75 target1 := makeTarget("//path/to:target1", "$(exe //path/to:target2) -o ${OUT}", target2) 76 77 expected := "java -jar path/to/target2.py -o ${OUT}" 78 cmd := replaceSequences(state, target1) 79 if cmd != expected { 80 t.Errorf("Replacement sequence not as expected; is %s, should be %s", cmd, expected) 81 } 82 } 83 84 func TestJavaOutExe(t *testing.T) { 85 target2 := makeTarget("//path/to:target2", "", nil) 86 target2.IsBinary = true 87 target2.AddLabel("java_non_exe") // This label tells us to prefix it with java -jar. 88 target1 := makeTarget("//path/to:target1", "$(out_exe //path/to:target2) -o ${OUT}", target2) 89 90 expected := "java -jar plz-out/bin/path/to/target2.py -o ${OUT}" 91 cmd := replaceSequences(state, target1) 92 if cmd != expected { 93 t.Errorf("Replacement sequence not as expected; is %s, should be %s", cmd, expected) 94 } 95 } 96 97 func TestReplacementsForTest(t *testing.T) { 98 target2 := makeTarget("//path/to:target2", "", nil) 99 target1 := makeTarget("//path/to:target1", "$(exe //path/to:target1) $(location //path/to:target2)", target2) 100 target1.IsBinary = true 101 target1.IsTest = true 102 103 expected := "./target1.py path/to/target2.py" 104 cmd := ReplaceTestSequences(state, target1, target1.Command) 105 if cmd != expected { 106 t.Errorf("Replacement sequence not as expected; is %s, should be %s", cmd, expected) 107 } 108 } 109 110 func TestDataReplacementForTest(t *testing.T) { 111 target := makeTarget("//path/to:target1", "cat $(location test_data.txt)", nil) 112 target.Data = append(target.Data, core.FileLabel{File: "test_data.txt", Package: "path/to"}) 113 114 expected := "cat path/to/test_data.txt" 115 cmd := ReplaceTestSequences(state, target, target.Command) 116 if cmd != expected { 117 t.Errorf("Replacement sequence not as expected; is %s, should be %s", cmd, expected) 118 } 119 } 120 121 func TestAmpersandReplacement(t *testing.T) { 122 target := makeTarget("//path/to:target1", "cat $(location b&b.txt)", nil) 123 expected := "cat \"path/to/b&b.txt\"" 124 cmd := ReplaceSequences(state, target, target.Command) 125 if cmd != expected { 126 t.Errorf("Replacement sequence not as expected; is %s, should be %s", cmd, expected) 127 } 128 } 129 130 func TestToolReplacement(t *testing.T) { 131 target2 := makeTarget("//path/to:target2", "blah", nil) 132 target1 := makeTarget("//path/to:target1", "$(location //path/to:target2)", target2) 133 target1.Tools = append(target1.Tools, target2.Label) 134 135 wd, _ := os.Getwd() 136 expected := quote(path.Join(wd, "plz-out/gen/path/to/target2.py")) 137 cmd := ReplaceSequences(state, target1, target1.Command) 138 if cmd != expected { 139 t.Errorf("Replacement sequence not as expected; is %s, should be %s", cmd, expected) 140 } 141 } 142 143 func TestDirReplacement(t *testing.T) { 144 target2 := makeTarget("//path/to:target2", "blah", nil) 145 target2.AddOutput("blah2.txt") 146 target1 := makeTarget("//path/to:target1", "$(dir //path/to:target2)", target2) 147 148 expected := "path/to" 149 cmd := ReplaceSequences(state, target1, target1.Command) 150 if cmd != expected { 151 t.Errorf("Replacement sequence not as expected; is %s, should be %s", cmd, expected) 152 } 153 } 154 155 func TestToolDirReplacement(t *testing.T) { 156 target2 := makeTarget("//path/to:target2", "blah", nil) 157 target2.AddOutput("blah2.txt") 158 target1 := makeTarget("//path/to:target1", "$(dir //path/to:target2)", target2) 159 target1.Tools = append(target1.Tools, target2.Label) 160 161 wd, _ := os.Getwd() 162 expected := quote(path.Join(wd, "plz-out/gen/path/to")) 163 cmd := ReplaceSequences(state, target1, target1.Command) 164 if cmd != expected { 165 t.Errorf("Replacement sequence not as expected; is %s, should be %s", cmd, expected) 166 } 167 } 168 169 func TestBazelCompatReplacements(t *testing.T) { 170 // Check that we don't do any of these things normally. 171 target := makeTarget("//path/to:target", "cp $< $@", nil) 172 assert.Equal(t, "cp $< $@", replaceSequences(state, target)) 173 // In Bazel compat mode we do though. 174 state := core.NewBuildState(1, nil, 1, core.DefaultConfiguration()) 175 state.Config.Bazel.Compatibility = true 176 assert.Equal(t, "cp $SRCS $OUTS", replaceSequences(state, target)) 177 // @D is the output dir, for us it's the tmp dir. 178 target.Command = "cp $SRCS $@D" 179 assert.Equal(t, "cp $SRCS $TMP_DIR", replaceSequences(state, target)) 180 // This parenthesised syntax seems to be allowed too. 181 target.Command = "cp $(<) $(@)" 182 assert.Equal(t, "cp $SRCS $OUTS", replaceSequences(state, target)) 183 } 184 185 func TestHashReplacement(t *testing.T) { 186 // Need to write the file that will be used to calculate the hash. 187 err := os.MkdirAll("plz-out/gen/path/to", 0755) 188 assert.NoError(t, err) 189 err = ioutil.WriteFile("plz-out/gen/path/to/target2.py", []byte(`"""Test file for command_replacements_test"""`), 0644) 190 assert.NoError(t, err) 191 192 target2 := makeTarget("//path/to:target2", "cp $< $@", nil) 193 target := makeTarget("//path/to:target", "echo $(hash //path/to:target2)", target2) 194 assert.Panics(t, func() { replaceSequences(state, target) }, "Can't use $(hash ) on a non-stamped target") 195 target.Stamp = true 196 // Note that this hash is determined arbitrarily, it doesn't matter for this test 197 // precisely what its value is. 198 assert.Equal(t, "echo gB4sUwsLkB1ODYKUxYrKGlpdYUI", replaceSequences(state, target)) 199 } 200 201 func TestWorkerReplacement(t *testing.T) { 202 tool := makeTarget("//path/to:target2", "", nil) 203 tool.IsBinary = true 204 target := makeTarget("//path/to:target", "$(worker //path/to:target2) --some_arg", tool) 205 target.Tools = append(target.Tools, tool.Label) 206 worker, remoteArgs, localCmd := workerCommandAndArgs(state, target) 207 assert.Equal(t, wd+"/plz-out/bin/path/to/target2.py", worker) 208 assert.Equal(t, "--some_arg", remoteArgs) 209 assert.Equal(t, "", localCmd) 210 } 211 212 func TestSystemWorkerReplacement(t *testing.T) { 213 target := makeTarget("//path/to:target", "$(worker /usr/bin/javac) --some_arg", nil) 214 target.Tools = append(target.Tools, core.SystemFileLabel{Path: "/usr/bin/javac"}) 215 worker, remoteArgs, localCmd := workerCommandAndArgs(state, target) 216 assert.Equal(t, "/usr/bin/javac", worker) 217 assert.Equal(t, "--some_arg", remoteArgs) 218 assert.Equal(t, "", localCmd) 219 } 220 221 func TestLocalCommandWorker(t *testing.T) { 222 tool := makeTarget("//path/to:target2", "", nil) 223 tool.IsBinary = true 224 target := makeTarget("//path/to:target", "$(worker //path/to:target2) --some_arg && find . | xargs rm && echo hello", tool) 225 target.Tools = append(target.Tools, tool.Label) 226 worker, remoteArgs, localCmd := workerCommandAndArgs(state, target) 227 assert.Equal(t, wd+"/plz-out/bin/path/to/target2.py", worker) 228 assert.Equal(t, "--some_arg", remoteArgs) 229 assert.Equal(t, "find . | xargs rm && echo hello", localCmd) 230 } 231 232 func TestWorkerCommandAndArgsMustComeFirst(t *testing.T) { 233 tool := makeTarget("//path/to:target2", "", nil) 234 tool.IsBinary = true 235 target := makeTarget("//path/to:target", "something something $(worker javac)", tool) 236 target.Tools = append(target.Tools, tool.Label) 237 assert.Panics(t, func() { workerCommandAndArgs(state, target) }) 238 } 239 240 func TestWorkerReplacementWithNoWorker(t *testing.T) { 241 target := makeTarget("//path/to:target", "echo hello", nil) 242 worker, remoteArgs, localCmd := workerCommandAndArgs(state, target) 243 assert.Equal(t, "", worker) 244 assert.Equal(t, "", remoteArgs) 245 assert.Equal(t, "echo hello", localCmd) 246 } 247 248 func makeTarget(name string, command string, dep *core.BuildTarget) *core.BuildTarget { 249 target := core.NewBuildTarget(core.ParseBuildLabel(name, "")) 250 target.Command = command 251 target.AddOutput(target.Label.Name + ".py") 252 if dep != nil { 253 target.AddDependency(dep.Label) 254 // This is a bit awkward but I don't want to add a public interface just for a test. 255 graph := core.NewGraph() 256 graph.AddTarget(target) 257 graph.AddTarget(dep) 258 graph.AddDependency(target.Label, dep.Label) 259 } 260 return target 261 } 262 263 func replaceSequences(state *core.BuildState, target *core.BuildTarget) string { 264 return ReplaceSequences(state, target, target.GetCommand(state)) 265 }