- Implemented core model components in `modules.py` including various convolutional layers and normalization techniques. - Added transformation functions in `transforms.py` for piecewise rational quadratic transformations. - Created utility functions in `utils.py` for checkpoint management, logging, and hyperparameter handling. - Introduced monotonic alignment functionality with Cython optimization in `monotonic_align`. - Developed a minimal inference server in `server.py` to handle synthesis requests. - Updated requirements to include necessary dependencies for Cython and scipy.
131 lines
3.5 KiB
Go
131 lines
3.5 KiB
Go
package jtalk
|
||
|
||
import (
|
||
"reflect"
|
||
"testing"
|
||
)
|
||
|
||
func TestIsJapaneseChar(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
r rune
|
||
want bool
|
||
}{
|
||
{"ascii letter", 'a', true},
|
||
{"ascii digit", '5', true},
|
||
{"hiragana", 'お', true},
|
||
{"katakana", 'ー', true}, // U+30FC, within -ヿ
|
||
{"kanji", '様', true},
|
||
{"iteration mark", '々', true},
|
||
{"fullwidth digit 1-9", '1', true},
|
||
{"fullwidth digit 0 excluded (quirk in the original regex)", '0', false},
|
||
{"fullwidth letter", 'A', true},
|
||
{"halfwidth katakana", 'ヲ', true},
|
||
{"japanese comma is a mark, not a char", '、', false},
|
||
{"japanese period is a mark", '。', false},
|
||
{"ascii punctuation is a mark", '!', false},
|
||
{"space is a mark", ' ', false},
|
||
}
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
if got := isJapaneseChar(tt.r); got != tt.want {
|
||
t.Errorf("isJapaneseChar(%q) = %v, want %v", tt.r, got, tt.want)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestSplitByMarks(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
text string
|
||
wantSpans []string
|
||
wantMarks []rune
|
||
}{
|
||
{
|
||
name: "no marks",
|
||
text: "おはよう",
|
||
wantSpans: []string{"おはよう"},
|
||
wantMarks: nil,
|
||
},
|
||
{
|
||
name: "one internal mark",
|
||
text: "おにー様、すきです",
|
||
wantSpans: []string{"おにー様", "すきです"},
|
||
wantMarks: []rune{'、'},
|
||
},
|
||
{
|
||
name: "consecutive marks produce an empty span between them",
|
||
text: "すごい!!すごい",
|
||
wantSpans: []string{"すごい", "", "すごい"},
|
||
wantMarks: []rune{'!', '!'},
|
||
},
|
||
{
|
||
name: "trailing mark leaves an empty final span",
|
||
text: "ありがとう。",
|
||
wantSpans: []string{"ありがとう", ""},
|
||
wantMarks: []rune{'。'},
|
||
},
|
||
}
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
gotSpans, gotMarks := splitByMarks(tt.text)
|
||
if !reflect.DeepEqual(gotSpans, tt.wantSpans) {
|
||
t.Errorf("splitByMarks() spans = %#v, want %#v", gotSpans, tt.wantSpans)
|
||
}
|
||
if !reflect.DeepEqual(gotMarks, tt.wantMarks) {
|
||
t.Errorf("splitByMarks() marks = %#v, want %#v", gotMarks, tt.wantMarks)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestMarkToASCII(t *testing.T) {
|
||
tests := []struct {
|
||
r rune
|
||
want string
|
||
}{
|
||
{'、', ","},
|
||
{'。', "."},
|
||
{'!', "!"},
|
||
{'!', "!"}, // ASCII passthrough
|
||
{'(', "("}, // ASCII passthrough
|
||
{'鳥', ""}, // unmapped non-ASCII rune falls back to empty, not a guess
|
||
}
|
||
for _, tt := range tests {
|
||
if got := markToASCII(tt.r); got != tt.want {
|
||
t.Errorf("markToASCII(%q) = %q, want %q", tt.r, got, tt.want)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestParseOutputLabelPhonemes(t *testing.T) {
|
||
t.Run("strips boundary sil and ignores content after the section", func(t *testing.T) {
|
||
raw := "[Text analysis result]\n" +
|
||
"some,morphological,analysis,-1\n" +
|
||
"\n[Output label]\n" +
|
||
"0 100 xx^xx-sil+o=n/A:xx+xx+xx\n" +
|
||
"100 200 xx^sil-o+n=i/A:0+1+5\n" +
|
||
"200 300 sil^o-n+i=i/A:1+2+4\n" +
|
||
"300 400 o^n-i+sil=xx/A:1+2+4\n" +
|
||
"\n[Global parameter]\n" +
|
||
"Some parameter -> value+with+plusses\n"
|
||
|
||
got, err := parseOutputLabelPhonemes(raw)
|
||
if err != nil {
|
||
t.Fatalf("parseOutputLabelPhonemes() error = %v", err)
|
||
}
|
||
want := []string{"o", "n", "i"}
|
||
if !reflect.DeepEqual(got, want) {
|
||
t.Fatalf("parseOutputLabelPhonemes() = %#v, want %#v (must not include [Global parameter] content)", got, want)
|
||
}
|
||
})
|
||
|
||
t.Run("missing section returns an error", func(t *testing.T) {
|
||
_, err := parseOutputLabelPhonemes("no label section here")
|
||
if err == nil {
|
||
t.Fatal("parseOutputLabelPhonemes() error = nil, want non-nil")
|
||
}
|
||
})
|
||
}
|