// Fetch returns a group from the collection with the given path. func (l *Library) Fetch(c index.Collection, path []string) (group, error) { if len(path) == 0 { return build(c, index.Key("Root")), nil } var g index.Group = c k := index.Key(path[0]) g = c.Get(k) if g == nil { return group{}, fmt.Errorf("invalid path: near '%v'", path[0]) } index.Sort(g.Tracks(), index.MultiSort(index.SortByInt("DiscNumber"), index.SortByInt("TrackNumber"))) g = index.Transform(g, index.SplitList("Artist", "AlbumArtist", "Composer")) c = index.Collect(g, index.ByPrefix("Name")) g = index.SubTransform(c, index.TrimEnumPrefix) g = index.SumGroupIntAttr("TotalTime", g) commonFields := []attr.Interface{ attr.String("Album"), attr.Strings("Artist"), attr.Strings("AlbumArtist"), attr.Strings("Composer"), attr.Int("Year"), attr.Int("BitRate"), attr.Int("DiscNumber"), } g = index.CommonGroupAttr(commonFields, g) g = index.RemoveEmptyCollections(g) for i, p := range path[1:] { var ok bool c, ok = g.(index.Collection) if !ok { return group{}, fmt.Errorf("retrieved Group is not a Collection") } k = index.Key(p) g = c.Get(k) if g == nil { return group{}, fmt.Errorf("invalid path near '%v'", path[1:][i]) } if _, ok = g.(index.Collection); !ok { if i == len(path[1:])-1 { break } return group{}, fmt.Errorf("retrieved Group isn't a Collection: %v", p) } } if g == nil { return group{}, fmt.Errorf("could not find group") } g = index.FirstTrackAttr(attr.String("ID"), g) return build(g, k), nil }
func TestByAttr(t *testing.T) { trackListing := []testTrack{ {Name: "A", Album: "Album A"}, {Name: "B", Album: "Album A"}, {Name: "C", Album: "Album B"}, {Name: "D", Album: "Album B"}, } expected := map[string][]Track{ "Album A": {trackListing[0], trackListing[1]}, "Album B": {trackListing[2], trackListing[3]}, } attrGroup := By(attr.String("Album")).Collect(testTracker(trackListing[:])) SortKeysByGroupName(attrGroup) nkm := nameKeyMap(attrGroup) for n, v := range expected { k, ok := nkm[n] if !ok { t.Errorf("%v is not a key of nkm", n) } gr := attrGroup.Get(Key(k)) if gr == nil { t.Errorf("attrGroup.Get(%v) = nil, expected non-nil!", k) } got := gr.Tracks() if !reflect.DeepEqual(v, got) { t.Errorf("gr.Tracks() = %#v, expected: %#v", got, v) } } }
func TestSubCollect(t *testing.T) { album1 := "Mahler Symphonies" album2 := "Shostakovich Symphonies" prefix1 := "Symphony No. 1 in D" prefix2 := "A B C Y" trackListing := []testTrack{ {Name: prefix1 + ": I. Langsam, schleppend - Immer sehr gemächlich", Album: album1}, {Name: prefix1 + ": II. Kräftig bewegt, doch nicht zu schnell - Recht gemächlich", Album: album1}, {Name: prefix1 + ": III. Feierlich und gemessen, ohne zu schleppen", Album: album1}, {Name: prefix2 + " 1", Album: album2}, {Name: prefix2 + " 2", Album: album2}, {Name: prefix2 + " 2", Album: album2}, } expectedAlbums := []string{album1, album2} albums := By(attr.String("Album")).Collect(testTracker(trackListing[:])) SortKeysByGroupName(albums) albNames := names(albums) if !reflect.DeepEqual(albNames, expectedAlbums) { t.Errorf("albums.Names() = %v, expected %#v", names, expectedAlbums) } albPfx := SubCollect(albums, ByPrefix("Name")) albPfxNames := names(albPfx) if !reflect.DeepEqual(albPfxNames, expectedAlbums[:]) { t.Errorf("albPfx.Names() = %v, expected %#v", albPfxNames, expectedAlbums) } nkm := nameKeyMap(albPfx) prefixGroup := albPfx.Get(nkm[album1]) pfxCol, ok := prefixGroup.(Collection) if !ok { t.Errorf("expected a Collection, but got %T", prefixGroup) } pfxColNames := names(pfxCol) expectedPrefixGroupNames := []string{prefix1} if !reflect.DeepEqual(pfxColNames, expectedPrefixGroupNames) { t.Errorf("prefixCollection.Names() = %#v, expected %#v", pfxColNames, expectedPrefixGroupNames) } }
func buildRootCollection(l index.Library) index.Collection { root := index.Collect(l, index.By(attr.String("Album"))) index.SortKeysByGroupName(root) return root }
func TestFirstTrackAttr(t *testing.T) { table := []struct { in Group field attr.Interface out interface{} }{ // One group with no tracks { in: group{ name: "Group One", }, field: attr.String("Name"), out: nil, }, { in: group{ name: "Group One", }, field: attr.Strings("Artist"), out: nil, }, // One group with one track { in: group{ name: "Group One", tracks: []Track{ testTrack{ Name: "Track One", }, }, }, field: attr.String("Name"), out: "Track One", }, { in: group{ name: "Group One", tracks: []Track{ testTrack{ Artist: "Track One", }, }, }, field: attr.Strings("Artist"), out: []string{"Track One"}, }, // One group with two tracks, empty first field { in: group{ name: "Group One", tracks: []Track{ testTrack{}, testTrack{ Name: "Track Two", Artist: "Artist One", }, }, }, field: attr.String("Artist"), out: nil, }, { in: group{ name: "Group One", tracks: []Track{ testTrack{}, testTrack{ Name: "Track Two", Artist: "Artist One", }, }, }, field: attr.Strings("Artist"), out: nil, }, // One collection, one group, one track { in: testCol{ name: "Group One (collection)", keys: []Key{"Group-One-One"}, grps: map[Key]Group{ "Group-One-One": group{ name: "Group One (One)", tracks: []Track{ testTrack{ Name: "Track One", }, }, }, }, }, field: attr.String("Name"), out: "Track One", }, // One group with one track { in: group{ name: "Group One", tracks: []Track{ testTrack{ Duration: 1234, }, }, }, field: attr.Int("Duration"), out: 1234, }, // One group with two tracks, empty first field { in: group{ name: "Group One", tracks: []Track{ testTrack{}, testTrack{ Duration: 1234, Artist: "Artist One", }, }, }, field: attr.Int("Duration"), out: nil, }, // One collection, one group, one track { in: testCol{ name: "Group One (collection)", keys: []Key{"Group-One-One"}, grps: map[Key]Group{ "Group-One-One": group{ name: "Group One (One)", tracks: []Track{ testTrack{ Duration: 1234, }, }, }, }, }, field: attr.Int("Duration"), out: 1234, }, } for ii, tt := range table { g := FirstTrackAttr(tt.field, tt.in) got := g.Field(tt.field.Name()) if !reflect.DeepEqual(got, tt.out) { t.Errorf("[%d] got %#v, expected %#v", ii, got, tt.out) } } }
func TestCommonGroupAttr(t *testing.T) { table := []struct { in Group fields []attr.Interface out []interface{} }{ // One group with one track, unset common field { in: group{ name: "Group One", tracks: []Track{ testTrack{ Name: "Track One", }, }, }, fields: []attr.Interface{attr.String("Artist")}, out: []interface{}{nil}, }, // One group with one track, unset common int field { in: group{ name: "Group One", tracks: []Track{ testTrack{ Name: "Empty common field", }, }, }, fields: []attr.Interface{attr.Int("Year")}, out: []interface{}{nil}, }, // One group with one track, unset common (string) and (int) fields { in: group{ name: "Group One", tracks: []Track{ testTrack{ Name: "Track One", }, }, }, fields: []attr.Interface{attr.String("Artist"), attr.Int("Year")}, out: []interface{}{nil, nil}, }, // One group with one track, set strings field { in: group{ name: "Group One", tracks: []Track{ testTrack{ stringsMap: map[string][]string{ "Artist": []string{"First Artist", "Second Artist"}, }, }, }, }, fields: []attr.Interface{attr.Strings("Artist")}, out: []interface{}{[]string{"First Artist", "Second Artist"}}, }, // One group with two tracks, check intersection of artists list { in: group{ name: "Group One", tracks: []Track{ testTrack{ stringsMap: map[string][]string{ "Artist": []string{"First Artist", "Second Artist"}, }, }, testTrack{ stringsMap: map[string][]string{ "Artist": []string{"First Artist"}, }, }, }, }, fields: []attr.Interface{attr.Strings("Artist")}, out: []interface{}{[]string{"First Artist"}}, }, // One group with two tracks, empty first string & int fields { in: group{ name: "Group One", tracks: []Track{ testTrack{ Name: "Track One", }, testTrack{ Name: "Track Two", Artist: "Artist One", Year: 1984, }, }, }, fields: []attr.Interface{attr.String("Artist"), attr.Int("Year")}, out: []interface{}{nil, nil}, }, // One group with two tracks, empty first field { in: group{ name: "Group One", tracks: []Track{ testTrack{ Name: "Track One", }, testTrack{ Name: "Track Two", Year: 1985, }, }, }, fields: []attr.Interface{attr.Int("Year")}, out: []interface{}{nil}, }, // One group with one track, common string/int fields { in: group{ name: "Group One", tracks: []Track{ testTrack{ Name: "Track One", Artist: "Artist One", Composer: "Composer One", Year: 1984, }, }, }, fields: []attr.Interface{attr.String("Artist"), attr.Int("Year")}, out: []interface{}{"Artist One", 1984}, }, // One group with one track, common string fields for Artist and Composer { in: group{ name: "Group One", tracks: []Track{ testTrack{ Name: "Track One", Artist: "Artist One", Composer: "Composer One", }, }, }, fields: []attr.Interface{attr.String("Artist"), attr.String("Composer")}, out: []interface{}{"Artist One", "Composer One"}, }, // One group with two tracks, common fields across pairs { in: group{ name: "Group One", tracks: []Track{ testTrack{ Name: "Track One", Artist: "Artist One", Composer: "Composer One", }, testTrack{ Name: "Track Two", Artist: "Artist Two", Composer: "Composer One", }, }, }, fields: []attr.Interface{attr.String("Artist"), attr.String("Composer")}, out: []interface{}{nil, "Composer One"}, }, // One collection, one group, one track { in: testCol{ name: "Group Three (collection)", keys: []Key{"Group-Three-One"}, grps: map[Key]Group{ "Group-Three-One": group{ name: "Group One (Three)", tracks: []Track{ testTrack{ Name: "Track One", Artist: "Artist One", }, }, }, }, }, fields: []attr.Interface{attr.String("Artist"), attr.String("Composer")}, out: []interface{}{"Artist One", nil}, }, // One collection, three groups, many tracks! { in: testCol{ name: "Root", keys: []Key{"Group-One", "Group-Two", "Group-Three"}, grps: map[Key]Group{ "Group-One": group{ name: "Group One", tracks: []Track{ testTrack{ Name: "Track One", Artist: "Artist One", Year: 1984, }, }, }, "Group-Two": group{ name: "Group Two", tracks: []Track{ testTrack{ Name: "Track One", Artist: "Artist One", Year: 1984, }, testTrack{ Name: "Track Two", Artist: "Artist One", Year: 1984, }, }, }, "Group-Three": testCol{ name: "Group Three (collection)", keys: []Key{"Group-Three-One"}, grps: map[Key]Group{ "Group-Three-One": group{ name: "Group One (Three)", tracks: []Track{ testTrack{ Name: "Track One", Artist: "Artist One", Year: 1984, }, testTrack{ Name: "Track Two", Artist: "Artist One", Year: 2000, }, testTrack{ Name: "Track One", Artist: "Artist One", Year: 1984, }, }, }, }, }, }, }, fields: []attr.Interface{attr.String("Artist"), attr.Int("Year")}, out: []interface{}{"Artist One", nil}, }, } for ii, tt := range table { g := CommonGroupAttr(tt.fields, tt.in) got := make([]interface{}, len(tt.out)) for i, f := range tt.fields { got[i] = g.Field(f.Name()) } if !reflect.DeepEqual(got, tt.out) { t.Errorf("[%d] got %#v, expected %#v", ii, got, tt.out) } } }