/* Checks round-trip hash consistency for the input and output halves of a transmat system. - Creates a fixture filesystem - Scans it with the output system - Places it in a new filesystem with the input system and the scanned hash - Checks the new filesystem matches the original */ func CheckRoundTrip(kind integrity.TransmatKind, transmatFabFn integrity.TransmatFactory, bounceURI string, addtnlDesc ...string) { Convey("SPEC: Round-trip scanning and remaking a filesystem should agree on hash and content"+testutil.AdditionalDescription(addtnlDesc...), testutil.Requires( testutil.RequiresRoot, func() { transmat := transmatFabFn("./workdir") for _, fixture := range filefixture.All { Convey(fmt.Sprintf("- Fixture %q", fixture.Name), FailureContinues, func() { uris := []integrity.SiloURI{integrity.SiloURI(bounceURI)} // setup fixture fixture.Create("./fixture") // scan it with the transmat dataHash := transmat.Scan(kind, "./fixture", uris) // materialize what we just scanned (along the way, requires hash match) arena := transmat.Materialize(kind, dataHash, uris, integrity.AcceptHashMismatch) // assert hash match // (normally survival would attest this, but we used the `AcceptHashMismatch` to supress panics in the name of letting the test see more after failures.) So(arena.Hash(), ShouldEqual, dataHash) // check filesystem to match original fixture // (do this check even if the input raised a hash mismatch, because it can help show why) rescan := filefixture.Scan(arena.Path()) comparisonLevel := filefixture.CompareDefaults &^ filefixture.CompareSubsecond So(rescan.Describe(comparisonLevel), ShouldEqual, fixture.Describe(comparisonLevel)) }) } }, )) }
func TestTarOutputCompat(t *testing.T) { Convey("Output should produce a tar recognizable to gnu tar", t, testutil.Requires( testutil.RequiresRoot, testutil.WithTmpdir(func() { for _, fixture := range filefixture.All { Convey(fmt.Sprintf("- Fixture %q", fixture.Name), func() { // create fixture fixture.Create("./data") // scan it transmat := New("./workdir") transmat.Scan( Kind, "./data", []integrity.SiloURI{ integrity.SiloURI("file://output.tar"), }, ) // sanity check that there's a file. So("./output.tar", testutil.ShouldBeFile, os.FileMode(0)) // now exec tar, and check that it doesn't barf outright. // this is not well isolated from the host; consider improving that a todo. os.Mkdir("./untar", 0755) tarProc := gosh.Gosh( "tar", "-xf", "./output.tar", "-C", "./untar", gosh.NullIO, ).RunAndReport() So(tarProc.GetExitCode(), ShouldEqual, 0) // should look roughly the same again even bounced through // some third-party tar implementation, one would hope. rescan := filefixture.Scan("./untar") comparisonLevel := filefixture.CompareDefaults &^ filefixture.CompareSubsecond So(rescan.Describe(comparisonLevel), ShouldEqual, fixture.Describe(comparisonLevel)) }) } }), ), ) }
func CheckAssemblerIsolatesSource(assemblerFn integrity.Assembler) { Convey("Writing to a placement should not alter the source", testutil.Requires( testutil.RequiresRoot, testutil.WithTmpdir(func() { filefixture.Alpha.Create("./material/alpha") assembly := assemblerFn("./assembled", []integrity.AssemblyPart{ {TargetPath: "/", SourcePath: "./material/alpha", Writable: true}, }) defer assembly.Teardown() f, err := os.OpenFile("./assembled/newfile", os.O_CREATE, 0644) defer f.Close() So(err, ShouldBeNil) scan := filefixture.Scan("./material/alpha") So(scan.Describe(filefixture.CompareDefaults), ShouldEqual, filefixture.Alpha.Describe(filefixture.CompareDefaults)) }), ), ) }
func assembleAndScan(assemblerFn integrity.Assembler, parts []integrity.AssemblyPart, expected filefixture.Fixture) { Convey("Assembly should not blow up", FailureContinues, func() { var assembly integrity.Assembly So(func() { assembly = assemblerFn("./assembled", parts) }, ShouldNotPanic) Reset(func() { if assembly != nil { // conditional only because we may have continued moving after an error earlier. assembly.Teardown() } }) Convey("Filesystem should scan as the expected union", func() { scan := filefixture.Scan("./assembled") So(scan.Describe(filefixture.CompareDefaults), ShouldEqual, expected.Describe(filefixture.CompareDefaults)) }) }) }
func CheckScanWithoutMutation(kind integrity.TransmatKind, transmatFabFn integrity.TransmatFactory) { Convey("SPEC: Scanning a filesystem shouldn't change it", testutil.Requires( testutil.RequiresRoot, func() { for _, fixture := range filefixture.All { transmat := transmatFabFn("./workdir") Convey(fmt.Sprintf("- Fixture %q", fixture.Name), func() { // set up fixture fixture.Create("./data") // scan it with the transmat transmat.Scan(kind, "./data", nil) // rescan it with the test system rescan := filefixture.Scan("./data") // should be unchanged So(rescan.Describe(filefixture.CompareDefaults), ShouldEqual, fixture.Describe(filefixture.CompareDefaults)) }) } }, )) }
func TestTarInputCompat(t *testing.T) { projPath, _ := os.Getwd() projPath = filepath.Dir(filepath.Dir(filepath.Dir(projPath))) Convey("Unpacking tars should match exec untar", t, testutil.Requires(testutil.RequiresRoot, testutil.WithTmpdir(func() { checkEquivalence := func(hash, filename string, paveBase bool) { transmat := New("./workdir/tar") // apply it; hope it doesn't blow up arena := transmat.Materialize( integrity.TransmatKind("tar"), integrity.CommitID(hash), []integrity.SiloURI{ integrity.SiloURI("file://" + filename), }, ) defer arena.Teardown() // do a native untar; since we don't have an upfront fixture // for this thing, we'll compare the two as filesystems. // this is not well isolated from the host; consider improving that a todo. os.Mkdir("./untar", 0755) tarProc := gosh.Gosh( "tar", "-xf", filename, "-C", "./untar", gosh.NullIO, ).RunAndReport() So(tarProc.GetExitCode(), ShouldEqual, 0) // native untar may or may not have an opinion about the base dir, depending on how it was formed. // but our scans do, so, if the `paveBase` flag was set to warn us that the tar was missing an "./" entry, flatten that here. if paveBase { So(fspatch.LUtimesNano("./untar", def.Epochwhen, def.Epochwhen), ShouldBeNil) } // scan and compare scan1 := filefixture.Scan(arena.Path()) scan2 := filefixture.Scan("./untar") // boy, that's entertaining though: gnu tar does all the same stuff, // except it doesn't honor our nanosecond timings. // also exclude bodies because they're *big*. comparisonLevel := filefixture.CompareDefaults &^ filefixture.CompareSubsecond &^ filefixture.CompareBody So(scan1.Describe(comparisonLevel), ShouldEqual, scan2.Describe(comparisonLevel)) } Convey("Given a fixture tarball complete with base dir", func() { checkEquivalence( "BX0jm4jRNCg1KMbZfv4zp7ZaShx9SUXKaDrO-Xy6mWIoWOCFP5VnDHDDR3nU4PrR", filepath.Join(projPath, "data/fixture/tar_withBase.tgz"), false, ) }) Convey("Given a fixture tarball lacking base dir", func() { checkEquivalence( "ZdV3xhCGWeJmsfeHpDF4nF9stwvdskYwcepKMcOf7a2ziax1YGjQvGTJjRWFkvG1", filepath.Join(projPath, "data/fixture/tar_sansBase.tgz"), true, ) }) Convey("Given a fixture tarball containing ubuntu", testutil.Requires(testutil.RequiresLongRun, func() { checkEquivalence( ubuntuTarballHash, filepath.Join(projPath, "assets/ubuntu.tar.gz"), true, ) }), ) })), ) Convey("Bouncing unusual tars should match hash", t, // where really all "unusual" means is "valid tar, but not from our own cannonical output". testutil.Requires( testutil.RequiresRoot, testutil.WithTmpdir(func() { checkBounce := func(hash, filename string) { transmat := New("./workdir/tar") // apply it; hope it doesn't blow up arena := transmat.Materialize( integrity.TransmatKind("tar"), integrity.CommitID(hash), []integrity.SiloURI{ integrity.SiloURI("file://" + filename), }, ) defer arena.Teardown() // scan and compare commitID := transmat.Scan(integrity.TransmatKind("tar"), arena.Path(), nil) // debug: gosh.Sh("tar", "--utc", "-xOvf", filename) So(commitID, ShouldEqual, integrity.CommitID(hash)) } Convey("Given a fixture tarball complete with base dir", func() { checkBounce( "BX0jm4jRNCg1KMbZfv4zp7ZaShx9SUXKaDrO-Xy6mWIoWOCFP5VnDHDDR3nU4PrR", filepath.Join(projPath, "data/fixture/tar_withBase.tgz"), ) }) Convey("Given a fixture tarball lacking base dir", func() { checkBounce( "ZdV3xhCGWeJmsfeHpDF4nF9stwvdskYwcepKMcOf7a2ziax1YGjQvGTJjRWFkvG1", filepath.Join(projPath, "data/fixture/tar_sansBase.tgz"), ) }) // this won't fly until we support hardlinks; the original asset uses them. // Convey("Given a fixture tarball containing ubuntu", // testutil.Requires(testutil.RequiresLongRun, func() { // checkBounce( // ubuntuTarballHash, // filepath.Join(projPath, "assets/ubuntu.tar.gz"), // ) // }), // ) }), ), ) }