343 lines
9 KiB
Go
343 lines
9 KiB
Go
|
// Copyright 2016 Google Inc. All Rights Reserved.
|
||
|
//
|
||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
// you may not use this file except in compliance with the License.
|
||
|
// You may obtain a copy of the License at
|
||
|
//
|
||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||
|
//
|
||
|
// Unless required by applicable law or agreed to in writing, software
|
||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
// See the License for the specific language governing permissions and
|
||
|
// limitations under the License.
|
||
|
|
||
|
package translate
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"io/ioutil"
|
||
|
"log"
|
||
|
"net/http"
|
||
|
"net/url"
|
||
|
"os"
|
||
|
"reflect"
|
||
|
"strings"
|
||
|
"sync"
|
||
|
"testing"
|
||
|
|
||
|
"cloud.google.com/go/internal/testutil"
|
||
|
|
||
|
"golang.org/x/net/context"
|
||
|
"golang.org/x/text/language"
|
||
|
"google.golang.org/api/option"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
once sync.Once
|
||
|
authOpt option.ClientOption
|
||
|
)
|
||
|
|
||
|
func initTest(ctx context.Context, t *testing.T) *Client {
|
||
|
if testing.Short() {
|
||
|
t.Skip("integration tests skipped in short mode")
|
||
|
}
|
||
|
once.Do(func() { authOpt = authOption() })
|
||
|
if authOpt == nil {
|
||
|
t.Skip("Integration tests skipped. See CONTRIBUTING.md for details")
|
||
|
}
|
||
|
client, err := NewClient(ctx, authOpt)
|
||
|
if err != nil {
|
||
|
t.Fatalf("NewClient: %v", err)
|
||
|
}
|
||
|
return client
|
||
|
}
|
||
|
|
||
|
func authOption() option.ClientOption {
|
||
|
ts := testutil.TokenSource(context.Background(), Scope)
|
||
|
if ts != nil {
|
||
|
log.Println("authenticating via OAuth2")
|
||
|
return option.WithTokenSource(ts)
|
||
|
}
|
||
|
apiKey := os.Getenv("GCLOUD_TESTS_API_KEY")
|
||
|
if apiKey != "" {
|
||
|
log.Println("authenticating with API key")
|
||
|
return option.WithAPIKey(apiKey)
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
type fakeTransport struct {
|
||
|
req *http.Request
|
||
|
}
|
||
|
|
||
|
func (t *fakeTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||
|
t.req = req
|
||
|
return &http.Response{
|
||
|
Status: fmt.Sprintf("%d OK", http.StatusOK),
|
||
|
StatusCode: http.StatusOK,
|
||
|
Body: ioutil.NopCloser(strings.NewReader("{}")),
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
func TestTranslateURL(t *testing.T) {
|
||
|
// The translate API has all inputs in the URL.
|
||
|
// Make sure we generate the right one.
|
||
|
ctx := context.Background()
|
||
|
ft := &fakeTransport{}
|
||
|
c, err := NewClient(ctx, option.WithHTTPClient(&http.Client{Transport: ft}))
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
for _, test := range []struct {
|
||
|
target language.Tag
|
||
|
inputs []string
|
||
|
opts *Options
|
||
|
want url.Values
|
||
|
}{
|
||
|
{language.Spanish, []string{"text"}, nil, url.Values{
|
||
|
"q": []string{"text"},
|
||
|
"target": []string{"es"},
|
||
|
}},
|
||
|
{language.English, []string{"text"}, &Options{}, url.Values{
|
||
|
"q": []string{"text"},
|
||
|
"target": []string{"en"},
|
||
|
}},
|
||
|
{language.Turkish, []string{"t1", "t2"}, nil, url.Values{
|
||
|
"q": []string{"t1", "t2"},
|
||
|
"target": []string{"tr"},
|
||
|
}},
|
||
|
{language.English, []string{"text"}, &Options{Source: language.French},
|
||
|
url.Values{
|
||
|
"q": []string{"text"},
|
||
|
"source": []string{"fr"},
|
||
|
"target": []string{"en"},
|
||
|
},
|
||
|
},
|
||
|
{language.English, []string{"text"}, &Options{Source: language.French, Format: HTML}, url.Values{
|
||
|
"q": []string{"text"},
|
||
|
"source": []string{"fr"},
|
||
|
"format": []string{"html"},
|
||
|
"target": []string{"en"},
|
||
|
}},
|
||
|
} {
|
||
|
_, err = c.Translate(ctx, test.inputs, test.target, test.opts)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
got := ft.req.URL.Query()
|
||
|
test.want.Add("alt", "json")
|
||
|
if !reflect.DeepEqual(got, test.want) {
|
||
|
t.Errorf("Translate(%s, %v, %+v):\ngot %s\nwant %s",
|
||
|
test.target, test.inputs, test.opts, got, test.want)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestTranslateOneInput(t *testing.T) {
|
||
|
ctx := context.Background()
|
||
|
c := initTest(ctx, t)
|
||
|
defer c.Close()
|
||
|
|
||
|
translate := func(input string, target language.Tag, opts *Options) Translation {
|
||
|
ts, err := c.Translate(ctx, []string{input}, target, opts)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if len(ts) != 1 {
|
||
|
t.Fatalf("wanted one Translation, got %d", len(ts))
|
||
|
}
|
||
|
return ts[0]
|
||
|
}
|
||
|
|
||
|
for _, test := range []struct {
|
||
|
input string
|
||
|
source language.Tag
|
||
|
output string
|
||
|
target language.Tag
|
||
|
}{
|
||
|
// https://www.youtube.com/watch?v=x1sQkEfAdfY
|
||
|
{"Le singe est sur la branche", language.French,
|
||
|
"The monkey is on the branch", language.English},
|
||
|
// https://www.youtube.com/watch?v=akbflkF_1zY
|
||
|
{"I will not buy this record, it is scratched", language.English,
|
||
|
"Nem fogok vásárolni ezt a lemezt, azt karcos", language.Hungarian},
|
||
|
} {
|
||
|
// Provide source and format.
|
||
|
tr := translate(test.input, test.target, &Options{Source: test.source, Format: Text})
|
||
|
if got, want := tr.Source, language.Und; got != want {
|
||
|
t.Errorf("source: got %q, wanted %q", got, want)
|
||
|
continue
|
||
|
}
|
||
|
if got, want := tr.Text, test.output; got != want {
|
||
|
t.Errorf("text: got %q, want %q", got, want)
|
||
|
}
|
||
|
// Omit source; it should be detected.
|
||
|
tr = translate(test.input, test.target, &Options{Format: Text})
|
||
|
if got, want := tr.Source, test.source; got != want {
|
||
|
t.Errorf("source: got %q, wanted %q", got, want)
|
||
|
continue
|
||
|
}
|
||
|
if got, want := tr.Text, test.output; got != want {
|
||
|
t.Errorf("text: got %q, want %q", got, want)
|
||
|
}
|
||
|
|
||
|
// Omit format. Defaults to HTML. Still works with plain text.
|
||
|
tr = translate(test.input, test.target, nil)
|
||
|
if got, want := tr.Source, test.source; got != want {
|
||
|
t.Errorf("source: got %q, wanted %q", got, want)
|
||
|
continue
|
||
|
}
|
||
|
if got, want := tr.Text, test.output; got != want {
|
||
|
t.Errorf("text: got %q, want %q", got, want)
|
||
|
}
|
||
|
|
||
|
// Add HTML tags to input. They should be in output.
|
||
|
htmlify := func(s string) string {
|
||
|
return "<b><i>" + s + "</i></b>"
|
||
|
}
|
||
|
tr = translate(htmlify(test.input), test.target, nil)
|
||
|
if got, want := tr.Text, htmlify(test.output); got != want {
|
||
|
t.Errorf("html: got %q, want %q", got, want)
|
||
|
}
|
||
|
// Using the HTML format behaves the same.
|
||
|
tr = translate(htmlify(test.input), test.target, &Options{Format: HTML})
|
||
|
if got, want := tr.Text, htmlify(test.output); got != want {
|
||
|
t.Errorf("html: got %q, want %q", got, want)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// This tests the beta "nmt" model.
|
||
|
func TestTranslateModel(t *testing.T) {
|
||
|
ctx := context.Background()
|
||
|
c := initTest(ctx, t)
|
||
|
defer c.Close()
|
||
|
|
||
|
trs, err := c.Translate(ctx, []string{"Hello"}, language.French, &Options{Model: "nmt"})
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if len(trs) != 1 {
|
||
|
t.Fatalf("wanted one Translation, got %d", len(trs))
|
||
|
}
|
||
|
tr := trs[0]
|
||
|
if got, want := tr.Text, "Bonjour"; got != want {
|
||
|
t.Errorf("text: got %q, want %q", got, want)
|
||
|
}
|
||
|
if got, want := tr.Model, "nmt"; got != want {
|
||
|
t.Errorf("model: got %q, want %q", got, want)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestTranslateMultipleInputs(t *testing.T) {
|
||
|
ctx := context.Background()
|
||
|
c := initTest(ctx, t)
|
||
|
defer c.Close()
|
||
|
|
||
|
inputs := []string{
|
||
|
"When you're a Jet, you're a Jet all the way",
|
||
|
"From your first cigarette to your last dying day",
|
||
|
"When you're a Jet if the spit hits the fan",
|
||
|
"You got brothers around, you're a family man",
|
||
|
}
|
||
|
ts, err := c.Translate(ctx, inputs, language.French, nil)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if got, want := len(ts), len(inputs); got != want {
|
||
|
t.Fatalf("got %d Translations, wanted %d", got, want)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestTranslateErrors(t *testing.T) {
|
||
|
ctx := context.Background()
|
||
|
c := initTest(ctx, t)
|
||
|
defer c.Close()
|
||
|
|
||
|
for _, test := range []struct {
|
||
|
ctx context.Context
|
||
|
target language.Tag
|
||
|
inputs []string
|
||
|
opts *Options
|
||
|
}{
|
||
|
{ctx, language.English, nil, nil},
|
||
|
{ctx, language.Und, []string{"input"}, nil},
|
||
|
{ctx, language.English, []string{}, nil},
|
||
|
{ctx, language.English, []string{"input"}, &Options{Format: "random"}},
|
||
|
} {
|
||
|
_, err := c.Translate(test.ctx, test.inputs, test.target, test.opts)
|
||
|
if err == nil {
|
||
|
t.Errorf("%+v: got nil, want error", test)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestDetectLanguage(t *testing.T) {
|
||
|
ctx := context.Background()
|
||
|
c := initTest(ctx, t)
|
||
|
defer c.Close()
|
||
|
ds, err := c.DetectLanguage(ctx, []string{
|
||
|
"Today is Monday",
|
||
|
"Aujourd'hui est lundi",
|
||
|
})
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if len(ds) != 2 {
|
||
|
t.Fatalf("got %d detection lists, want 2", len(ds))
|
||
|
}
|
||
|
checkDetections(t, ds[0], language.English)
|
||
|
checkDetections(t, ds[1], language.French)
|
||
|
}
|
||
|
|
||
|
func checkDetections(t *testing.T, ds []Detection, want language.Tag) {
|
||
|
for _, d := range ds {
|
||
|
if d.Language == want {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
t.Errorf("%v: missing %s", ds, want)
|
||
|
}
|
||
|
|
||
|
// A small subset of the supported languages.
|
||
|
var supportedLangs = []Language{
|
||
|
{Name: "Danish", Tag: language.Danish},
|
||
|
{Name: "English", Tag: language.English},
|
||
|
{Name: "French", Tag: language.French},
|
||
|
{Name: "German", Tag: language.German},
|
||
|
{Name: "Greek", Tag: language.Greek},
|
||
|
{Name: "Hindi", Tag: language.Hindi},
|
||
|
{Name: "Hungarian", Tag: language.Hungarian},
|
||
|
{Name: "Italian", Tag: language.Italian},
|
||
|
{Name: "Russian", Tag: language.Russian},
|
||
|
{Name: "Turkish", Tag: language.Turkish},
|
||
|
}
|
||
|
|
||
|
func TestSupportedLanguages(t *testing.T) {
|
||
|
ctx := context.Background()
|
||
|
c := initTest(ctx, t)
|
||
|
defer c.Close()
|
||
|
got, err := c.SupportedLanguages(ctx, language.English)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
want := map[language.Tag]Language{}
|
||
|
for _, sl := range supportedLangs {
|
||
|
want[sl.Tag] = sl
|
||
|
}
|
||
|
for _, g := range got {
|
||
|
w, ok := want[g.Tag]
|
||
|
if !ok {
|
||
|
continue
|
||
|
}
|
||
|
if g != w {
|
||
|
t.Errorf("got %+v, want %+v", g, w)
|
||
|
}
|
||
|
delete(want, g.Tag)
|
||
|
}
|
||
|
if len(want) > 0 {
|
||
|
t.Errorf("missing: %+v", want)
|
||
|
}
|
||
|
}
|