func TestAllTheThings(t *testing.T) { perm := os.FileMode(0777) workingDir, err := ioutil.TempDir("", "butler-tests") mist(t, err) defer os.RemoveAll(workingDir) sample := path.Join(workingDir, "sample") mist(t, os.MkdirAll(sample, perm)) mist(t, ioutil.WriteFile(path.Join(sample, "hello.txt"), []byte("hello!"), perm)) sample2 := path.Join(workingDir, "sample2") mist(t, os.MkdirAll(sample2, perm)) for i := 0; i < 5; i++ { if i == 3 { // e.g. .gitkeep putfile(t, sample2, i, []byte{}) } else { putfile(t, sample2, i, bytes.Repeat([]byte{0x42, 0x69}, i*200+1)) } } sample3 := path.Join(workingDir, "sample3") mist(t, os.MkdirAll(sample3, perm)) for i := 0; i < 60; i++ { putfile(t, sample3, i, bytes.Repeat([]byte{0x42, 0x69}, i*300+1)) } sample4 := path.Join(workingDir, "sample4") mist(t, os.MkdirAll(sample4, perm)) for i := 0; i < 120; i++ { putfile(t, sample4, i, bytes.Repeat([]byte{0x42, 0x69}, i*150+1)) } sample5 := path.Join(workingDir, "sample5") mist(t, os.MkdirAll(sample5, perm)) rg := rand.New(rand.NewSource(0x239487)) for i := 0; i < 25; i++ { l := 1024 * (i + 2) // our own little twist on fizzbuzz to look out for 1-off errors if i%5 == 0 { l = int(pwr.BlockSize) } else if i%3 == 0 { l = 0 } buf := make([]byte, l) _, err := io.CopyN(bytes.NewBuffer(buf), rg, int64(l)) mist(t, err) putfile(t, sample5, i, buf) } files := map[string]string{ "hello": sample, "80-fixed": sample2, "60-fixed": sample3, "120-fixed": sample4, "random": sample5, "null": "/dev/null", } patch := path.Join(workingDir, "patch.pwr") comm.Configure(true, true, false, false, false, false, false) for _, q := range []int{1, 9} { t.Logf("============ Quality %d ============", q) compression := pwr.CompressionSettings{ Algorithm: pwr.CompressionAlgorithm_BROTLI, Quality: int32(q), } for lhs := range files { for rhs := range files { mist(t, doDiff(files[lhs], files[rhs], patch, compression)) stat, err := os.Lstat(patch) mist(t, err) t.Logf("%10s -> %10s = %s", lhs, rhs, humanize.IBytes(uint64(stat.Size()))) } } } compression := pwr.CompressionSettings{ Algorithm: pwr.CompressionAlgorithm_BROTLI, Quality: 1, } for _, filepath := range files { t.Logf("Signing %s\n", filepath) sigPath := path.Join(workingDir, "signature.pwr.sig") mist(t, doSign(filepath, sigPath, compression, false)) sigReader, err := eos.Open(sigPath) mist(t, err) signature, err := pwr.ReadSignature(sigReader) mist(t, err) mist(t, sigReader.Close()) validator := &pwr.ValidatorContext{ FailFast: true, } mist(t, validator.Validate(filepath, signature)) } // K windows you just sit this one out we'll catch you on the flip side if runtime.GOOS != "windows" { // In-place preserve permissions tests t.Logf("In-place patching should preserve permissions") eperm := os.FileMode(0750) samplePerm1 := path.Join(workingDir, "samplePerm1") mist(t, os.MkdirAll(samplePerm1, perm)) putfileEx(t, samplePerm1, 1, bytes.Repeat([]byte{0x42, 0x69}, 8192), eperm) assert.Equal(t, octal(eperm), octal(permFor(t, path.Join(samplePerm1, "dummy1.dat")))) samplePerm2 := path.Join(workingDir, "samplePerm2") mist(t, os.MkdirAll(samplePerm2, perm)) putfileEx(t, samplePerm2, 1, bytes.Repeat([]byte{0x69, 0x42}, 16384), eperm) assert.Equal(t, octal(eperm), octal(permFor(t, path.Join(samplePerm2, "dummy1.dat")))) mist(t, doDiff(samplePerm1, samplePerm2, patch, compression)) _, err := os.Lstat(patch) mist(t, err) cave := path.Join(workingDir, "cave") ditto(samplePerm1, cave) mist(t, doApply(patch, cave, cave, true, "", "")) assert.Equal(t, octal(eperm|pwr.ModeMask), octal(permFor(t, path.Join(cave, "dummy1.dat")))) } }
func main() { app.UsageTemplate(kingpin.CompactUsageTemplate) app.Flag("ignore", "Glob patterns of files to ignore when diffing").StringsVar(&ignoredPaths) app.HelpFlag.Short('h') if builtAt != "" { epoch, err := strconv.ParseInt(builtAt, 10, 64) must(err) versionString = fmt.Sprintf("%s, built on %s", version, time.Unix(epoch, 0).Format("Jan _2 2006 @ 15:04:05")) } else { versionString = fmt.Sprintf("%s, no build date", version) } if commit != "" { versionString = fmt.Sprintf("%s, ref %s", versionString, commit) } app.Version(versionString) app.VersionFlag.Short('V') app.Author("Amos Wenger <*****@*****.**>") cmd, err := app.Parse(os.Args[1:]) if err != nil { ctx, _ := app.ParseContext(os.Args[1:]) app.FatalUsageContext(ctx, "%s\n", err.Error()) } if *appArgs.timestamps { log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds) } else { log.SetFlags(0) } log.SetOutput(os.Stdout) eos.RegisterHandler(&itchfs.ItchFS{ ItchServer: *appArgs.address, }) if *appArgs.quiet { *appArgs.noProgress = true *appArgs.verbose = false } if !isTerminal() { *appArgs.noProgress = true } comm.Configure(*appArgs.noProgress, *appArgs.quiet, *appArgs.verbose, *appArgs.json, *appArgs.panic, *appArgs.assumeYes, *appArgs.beeps4Life) if !isTerminal() { comm.Debug("Not a terminal, disabling progress indicator") } setupHTTPDebug() if *appArgs.cpuprofile != "" { f, err := os.Create(*appArgs.cpuprofile) if err != nil { log.Fatal(err) } pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() } switch kingpin.MustParse(cmd, err) { case dlCmd.FullCommand(): dl(*dlArgs.url, *dlArgs.dest) case cpCmd.FullCommand(): cp(*cpArgs.src, *cpArgs.dest, *cpArgs.resume) case loginCmd.FullCommand(): login() case logoutCmd.FullCommand(): logout() case pushCmd.FullCommand(): { userVersion := *pushArgs.userVersion if userVersion == "" && *pushArgs.userVersionFile != "" { buf, err := ioutil.ReadFile(*pushArgs.userVersionFile) must(err) userVersion = strings.TrimSpace(string(buf)) if strings.ContainsAny(userVersion, "\r\n") { must(fmt.Errorf("%s contains line breaks, refusing to use as userversion", *pushArgs.userVersionFile)) } } push(*pushArgs.src, *pushArgs.target, userVersion, *pushArgs.fixPerms) } case fetchCmd.FullCommand(): fetch(*fetchArgs.target, *fetchArgs.out) case statusCmd.FullCommand(): status(*statusArgs.target) case untarCmd.FullCommand(): untar(*untarArgs.file, *untarArgs.dir) case unzipCmd.FullCommand(): unzip(*unzipArgs.file, *unzipArgs.dir, *unzipArgs.resumeFile) case wipeCmd.FullCommand(): wipe(*wipeArgs.path) case mkdirCmd.FullCommand(): mkdir(*mkdirArgs.path) case dittoCmd.FullCommand(): ditto(*dittoArgs.src, *dittoArgs.dst) case sizeofCmd.FullCommand(): sizeof(*sizeofArgs.path) case diffCmd.FullCommand(): diff(*diffArgs.old, *diffArgs.new, *diffArgs.patch, butlerCompressionSettings()) case applyCmd.FullCommand(): apply(*applyArgs.patch, *applyArgs.old, *applyArgs.dir, *applyArgs.inplace, *applyArgs.signature, *applyArgs.wounds) case verifyCmd.FullCommand(): verify(*verifyArgs.signature, *verifyArgs.dir, *verifyArgs.wounds, *verifyArgs.heal) case signCmd.FullCommand(): sign(*signArgs.output, *signArgs.signature, butlerCompressionSettings(), *signArgs.fixPerms) case healCmd.FullCommand(): heal(*healArgs.dir, *healArgs.wounds, *healArgs.spec) case probeCmd.FullCommand(): probe(*probeArgs.patch) case bsdiffCmd.FullCommand(): cmdBsdiff(*bsdiffArgs.target, *bsdiffArgs.source, *bsdiffArgs.patch, *bsdiffArgs.concurrency, *bsdiffArgs.measureOverhead) case bspatchCmd.FullCommand(): bspatch(*bspatchArgs.patch, *bspatchArgs.target, *bspatchArgs.output) case whichCmd.FullCommand(): which() case versionCmd.FullCommand(): log.Println(versionString) os.Exit(0) case fileCmd.FullCommand(): file(*fileArgs.file) case lsCmd.FullCommand(): ls(*lsArgs.file) case upgradeCmd.FullCommand(): upgrade(*upgradeArgs.head) case ugpradeCmd.FullCommand(): upgrade(*upgradeArgs.head) case updateCmd.FullCommand(): upgrade(*updateArgs.head) } }