github.com/sdboyer/gps@v0.16.3/hash_test.go (about)

     1  package gps
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/sha256"
     6  	"fmt"
     7  	"strings"
     8  	"testing"
     9  	"text/tabwriter"
    10  )
    11  
    12  func TestHashInputs(t *testing.T) {
    13  	fix := basicFixtures["shared dependency with overlapping constraints"]
    14  
    15  	params := SolveParameters{
    16  		RootDir:         string(fix.ds[0].n),
    17  		RootPackageTree: fix.rootTree(),
    18  		Manifest:        fix.rootmanifest(),
    19  		ProjectAnalyzer: naiveAnalyzer{},
    20  	}
    21  
    22  	s, err := Prepare(params, newdepspecSM(fix.ds, nil))
    23  	if err != nil {
    24  		t.Fatalf("Unexpected error while prepping solver: %s", err)
    25  	}
    26  
    27  	dig := s.HashInputs()
    28  	h := sha256.New()
    29  
    30  	elems := []string{
    31  		hhConstraints,
    32  		"a",
    33  		"sv-1.0.0",
    34  		"b",
    35  		"sv-1.0.0",
    36  		hhImportsReqs,
    37  		"a",
    38  		"b",
    39  		hhIgnores,
    40  		hhOverrides,
    41  		hhAnalyzer,
    42  		"naive-analyzer",
    43  		"1",
    44  	}
    45  	for _, v := range elems {
    46  		h.Write([]byte(v))
    47  	}
    48  	correct := h.Sum(nil)
    49  
    50  	if !bytes.Equal(dig, correct) {
    51  		t.Errorf("Hashes are not equal. Inputs:\n%s", diffHashingInputs(s, elems))
    52  	} else if strings.Join(elems, "\n")+"\n" != HashingInputsAsString(s) {
    53  		t.Errorf("Hashes are equal, but hashing input strings are not:\n%s", diffHashingInputs(s, elems))
    54  	}
    55  }
    56  
    57  func TestHashInputsReqsIgs(t *testing.T) {
    58  	fix := basicFixtures["shared dependency with overlapping constraints"]
    59  
    60  	rm := fix.rootmanifest().(simpleRootManifest).dup()
    61  	rm.ig = map[string]bool{
    62  		"foo": true,
    63  		"bar": true,
    64  	}
    65  
    66  	params := SolveParameters{
    67  		RootDir:         string(fix.ds[0].n),
    68  		RootPackageTree: fix.rootTree(),
    69  		Manifest:        rm,
    70  		ProjectAnalyzer: naiveAnalyzer{},
    71  	}
    72  
    73  	s, err := Prepare(params, newdepspecSM(fix.ds, nil))
    74  	if err != nil {
    75  		t.Fatalf("Unexpected error while prepping solver: %s", err)
    76  	}
    77  
    78  	dig := s.HashInputs()
    79  	h := sha256.New()
    80  
    81  	elems := []string{
    82  		hhConstraints,
    83  		"a",
    84  		"sv-1.0.0",
    85  		"b",
    86  		"sv-1.0.0",
    87  		hhImportsReqs,
    88  		"a",
    89  		"b",
    90  		hhIgnores,
    91  		"bar",
    92  		"foo",
    93  		hhOverrides,
    94  		hhAnalyzer,
    95  		"naive-analyzer",
    96  		"1",
    97  	}
    98  	for _, v := range elems {
    99  		h.Write([]byte(v))
   100  	}
   101  	correct := h.Sum(nil)
   102  
   103  	if !bytes.Equal(dig, correct) {
   104  		t.Errorf("Hashes are not equal. Inputs:\n%s", diffHashingInputs(s, elems))
   105  	}
   106  
   107  	// Add requires
   108  	rm.req = map[string]bool{
   109  		"baz": true,
   110  		"qux": true,
   111  	}
   112  
   113  	params.Manifest = rm
   114  
   115  	s, err = Prepare(params, newdepspecSM(fix.ds, nil))
   116  	if err != nil {
   117  		t.Fatalf("Unexpected error while prepping solver: %s", err)
   118  	}
   119  
   120  	dig = s.HashInputs()
   121  	h = sha256.New()
   122  
   123  	elems = []string{
   124  		hhConstraints,
   125  		"a",
   126  		"sv-1.0.0",
   127  		"b",
   128  		"sv-1.0.0",
   129  		hhImportsReqs,
   130  		"a",
   131  		"b",
   132  		"baz",
   133  		"qux",
   134  		hhIgnores,
   135  		"bar",
   136  		"foo",
   137  		hhOverrides,
   138  		hhAnalyzer,
   139  		"naive-analyzer",
   140  		"1",
   141  	}
   142  	for _, v := range elems {
   143  		h.Write([]byte(v))
   144  	}
   145  	correct = h.Sum(nil)
   146  
   147  	if !bytes.Equal(dig, correct) {
   148  		t.Errorf("Hashes are not equal. Inputs:\n%s", diffHashingInputs(s, elems))
   149  	}
   150  
   151  	// remove ignores, just test requires alone
   152  	rm.ig = nil
   153  	params.Manifest = rm
   154  
   155  	s, err = Prepare(params, newdepspecSM(fix.ds, nil))
   156  	if err != nil {
   157  		t.Fatalf("Unexpected error while prepping solver: %s", err)
   158  	}
   159  
   160  	dig = s.HashInputs()
   161  	h = sha256.New()
   162  
   163  	elems = []string{
   164  		hhConstraints,
   165  		"a",
   166  		"sv-1.0.0",
   167  		"b",
   168  		"sv-1.0.0",
   169  		hhImportsReqs,
   170  		"a",
   171  		"b",
   172  		"baz",
   173  		"qux",
   174  		hhIgnores,
   175  		hhOverrides,
   176  		hhAnalyzer,
   177  		"naive-analyzer",
   178  		"1",
   179  	}
   180  	for _, v := range elems {
   181  		h.Write([]byte(v))
   182  	}
   183  	correct = h.Sum(nil)
   184  
   185  	if !bytes.Equal(dig, correct) {
   186  		t.Errorf("Hashes are not equal. Inputs:\n%s", diffHashingInputs(s, elems))
   187  	}
   188  }
   189  
   190  func TestHashInputsOverrides(t *testing.T) {
   191  	basefix := basicFixtures["shared dependency with overlapping constraints"]
   192  
   193  	// Set up base state that we'll mutate over the course of each test
   194  	rm := basefix.rootmanifest().(simpleRootManifest).dup()
   195  	params := SolveParameters{
   196  		RootDir:         string(basefix.ds[0].n),
   197  		RootPackageTree: basefix.rootTree(),
   198  		Manifest:        rm,
   199  		ProjectAnalyzer: naiveAnalyzer{},
   200  	}
   201  
   202  	table := []struct {
   203  		name  string
   204  		mut   func()
   205  		elems []string
   206  	}{
   207  		{
   208  			name: "override source; not imported, no deps pp",
   209  			mut: func() {
   210  				// First case - override just source, on something without
   211  				// corresponding project properties in the dependencies from
   212  				// root
   213  				rm.ovr = map[ProjectRoot]ProjectProperties{
   214  					"c": ProjectProperties{
   215  						Source: "car",
   216  					},
   217  				}
   218  			},
   219  			elems: []string{
   220  				hhConstraints,
   221  				"a",
   222  				"sv-1.0.0",
   223  				"b",
   224  				"sv-1.0.0",
   225  				hhImportsReqs,
   226  				"a",
   227  				"b",
   228  				hhIgnores,
   229  				hhOverrides,
   230  				"c",
   231  				"car",
   232  				hhAnalyzer,
   233  				"naive-analyzer",
   234  				"1",
   235  			},
   236  		},
   237  		{
   238  			name: "override source; required, no deps pp",
   239  			mut: func() {
   240  				// Put c into the requires list, which should make it show up under
   241  				// constraints
   242  				rm.req = map[string]bool{
   243  					"c": true,
   244  				}
   245  			},
   246  			elems: []string{
   247  				hhConstraints,
   248  				"a",
   249  				"sv-1.0.0",
   250  				"b",
   251  				"sv-1.0.0",
   252  				"c",
   253  				"car",
   254  				"any-*", // Any isn't included under the override, but IS for the constraint b/c it's equivalent
   255  				hhImportsReqs,
   256  				"a",
   257  				"b",
   258  				"c",
   259  				hhIgnores,
   260  				hhOverrides,
   261  				"c",
   262  				"car",
   263  				hhAnalyzer,
   264  				"naive-analyzer",
   265  				"1",
   266  			},
   267  		},
   268  		{
   269  			name: "override source; required & imported, no deps pp",
   270  			mut: func() {
   271  				// Put c in the root's imports
   272  				poe := params.RootPackageTree.Packages["root"]
   273  				poe.P.Imports = []string{"a", "b", "c"}
   274  				params.RootPackageTree.Packages["root"] = poe
   275  			},
   276  			elems: []string{
   277  				hhConstraints,
   278  				"a",
   279  				"sv-1.0.0",
   280  				"b",
   281  				"sv-1.0.0",
   282  				"c",
   283  				"car",
   284  				"any-*", // Any isn't included under the override, but IS for the constraint b/c it's equivalent
   285  				hhImportsReqs,
   286  				"a",
   287  				"b",
   288  				"c",
   289  				hhIgnores,
   290  				hhOverrides,
   291  				"c",
   292  				"car",
   293  				hhAnalyzer,
   294  				"naive-analyzer",
   295  				"1",
   296  			},
   297  		},
   298  		{
   299  			name: "override source; imported, no deps pp",
   300  			mut: func() {
   301  				// Take c out of requires list - now it's only imported
   302  				rm.req = nil
   303  			},
   304  			elems: []string{
   305  				hhConstraints,
   306  				"a",
   307  				"sv-1.0.0",
   308  				"b",
   309  				"sv-1.0.0",
   310  				"c",
   311  				"car",
   312  				"any-*",
   313  				hhImportsReqs,
   314  				"a",
   315  				"b",
   316  				"c",
   317  				hhIgnores,
   318  				hhOverrides,
   319  				"c",
   320  				"car",
   321  				hhAnalyzer,
   322  				"naive-analyzer",
   323  				"1",
   324  			},
   325  		},
   326  		{
   327  			name: "other override constraint; not imported, no deps pp",
   328  			mut: func() {
   329  				// Override not in root, just with constraint
   330  				rm.ovr["d"] = ProjectProperties{
   331  					Constraint: NewBranch("foobranch"),
   332  				}
   333  			},
   334  			elems: []string{
   335  				hhConstraints,
   336  				"a",
   337  				"sv-1.0.0",
   338  				"b",
   339  				"sv-1.0.0",
   340  				"c",
   341  				"car",
   342  				"any-*",
   343  				hhImportsReqs,
   344  				"a",
   345  				"b",
   346  				"c",
   347  				hhIgnores,
   348  				hhOverrides,
   349  				"c",
   350  				"car",
   351  				"d",
   352  				"b-foobranch",
   353  				hhAnalyzer,
   354  				"naive-analyzer",
   355  				"1",
   356  			},
   357  		},
   358  		{
   359  			name: "override constraint; not imported, no deps pp",
   360  			mut: func() {
   361  				// Remove the "c" pkg from imports for remainder of tests
   362  				poe := params.RootPackageTree.Packages["root"]
   363  				poe.P.Imports = []string{"a", "b"}
   364  				params.RootPackageTree.Packages["root"] = poe
   365  			},
   366  			elems: []string{
   367  				hhConstraints,
   368  				"a",
   369  				"sv-1.0.0",
   370  				"b",
   371  				"sv-1.0.0",
   372  				hhImportsReqs,
   373  				"a",
   374  				"b",
   375  				hhIgnores,
   376  				hhOverrides,
   377  				"c",
   378  				"car",
   379  				"d",
   380  				"b-foobranch",
   381  				hhAnalyzer,
   382  				"naive-analyzer",
   383  				"1",
   384  			},
   385  		},
   386  		{
   387  			name: "override both; not imported, no deps pp",
   388  			mut: func() {
   389  				// Override not in root, both constraint and network name
   390  				rm.ovr["c"] = ProjectProperties{
   391  					Source:     "groucho",
   392  					Constraint: NewBranch("plexiglass"),
   393  				}
   394  			},
   395  			elems: []string{
   396  				hhConstraints,
   397  				"a",
   398  				"sv-1.0.0",
   399  				"b",
   400  				"sv-1.0.0",
   401  				hhImportsReqs,
   402  				"a",
   403  				"b",
   404  				hhIgnores,
   405  				hhOverrides,
   406  				"c",
   407  				"groucho",
   408  				"b-plexiglass",
   409  				"d",
   410  				"b-foobranch",
   411  				hhAnalyzer,
   412  				"naive-analyzer",
   413  				"1",
   414  			},
   415  		},
   416  		{
   417  			name: "override constraint; imported, with constraint",
   418  			mut: func() {
   419  				// Override dep present in root, just constraint
   420  				rm.ovr["a"] = ProjectProperties{
   421  					Constraint: NewVersion("fluglehorn"),
   422  				}
   423  			},
   424  			elems: []string{
   425  				hhConstraints,
   426  				"a",
   427  				"pv-fluglehorn",
   428  				"b",
   429  				"sv-1.0.0",
   430  				hhImportsReqs,
   431  				"a",
   432  				"b",
   433  				hhIgnores,
   434  				hhOverrides,
   435  				"a",
   436  				"pv-fluglehorn",
   437  				"c",
   438  				"groucho",
   439  				"b-plexiglass",
   440  				"d",
   441  				"b-foobranch",
   442  				hhAnalyzer,
   443  				"naive-analyzer",
   444  				"1",
   445  			},
   446  		},
   447  		{
   448  			name: "override source; imported, with constraint",
   449  			mut: func() {
   450  				// Override in root, only network name
   451  				rm.ovr["a"] = ProjectProperties{
   452  					Source: "nota",
   453  				}
   454  			},
   455  			elems: []string{
   456  				hhConstraints,
   457  				"a",
   458  				"nota",
   459  				"sv-1.0.0",
   460  				"b",
   461  				"sv-1.0.0",
   462  				hhImportsReqs,
   463  				"a",
   464  				"b",
   465  				hhIgnores,
   466  				hhOverrides,
   467  				"a",
   468  				"nota",
   469  				"c",
   470  				"groucho",
   471  				"b-plexiglass",
   472  				"d",
   473  				"b-foobranch",
   474  				hhAnalyzer,
   475  				"naive-analyzer",
   476  				"1",
   477  			},
   478  		},
   479  		{
   480  			name: "override both; imported, with constraint",
   481  			mut: func() {
   482  				// Override in root, network name and constraint
   483  				rm.ovr["a"] = ProjectProperties{
   484  					Source:     "nota",
   485  					Constraint: NewVersion("fluglehorn"),
   486  				}
   487  			},
   488  			elems: []string{
   489  				hhConstraints,
   490  				"a",
   491  				"nota",
   492  				"pv-fluglehorn",
   493  				"b",
   494  				"sv-1.0.0",
   495  				hhImportsReqs,
   496  				"a",
   497  				"b",
   498  				hhIgnores,
   499  				hhOverrides,
   500  				"a",
   501  				"nota",
   502  				"pv-fluglehorn",
   503  				"c",
   504  				"groucho",
   505  				"b-plexiglass",
   506  				"d",
   507  				"b-foobranch",
   508  				hhAnalyzer,
   509  				"naive-analyzer",
   510  				"1",
   511  			},
   512  		},
   513  	}
   514  
   515  	for _, fix := range table {
   516  		fix.mut()
   517  		params.Manifest = rm
   518  
   519  		s, err := Prepare(params, newdepspecSM(basefix.ds, nil))
   520  		if err != nil {
   521  			t.Fatalf("(fix: %q) Unexpected error while prepping solver: %s", fix.name, err)
   522  		}
   523  
   524  		h := sha256.New()
   525  		for _, v := range fix.elems {
   526  			h.Write([]byte(v))
   527  		}
   528  
   529  		if !bytes.Equal(s.HashInputs(), h.Sum(nil)) {
   530  			t.Errorf("(fix: %q) Hashes are not equal. Inputs:\n%s", fix.name, diffHashingInputs(s, fix.elems))
   531  		}
   532  	}
   533  }
   534  
   535  func diffHashingInputs(s Solver, wnt []string) string {
   536  	actual := HashingInputsAsString(s)
   537  	got := strings.Split(actual, "\n")
   538  	// got has a trailing empty, add that to wnt
   539  	wnt = append(wnt, "")
   540  
   541  	lg, lw := len(got), len(wnt)
   542  
   543  	var buf bytes.Buffer
   544  	tw := tabwriter.NewWriter(&buf, 4, 4, 2, ' ', 0)
   545  	fmt.Fprintln(tw, "  (GOT)  \t  (WANT)  \t")
   546  
   547  	lmiss, rmiss := ">>>>>>>>>>", "<<<<<<<<<<"
   548  	if lg == lw {
   549  		// same length makes the loop pretty straightforward
   550  		for i := 0; i < lg; i++ {
   551  			fmt.Fprintf(tw, "%s\t%s\t\n", got[i], wnt[i])
   552  		}
   553  	} else if lg > lw {
   554  		offset := 0
   555  		for i := 0; i < lg; i++ {
   556  			if lw <= i-offset {
   557  				fmt.Fprintf(tw, "%s\t%s\t\n", got[i], rmiss)
   558  			} else if got[i] != wnt[i-offset] && i+1 < lg && got[i+1] == wnt[i-offset] {
   559  				// if the next slot is a match, realign by skipping this one and
   560  				// bumping the offset
   561  				fmt.Fprintf(tw, "%s\t%s\t\n", got[i], rmiss)
   562  				offset++
   563  			} else {
   564  				fmt.Fprintf(tw, "%s\t%s\t\n", got[i], wnt[i-offset])
   565  			}
   566  		}
   567  	} else {
   568  		offset := 0
   569  		for i := 0; i < lw; i++ {
   570  			if lg <= i-offset {
   571  				fmt.Fprintf(tw, "%s\t%s\t\n", lmiss, wnt[i])
   572  			} else if got[i-offset] != wnt[i] && i+1 < lw && got[i-offset] == wnt[i+1] {
   573  				// if the next slot is a match, realign by skipping this one and
   574  				// bumping the offset
   575  				fmt.Fprintf(tw, "%s\t%s\t\n", lmiss, wnt[i])
   576  				offset++
   577  			} else {
   578  				fmt.Fprintf(tw, "%s\t%s\t\n", got[i-offset], wnt[i])
   579  			}
   580  		}
   581  	}
   582  
   583  	tw.Flush()
   584  	return buf.String()
   585  }