git/intake/main
internal/normalize/normalize_test.go
112 lines4 KB last change raw · history
package normalize

import "testing"

func TestName(t *testing.T) {
	cases := []struct{ in, want string }{
		{"The Method to Love", "the-method-to-love"},
		{"Prophecy At 1420 MHz", "prophecy-at-1420-mhz"},
		{"Don't Stop", "dont-stop"},
		{"Don\u2019t Stop", "dont-stop"},
		{"Caf\u00e9 M\u00fcnster", "cafe-munster"},
		{"MIXED Case TiTlE", "mixed-case-title"},
		{"  leading and trailing  ", "leading-and-trailing"},
		{"trailing dots...", "trailing-dots"},
		{"\u591c\u660e\u3051", ""},
		{"\u2605\u2605\u2605\u2605\u2605", ""},
		{"", ""},
		{"---", ""},
		{"1", "1"},

		// Symbols that every filesystem accepts are kept, so a title stays
		// readable instead of collapsing into a run of dashes.
		{"Track (feat. Someone) [Remix]", "track-(feat-someone)-[remix]"},
		{"Bj\u00f6rk & Sigur R\u00f3s", "bjork-&-sigur-ros"},
		{"while(1<2), Part 1", "while(1_2)-part-1"},

		// A symbol that is illegal somewhere becomes an underscore inside a
		// word, so the two characters it sat between stay separated rather than
		// turning into two words.
		{"a<b", "a_b"},
		{"Foo*Bar", "foo_bar"},
		{"Path/With/Slashes", "path_with_slashes"},

		// Next to a space it is just a separator, or the words either side run
		// together as album_the.
		{"\u00c1lbum: The (Deluxe) Edition!", "album-the-(deluxe)-edition"},
		{"Foo: Bar", "foo-bar"},
		{"Foo < Bar", "foo-bar"},

		// Nothing is left dangling at either end.
		{"Foo<", "foo"},
		{"<Foo", "foo"},
		{"()", ""},
	}
	for _, c := range cases {
		if got := Name(c.in); got != c.want {
			t.Errorf("Name(%q) = %q, want %q", c.in, got, c.want)
		}
	}
}

func TestPadWidth(t *testing.T) {
	cases := []struct {
		max, want int
	}{
		{1, 2}, {9, 2}, {99, 2}, {100, 3}, {120, 3}, {999, 3}, {1000, 4},
	}
	for _, c := range cases {
		if got := PadWidth(c.max); got != c.want {
			t.Errorf("PadWidth(%d) = %d, want %d", c.max, got, c.want)
		}
	}
}

func TestTrackFile(t *testing.T) {
	if got := TrackFile(1, 1, 2, "The Method to Love", ".flac", true); got != "1-01_the-method-to-love.flac" {
		t.Errorf("2-digit: got %q", got)
	}
	if got := TrackFile(1, 7, 3, "Long Player Track 7", ".flac", true); got != "1-007_long-player-track-7.flac" {
		t.Errorf("3-digit: got %q", got)
	}
	if got := TrackFile(2, 10, 2, "X", "flac", true); got != "2-10_x.flac" {
		t.Errorf("ext: got %q", got)
	}
}

func TestDetectCollisions(t *testing.T) {
	items := [][2]string{
		{"First", "1-01_same.flac"},
		{"Second", "1-02_same.flac"},
		{"Other", "1-03_other.flac"},
	}
	got := DetectCollisions(items)
	if len(got) != 1 {
		t.Fatalf("want 1 collision, got %d (%v)", len(got), got)
	}
	if got[0].Target != "same" || len(got[0].Causes) != 2 {
		t.Errorf("unexpected collision: %+v", got[0])
	}
	dup := [][2]string{{"Same", "1-01_same.flac"}, {"Same", "1-01_same.flac"}}
	if c := DetectCollisions(dup); len(c) != 0 {
		t.Errorf("identical sources should not collide, got %v", c)
	}
}

func TestTruncate(t *testing.T) {
	long := "1-01_" + string(make([]byte, 0)) + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.flac"
	got, cut := Truncate(long, 200)
	if !cut {
		t.Fatal("expected truncation")
	}
	if len(got) > 200 {
		t.Errorf("truncated name is %d bytes, want <= 200", len(got))
	}
	if ext := got[len(got)-5:]; ext != ".flac" {
		t.Errorf("extension not preserved: %q", got)
	}
	if s, cut := Truncate("short.flac", 200); cut || s != "short.flac" {
		t.Errorf("short name should be untouched, got %q cut=%v", s, cut)
	}
}