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  }