func TestCopySuccessful(test *testing.T) { // set-up databasePath := configureDatabase() defer os.Remove(databasePath) store, err := storage.Open() if err != nil { test.Fatal(err) } defer store.Close() fileA, err := store.AddFile("/tmp/a", fingerprint.Fingerprint("abc"), time.Now(), 123, true) if err != nil { test.Fatal(err) } fileAB, err := store.AddFile("/tmp/a/b", fingerprint.Fingerprint("abc"), time.Now(), 123, false) if err != nil { test.Fatal(err) } sourceTag, err := store.AddTag("source") if err != nil { test.Fatal(err) } if _, err := store.AddFileTag(fileA.Id, sourceTag.Id); err != nil { test.Fatal(err) } if _, err := store.AddFileTag(fileAB.Id, sourceTag.Id); err != nil { test.Fatal(err) } command := CopyCommand{false} // test if err := command.Exec(cli.Options{}, []string{"source", "dest"}); err != nil { test.Fatal(err) } // validate destTag, err := store.TagByName("dest") if err != nil { test.Fatal(err) } if destTag == nil { test.Fatal("Destination tag does not exist.") } expectTags(test, store, fileA, sourceTag, destTag) expectTags(test, store, fileAB, sourceTag, destTag) }
func TestDupesMultiple(test *testing.T) { // set-up databasePath := configureDatabase() defer os.Remove(databasePath) outPath, errPath, err := configureOutput() if err != nil { test.Fatal(err) } defer os.Remove(outPath) defer os.Remove(errPath) store, err := storage.Open() if err != nil { test.Fatal(err) } defer store.Close() _, err = store.AddFile("/tmp/a", fingerprint.Fingerprint("abc"), time.Now(), 123, true) if err != nil { test.Fatal(err) } _, err = store.AddFile("/tmp/a/b", fingerprint.Fingerprint("abc"), time.Now(), 123, false) if err != nil { test.Fatal(err) } _, err = store.AddFile("/tmp/b", fingerprint.Fingerprint("def"), time.Now(), 123, false) if err != nil { test.Fatal(err) } _, err = store.AddFile("/tmp/e/f", fingerprint.Fingerprint("def"), time.Now(), 123, false) if err != nil { test.Fatal(err) } _, err = store.AddFile("/tmp/a/d", fingerprint.Fingerprint("def"), time.Now(), 123, false) if err != nil { test.Fatal(err) } command := DupesCommand{false} // test if err := command.Exec(cli.Options{}, []string{}); err != nil { test.Fatal(err) } // validate log.Outfile.Seek(0, 0) bytes, err := ioutil.ReadAll(log.Outfile) compareOutput(test, "Set of 2 duplicates:\n /tmp/a\n /tmp/a/b\n\nSet of 3 duplicates:\n /tmp/a/d\n /tmp/b\n /tmp/e/f\n", string(bytes)) }
func TestFilesAll(test *testing.T) { // set-up databasePath := configureDatabase() defer os.Remove(databasePath) outPath, errPath, err := configureOutput() if err != nil { test.Fatal(err) } defer os.Remove(outPath) defer os.Remove(errPath) store, err := storage.Open() if err != nil { test.Fatal(err) } defer store.Close() _, err = store.AddFile("/tmp/d", fingerprint.Fingerprint("abc"), time.Now(), 123, false) if err != nil { test.Fatal(err) } _, err = store.AddFile("/tmp/b/a", fingerprint.Fingerprint("abc"), time.Now(), 123, false) if err != nil { test.Fatal(err) } _, err = store.AddFile("/tmp/b", fingerprint.Fingerprint("abc"), time.Now(), 123, true) if err != nil { test.Fatal(err) } command := FilesCommand{} // test if err := command.Exec(cli.Options{cli.Option{"-a", "--all", "", false, ""}}, []string{}); err != nil { test.Fatal(err) } // validate log.Outfile.Seek(0, 0) bytes, err := ioutil.ReadAll(log.Outfile) compareOutput(test, "/tmp/b\n/tmp/b/a\n/tmp/d\n", string(bytes)) }
func TestTagsForSingleFile(test *testing.T) { // set-up databasePath := configureDatabase() defer os.Remove(databasePath) outPath, errPath, err := configureOutput() if err != nil { test.Fatal(err) } defer os.Remove(outPath) defer os.Remove(errPath) store, err := storage.Open() if err != nil { test.Fatal(err) } defer store.Close() file, err := store.AddFile("/tmp/tmsu/a", fingerprint.Fingerprint("123"), time.Now(), 0, false) if err != nil { test.Fatal(err) } appleTag, err := store.AddTag("apple") if err != nil { test.Fatal(err) } bananaTag, err := store.AddTag("banana") if err != nil { test.Fatal(err) } _, err = store.AddFileTag(file.Id, appleTag.Id) if err != nil { test.Fatal(err) } _, err = store.AddFileTag(file.Id, bananaTag.Id) if err != nil { test.Fatal(err) } tagsCommand := TagsCommand{false, false} // test if err := tagsCommand.Exec(cli.Options{}, []string{"/tmp/tmsu/a"}); err != nil { test.Fatal(err) } // verify log.Outfile.Seek(0, 0) bytes, err := ioutil.ReadAll(log.Outfile) compareOutput(test, "apple\nbanana\n", string(bytes)) }
func (command DupesCommand) findDuplicatesOf(paths []string) error { store, err := storage.Open() if err != nil { return fmt.Errorf("could not open storage: %v", err) } defer store.Close() first := true for _, path := range paths { if command.verbose { log.Infof("%v: identifying duplicate files.\n", path) } fp, err := fingerprint.Create(path) if err != nil { return fmt.Errorf("%v: could not create fingerprint: %v", path, err) } if fp == fingerprint.Fingerprint("") { return nil } files, err := store.FilesByFingerprint(fp) if err != nil { return fmt.Errorf("%v: could not retrieve files matching fingerprint '%v': %v", path, fp, err) } absPath, err := filepath.Abs(path) if err != nil { return fmt.Errorf("%v: could not determine absolute path: %v", path, err) } // filter out the file we're searching on dupes := files.Where(func(file *database.File) bool { return file.Path() != absPath }) if len(paths) > 1 && len(dupes) > 0 { if first { first = false } else { log.Print() } log.Printf("%v duplicates of %v:", len(dupes), path) for _, dupe := range dupes { relPath := _path.Rel(dupe.Path()) log.Printf(" %v", relPath) } } else { for _, dupe := range dupes { relPath := _path.Rel(dupe.Path()) log.Print(relPath) } } } return nil }
func TestSingleUntag(test *testing.T) { // set-up databasePath := configureDatabase() defer os.Remove(databasePath) store, err := storage.Open() if err != nil { test.Fatal(err) } defer store.Close() file, err := store.AddFile("/tmp/tmsu/a", fingerprint.Fingerprint("abc123"), time.Now(), 0, false) if err != nil { test.Fatal(err) } appleTag, err := store.AddTag("apple") if err != nil { test.Fatal(err) } bananaTag, err := store.AddTag("banana") if err != nil { test.Fatal(err) } _, err = store.AddFileTag(file.Id, appleTag.Id) if err != nil { test.Fatal(err) } _, err = store.AddFileTag(file.Id, bananaTag.Id) if err != nil { test.Fatal(err) } untagCommand := UntagCommand{false, false} // test if err := untagCommand.Exec(cli.Options{}, []string{"/tmp/tmsu/a", "apple"}); err != nil { test.Fatal(err) } // validate fileTags, err := store.FileTags() if err != nil { test.Fatal(err) } if len(fileTags) != 1 { test.Fatalf("Expected one file-tag but are %v", len(fileTags)) } if fileTags[0].TagId != bananaTag.Id { test.Fatalf("Incorrect tag was applied.") } }
// Retrieves the sets of duplicate files within the database. func (db *Database) DuplicateFiles() ([]Files, error) { sql := `SELECT id, directory, name, fingerprint, mod_time, size, is_dir FROM file WHERE fingerprint IN ( SELECT fingerprint FROM file WHERE fingerprint != '' GROUP BY fingerprint HAVING count(1) > 1 ) ORDER BY fingerprint, directory || '/' || name` rows, err := db.connection.Query(sql) if err != nil { return nil, err } defer rows.Close() fileSets := make([]Files, 0, 10) var fileSet Files var previousFingerprint fingerprint.Fingerprint for rows.Next() { if rows.Err() != nil { return nil, err } var fileId uint var directory, name, fp string var modTime time.Time var size int64 var isDir bool err = rows.Scan(&fileId, &directory, &name, &fp, &modTime, &size, &isDir) if err != nil { return nil, err } fingerprint := fingerprint.Fingerprint(fp) if fingerprint != previousFingerprint { if fileSet != nil { fileSets = append(fileSets, fileSet) } fileSet = make(Files, 0, 10) previousFingerprint = fingerprint } fileSet = append(fileSet, &File{fileId, directory, name, fingerprint, modTime, size, isDir}) } // ensure last file set is added if len(fileSet) > 0 { fileSets = append(fileSets, fileSet) } return fileSets, nil }
func readFile(rows *sql.Rows) (*File, error) { if !rows.Next() { return nil, nil } if rows.Err() != nil { return nil, rows.Err() } var fileId uint var directory, name, fp string var modTime time.Time var size int64 var isDir bool err := rows.Scan(&fileId, &directory, &name, &fp, &modTime, &size, &isDir) if err != nil { return nil, err } return &File{fileId, directory, name, fingerprint.Fingerprint(fp), modTime, size, isDir}, nil }
func TestFilesSingleTag(test *testing.T) { // set-up databasePath := configureDatabase() defer os.Remove(databasePath) outPath, errPath, err := configureOutput() if err != nil { test.Fatal(err) } defer os.Remove(outPath) defer os.Remove(errPath) store, err := storage.Open() if err != nil { test.Fatal(err) } defer store.Close() fileD, err := store.AddFile("/tmp/d", fingerprint.Fingerprint("abc"), time.Now(), 123, false) if err != nil { test.Fatal(err) } fileBA, err := store.AddFile("/tmp/b/a", fingerprint.Fingerprint("abc"), time.Now(), 123, false) if err != nil { test.Fatal(err) } fileB, err := store.AddFile("/tmp/b", fingerprint.Fingerprint("abc"), time.Now(), 123, true) if err != nil { test.Fatal(err) } tagD, err := store.AddTag("d") if err != nil { test.Fatal(err) } tagB, err := store.AddTag("b") if err != nil { test.Fatal(err) } if _, err := store.AddFileTag(fileD.Id, tagD.Id); err != nil { test.Fatal(err) } if _, err := store.AddFileTag(fileB.Id, tagB.Id); err != nil { test.Fatal(err) } if _, err := store.AddFileTag(fileBA.Id, tagB.Id); err != nil { test.Fatal(err) } command := FilesCommand{} // test if err := command.Exec(cli.Options{}, []string{"b"}); err != nil { test.Fatal(err) } // validate log.Outfile.Seek(0, 0) bytes, err := ioutil.ReadAll(log.Outfile) compareOutput(test, "/tmp/b\n/tmp/b/a\n", string(bytes)) }
func TestDupesNoneUntaggedFile(test *testing.T) { // set-up databasePath := configureDatabase() defer os.Remove(databasePath) outPath, errPath, err := configureOutput() if err != nil { test.Fatal(err) } defer os.Remove(outPath) defer os.Remove(errPath) path := filepath.Join(os.TempDir(), "tmsu-file") _, err = os.Create(path) if err != nil { test.Fatal(err) } defer os.Remove(path) store, err := storage.Open() if err != nil { test.Fatal(err) } defer store.Close() _, err = store.AddFile("/tmp/a", fingerprint.Fingerprint("abc"), time.Now(), 123, true) if err != nil { test.Fatal(err) } _, err = store.AddFile("/tmp/a/b", fingerprint.Fingerprint("def"), time.Now(), 123, false) if err != nil { test.Fatal(err) } _, err = store.AddFile("/tmp/b", fingerprint.Fingerprint("ghi"), time.Now(), 123, false) if err != nil { test.Fatal(err) } _, err = store.AddFile("/tmp/e/f", fingerprint.Fingerprint("klm"), time.Now(), 123, false) if err != nil { test.Fatal(err) } _, err = store.AddFile("/tmp/a/d", fingerprint.Fingerprint("nop"), time.Now(), 123, false) if err != nil { test.Fatal(err) } command := DupesCommand{false} // test if err := command.Exec(cli.Options{}, []string{path}); err != nil { test.Fatal(err) } // validate log.Outfile.Seek(0, 0) bytes, err := ioutil.ReadAll(log.Outfile) compareOutput(test, "", string(bytes)) }
func TestDupesMultipleUntaggedFile(test *testing.T) { // set-up databasePath := configureDatabase() defer os.Remove(databasePath) outPath, errPath, err := configureOutput() if err != nil { test.Fatal(err) } defer os.Remove(outPath) defer os.Remove(errPath) path := filepath.Join(os.TempDir(), "tmsu-file") _, err = os.Create(path) if err != nil { test.Fatal(err) } defer os.Remove(path) store, err := storage.Open() if err != nil { test.Fatal(err) } defer store.Close() _, err = store.AddFile("/tmp/a", fingerprint.Fingerprint("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"), time.Now(), 123, true) if err != nil { test.Fatal(err) } _, err = store.AddFile("/tmp/a/b", fingerprint.Fingerprint("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"), time.Now(), 123, false) if err != nil { test.Fatal(err) } _, err = store.AddFile("/tmp/b", fingerprint.Fingerprint("xxx"), time.Now(), 123, false) if err != nil { test.Fatal(err) } _, err = store.AddFile("/tmp/e/f", fingerprint.Fingerprint("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"), time.Now(), 123, false) if err != nil { test.Fatal(err) } _, err = store.AddFile("/tmp/a/d", fingerprint.Fingerprint("xxx"), time.Now(), 123, false) if err != nil { test.Fatal(err) } command := DupesCommand{false} // test if err := command.Exec(cli.Options{}, []string{path}); err != nil { test.Fatal(err) } // validate log.Outfile.Seek(0, 0) bytes, err := ioutil.ReadAll(log.Outfile) compareOutput(test, "/tmp/a\n/tmp/a/b\n/tmp/e/f\n", string(bytes)) }
func TestUntagAll(test *testing.T) { // set-up databasePath := configureDatabase() defer os.Remove(databasePath) store, err := storage.Open() if err != nil { test.Fatal(err) } defer store.Close() fileA, err := store.AddFile("/tmp/tmsu/a", fingerprint.Fingerprint("abc123"), time.Now(), 0, false) if err != nil { test.Fatal(err) } fileB, err := store.AddFile("/tmp/tmsu/b", fingerprint.Fingerprint("abc123"), time.Now(), 0, false) if err != nil { test.Fatal(err) } appleTag, err := store.AddTag("apple") if err != nil { test.Fatal(err) } _, err = store.AddFileTag(fileA.Id, appleTag.Id) if err != nil { test.Fatal(err) } _, err = store.AddFileTag(fileB.Id, appleTag.Id) if err != nil { test.Fatal(err) } untagCommand := UntagCommand{false, false} // test if err := untagCommand.Exec(cli.Options{cli.Option{"--all", "-a", "", false, ""}}, []string{"/tmp/tmsu/a", "/tmp/tmsu/b"}); err != nil { test.Fatal(err) } // validate fileTags, err := store.FileTags() if err != nil { test.Fatal(err) } if len(fileTags) != 0 { test.Fatalf("Expected no file-tags but are %v", len(fileTags)) } files, err := store.Files() if err != nil { test.Fatal(err) } if len(files) != 0 { test.Fatalf("Expected no files but are %v", len(files)) } }
func TestDeleteSuccessful(test *testing.T) { // set-up databasePath := configureDatabase() defer os.Remove(databasePath) store, err := storage.Open() if err != nil { test.Fatal(err) } defer store.Close() fileD, err := store.AddFile("/tmp/d", fingerprint.Fingerprint("abc"), time.Now(), 123, false) if err != nil { test.Fatal(err) } fileF, err := store.AddFile("/tmp/f", fingerprint.Fingerprint("abc"), time.Now(), 123, false) if err != nil { test.Fatal(err) } fileB, err := store.AddFile("/tmp/b", fingerprint.Fingerprint("abc"), time.Now(), 123, false) if err != nil { test.Fatal(err) } tagDeathrow, err := store.AddTag("deathrow") if err != nil { test.Fatal(err) } tagFreeman, err := store.AddTag("freeman") if err != nil { test.Fatal(err) } if _, err := store.AddFileTag(fileD.Id, tagDeathrow.Id); err != nil { test.Fatal(err) } if _, err := store.AddFileTag(fileF.Id, tagFreeman.Id); err != nil { test.Fatal(err) } if _, err := store.AddFileTag(fileB.Id, tagDeathrow.Id); err != nil { test.Fatal(err) } if _, err := store.AddFileTag(fileB.Id, tagFreeman.Id); err != nil { test.Fatal(err) } command := DeleteCommand{false} // test if err := command.Exec(cli.Options{}, []string{"deathrow"}); err != nil { test.Fatal(err) } // validate tagDeathrow, err = store.TagByName("deathrow") if err != nil { test.Fatal(err) } if tagDeathrow != nil { test.Fatal("Deleted tag still exists.") } fileTagsD, err := store.FileTagsByFileId(fileD.Id) if err != nil { test.Fatal(err) } if len(fileTagsD) != 0 { test.Fatal("Expected no file-tags for file 'd'.") } fileTagsF, err := store.FileTagsByFileId(fileF.Id) if err != nil { test.Fatal(err) } if len(fileTagsF) != 1 { test.Fatal("Expected one file-tag for file 'f'.") } if fileTagsF[0].TagId != tagFreeman.Id { test.Fatal("Expected file-tag for tag 'freeman'.") } fileTagsB, err := store.FileTagsByFileId(fileB.Id) if err != nil { test.Fatal(err) } if len(fileTagsB) != 1 { test.Fatal("Expected one file-tag for file 'b'.") } if fileTagsB[0].TagId != tagFreeman.Id { test.Fatal("Expected file-tag for tag 'freeman'.") } }
func TestMergeSingleTag(test *testing.T) { // set-up databasePath := configureDatabase() defer os.Remove(databasePath) store, err := storage.Open() if err != nil { test.Fatal(err) } defer store.Close() fileA, err := store.AddFile("/tmp/a", fingerprint.Fingerprint("abc"), time.Now(), 123, true) if err != nil { test.Fatal(err) } fileA1, err := store.AddFile("/tmp/a/1", fingerprint.Fingerprint("abc"), time.Now(), 123, false) if err != nil { test.Fatal(err) } fileB, err := store.AddFile("/tmp/b", fingerprint.Fingerprint("abc"), time.Now(), 123, true) if err != nil { test.Fatal(err) } fileB1, err := store.AddFile("/tmp/b/1", fingerprint.Fingerprint("abc"), time.Now(), 123, false) if err != nil { test.Fatal(err) } tagA, err := store.AddTag("a") if err != nil { test.Fatal(err) } tagB, err := store.AddTag("b") if err != nil { test.Fatal(err) } if _, err := store.AddFileTag(fileA.Id, tagA.Id); err != nil { test.Fatal(err) } if _, err := store.AddFileTag(fileA1.Id, tagA.Id); err != nil { test.Fatal(err) } if _, err := store.AddFileTag(fileB.Id, tagB.Id); err != nil { test.Fatal(err) } if _, err := store.AddFileTag(fileB1.Id, tagB.Id); err != nil { test.Fatal(err) } command := MergeCommand{false} // test if err := command.Exec(cli.Options{}, []string{"a", "b"}); err != nil { test.Fatal(err) } // validate tagA, err = store.TagByName("a") if err != nil { test.Fatal(err) } if tagA != nil { test.Fatal("Tag 'a' still exists.") } tagB, err = store.TagByName("b") if err != nil { test.Fatal(err) } if tagB == nil { test.Fatal("Tag 'b' does not exist.") } expectTags(test, store, fileA, tagB) expectTags(test, store, fileA1, tagB) expectTags(test, store, fileB, tagB) expectTags(test, store, fileB1, tagB) }