...

Source file src/golang.org/x/text/internal/catmsg/catmsg_test.go

Documentation: golang.org/x/text/internal/catmsg

     1  // Copyright 2017 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package catmsg
     6  
     7  import (
     8  	"errors"
     9  	"strings"
    10  	"testing"
    11  
    12  	"golang.org/x/text/language"
    13  )
    14  
    15  type renderer struct {
    16  	args   []int
    17  	result string
    18  }
    19  
    20  func (r *renderer) Arg(i int) interface{} {
    21  	if i >= len(r.args) {
    22  		return nil
    23  	}
    24  	return r.args[i]
    25  }
    26  
    27  func (r *renderer) Render(s string) {
    28  	if r.result != "" {
    29  		r.result += "|"
    30  	}
    31  	r.result += s
    32  }
    33  
    34  func TestCodec(t *testing.T) {
    35  	type test struct {
    36  		args   []int
    37  		out    string
    38  		decErr string
    39  	}
    40  	single := func(out, err string) []test { return []test{{out: out, decErr: err}} }
    41  	testCases := []struct {
    42  		desc   string
    43  		m      Message
    44  		enc    string
    45  		encErr string
    46  		tests  []test
    47  	}{{
    48  		desc:   "unused variable",
    49  		m:      &Var{"name", String("foo")},
    50  		encErr: errIsVar.Error(),
    51  		tests:  single("", ""),
    52  	}, {
    53  		desc:  "empty",
    54  		m:     empty{},
    55  		tests: single("", ""),
    56  	}, {
    57  		desc:  "sequence with empty",
    58  		m:     seq{empty{}},
    59  		tests: single("", ""),
    60  	}, {
    61  		desc:  "raw string",
    62  		m:     Raw("foo"),
    63  		tests: single("foo", ""),
    64  	}, {
    65  		desc:  "raw string no sub",
    66  		m:     Raw("${foo}"),
    67  		enc:   "\x02${foo}",
    68  		tests: single("${foo}", ""),
    69  	}, {
    70  		desc:  "simple string",
    71  		m:     String("foo"),
    72  		tests: single("foo", ""),
    73  	}, {
    74  		desc:  "affix",
    75  		m:     &Affix{String("foo"), "\t", "\n"},
    76  		tests: single("\t|foo|\n", ""),
    77  	}, {
    78  		desc:   "missing var",
    79  		m:      String("foo${bar}"),
    80  		enc:    "\x03\x03foo\x02\x03bar",
    81  		encErr: `unknown var "bar"`,
    82  		tests:  single("foo|bar", ""),
    83  	}, {
    84  		desc: "empty var",
    85  		m: seq{
    86  			&Var{"bar", seq{}},
    87  			String("foo${bar}"),
    88  		},
    89  		enc: "\x00\x05\x04\x02bar\x03\x03foo\x00\x00",
    90  		// TODO: recognize that it is cheaper to substitute bar.
    91  		tests: single("foo|bar", ""),
    92  	}, {
    93  		desc: "var after value",
    94  		m: seq{
    95  			String("foo${bar}"),
    96  			&Var{"bar", String("baz")},
    97  		},
    98  		encErr: errIsVar.Error(),
    99  		tests:  single("foo|bar", ""),
   100  	}, {
   101  		desc: "substitution",
   102  		m: seq{
   103  			&Var{"bar", String("baz")},
   104  			String("foo${bar}"),
   105  		},
   106  		tests: single("foo|baz", ""),
   107  	}, {
   108  		desc: "affix with substitution",
   109  		m: &Affix{seq{
   110  			&Var{"bar", String("baz")},
   111  			String("foo${bar}"),
   112  		}, "\t", "\n"},
   113  		tests: single("\t|foo|baz|\n", ""),
   114  	}, {
   115  		desc: "shadowed variable",
   116  		m: seq{
   117  			&Var{"bar", String("baz")},
   118  			seq{
   119  				&Var{"bar", String("BAZ")},
   120  				String("foo${bar}"),
   121  			},
   122  		},
   123  		tests: single("foo|BAZ", ""),
   124  	}, {
   125  		desc:  "nested value",
   126  		m:     nestedLang{nestedLang{empty{}}},
   127  		tests: single("nl|nl", ""),
   128  	}, {
   129  		desc: "not shadowed variable",
   130  		m: seq{
   131  			&Var{"bar", String("baz")},
   132  			seq{
   133  				String("foo${bar}"),
   134  				&Var{"bar", String("BAZ")},
   135  			},
   136  		},
   137  		encErr: errIsVar.Error(),
   138  		tests:  single("foo|baz", ""),
   139  	}, {
   140  		desc: "duplicate variable",
   141  		m: seq{
   142  			&Var{"bar", String("baz")},
   143  			&Var{"bar", String("BAZ")},
   144  			String("${bar}"),
   145  		},
   146  		encErr: "catmsg: duplicate variable \"bar\"",
   147  		tests:  single("baz", ""),
   148  	}, {
   149  		desc: "complete incomplete variable",
   150  		m: seq{
   151  			&Var{"bar", incomplete{}},
   152  			String("${bar}"),
   153  		},
   154  		enc: "\x00\t\b\x01\x01\x14\x04\x02bar\x03\x00\x00\x00",
   155  		// TODO: recognize that it is cheaper to substitute bar.
   156  		tests: single("bar", ""),
   157  	}, {
   158  		desc: "incomplete sequence",
   159  		m: seq{
   160  			incomplete{},
   161  			incomplete{},
   162  		},
   163  		encErr: ErrIncomplete.Error(),
   164  		tests:  single("", ErrNoMatch.Error()),
   165  	}, {
   166  		desc: "compile error variable",
   167  		m: seq{
   168  			&Var{"bar", errorCompileMsg{}},
   169  			String("${bar}"),
   170  		},
   171  		encErr: errCompileTest.Error(),
   172  		tests:  single("bar", ""),
   173  	}, {
   174  		desc:   "compile error message",
   175  		m:      errorCompileMsg{},
   176  		encErr: errCompileTest.Error(),
   177  		tests:  single("", ""),
   178  	}, {
   179  		desc: "compile error sequence",
   180  		m: seq{
   181  			errorCompileMsg{},
   182  			errorCompileMsg{},
   183  		},
   184  		encErr: errCompileTest.Error(),
   185  		tests:  single("", ""),
   186  	}, {
   187  		desc:  "macro",
   188  		m:     String("${exists(1)}"),
   189  		tests: single("you betya!", ""),
   190  	}, {
   191  		desc:  "macro incomplete",
   192  		m:     String("${incomplete(1)}"),
   193  		enc:   "\x03\x00\x01\nincomplete\x01",
   194  		tests: single("incomplete", ""),
   195  	}, {
   196  		desc:  "macro undefined at end",
   197  		m:     String("${undefined(1)}"),
   198  		enc:   "\x03\x00\x01\tundefined\x01",
   199  		tests: single("undefined", "catmsg: undefined macro \"undefined\""),
   200  	}, {
   201  		desc:  "macro undefined with more text following",
   202  		m:     String("${undefined(1)}."),
   203  		enc:   "\x03\x00\x01\tundefined\x01\x01.",
   204  		tests: single("undefined|.", "catmsg: undefined macro \"undefined\""),
   205  	}, {
   206  		desc:   "macro missing paren",
   207  		m:      String("${missing(1}"),
   208  		encErr: "catmsg: missing ')'",
   209  		tests:  single("$!(MISSINGPAREN)", ""),
   210  	}, {
   211  		desc:   "macro bad num",
   212  		m:      String("aa${bad(a)}"),
   213  		encErr: "catmsg: invalid number \"a\"",
   214  		tests:  single("aa$!(BADNUM)", ""),
   215  	}, {
   216  		desc:   "var missing brace",
   217  		m:      String("a${missing"),
   218  		encErr: "catmsg: missing '}'",
   219  		tests:  single("a$!(MISSINGBRACE)", ""),
   220  	}}
   221  	r := &renderer{}
   222  	dec := NewDecoder(language.Und, r, macros)
   223  	for _, tc := range testCases {
   224  		t.Run(tc.desc, func(t *testing.T) {
   225  			// Use a language other than Und so that we can test
   226  			// passing the language to nested values.
   227  			data, err := Compile(language.Dutch, macros, tc.m)
   228  			if failErr(err, tc.encErr) {
   229  				t.Errorf("encoding error: got %+q; want %+q", err, tc.encErr)
   230  			}
   231  			if tc.enc != "" && data != tc.enc {
   232  				t.Errorf("encoding: got %+q; want %+q", data, tc.enc)
   233  			}
   234  			for _, st := range tc.tests {
   235  				t.Run("", func(t *testing.T) {
   236  					*r = renderer{args: st.args}
   237  					if err = dec.Execute(data); failErr(err, st.decErr) {
   238  						t.Errorf("decoding error: got %+q; want %+q", err, st.decErr)
   239  					}
   240  					if r.result != st.out {
   241  						t.Errorf("decode: got %+q; want %+q", r.result, st.out)
   242  					}
   243  				})
   244  			}
   245  		})
   246  	}
   247  }
   248  
   249  func failErr(got error, want string) bool {
   250  	if got == nil {
   251  		return want != ""
   252  	}
   253  	return want == "" || !strings.Contains(got.Error(), want)
   254  }
   255  
   256  type seq []Message
   257  
   258  func (s seq) Compile(e *Encoder) (err error) {
   259  	err = ErrIncomplete
   260  	e.EncodeMessageType(msgFirst)
   261  	for _, m := range s {
   262  		// Pass only the last error, but allow erroneous or complete messages
   263  		// here to allow testing different scenarios.
   264  		err = e.EncodeMessage(m)
   265  	}
   266  	return err
   267  }
   268  
   269  type empty struct{}
   270  
   271  func (empty) Compile(e *Encoder) (err error) { return nil }
   272  
   273  var msgIncomplete = Register(
   274  	"golang.org/x/text/internal/catmsg.incomplete",
   275  	func(d *Decoder) bool { return false })
   276  
   277  type incomplete struct{}
   278  
   279  func (incomplete) Compile(e *Encoder) (err error) {
   280  	e.EncodeMessageType(msgIncomplete)
   281  	return ErrIncomplete
   282  }
   283  
   284  var msgNested = Register(
   285  	"golang.org/x/text/internal/catmsg.nested",
   286  	func(d *Decoder) bool {
   287  		d.Render(d.DecodeString())
   288  		d.ExecuteMessage()
   289  		return true
   290  	})
   291  
   292  type nestedLang struct{ Message }
   293  
   294  func (n nestedLang) Compile(e *Encoder) (err error) {
   295  	e.EncodeMessageType(msgNested)
   296  	e.EncodeString(e.Language().String())
   297  	e.EncodeMessage(n.Message)
   298  	return nil
   299  }
   300  
   301  type errorCompileMsg struct{}
   302  
   303  var errCompileTest = errors.New("catmsg: compile error test")
   304  
   305  func (errorCompileMsg) Compile(e *Encoder) (err error) {
   306  	return errCompileTest
   307  }
   308  
   309  type dictionary struct{}
   310  
   311  var (
   312  	macros       = dictionary{}
   313  	dictMessages = map[string]string{
   314  		"exists":     compile(String("you betya!")),
   315  		"incomplete": compile(incomplete{}),
   316  	}
   317  )
   318  
   319  func (d dictionary) Lookup(key string) (data string, ok bool) {
   320  	data, ok = dictMessages[key]
   321  	return
   322  }
   323  
   324  func compile(m Message) (data string) {
   325  	data, _ = Compile(language.Und, macros, m)
   326  	return data
   327  }
   328  

View as plain text