Add race conditions tests. Fix races.
This commit is contained in:
parent
6e728a3df5
commit
d6f4cbb2d5
5 changed files with 187 additions and 0 deletions
|
@ -63,3 +63,60 @@ msgstr[2] "And this is the second plural form: %s"
|
|||
t.Errorf("Expected 'And this is the second plural form: Variable' but got '%s'", tr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPackageRace(t *testing.T) {
|
||||
// Set PO content
|
||||
str := `# Some comment
|
||||
msgid "My text"
|
||||
msgstr "Translated text"
|
||||
|
||||
# More comments
|
||||
msgid "Another string"
|
||||
msgstr ""
|
||||
|
||||
msgid "One with var: %s"
|
||||
msgid_plural "Several with vars: %s"
|
||||
msgstr[0] "This one is the singular: %s"
|
||||
msgstr[1] "This one is the plural: %s"
|
||||
msgstr[2] "And this is the second plural form: %s"
|
||||
|
||||
`
|
||||
|
||||
// Create Locales directory on default location
|
||||
dirname := path.Clean(library + string(os.PathSeparator) + "en_US")
|
||||
err := os.MkdirAll(dirname, os.ModePerm)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't create test directory: %s", err.Error())
|
||||
}
|
||||
|
||||
// Write PO content to default domain file
|
||||
filename := path.Clean(dirname + string(os.PathSeparator) + domain + ".po")
|
||||
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't create test file: %s", err.Error())
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
_, err = f.WriteString(str)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't write to test file: %s", err.Error())
|
||||
}
|
||||
|
||||
// Init sync channels
|
||||
c1 := make(chan bool)
|
||||
c2 := make(chan bool)
|
||||
|
||||
// Test translations
|
||||
go func(done chan bool) {
|
||||
println(Get("My text"))
|
||||
done <- true
|
||||
}(c1)
|
||||
|
||||
go func(done chan bool) {
|
||||
println(Get("My text"))
|
||||
done <- true
|
||||
}(c2)
|
||||
|
||||
println(Get("My text"))
|
||||
}
|
||||
|
|
11
locale.go
11
locale.go
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"sync"
|
||||
)
|
||||
|
||||
/*
|
||||
|
@ -42,6 +43,9 @@ type Locale struct {
|
|||
|
||||
// List of available domains for this locale.
|
||||
domains map[string]*Po
|
||||
|
||||
// Sync Mutex
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
// NewLocale creates and initializes a new Locale object for a given language.
|
||||
|
@ -73,6 +77,9 @@ func (l *Locale) AddDomain(dom string) {
|
|||
po.ParseFile(filename)
|
||||
|
||||
// Save new domain
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
if l.domains == nil {
|
||||
l.domains = make(map[string]*Po)
|
||||
}
|
||||
|
@ -102,6 +109,10 @@ func (l *Locale) GetD(dom, str string, vars ...interface{}) string {
|
|||
// If n == 0, usually the singular form of the string is returned as defined in the PO file.
|
||||
// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax.
|
||||
func (l *Locale) GetND(dom, str, plural string, n int, vars ...interface{}) string {
|
||||
// Sync read
|
||||
l.RLock()
|
||||
defer l.RUnlock()
|
||||
|
||||
if l.domains != nil {
|
||||
if _, ok := l.domains[dom]; ok {
|
||||
if l.domains[dom] != nil {
|
||||
|
|
|
@ -69,3 +69,69 @@ msgstr[2] "And this is the second plural form: %s"
|
|||
t.Errorf("Expected 'And this is the second plural form: Variable' but got '%s'", tr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLocaleRace(t *testing.T) {
|
||||
// Set PO content
|
||||
str := `# Some comment
|
||||
msgid "My text"
|
||||
msgstr "Translated text"
|
||||
|
||||
# More comments
|
||||
msgid "Another string"
|
||||
msgstr ""
|
||||
|
||||
msgid "One with var: %s"
|
||||
msgid_plural "Several with vars: %s"
|
||||
msgstr[0] "This one is the singular: %s"
|
||||
msgstr[1] "This one is the plural: %s"
|
||||
msgstr[2] "And this is the second plural form: %s"
|
||||
|
||||
`
|
||||
|
||||
// Create Locales directory with simplified language code
|
||||
dirname := path.Clean("/tmp" + string(os.PathSeparator) + "es")
|
||||
err := os.MkdirAll(dirname, os.ModePerm)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't create test directory: %s", err.Error())
|
||||
}
|
||||
|
||||
// Write PO content to file
|
||||
filename := path.Clean(dirname + string(os.PathSeparator) + "race.po")
|
||||
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't create test file: %s", err.Error())
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
_, err = f.WriteString(str)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't write to test file: %s", err.Error())
|
||||
}
|
||||
|
||||
// Create Locale with full language code
|
||||
l := NewLocale("/tmp", "es")
|
||||
|
||||
// Init sync channels
|
||||
ac := make(chan bool)
|
||||
rc := make(chan bool)
|
||||
|
||||
// Add domain in goroutine
|
||||
go func(l *Locale, done chan bool) {
|
||||
l.AddDomain("race")
|
||||
done <- true
|
||||
}(l, ac)
|
||||
|
||||
// Get translations in goroutine
|
||||
go func(l *Locale, done chan bool) {
|
||||
println(l.GetD("race", "My text"))
|
||||
done <- true
|
||||
}(l, rc)
|
||||
|
||||
// Get translations at top level
|
||||
println(l.GetD("race", "My text"))
|
||||
|
||||
// Wait for goroutines to finish
|
||||
<-ac
|
||||
<-rc
|
||||
}
|
||||
|
|
8
po.go
8
po.go
|
@ -186,6 +186,10 @@ func (po *Po) Parse(str string) {
|
|||
// Get retrieves the corresponding translation for the given string.
|
||||
// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax.
|
||||
func (po *Po) Get(str string, vars ...interface{}) string {
|
||||
// Sync read
|
||||
po.RLock()
|
||||
defer po.RUnlock()
|
||||
|
||||
if po.translations != nil {
|
||||
if _, ok := po.translations[str]; ok {
|
||||
return fmt.Sprintf(po.translations[str].get(), vars...)
|
||||
|
@ -200,6 +204,10 @@ func (po *Po) Get(str string, vars ...interface{}) string {
|
|||
// If n == 0, usually the singular form of the string is returned as defined in the PO file.
|
||||
// Supports optional parameters (vars... interface{}) to be inserted on the formatted string using the fmt.Printf syntax.
|
||||
func (po *Po) GetN(str, plural string, n int, vars ...interface{}) string {
|
||||
// Sync read
|
||||
po.RLock()
|
||||
defer po.RUnlock()
|
||||
|
||||
if po.translations != nil {
|
||||
if _, ok := po.translations[str]; ok {
|
||||
return fmt.Sprintf(po.translations[str].getN(n), vars...)
|
||||
|
|
45
po_test.go
45
po_test.go
|
@ -59,3 +59,48 @@ msgstr[2] "And this is the second plural form: %s"
|
|||
t.Errorf("Expected 'And this is the second plural form: Variable' but got '%s'", tr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPoRace(t *testing.T) {
|
||||
// Set PO content
|
||||
str := `# Some comment
|
||||
msgid "My text"
|
||||
msgstr "Translated text"
|
||||
|
||||
# More comments
|
||||
msgid "Another string"
|
||||
msgstr ""
|
||||
|
||||
msgid "One with var: %s"
|
||||
msgid_plural "Several with vars: %s"
|
||||
msgstr[0] "This one is the singular: %s"
|
||||
msgstr[1] "This one is the plural: %s"
|
||||
msgstr[2] "And this is the second plural form: %s"
|
||||
|
||||
`
|
||||
|
||||
// Create Po object
|
||||
po := new(Po)
|
||||
|
||||
// Create sync channels
|
||||
pc := make(chan bool)
|
||||
rc := make(chan bool)
|
||||
|
||||
// Parse po content in a goroutine
|
||||
go func(po *Po, done chan bool) {
|
||||
po.Parse(str)
|
||||
done <- true
|
||||
}(po, pc)
|
||||
|
||||
// Read some translation on a goroutine
|
||||
go func(po *Po, done chan bool) {
|
||||
println(po.Get("My text"))
|
||||
done <- true
|
||||
}(po, rc)
|
||||
|
||||
// Read something at top level
|
||||
println(po.Get("My text"))
|
||||
|
||||
// Wait for goroutines to finish
|
||||
<-pc
|
||||
<-rc
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue