cuelang.org/go@v0.13.0/internal/core/convert/go_test.go (about)

     1  // Copyright 2019 CUE Authors
     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  
    15  package convert_test
    16  
    17  // TODO: generate tests from Go's json encoder.
    18  
    19  import (
    20  	"encoding"
    21  	"math/big"
    22  	"reflect"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/cockroachdb/apd/v3"
    27  	"github.com/google/go-cmp/cmp"
    28  
    29  	"cuelang.org/go/cue/errors"
    30  	"cuelang.org/go/internal/core/adt"
    31  	"cuelang.org/go/internal/core/convert"
    32  	"cuelang.org/go/internal/core/debug"
    33  	"cuelang.org/go/internal/core/runtime"
    34  
    35  	_ "cuelang.org/go/pkg"
    36  )
    37  
    38  func mkBigInt(a int64) (v apd.Decimal) { v.SetInt64(a); return }
    39  
    40  type textMarshaller struct {
    41  	b string
    42  }
    43  
    44  func (t *textMarshaller) MarshalText() (b []byte, err error) {
    45  	return []byte(t.b), nil
    46  }
    47  
    48  var _ encoding.TextMarshaler = &textMarshaller{}
    49  
    50  func TestConvert(t *testing.T) {
    51  	type key struct {
    52  		a int
    53  	}
    54  	type stringType string
    55  	i34 := big.NewInt(34)
    56  	d35 := mkBigInt(35)
    57  	n36 := mkBigInt(-36)
    58  	f37 := big.NewFloat(37.0000)
    59  	testCases := []struct {
    60  		goVal interface{}
    61  		want  string
    62  	}{{
    63  		nil, "(_){ _ }",
    64  	}, {
    65  		true, "(bool){ true }",
    66  	}, {
    67  		false, "(bool){ false }",
    68  	}, {
    69  		errors.New("oh noes"), "(_|_){\n  // [eval] oh noes\n}",
    70  	}, {
    71  		"foo", `(string){ "foo" }`,
    72  	}, {
    73  		"\x80", `(string){ "�" }`,
    74  	}, {
    75  		3, "(int){ 3 }",
    76  	}, {
    77  		uint(3), "(int){ 3 }",
    78  	}, {
    79  		uint8(3), "(int){ 3 }",
    80  	}, {
    81  		uint16(3), "(int){ 3 }",
    82  	}, {
    83  		uint32(3), "(int){ 3 }",
    84  	}, {
    85  		uint64(3), "(int){ 3 }",
    86  	}, {
    87  		int8(-3), "(int){ -3 }",
    88  	}, {
    89  		int16(-3), "(int){ -3 }",
    90  	}, {
    91  		int32(-3), "(int){ -3 }",
    92  	}, {
    93  		int64(-3), "(int){ -3 }",
    94  	}, {
    95  		float64(3), "(float){ 3 }",
    96  	}, {
    97  		float64(3.1), "(float){ 3.1 }",
    98  	}, {
    99  		float32(3.1), "(float){ 3.1 }",
   100  	}, {
   101  		uintptr(3), "(int){ 3 }",
   102  	}, {
   103  		&i34, "(int){ 34 }",
   104  	}, {
   105  		&f37, "(float){ 37 }",
   106  	}, {
   107  		&d35, "(int){ 35 }",
   108  	}, {
   109  		&n36, "(int){ -36 }",
   110  	}, {
   111  		[]int{1, 2, 3, 4}, `(#list){
   112    0: (int){ 1 }
   113    1: (int){ 2 }
   114    2: (int){ 3 }
   115    3: (int){ 4 }
   116  }`,
   117  	}, {
   118  		struct {
   119  			A int
   120  			B *int
   121  		}{3, nil},
   122  		"(struct){\n  A: (int){ 3 }\n}",
   123  	}, {
   124  		[]interface{}{}, "(#list){\n}",
   125  	}, {
   126  		[]interface{}{nil}, "(#list){\n  0: (_){ _ }\n}",
   127  	}, {
   128  		map[string]interface{}{"a": 1, "x": nil}, `(struct){
   129    a: (int){ 1 }
   130    x: (_){ _ }
   131  }`,
   132  	}, {
   133  		map[string][]int{
   134  			"a": {1},
   135  			"b": {3, 4},
   136  		}, `(struct){
   137    a: (#list){
   138      0: (int){ 1 }
   139    }
   140    b: (#list){
   141      0: (int){ 3 }
   142      1: (int){ 4 }
   143    }
   144  }`,
   145  	}, {
   146  		map[bool]int{}, "(_|_){\n  // [eval] unsupported Go type for map key (bool)\n}",
   147  	}, {
   148  		map[struct{}]int{{}: 2}, "(_|_){\n  // [eval] unsupported Go type for map key (struct {})\n}",
   149  	}, {
   150  		map[int]int{1: 2}, `(struct){
   151    "1": (int){ 2 }
   152  }`,
   153  	}, {
   154  		struct {
   155  			a int
   156  			b int
   157  		}{3, 4},
   158  		"(struct){\n}",
   159  	}, {
   160  		struct {
   161  			A int
   162  			B int `json:"-"`
   163  			C int `json:",omitempty"`
   164  		}{3, 4, 0},
   165  		`(struct){
   166    A: (int){ 3 }
   167  }`,
   168  	}, {
   169  		struct {
   170  			A int
   171  			B int
   172  		}{3, 4},
   173  		`(struct){
   174    A: (int){ 3 }
   175    B: (int){ 4 }
   176  }`,
   177  	}, {
   178  		struct {
   179  			A int `json:"a"`
   180  			B int `yaml:"b"`
   181  		}{3, 4},
   182  		`(struct){
   183    a: (int){ 3 }
   184    b: (int){ 4 }
   185  }`,
   186  	}, {
   187  		struct {
   188  			A int `json:"" yaml:"" protobuf:"aa"`
   189  			B int `yaml:"cc" json:"bb" protobuf:"aa"`
   190  		}{3, 4},
   191  		`(struct){
   192    aa: (int){ 3 }
   193    bb: (int){ 4 }
   194  }`,
   195  	}, {
   196  		&struct{ A int }{3}, `(struct){
   197    A: (int){ 3 }
   198  }`,
   199  	}, {
   200  		(*struct{ A int })(nil), "(_){ _ }",
   201  	}, {
   202  		reflect.ValueOf(3), "(int){ 3 }",
   203  	}, {
   204  		time.Date(2019, 4, 1, 0, 0, 0, 0, time.UTC), `(string){ "2019-04-01T00:00:00Z" }`,
   205  	}, {
   206  		func() interface{} {
   207  			type T struct {
   208  				B int
   209  			}
   210  			type S struct {
   211  				A string
   212  				T
   213  			}
   214  			return S{}
   215  		}(),
   216  		`(struct){
   217    A: (string){ "" }
   218    B: (int){ 0 }
   219  }`,
   220  	},
   221  		{map[key]string{{a: 1}: "foo"},
   222  			"(_|_){\n  // [eval] unsupported Go type for map key (convert_test.key)\n}"},
   223  		{map[*textMarshaller]string{{b: "bar"}: "foo"},
   224  			"(struct){\n  \"&{bar}\": (string){ \"foo\" }\n}"},
   225  		{map[int]string{1: "foo"},
   226  			"(struct){\n  \"1\": (string){ \"foo\" }\n}"},
   227  		{map[string]encoding.TextMarshaler{"foo": nil},
   228  			"(struct){\n  foo: (_){ _ }\n}"},
   229  		{make(chan int),
   230  			"(_|_){\n  // [eval] unsupported Go type (chan int)\n}"},
   231  		{[]interface{}{func() {}},
   232  			"(_|_){\n  // [eval] unsupported Go type (func())\n}"},
   233  		{stringType("\x80"), `(string){ "�" }`},
   234  	}
   235  	r := runtime.New()
   236  	for _, tc := range testCases {
   237  		ctx := adt.NewContext(r, &adt.Vertex{})
   238  		t.Run("", func(t *testing.T) {
   239  			v := convert.GoValueToValue(ctx, tc.goVal, true)
   240  			n, ok := v.(*adt.Vertex)
   241  			if !ok {
   242  				n = &adt.Vertex{BaseValue: v}
   243  			}
   244  			got := debug.NodeString(ctx, n, nil)
   245  			if got != tc.want {
   246  				t.Error(cmp.Diff(got, tc.want))
   247  			}
   248  		})
   249  	}
   250  }
   251  
   252  func TestX(t *testing.T) {
   253  	t.Skip()
   254  
   255  	x := []string{}
   256  
   257  	r := runtime.New()
   258  	ctx := adt.NewContext(r, &adt.Vertex{})
   259  
   260  	v := convert.GoValueToValue(ctx, x, false)
   261  	// if err != nil {
   262  	// 	t.Fatal(err)
   263  	// }
   264  	got := debug.NodeString(ctx, v, nil)
   265  	t.Error(got)
   266  }
   267  
   268  func TestConvertType(t *testing.T) {
   269  	testCases := []struct {
   270  		goTyp       interface{}
   271  		want        string
   272  		expectError bool
   273  	}{{
   274  		goTyp: struct {
   275  			A int      `cue:">=0&<100"`
   276  			B *big.Int `cue:">=0"`
   277  			C *big.Int
   278  			D big.Int
   279  			F *big.Float
   280  		}{},
   281  		// TODO: indicate that B is explicitly an int only.
   282  		want: `{
   283    A: (((int & >=-9223372036854775808) & <=9223372036854775807) & (>=0 & <100))
   284    B: (int & >=0)
   285    C?: int
   286    D: int
   287    F?: number
   288  }`,
   289  	}, {
   290  		goTyp: &struct {
   291  			A int16 `cue:">=0&<100"`
   292  			B error `json:"b,"`
   293  			C string
   294  			D bool
   295  			F float64
   296  			L []byte
   297  			T time.Time
   298  			G func()
   299  		}{},
   300  		want: `(*null|{
   301    A: (((int & >=-32768) & <=32767) & (>=0 & <100))
   302    b: null
   303    C: string
   304    D: bool
   305    F: number
   306    L?: (*null|bytes)
   307    T: _
   308  })`,
   309  	}, {
   310  		goTyp: struct {
   311  			A int `cue:"<"` // invalid
   312  		}{},
   313  		want:        "_|_(invalid tag \"<\" for field \"A\": expected operand, found 'EOF' (and 1 more errors))",
   314  		expectError: true,
   315  	}, {
   316  		goTyp: struct {
   317  			A int `json:"-"` // skip
   318  			D *apd.Decimal
   319  			P ***apd.Decimal
   320  			I interface{ Foo() }
   321  			T string `cue:""` // allowed
   322  			h int
   323  		}{},
   324  		want: `{
   325    D?: number
   326    P?: (*null|number)
   327    I?: _
   328    T: (string & _)
   329  }`,
   330  	}, {
   331  		goTyp: struct {
   332  			A int8 `cue:"C-B"`
   333  			B int8 `cue:"C-A,opt"`
   334  			C int8 `cue:"A+B"`
   335  		}{},
   336  		// TODO: should B be marked as optional?
   337  		want: `{
   338    A: (((int & >=-128) & <=127) & (〈0;C〉 - 〈0;B〉))
   339    B?: (((int & >=-128) & <=127) & (〈0;C〉 - 〈0;A〉))
   340    C: (((int & >=-128) & <=127) & (〈0;A〉 + 〈0;B〉))
   341  }`,
   342  	}, {
   343  		goTyp: []string{},
   344  		want: `(*null|[
   345    ...string,
   346  ])`,
   347  	}, {
   348  		goTyp: [4]string{},
   349  		want: `〈import;list〉.Repeat([
   350    string,
   351  ], 4)`,
   352  	}, {
   353  		goTyp:       []func(){},
   354  		want:        "_|_(unsupported Go type (func()))",
   355  		expectError: true,
   356  	}, {
   357  		goTyp: map[string]struct{ A map[string]uint }{},
   358  		want: `(*null|{
   359    [string]: {
   360      A?: (*null|{
   361        [string]: ((int & >=0) & <=18446744073709551615)
   362      })
   363    }
   364  })`,
   365  	}, {
   366  		goTyp:       map[float32]int{},
   367  		want:        `_|_(unsupported Go type for map key (float32))`,
   368  		expectError: true,
   369  	}, {
   370  		goTyp:       map[int]map[float32]int{},
   371  		want:        `_|_(unsupported Go type for map key (float32))`,
   372  		expectError: true,
   373  	}, {
   374  		goTyp:       map[int]func(){},
   375  		want:        `_|_(unsupported Go type (func()))`,
   376  		expectError: true,
   377  	}, {
   378  		goTyp:       time.Now, // a function
   379  		want:        "_|_(unsupported Go type (func() time.Time))",
   380  		expectError: true,
   381  	}, {
   382  		goTyp: struct {
   383  			Foobar string `cue:"\"foo,bar\",opt"`
   384  		}{},
   385  		want: `{
   386    Foobar?: (string & "foo,bar")
   387  }`,
   388  	}, {
   389  		goTyp: struct {
   390  			Foobar string `cue:"\"foo,opt,bar\""`
   391  		}{},
   392  		want: `{
   393    Foobar: (string & "foo,opt,bar")
   394  }`,
   395  	}}
   396  
   397  	r := runtime.New()
   398  
   399  	for _, tc := range testCases {
   400  		t.Run("", func(t *testing.T) {
   401  			ctx := adt.NewContext(r, &adt.Vertex{})
   402  			v, err := convert.GoTypeToExpr(ctx, tc.goTyp)
   403  			got := debug.NodeString(ctx, v, nil)
   404  			if got != tc.want {
   405  				t.Errorf("\n got %q;\nwant %q", got, tc.want)
   406  			}
   407  			if tc.expectError && err == nil {
   408  				t.Errorf("\n expected an error but didn't get one")
   409  			} else if !tc.expectError && err != nil {
   410  				t.Errorf("\n got unexpected error: %v", err)
   411  			}
   412  			if err == nil && !tc.expectError {
   413  				val, _ := ctx.Evaluate(&adt.Environment{}, v)
   414  				if bot, ok := val.(*adt.Bottom); ok {
   415  					t.Errorf("\n unexpected error when evaluating result of conversion: %v", bot)
   416  				}
   417  			}
   418  		})
   419  	}
   420  }