refactored & audited, real analizer added

This commit is contained in:
Yevhen Odynets
2026-08-31 22:24:38 +03:00
parent e11c82bd76
commit 1b2521d4ac
15 changed files with 629 additions and 1 deletions
@@ -0,0 +1,178 @@
package moonblood
import (
"bufio"
"os"
"testing"
)
func TestParseMoonblood(t *testing.T) {
file, err := os.Open("../../resources/moonblood.txt")
if err != nil {
t.Fatal(err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
total, success := 0, 0
for scanner.Scan() {
line := scanner.Text()
if line == "" {
continue
}
result := Parse(line)
total++
if result.Group == "MOONBLOOD" && result.Year != 0 && result.Format == "FLAC" {
success++
}
if result.Confidence < 0.7 {
t.Logf("Low confidence: %s (%.2f)", line, result.Confidence)
}
}
t.Logf("Parsed %d releases, %d successful (%.1f%%)", total, success, float64(success)/float64(total)*100)
}
func TestParseFullTail(t *testing.T) {
r := Parse("Fane-Dragged_Through_the_Unending-24BIT-WEB-FLAC-2025-MOONBLOOD")
if r.Artist != "Fane" {
t.Errorf("Artist = %q, want %q", r.Artist, "Fane")
}
if r.Title != "Dragged_Through_the_Unending" {
t.Errorf("Title = %q, want %q", r.Title, "Dragged_Through_the_Unending")
}
if r.Year != 2025 || r.Format != "FLAC" || r.Source != "WEB" || r.BitDepth != "24BIT" {
t.Errorf("tail fields wrong: %+v", r)
}
if len(r.UnknownTokens) != 0 {
t.Errorf("UnknownTokens = %v, want empty", r.UnknownTokens)
}
if r.Confidence < 0.99 {
t.Errorf("Confidence = %.2f, want ~1.0", r.Confidence)
}
}
func TestParseEditionAndDemo(t *testing.T) {
r := Parse("Satans_Grind-Rotten_Demo-DEMO-16BIT-WEB-FLAC-2016-MOONBLOOD")
if r.Edition != "DEMO" {
t.Errorf("Edition = %q, want %q", r.Edition, "DEMO")
}
if r.Artist != "Satans_Grind" || r.Title != "Rotten_Demo" {
t.Errorf("Artist/Title = %q/%q", r.Artist, r.Title)
}
if len(r.UnknownTokens) != 0 {
t.Errorf("UnknownTokens = %v, want empty", r.UnknownTokens)
}
}
func TestParseArtistWithHyphen(t *testing.T) {
// Artist itself contains a hyphen; no part of it may leak into UnknownTokens.
r := Parse("Ultra-Violence-All_My_Life-EP-16BIT-WEB-FLAC-2026-MOONBLOOD")
if r.Artist != "Ultra" || r.Title != "Violence-All_My_Life" {
t.Errorf("Artist/Title = %q/%q, want Ultra / Violence-All_My_Life", r.Artist, r.Title)
}
if r.Edition != "EP" || r.BitDepth != "16BIT" || r.Source != "WEB" || r.Year != 2026 {
t.Errorf("tail fields wrong: %+v", r)
}
if len(r.UnknownTokens) != 0 {
t.Errorf("UnknownTokens = %v, want empty", r.UnknownTokens)
}
}
func TestParseLanguageAndCDSource(t *testing.T) {
r := Parse("Burn_Thou_in_Effigy-Memorias_Postumas_do_Imperador-BR-CD-FLAC-2026-MOONBLOOD")
if r.Language != "BR" || r.Source != "CD" || r.Year != 2026 || r.Format != "FLAC" {
t.Errorf("tail fields wrong: %+v", r)
}
if len(r.UnknownTokens) != 0 {
t.Errorf("UnknownTokens = %v, want empty", r.UnknownTokens)
}
}
func TestParseNoYear(t *testing.T) {
// Without a year the tail still parses; group stays the last token.
r := Parse("Fane-Gallant_Shroud-EP-16BIT-WEB-FLAC-MOONBLOOD")
if r.Group != "MOONBLOOD" || r.Year != 0 {
t.Errorf("Group/Year = %q/%d", r.Group, r.Year)
}
if r.Edition != "EP" || r.Source != "WEB" || r.Format != "FLAC" {
t.Errorf("tail fields wrong: %+v", r)
}
if r.Artist != "Fane" || r.Title != "Gallant_Shroud" {
t.Errorf("Artist/Title = %q/%q", r.Artist, r.Title)
}
}
func TestParseResolutionSlot(t *testing.T) {
r := Parse("Ice_Nine_Kills-Last_Chance_To_Make_Amends-REMASTERED-24BIT-44KHZ-WEB-FLAC-2024-FLACCiD")
if r.Group != "FLACCiD" {
t.Errorf("Group = %q, want FLACCiD", r.Group)
}
if r.Resolution != "44KHZ" {
t.Errorf("Resolution = %q, want 44KHZ", r.Resolution)
}
if r.BitDepth != "24BIT" || r.Edition != "REMASTERED" || r.Source != "WEB" || r.Year != 2024 {
t.Errorf("tail fields wrong: %+v", r)
}
if r.Artist != "Ice_Nine_Kills" || r.Title != "Last_Chance_To_Make_Amends" {
t.Errorf("Artist/Title = %q/%q", r.Artist, r.Title)
}
if len(r.UnknownTokens) != 0 {
t.Errorf("UnknownTokens = %v, want empty", r.UnknownTokens)
}
}
func TestParseVinylIsSource(t *testing.T) {
r := Parse("ROTH-Alles_Endet-DE-VINYL-FLAC-2026-TOTENKVLT")
if r.Language != "DE" {
t.Errorf("Language = %q, want DE", r.Language)
}
if r.Source != "VINYL" {
t.Errorf("Source = %q, want VINYL", r.Source)
}
if r.Edition != "" {
t.Errorf("Edition = %q, want empty (VINYL is source, not edition)", r.Edition)
}
if len(r.UnknownTokens) != 0 {
t.Errorf("UnknownTokens = %v, want empty", r.UnknownTokens)
}
}
func TestParseCaseInsensitiveDictionaries(t *testing.T) {
r := Parse("Artist-Some_Title-16bit-web-flac-2020-moonblood")
if r.BitDepth != "16BIT" || r.Source != "WEB" || r.Format != "FLAC" {
t.Errorf("case-insensitive lookup failed: %+v", r)
}
if r.Artist != "Artist" || r.Title != "Some_Title" {
t.Errorf("Artist/Title = %q/%q", r.Artist, r.Title)
}
}
func TestParseUnknownTailTokenReported(t *testing.T) {
// Невідомий токен між двома відомими маркерами → у UnknownTokens.
r := Parse("Artist-Some_Title-SPLIT-FOO-16BIT-WEB-FLAC-2025-MOONBLOOD")
if len(r.UnknownTokens) != 1 || r.UnknownTokens[0] != "FOO" {
t.Errorf("UnknownTokens = %v, want [FOO]", r.UnknownTokens)
}
if r.Edition != "SPLIT" || r.BitDepth != "16BIT" || r.Source != "WEB" || r.Year != 2025 {
t.Errorf("tail fields wrong: %+v", r)
}
if r.Artist != "Artist" || r.Title != "Some_Title" {
t.Errorf("Artist/Title = %q/%q", r.Artist, r.Title)
}
}
func TestParseCoreTokensNeverReported(t *testing.T) {
// Артист/тайтл ніколи не потрапляють в UnknownTokens.
r := Parse("Fane-Dragged_Through_the_Unending-24BIT-WEB-FLAC-2025-MOONBLOOD")
if len(r.UnknownTokens) != 0 {
t.Errorf("UnknownTokens = %v, want empty", r.UnknownTokens)
}
if r.Artist != "Fane" || r.Title != "Dragged_Through_the_Unending" {
t.Errorf("Artist/Title = %q/%q", r.Artist, r.Title)
}
}