github.com/maruel/nin@v0.0.0-20220112143044-f35891e3ce7e/depfile_parser_test.go (about) 1 // Copyright 2011 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package nin 16 17 import ( 18 "testing" 19 20 "github.com/google/go-cmp/cmp" 21 ) 22 23 func parse(t *testing.T, s string) DepfileParser { 24 p := DepfileParser{} 25 if err := p.Parse([]byte(s + "\x00")); err != nil { 26 t.Fatal(err) 27 } 28 return p 29 } 30 31 func TestDepfileParserTest_Basic(t *testing.T) { 32 p := parse(t, "build/ninja.o: ninja.cc ninja.h eval_env.h manifest_parser.h\n") 33 if 1 != len(p.outs) { 34 t.Fatal(p.outs) 35 } 36 if "build/ninja.o" != p.outs[0] { 37 t.Fatal(p.outs) 38 } 39 if 4 != len(p.ins) { 40 t.Fatal(p.ins) 41 } 42 } 43 44 func TestDepfileParserTest_EarlyNewlineAndWhitespace(t *testing.T) { 45 _ = parse(t, " \\\n out: in\n") 46 } 47 48 func TestDepfileParserTest_Continuation(t *testing.T) { 49 p := parse(t, "foo.o: \\\n bar.h baz.h\n") 50 if 1 != len(p.outs) { 51 t.Fatal(p.outs) 52 } 53 if "foo.o" != p.outs[0] { 54 t.Fatal(p.outs) 55 } 56 if 2 != len(p.ins) { 57 t.Fatal(p.ins) 58 } 59 } 60 61 func TestDepfileParserTest_CarriageReturnContinuation(t *testing.T) { 62 p := parse(t, "foo.o: \\\r\n bar.h baz.h\r\n") 63 if 1 != len(p.outs) { 64 t.FailNow() 65 } 66 if "foo.o" != p.outs[0] { 67 t.FailNow() 68 } 69 if 2 != len(p.ins) { 70 t.FailNow() 71 } 72 } 73 74 func TestDepfileParserTest_BackSlashes(t *testing.T) { 75 p := parse(t, "Project\\Dir\\Build\\Release8\\Foo\\Foo.res : \\\n Dir\\Library\\Foo.rc \\\n Dir\\Library\\Version\\Bar.h \\\n Dir\\Library\\Foo.ico \\\n Project\\Thing\\Bar.tlb \\\n") 76 if 1 != len(p.outs) { 77 t.Fatal(p.outs) 78 } 79 if "Project\\Dir\\Build\\Release8\\Foo\\Foo.res" != p.outs[0] { 80 t.Fatal(p.outs) 81 } 82 if 4 != len(p.ins) { 83 t.Fatal(p.ins) 84 } 85 } 86 87 func TestDepfileParserTest_Spaces(t *testing.T) { 88 p := parse(t, "a\\ bc\\ def: a\\ b c d") 89 if 1 != len(p.outs) { 90 t.FailNow() 91 } 92 if "a bc def" != p.outs[0] { 93 t.FailNow() 94 } 95 if 3 != len(p.ins) { 96 t.FailNow() 97 } 98 if "a b" != p.ins[0] { 99 t.FailNow() 100 } 101 if "c" != p.ins[1] { 102 t.FailNow() 103 } 104 if "d" != p.ins[2] { 105 t.FailNow() 106 } 107 } 108 109 func TestDepfileParserTest_MultipleBackslashes(t *testing.T) { 110 // Successive 2N+1 backslashes followed by space (' ') are replaced by N >= 0 111 // backslashes and the space. A single backslash before hash sign is removed. 112 // Other backslashes remain untouched (including 2N backslashes followed by 113 // space). 114 p := parse(t, "a\\ b\\#c.h: \\\\\\\\\\ \\\\\\\\ \\\\share\\info\\\\#1") 115 if 1 != len(p.outs) { 116 t.FailNow() 117 } 118 if "a b#c.h" != p.outs[0] { 119 t.FailNow() 120 } 121 if 3 != len(p.ins) { 122 t.FailNow() 123 } 124 if "\\\\ " != p.ins[0] { 125 t.FailNow() 126 } 127 if "\\\\\\\\" != p.ins[1] { 128 t.FailNow() 129 } 130 if "\\\\share\\info\\#1" != p.ins[2] { 131 t.FailNow() 132 } 133 } 134 135 func TestDepfileParserTest_Escapes(t *testing.T) { 136 // Put backslashes before a variety of characters, see which ones make 137 // it through. 138 p := parse(t, "\\!\\@\\#$$\\%\\^\\&\\[\\]\\\\:") 139 if 1 != len(p.outs) { 140 t.Fatal(p.outs) 141 } 142 if diff := cmp.Diff("\\!\\@#$\\%\\^\\&\\[\\]\\\\", p.outs[0]); diff != "" { 143 t.Fatal(diff) 144 } 145 if 0 != len(p.ins) { 146 t.Fatal(p.ins) 147 } 148 } 149 150 func TestDepfileParserTest_EscapedColons(t *testing.T) { 151 // Tests for correct parsing of depfiles produced on Windows 152 // by both Clang, GCC pre 10 and GCC 10 153 p := parse(t, "c\\:\\gcc\\x86_64-w64-mingw32\\include\\stddef.o: \\\n c:\\gcc\\x86_64-w64-mingw32\\include\\stddef.h \n") 154 if 1 != len(p.outs) { 155 t.FailNow() 156 } 157 if "c:\\gcc\\x86_64-w64-mingw32\\include\\stddef.o" != p.outs[0] { 158 t.FailNow() 159 } 160 if 1 != len(p.ins) { 161 t.FailNow() 162 } 163 if "c:\\gcc\\x86_64-w64-mingw32\\include\\stddef.h" != p.ins[0] { 164 t.FailNow() 165 } 166 } 167 168 func TestDepfileParserTest_EscapedTargetColon(t *testing.T) { 169 p := parse(t, "foo1\\: x\nfoo1\\:\nfoo1\\:\r\nfoo1\\:\t\nfoo1\\:") 170 if 1 != len(p.outs) { 171 t.FailNow() 172 } 173 if "foo1\\" != p.outs[0] { 174 t.FailNow() 175 } 176 if 1 != len(p.ins) { 177 t.FailNow() 178 } 179 if "x" != p.ins[0] { 180 t.FailNow() 181 } 182 } 183 184 func TestDepfileParserTest_SpecialChars(t *testing.T) { 185 // See filenames like istreambuf.iteratorOp!= in 186 // https://github.com/google/libcxx/tree/master/test/iterators/stream.iterators/istreambuf.iterator/ 187 p := parse(t, "C:/Program\\ Files\\ (x86)/Microsoft\\ crtdefs.h: \\\n en@quot.header~ t+t-x!=1 \\\n openldap/slapd.d/cn=config/cn=schema/cn={0}core.ldif\\\n Fu\303\244ball\\\n a[1]b@2%c") 188 if 1 != len(p.outs) { 189 t.FailNow() 190 } 191 if "C:/Program Files (x86)/Microsoft crtdefs.h" != p.outs[0] { 192 t.FailNow() 193 } 194 if 5 != len(p.ins) { 195 t.FailNow() 196 } 197 if "en@quot.header~" != p.ins[0] { 198 t.FailNow() 199 } 200 if "t+t-x!=1" != p.ins[1] { 201 t.FailNow() 202 } 203 if "openldap/slapd.d/cn=config/cn=schema/cn={0}core.ldif" != p.ins[2] { 204 t.FailNow() 205 } 206 if "Fu\303\244ball" != p.ins[3] { 207 t.FailNow() 208 } 209 if "a[1]b@2%c" != p.ins[4] { 210 t.FailNow() 211 } 212 } 213 214 func TestDepfileParserTest_UnifyMultipleOutputs(t *testing.T) { 215 // check that multiple duplicate targets are properly unified 216 p := parse(t, "foo foo: x y z") 217 if "foo" != p.outs[0] { 218 t.FailNow() 219 } 220 if 3 != len(p.ins) { 221 t.FailNow() 222 } 223 if "x" != p.ins[0] { 224 t.FailNow() 225 } 226 if "y" != p.ins[1] { 227 t.FailNow() 228 } 229 if "z" != p.ins[2] { 230 t.FailNow() 231 } 232 } 233 234 func TestDepfileParserTest_MultipleDifferentOutputs(t *testing.T) { 235 // check that multiple different outputs are accepted by the parser 236 p := parse(t, "foo bar: x y z") 237 if 2 != len(p.outs) { 238 t.FailNow() 239 } 240 if "foo" != p.outs[0] { 241 t.FailNow() 242 } 243 if "bar" != p.outs[1] { 244 t.FailNow() 245 } 246 if 3 != len(p.ins) { 247 t.FailNow() 248 } 249 if "x" != p.ins[0] { 250 t.FailNow() 251 } 252 if "y" != p.ins[1] { 253 t.FailNow() 254 } 255 if "z" != p.ins[2] { 256 t.FailNow() 257 } 258 } 259 260 func TestDepfileParserTest_MultipleEmptyRules(t *testing.T) { 261 p := parse(t, "foo: x\nfoo: \nfoo:\n") 262 if 1 != len(p.outs) { 263 t.FailNow() 264 } 265 if "foo" != p.outs[0] { 266 t.FailNow() 267 } 268 if 1 != len(p.ins) { 269 t.FailNow() 270 } 271 if "x" != p.ins[0] { 272 t.FailNow() 273 } 274 } 275 276 func TestDepfileParserTest_UnifyMultipleRulesLF(t *testing.T) { 277 p := parse(t, "foo: x\nfoo: y\nfoo \\\nfoo: z\n") 278 if 1 != len(p.outs) { 279 t.FailNow() 280 } 281 if "foo" != p.outs[0] { 282 t.FailNow() 283 } 284 if 3 != len(p.ins) { 285 t.FailNow() 286 } 287 if "x" != p.ins[0] { 288 t.FailNow() 289 } 290 if "y" != p.ins[1] { 291 t.FailNow() 292 } 293 if "z" != p.ins[2] { 294 t.FailNow() 295 } 296 } 297 298 func TestDepfileParserTest_UnifyMultipleRulesCRLF(t *testing.T) { 299 p := parse(t, "foo: x\r\nfoo: y\r\nfoo \\\r\nfoo: z\r\n") 300 if 1 != len(p.outs) { 301 t.FailNow() 302 } 303 if "foo" != p.outs[0] { 304 t.FailNow() 305 } 306 if 3 != len(p.ins) { 307 t.FailNow() 308 } 309 if "x" != p.ins[0] { 310 t.FailNow() 311 } 312 if "y" != p.ins[1] { 313 t.FailNow() 314 } 315 if "z" != p.ins[2] { 316 t.FailNow() 317 } 318 } 319 320 func TestDepfileParserTest_UnifyMixedRulesLF(t *testing.T) { 321 p := parse(t, "foo: x\\\n y\nfoo \\\nfoo: z\n") 322 if 1 != len(p.outs) { 323 t.FailNow() 324 } 325 if "foo" != p.outs[0] { 326 t.FailNow() 327 } 328 if 3 != len(p.ins) { 329 t.FailNow() 330 } 331 if "x" != p.ins[0] { 332 t.FailNow() 333 } 334 if "y" != p.ins[1] { 335 t.FailNow() 336 } 337 if "z" != p.ins[2] { 338 t.FailNow() 339 } 340 } 341 342 func TestDepfileParserTest_UnifyMixedRulesCRLF(t *testing.T) { 343 p := parse(t, "foo: x\\\r\n y\r\nfoo \\\r\nfoo: z\r\n") 344 if 1 != len(p.outs) { 345 t.FailNow() 346 } 347 if "foo" != p.outs[0] { 348 t.FailNow() 349 } 350 if 3 != len(p.ins) { 351 t.FailNow() 352 } 353 if "x" != p.ins[0] { 354 t.FailNow() 355 } 356 if "y" != p.ins[1] { 357 t.FailNow() 358 } 359 if "z" != p.ins[2] { 360 t.FailNow() 361 } 362 } 363 364 func TestDepfileParserTest_IndentedRulesLF(t *testing.T) { 365 p := parse(t, " foo: x\n foo: y\n foo: z\n") 366 if 1 != len(p.outs) { 367 t.FailNow() 368 } 369 if "foo" != p.outs[0] { 370 t.FailNow() 371 } 372 if 3 != len(p.ins) { 373 t.FailNow() 374 } 375 if "x" != p.ins[0] { 376 t.FailNow() 377 } 378 if "y" != p.ins[1] { 379 t.FailNow() 380 } 381 if "z" != p.ins[2] { 382 t.FailNow() 383 } 384 } 385 386 func TestDepfileParserTest_IndentedRulesCRLF(t *testing.T) { 387 p := parse(t, " foo: x\r\n foo: y\r\n foo: z\r\n") 388 if 1 != len(p.outs) { 389 t.FailNow() 390 } 391 if "foo" != p.outs[0] { 392 t.FailNow() 393 } 394 if 3 != len(p.ins) { 395 t.FailNow() 396 } 397 if "x" != p.ins[0] { 398 t.FailNow() 399 } 400 if "y" != p.ins[1] { 401 t.FailNow() 402 } 403 if "z" != p.ins[2] { 404 t.FailNow() 405 } 406 } 407 408 func TestDepfileParserTest_TolerateMP(t *testing.T) { 409 p := parse(t, "foo: x y z\nx:\ny:\nz:\n") 410 if 1 != len(p.outs) { 411 t.FailNow() 412 } 413 if "foo" != p.outs[0] { 414 t.FailNow() 415 } 416 if 3 != len(p.ins) { 417 t.FailNow() 418 } 419 if "x" != p.ins[0] { 420 t.FailNow() 421 } 422 if "y" != p.ins[1] { 423 t.FailNow() 424 } 425 if "z" != p.ins[2] { 426 t.FailNow() 427 } 428 } 429 430 func TestDepfileParserTest_MultipleRulesTolerateMP(t *testing.T) { 431 p := parse(t, "foo: x\nx:\nfoo: y\ny:\nfoo: z\nz:\n") 432 if 1 != len(p.outs) { 433 t.FailNow() 434 } 435 if "foo" != p.outs[0] { 436 t.FailNow() 437 } 438 if 3 != len(p.ins) { 439 t.FailNow() 440 } 441 if "x" != p.ins[0] { 442 t.FailNow() 443 } 444 if "y" != p.ins[1] { 445 t.FailNow() 446 } 447 if "z" != p.ins[2] { 448 t.FailNow() 449 } 450 } 451 452 func TestDepfileParserTest_MultipleRulesDifferentOutputs(t *testing.T) { 453 // check that multiple different outputs are accepted by the parser 454 // when spread across multiple rules 455 p := parse(t, "foo: x y\nbar: y z\n") 456 if 2 != len(p.outs) { 457 t.FailNow() 458 } 459 if "foo" != p.outs[0] { 460 t.FailNow() 461 } 462 if "bar" != p.outs[1] { 463 t.FailNow() 464 } 465 if 3 != len(p.ins) { 466 t.FailNow() 467 } 468 if "x" != p.ins[0] { 469 t.FailNow() 470 } 471 if "y" != p.ins[1] { 472 t.FailNow() 473 } 474 if "z" != p.ins[2] { 475 t.FailNow() 476 } 477 } 478 479 func TestDepfileParserTest_BuggyMP(t *testing.T) { 480 p := DepfileParser{} 481 if err := p.Parse([]byte("foo: x y z\nx: alsoin\ny:\nz:\n\x00")); err == nil { 482 t.Error("unexpected Parse success") 483 } else if err.Error() != "inputs may not also have inputs" { 484 t.Fatal(err) 485 } 486 }