You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

340 lines
8.9 KiB

  1. // Copyright 2016 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package translate
  15. import (
  16. "context"
  17. "fmt"
  18. "io/ioutil"
  19. "log"
  20. "net/http"
  21. "net/url"
  22. "os"
  23. "strings"
  24. "sync"
  25. "testing"
  26. "cloud.google.com/go/internal/testutil"
  27. "golang.org/x/text/language"
  28. "google.golang.org/api/option"
  29. )
  30. var (
  31. once sync.Once
  32. authOpt option.ClientOption
  33. )
  34. func initTest(ctx context.Context, t *testing.T) *Client {
  35. if testing.Short() {
  36. t.Skip("integration tests skipped in short mode")
  37. }
  38. once.Do(func() { authOpt = authOption() })
  39. if authOpt == nil {
  40. t.Skip("Integration tests skipped. See CONTRIBUTING.md for details")
  41. }
  42. client, err := NewClient(ctx, authOpt)
  43. if err != nil {
  44. t.Fatalf("NewClient: %v", err)
  45. }
  46. return client
  47. }
  48. func authOption() option.ClientOption {
  49. ts := testutil.TokenSource(context.Background(), Scope)
  50. if ts != nil {
  51. log.Println("authenticating via OAuth2")
  52. return option.WithTokenSource(ts)
  53. }
  54. apiKey := os.Getenv("GCLOUD_TESTS_API_KEY")
  55. if apiKey != "" {
  56. log.Println("authenticating with API key")
  57. return option.WithAPIKey(apiKey)
  58. }
  59. return nil
  60. }
  61. type fakeTransport struct {
  62. req *http.Request
  63. }
  64. func (t *fakeTransport) RoundTrip(req *http.Request) (*http.Response, error) {
  65. t.req = req
  66. return &http.Response{
  67. Status: fmt.Sprintf("%d OK", http.StatusOK),
  68. StatusCode: http.StatusOK,
  69. Body: ioutil.NopCloser(strings.NewReader("{}")),
  70. }, nil
  71. }
  72. func TestTranslateURL(t *testing.T) {
  73. // The translate API has all inputs in the URL.
  74. // Make sure we generate the right one.
  75. ctx := context.Background()
  76. ft := &fakeTransport{}
  77. c, err := NewClient(ctx, option.WithHTTPClient(&http.Client{Transport: ft}))
  78. if err != nil {
  79. t.Fatal(err)
  80. }
  81. for _, test := range []struct {
  82. target language.Tag
  83. inputs []string
  84. opts *Options
  85. want url.Values
  86. }{
  87. {language.Spanish, []string{"text"}, nil, url.Values{
  88. "q": []string{"text"},
  89. "target": []string{"es"},
  90. }},
  91. {language.English, []string{"text"}, &Options{}, url.Values{
  92. "q": []string{"text"},
  93. "target": []string{"en"},
  94. }},
  95. {language.Turkish, []string{"t1", "t2"}, nil, url.Values{
  96. "q": []string{"t1", "t2"},
  97. "target": []string{"tr"},
  98. }},
  99. {language.English, []string{"text"}, &Options{Source: language.French},
  100. url.Values{
  101. "q": []string{"text"},
  102. "source": []string{"fr"},
  103. "target": []string{"en"},
  104. },
  105. },
  106. {language.English, []string{"text"}, &Options{Source: language.French, Format: HTML}, url.Values{
  107. "q": []string{"text"},
  108. "source": []string{"fr"},
  109. "format": []string{"html"},
  110. "target": []string{"en"},
  111. }},
  112. } {
  113. _, err = c.Translate(ctx, test.inputs, test.target, test.opts)
  114. if err != nil {
  115. t.Fatal(err)
  116. }
  117. got := ft.req.URL.Query()
  118. test.want.Add("alt", "json")
  119. if !testutil.Equal(got, test.want) {
  120. t.Errorf("Translate(%s, %v, %+v):\ngot %s\nwant %s",
  121. test.target, test.inputs, test.opts, got, test.want)
  122. }
  123. }
  124. }
  125. func TestTranslateOneInput(t *testing.T) {
  126. ctx := context.Background()
  127. c := initTest(ctx, t)
  128. defer c.Close()
  129. translate := func(input string, target language.Tag, opts *Options) Translation {
  130. ts, err := c.Translate(ctx, []string{input}, target, opts)
  131. if err != nil {
  132. t.Fatal(err)
  133. }
  134. if len(ts) != 1 {
  135. t.Fatalf("wanted one Translation, got %d", len(ts))
  136. }
  137. return ts[0]
  138. }
  139. for _, test := range []struct {
  140. input string
  141. source language.Tag
  142. output string
  143. target language.Tag
  144. }{
  145. // https://www.youtube.com/watch?v=x1sQkEfAdfY
  146. {"Le singe est sur la branche", language.French,
  147. "The monkey is on the branch", language.English},
  148. {"The cat is on the chair", language.English,
  149. "Le chat est sur la chaise", language.French},
  150. } {
  151. // Provide source and format.
  152. tr := translate(test.input, test.target, &Options{Source: test.source, Format: Text})
  153. if got, want := tr.Source, language.Und; got != want {
  154. t.Errorf("source: got %q, wanted %q", got, want)
  155. continue
  156. }
  157. if got, want := tr.Text, test.output; got != want {
  158. t.Errorf("text: got %q, want %q", got, want)
  159. }
  160. // Omit source; it should be detected.
  161. tr = translate(test.input, test.target, &Options{Format: Text})
  162. if got, want := tr.Source, test.source; got != want {
  163. t.Errorf("source: got %q, wanted %q", got, want)
  164. continue
  165. }
  166. if got, want := tr.Text, test.output; got != want {
  167. t.Errorf("text: got %q, want %q", got, want)
  168. }
  169. // Omit format. Defaults to HTML. Still works with plain text.
  170. tr = translate(test.input, test.target, nil)
  171. if got, want := tr.Source, test.source; got != want {
  172. t.Errorf("source: got %q, wanted %q", got, want)
  173. continue
  174. }
  175. if got, want := tr.Text, test.output; got != want {
  176. t.Errorf("text: got %q, want %q", got, want)
  177. }
  178. // Add HTML tags to input. They should be in output.
  179. htmlify := func(s string) string {
  180. return "<b><i>" + s + "</i></b>"
  181. }
  182. tr = translate(htmlify(test.input), test.target, nil)
  183. if got, want := tr.Text, htmlify(test.output); got != want {
  184. t.Errorf("html: got %q, want %q", got, want)
  185. }
  186. // Using the HTML format behaves the same.
  187. tr = translate(htmlify(test.input), test.target, &Options{Format: HTML})
  188. if got, want := tr.Text, htmlify(test.output); got != want {
  189. t.Errorf("html: got %q, want %q", got, want)
  190. }
  191. }
  192. }
  193. // This tests the beta "nmt" model.
  194. func TestTranslateModel(t *testing.T) {
  195. ctx := context.Background()
  196. c := initTest(ctx, t)
  197. defer c.Close()
  198. trs, err := c.Translate(ctx, []string{"Hello"}, language.French, &Options{Model: "nmt"})
  199. if err != nil {
  200. t.Fatal(err)
  201. }
  202. if len(trs) != 1 {
  203. t.Fatalf("wanted one Translation, got %d", len(trs))
  204. }
  205. tr := trs[0]
  206. if got, want := tr.Text, "Bonjour"; got != want {
  207. t.Errorf("text: got %q, want %q", got, want)
  208. }
  209. if got, want := tr.Model, "nmt"; got != want {
  210. t.Errorf("model: got %q, want %q", got, want)
  211. }
  212. }
  213. func TestTranslateMultipleInputs(t *testing.T) {
  214. ctx := context.Background()
  215. c := initTest(ctx, t)
  216. defer c.Close()
  217. inputs := []string{
  218. "When you're a Jet, you're a Jet all the way",
  219. "From your first cigarette to your last dying day",
  220. "When you're a Jet if the spit hits the fan",
  221. "You got brothers around, you're a family man",
  222. }
  223. ts, err := c.Translate(ctx, inputs, language.French, nil)
  224. if err != nil {
  225. t.Fatal(err)
  226. }
  227. if got, want := len(ts), len(inputs); got != want {
  228. t.Fatalf("got %d Translations, wanted %d", got, want)
  229. }
  230. }
  231. func TestTranslateErrors(t *testing.T) {
  232. ctx := context.Background()
  233. c := initTest(ctx, t)
  234. defer c.Close()
  235. for _, test := range []struct {
  236. ctx context.Context
  237. target language.Tag
  238. inputs []string
  239. opts *Options
  240. }{
  241. {ctx, language.English, nil, nil},
  242. {ctx, language.Und, []string{"input"}, nil},
  243. {ctx, language.English, []string{}, nil},
  244. {ctx, language.English, []string{"input"}, &Options{Format: "random"}},
  245. } {
  246. _, err := c.Translate(test.ctx, test.inputs, test.target, test.opts)
  247. if err == nil {
  248. t.Errorf("%+v: got nil, want error", test)
  249. }
  250. }
  251. }
  252. func TestDetectLanguage(t *testing.T) {
  253. ctx := context.Background()
  254. c := initTest(ctx, t)
  255. defer c.Close()
  256. ds, err := c.DetectLanguage(ctx, []string{
  257. "Today is Monday",
  258. "Aujourd'hui est lundi",
  259. })
  260. if err != nil {
  261. t.Fatal(err)
  262. }
  263. if len(ds) != 2 {
  264. t.Fatalf("got %d detection lists, want 2", len(ds))
  265. }
  266. checkDetections(t, ds[0], language.English)
  267. checkDetections(t, ds[1], language.French)
  268. }
  269. func checkDetections(t *testing.T, ds []Detection, want language.Tag) {
  270. for _, d := range ds {
  271. if d.Language == want {
  272. return
  273. }
  274. }
  275. t.Errorf("%v: missing %s", ds, want)
  276. }
  277. // A small subset of the supported languages.
  278. var supportedLangs = []Language{
  279. {Name: "Danish", Tag: language.Danish},
  280. {Name: "English", Tag: language.English},
  281. {Name: "French", Tag: language.French},
  282. {Name: "German", Tag: language.German},
  283. {Name: "Greek", Tag: language.Greek},
  284. {Name: "Hindi", Tag: language.Hindi},
  285. {Name: "Hungarian", Tag: language.Hungarian},
  286. {Name: "Italian", Tag: language.Italian},
  287. {Name: "Russian", Tag: language.Russian},
  288. {Name: "Turkish", Tag: language.Turkish},
  289. }
  290. func TestSupportedLanguages(t *testing.T) {
  291. ctx := context.Background()
  292. c := initTest(ctx, t)
  293. defer c.Close()
  294. got, err := c.SupportedLanguages(ctx, language.English)
  295. if err != nil {
  296. t.Fatal(err)
  297. }
  298. want := map[language.Tag]Language{}
  299. for _, sl := range supportedLangs {
  300. want[sl.Tag] = sl
  301. }
  302. for _, g := range got {
  303. w, ok := want[g.Tag]
  304. if !ok {
  305. continue
  306. }
  307. if g != w {
  308. t.Errorf("got %+v, want %+v", g, w)
  309. }
  310. delete(want, g.Tag)
  311. }
  312. if len(want) > 0 {
  313. t.Errorf("missing: %+v", want)
  314. }
  315. }