| // Copyright 2016 Google LLC |
| // |
| // 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 ( |
| "context" |
| "fmt" |
| "io/ioutil" |
| "log" |
| "net/http" |
| "net/url" |
| "os" |
| "strings" |
| "sync" |
| "testing" |
| |
| "cloud.google.com/go/internal/testutil" |
| "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 !testutil.Equal(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}, |
| {"The cat is on the chair", language.English, |
| "Le chat est sur la chaise", language.French}, |
| } { |
| // 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) |
| } |
| } |