github.com/Aoi-hosizora/ahlib@v1.5.1-0.20230404072829-241b93cf91c7/xmodule/xmodule_test.go (about)

     1  package xmodule
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"github.com/Aoi-hosizora/ahlib/xtesting"
     7  	"reflect"
     8  	"strconv"
     9  	"strings"
    10  	"testing"
    11  )
    12  
    13  func TestSimpleTypes(t *testing.T) {
    14  	xtesting.Equal(t, ModuleName("").String(), "")
    15  	xtesting.Equal(t, ModuleName("-").String(), "-")
    16  	xtesting.Equal(t, ModuleName("~").String(), "~")
    17  	xtesting.Equal(t, ModuleName("test").String(), "test")
    18  
    19  	xtesting.Equal(t, nameKey("").String(), "<invalid>")
    20  	xtesting.Equal(t, nameKey("-").String(), "name:-")
    21  	xtesting.Equal(t, nameKey("~").String(), "name:~")
    22  	xtesting.Equal(t, nameKey("name").String(), "name:name")
    23  
    24  	xtesting.Equal(t, typeKey(nil).String(), "<invalid>")
    25  	xtesting.Equal(t, typeKey(reflect.TypeOf("s")).String(), "type:string")
    26  	xtesting.Equal(t, typeKey(reflect.TypeOf(&strconv.NumError{})).String(), "type:*strconv.NumError")
    27  	xtesting.Equal(t, typeKey(reflect.TypeOf(new(fmt.Stringer)).Elem()).String(), "type:fmt.Stringer")
    28  }
    29  
    30  func TestProvideByName(t *testing.T) {
    31  	defer func() { _mc = NewModuleContainer() }()
    32  	SetLogger(DefaultLogger(LogSilent, nil, nil))
    33  	for _, tc := range []struct {
    34  		giveName   ModuleName
    35  		giveModule interface{}
    36  		wantPanicP bool
    37  		wantPanicR bool
    38  	}{
    39  		{ModuleName(""), 0, true, true},
    40  		{ModuleName("-"), 0, true, true},
    41  		{ModuleName("~"), 0, true, true},
    42  		{ModuleName("0"), nil, true, false},
    43  		{ModuleName("int"), 12, false, false},                    // int
    44  		{ModuleName("uint"), uint(12), false, false},             // uint
    45  		{ModuleName("float"), 12.5, false, false},                // float
    46  		{ModuleName("bool"), true, false, false},                 // bool
    47  		{ModuleName("string"), "a", false, false},                // string
    48  		{ModuleName("array"), [2]string{"1", "2"}, false, false}, // array
    49  		{ModuleName("slice"), []string{"1", "2"}, false, false},  // slice
    50  		{ModuleName("pointer"), &struct{}{}, false, false},       // pointer
    51  		{ModuleName("struct"), struct{ int }{1}, false, false},   // struct
    52  	} {
    53  		if tc.wantPanicP {
    54  			xtesting.Panic(t, func() { ProvideByName(tc.giveName, tc.giveModule) })
    55  		}
    56  		if tc.wantPanicR {
    57  			xtesting.Panic(t, func() { RemoveByName(tc.giveName) })
    58  		}
    59  		if !tc.wantPanicP && !tc.wantPanicR {
    60  			ProvideByName(tc.giveName, tc.giveModule)
    61  			xtesting.Equal(t, _mc.modules[nameKey(tc.giveName)], tc.giveModule)
    62  			xtesting.True(t, RemoveByName(tc.giveName))
    63  			_, ok := _mc.modules[nameKey(tc.giveName)]
    64  			xtesting.False(t, ok)
    65  			xtesting.False(t, RemoveByName(tc.giveName))
    66  		}
    67  	}
    68  }
    69  
    70  func TestProvideByType(t *testing.T) {
    71  	defer func() { _mc = NewModuleContainer() }()
    72  	SetLogger(DefaultLogger(LogSilent, nil, nil))
    73  	for _, tc := range []struct {
    74  		giveModule interface{}
    75  		wantPanicP bool
    76  		wantPanicR bool
    77  	}{
    78  		{nil, true, true},
    79  		{12, false, false},                  // int
    80  		{uint(12), false, false},            // uint
    81  		{12.5, false, false},                // float
    82  		{true, false, false},                // bool
    83  		{"a", false, false},                 // string
    84  		{[2]string{"1", "2"}, false, false}, // array
    85  		{[]string{"1", "2"}, false, false},  // slice
    86  		{&struct{}{}, false, false},         // pointer
    87  		{struct{ int }{1}, false, false},    // struct
    88  	} {
    89  		if tc.wantPanicP {
    90  			xtesting.Panic(t, func() { ProvideByType(tc.giveModule) })
    91  		}
    92  		if tc.wantPanicR {
    93  			xtesting.Panic(t, func() { RemoveByType(tc.giveModule) })
    94  		}
    95  		if !tc.wantPanicP && !tc.wantPanicR {
    96  			ProvideByType(tc.giveModule)
    97  			xtesting.Equal(t, _mc.modules[typeKey(reflect.TypeOf(tc.giveModule))], tc.giveModule)
    98  			xtesting.True(t, RemoveByType(tc.giveModule))
    99  			_, ok := _mc.modules[typeKey(reflect.TypeOf(tc.giveModule))]
   100  			xtesting.False(t, ok)
   101  			xtesting.False(t, RemoveByType(tc.giveModule))
   102  		}
   103  	}
   104  }
   105  
   106  func TestProvideByIntf(t *testing.T) {
   107  	defer func() { _mc = NewModuleContainer() }()
   108  	SetLogger(DefaultLogger(LogSilent, nil, nil))
   109  	for _, tc := range []struct {
   110  		givePtr    interface{}
   111  		giveImpl   interface{}
   112  		wantPanicP bool
   113  		wantPanicR bool
   114  	}{
   115  		{nil, 0, true, false},
   116  		{0, nil, true, true},
   117  		{0, errors.New(""), true, true},  // non ptr
   118  		{t, "", true, true},              // non itf
   119  		{(*error)(nil), "", true, false}, // non impl
   120  		{(*error)(nil), errors.New("test"), false, false},
   121  		{(*fmt.Stringer)(nil), &strings.Builder{}, false, false},
   122  	} {
   123  		if tc.wantPanicP {
   124  			xtesting.Panic(t, func() { ProvideByIntf(tc.givePtr, tc.giveImpl) })
   125  		}
   126  		if tc.wantPanicR {
   127  			xtesting.Panic(t, func() { RemoveByIntf(tc.givePtr) })
   128  		}
   129  		if !tc.wantPanicP && !tc.wantPanicR {
   130  			ProvideByIntf(tc.givePtr, tc.giveImpl)
   131  			xtesting.Equal(t, _mc.modules[typeKey(reflect.TypeOf(tc.givePtr).Elem())], tc.giveImpl)
   132  			xtesting.True(t, RemoveByIntf(tc.givePtr))
   133  			_, ok := _mc.modules[typeKey(reflect.TypeOf(tc.givePtr).Elem())]
   134  			xtesting.False(t, ok)
   135  			xtesting.False(t, RemoveByIntf(tc.givePtr))
   136  		}
   137  	}
   138  }
   139  
   140  func TestGetByName(t *testing.T) {
   141  	defer func() { _mc = NewModuleContainer() }()
   142  	SetLogger(DefaultLogger(LogSilent, nil, nil))
   143  	ProvideByName("int", 12)
   144  	ProvideByName("uint", uint(12))
   145  	ProvideByName("float", 12.5)
   146  	ProvideByName("bool", true)
   147  	ProvideByName("string", "a")
   148  	ProvideByName("array", [2]string{"1", "2"})
   149  	ProvideByName("slice", []string{"1", "2"})
   150  	ProvideByName("pointer", &struct{}{})
   151  	ProvideByName("struct", struct{ int }{1})
   152  
   153  	for _, tc := range []struct {
   154  		giveName   ModuleName
   155  		wantModule interface{}
   156  		wantPanic  bool
   157  	}{
   158  		{ModuleName(""), nil, true},
   159  		{ModuleName("~"), nil, true},
   160  		{ModuleName("-"), nil, true},
   161  		{ModuleName("int"), 12, false},                    // int
   162  		{ModuleName("uint"), uint(12), false},             // uint
   163  		{ModuleName("float"), 12.5, false},                // float
   164  		{ModuleName("bool"), true, false},                 // bool
   165  		{ModuleName("string"), "a", false},                // string
   166  		{ModuleName("array"), [2]string{"1", "2"}, false}, // array
   167  		{ModuleName("slice"), []string{"1", "2"}, false},  // slice
   168  		{ModuleName("pointer"), &struct{}{}, false},       // pointer
   169  		{ModuleName("struct"), struct{ int }{1}, false},   // struct
   170  	} {
   171  		if tc.wantPanic {
   172  			xtesting.Panic(t, func() { GetByName(tc.giveName) })
   173  		} else {
   174  			module, ok := GetByName(tc.giveName)
   175  			xtesting.Equal(t, module, tc.wantModule)
   176  			xtesting.True(t, ok)
   177  			xtesting.Equal(t, MustGetByName(tc.giveName), tc.wantModule)
   178  		}
   179  	}
   180  
   181  	xtesting.Panic(t, func() { MustGetByName("") })
   182  	xtesting.Panic(t, func() { MustGetByName("~") })
   183  	xtesting.Panic(t, func() { MustGetByName("-") })
   184  	xtesting.Panic(t, func() { MustGetByName("not exist") })
   185  }
   186  
   187  func TestGetByType(t *testing.T) {
   188  	defer func() { _mc = NewModuleContainer() }()
   189  	SetLogger(DefaultLogger(LogSilent, nil, nil))
   190  	ProvideByType(12)
   191  	ProvideByType(uint(12))
   192  	ProvideByType(12.5)
   193  	ProvideByType(true)
   194  	ProvideByType("a")
   195  	ProvideByType([2]string{"1", "2"})
   196  	ProvideByType([]string{"1", "2"})
   197  	ProvideByType(&struct{}{})
   198  	ProvideByType(struct{ int }{1})
   199  
   200  	for _, tc := range []struct {
   201  		wantModule interface{}
   202  		wantPanic  bool
   203  	}{
   204  		{nil, true},
   205  		{nil, true},
   206  		{nil, true},
   207  		{12, false},                  // int
   208  		{uint(12), false},            // uint
   209  		{12.5, false},                // float
   210  		{true, false},                // bool
   211  		{"a", false},                 // string
   212  		{[2]string{"1", "2"}, false}, // array
   213  		{[]string{"1", "2"}, false},  // slice
   214  		{&struct{}{}, false},         // pointer
   215  		{struct{ int }{1}, false},    // struct
   216  	} {
   217  		if tc.wantPanic {
   218  			xtesting.Panic(t, func() { GetByType(tc.wantModule) })
   219  		} else {
   220  			module, ok := GetByType(tc.wantModule)
   221  			xtesting.Equal(t, module, tc.wantModule)
   222  			xtesting.True(t, ok)
   223  			xtesting.Equal(t, MustGetByType(tc.wantModule), tc.wantModule)
   224  		}
   225  	}
   226  
   227  	xtesting.Panic(t, func() { MustGetByType(struct{}{}) })
   228  }
   229  
   230  func TestGetByIntf(t *testing.T) {
   231  	defer func() { _mc = NewModuleContainer() }()
   232  	SetLogger(DefaultLogger(LogSilent, nil, nil))
   233  	ProvideByIntf((*error)(nil), errors.New("test"))
   234  	ProvideByIntf((*fmt.Stringer)(nil), &strings.Builder{})
   235  
   236  	for _, tc := range []struct {
   237  		givePtr    interface{}
   238  		wantModule interface{}
   239  		wantPanic  bool
   240  	}{
   241  		{nil, 0, true},
   242  		{0, errors.New(""), true}, // non ptr
   243  		{t, "", true},             // non itf
   244  		{(*error)(nil), errors.New("test"), false},
   245  		{(*fmt.Stringer)(nil), &strings.Builder{}, false},
   246  	} {
   247  		if tc.wantPanic {
   248  			xtesting.Panic(t, func() { GetByIntf(tc.givePtr) })
   249  		} else {
   250  			module, ok := GetByIntf(tc.givePtr)
   251  			xtesting.Equal(t, module, tc.wantModule)
   252  			xtesting.True(t, ok)
   253  			xtesting.Equal(t, MustGetByIntf(tc.givePtr), tc.wantModule)
   254  		}
   255  	}
   256  
   257  	xtesting.Panic(t, func() { MustGetByIntf((*fmt.GoStringer)(nil)) })
   258  }
   259  
   260  func TestInject(t *testing.T) {
   261  	defer func() { _mc = NewModuleContainer() }()
   262  	SetLogger(DefaultLogger(LogSilent, nil, nil))
   263  
   264  	t.Run("abnormal", func(t *testing.T) {
   265  		type allIgnored struct {
   266  			unexportedField string
   267  			ExportedField1  string
   268  			ExportedField2  string `module:""`
   269  			ExportedField3  string `module:"-"`
   270  		}
   271  		test1 := &allIgnored{}
   272  
   273  		for _, tc := range []struct {
   274  			giveCtrl  interface{}
   275  			wantErr   bool
   276  			wantPanic bool
   277  		}{
   278  			{nil, false, true},        // nil
   279  			{struct{}{}, false, true}, // struct
   280  			{new(int), true, true},    // *int
   281  			{test1, false, false},     // *struct
   282  		} {
   283  			if tc.wantPanic {
   284  				xtesting.Panic(t, func() { _ = Inject(tc.giveCtrl) })
   285  			} else {
   286  				xtesting.Equal(t, Inject(tc.giveCtrl) != nil, tc.wantErr)
   287  			}
   288  		}
   289  		for _, tc := range []struct {
   290  			give interface{}
   291  			want interface{}
   292  		}{
   293  			{test1.unexportedField, ""},
   294  			{test1.ExportedField1, ""},
   295  			{test1.ExportedField2, ""},
   296  			{test1.ExportedField3, ""},
   297  		} {
   298  			xtesting.Equal(t, tc.give, tc.want)
   299  		}
   300  	})
   301  
   302  	type testStruct struct {
   303  		Int1     int           `module:"int"`
   304  		Uint1    uint          `module:"uint"`
   305  		Float1   float64       `module:"float"`
   306  		Bool1    bool          `module:"bool"`
   307  		String1  string        `module:"string"`
   308  		Array1   [2]string     `module:"array"`
   309  		Slice1   []string      `module:"slice"`
   310  		Pointer1 *struct{}     `module:"pointer"`
   311  		Struct1  struct{ int } `module:"struct"`
   312  		Int2     int           `module:"~"`
   313  		Uint2    uint          `module:"~"`
   314  		Float2   float64       `module:"~"`
   315  		Bool2    bool          `module:"~"`
   316  		String2  string        `module:"~"`
   317  		Array2   [2]string     `module:"~"`
   318  		Slice2   []string      `module:"~"`
   319  		Pointer2 *struct{}     `module:"~"`
   320  		Struct2  struct{ int } `module:"~"`
   321  		Err      error         `module:"~"`
   322  		Sb       fmt.Stringer  `module:"~"`
   323  	}
   324  	ProvideByName("int", 12)
   325  	ProvideByName("uint", uint(12))
   326  	ProvideByName("float", 12.5)
   327  	ProvideByName("bool", true)
   328  	ProvideByName("string", "a")
   329  	ProvideByName("array", [2]string{"1", "2"})
   330  	ProvideByName("slice", []string{"1", "2"})
   331  	ProvideByName("pointer", &struct{}{})
   332  	ProvideByName("struct", struct{ int }{1})
   333  	ProvideByType(12)
   334  	ProvideByType(uint(12))
   335  	ProvideByType(12.5)
   336  	ProvideByType(true)
   337  	ProvideByType("a")
   338  	ProvideByType([2]string{"1", "2"})
   339  	ProvideByType([]string{"1", "2"})
   340  	ProvideByType(&struct{}{})
   341  	ProvideByType(struct{ int }{1})
   342  	ProvideByIntf((*error)(nil), errors.New("test"))
   343  	ProvideByIntf((*fmt.Stringer)(nil), &strings.Builder{})
   344  
   345  	t.Run("normal", func(t *testing.T) {
   346  		test2 := &testStruct{}
   347  		xtesting.Nil(t, Inject(test2))
   348  		xtesting.NotPanic(t, func() { MustInject(test2) })
   349  		for _, tc := range []struct {
   350  			give interface{}
   351  			want interface{}
   352  		}{
   353  			{test2.Int1, 12},
   354  			{test2.Uint1, uint(12)},
   355  			{test2.Float1, 12.5},
   356  			{test2.Bool1, true},
   357  			{test2.String1, "a"},
   358  			{test2.Array1, [2]string{"1", "2"}},
   359  			{test2.Slice1, []string{"1", "2"}},
   360  			{test2.Pointer1, &struct{}{}},
   361  			{test2.Struct1, struct{ int }{1}},
   362  			{test2.Int2, 12},
   363  			{test2.Uint2, uint(12)},
   364  			{test2.Float2, 12.5},
   365  			{test2.Bool2, true},
   366  			{test2.String2, "a"},
   367  			{test2.Array2, [2]string{"1", "2"}},
   368  			{test2.Slice2, []string{"1", "2"}},
   369  			{test2.Pointer2, &struct{}{}},
   370  			{test2.Struct2, struct{ int }{1}},
   371  			{test2.Err, errors.New("test")},
   372  			{test2.Sb, &strings.Builder{}},
   373  		} {
   374  			xtesting.Equal(t, tc.give, tc.want)
   375  		}
   376  	})
   377  
   378  	t.Run("some errors", func(t *testing.T) {
   379  		// type mismatch
   380  		ProvideByName("uint", 12)
   381  		test := &testStruct{}
   382  		xtesting.NotNil(t, Inject(test))
   383  		xtesting.Panic(t, func() { MustInject(test) })
   384  
   385  		// module not found
   386  		ProvideByName("uint", uint(12))
   387  		_mc.modules = map[mkey]interface{}{}
   388  		test = &testStruct{}
   389  		xtesting.NotNil(t, Inject(test))
   390  		xtesting.Panic(t, func() { MustInject(test) })
   391  	})
   392  }
   393  
   394  func TestAutoProvide(t *testing.T) {
   395  	t.Run("providers", func(t *testing.T) {
   396  		xtesting.Panic(t, func() { NameProvider("", 0) })
   397  		xtesting.Panic(t, func() { NameProvider("~", struct{}{}) })
   398  		xtesting.Panic(t, func() { NameProvider("xxx", nil) })
   399  		xtesting.NotPanic(t, func() { NameProvider("xxx", "module") })
   400  		xtesting.Panic(t, func() { TypeProvider(nil) })
   401  		xtesting.NotPanic(t, func() { TypeProvider("module") })
   402  		xtesting.Panic(t, func() { IntfProvider(0, struct{}{}) })
   403  		xtesting.Panic(t, func() { IntfProvider(fmt.Stringer(nil), ModuleName("x")) })
   404  		xtesting.NotPanic(t, func() { IntfProvider((*fmt.Stringer)(nil), ModuleName("x")) })
   405  
   406  		_mc = NewModuleContainer()
   407  		_mc.SetLogger(DefaultLogger(LogSilent, nil, nil))
   408  		xtesting.Panic(t, func() { _ = _mc.AutoProvide(&ModuleProvider{}) })
   409  		xtesting.NotPanic(t, func() { _ = _mc.AutoProvide(nil, NameProvider("xxx", 0)) })
   410  		xtesting.NotPanic(t, func() { _mc.MustAutoProvide(nil, nil, nil) })
   411  	})
   412  
   413  	t.Run("simple", func(t *testing.T) {
   414  		defer func() { _mc = NewModuleContainer() }()
   415  		SetLogger(DefaultLogger(LogSilent, nil, nil))
   416  
   417  		repository := []int{1, 2, 3, 4}
   418  		type DBer interface {
   419  			GetData() interface{}
   420  		}
   421  		type DB struct {
   422  			DBer // embed directly
   423  			Flag int8
   424  		}
   425  		type Service struct {
   426  			Repository []int `module:"repo"`
   427  			DB         DBer  `module:"~"`
   428  			Variable   string
   429  		}
   430  		type Controller struct {
   431  			Service *Service `module:"~"`
   432  		}
   433  
   434  		providers := []*ModuleProvider{
   435  			TypeProvider(&Service{Variable: "hello world"}),
   436  			NameProvider("repo", repository),
   437  			IntfProvider((*DBer)(nil), &DB{Flag: 3}),
   438  		}
   439  		xtesting.Nil(t, AutoProvide(providers...))
   440  		ctrl := &Controller{Service: MustGetByType(&Service{}).(*Service)}
   441  		xtesting.Equal(t, ctrl.Service.Variable, "hello world")
   442  		xtesting.Equal(t, ctrl.Service.Repository, []int{1, 2, 3, 4})
   443  		xtesting.Equal(t, ctrl.Service.DB.(*DB).Flag, int8(3))
   444  
   445  		_mc.modules = map[mkey]interface{}{}
   446  		xtesting.NotPanic(t, func() { _mc.MustAutoProvide(providers...) })
   447  		ctrl = &Controller{Service: MustGetByType(&Service{}).(*Service)}
   448  		xtesting.Equal(t, ctrl.Service.DB.(*DB).Flag, int8(3))
   449  	})
   450  
   451  	t.Run("complex", func(t *testing.T) {
   452  		defer func() { _mc = NewModuleContainer() }()
   453  		SetLogger(DefaultLogger(LogSilent, nil, nil))
   454  
   455  		type (
   456  			IE interface{ EEE() }
   457  			ID interface{ DDD() }
   458  			F  struct{}
   459  			E  struct {
   460  				IE
   461  				F *F     `module:"fff"`
   462  				X string `module:"str1"`
   463  				Y string `module:"str2"`
   464  				Z string `module:"~"`
   465  			}
   466  			C struct {
   467  				E IE     `module:"~"`
   468  				F *F     `module:"fff"`
   469  				X string `module:"str2"`
   470  				Y string `module:"~"`
   471  				Z uint64 `module:"~"`
   472  			}
   473  			D struct {
   474  				ID
   475  				C *C    `module:"~"`
   476  				X int32 `module:"int1"`
   477  				Y int64 `module:"int2"`
   478  				Z string
   479  			}
   480  			B struct {
   481  				C *C    `module:"~"`
   482  				D ID    `module:"~"`
   483  				X int64 `module:"int2"`
   484  				Y int8
   485  			}
   486  			A struct {
   487  				B *B     `module:"bbb"`
   488  				C *C     `module:"~"`
   489  				D *D     `module:"ddd"`
   490  				E IE     `module:"~"`
   491  				X string `module:"str1"`
   492  				Y string `module:"str2"`
   493  				Z int32  `module:"int1"`
   494  				W int64  `module:"int2"`
   495  			}
   496  			O struct {
   497  				A *A `module:"~"`
   498  				F *F `module:"~"`
   499  				X string
   500  				Y int64 `module:"int2"`
   501  			}
   502  		)
   503  		providers := []*ModuleProvider{
   504  			TypeProvider(&O{X: "xxx"}),
   505  			TypeProvider(&A{}),
   506  			NameProvider("bbb", &B{Y: 127}),
   507  			TypeProvider(&C{}),
   508  			IntfProvider((*ID)(nil), &D{Z: "zzz"}), NameProvider("ddd", &D{Z: "zzz2"}),
   509  			IntfProvider((*IE)(nil), &E{}),
   510  			NameProvider("fff", &F{}), TypeProvider(&F{}),
   511  			TypeProvider("abc"), TypeProvider(uint64(789)),
   512  			NameProvider("int1", int32(111)), NameProvider("int2", int64(222)),
   513  			NameProvider("str1", "sss"), NameProvider("str2", "ttt"),
   514  		}
   515  		_mc = NewModuleContainer()
   516  		_mc.SetLogger(DefaultLogger(LogPrvName|LogPrvType|LogPrvIntf|LogInjFinish, nil, nil))
   517  		xtesting.Nil(t, AutoProvide(providers...))
   518  		fmt.Println("==============")
   519  		_mc = NewModuleContainer()
   520  		_mc.SetLogger(DefaultLogger(LogInjField|LogInjFinish, nil, nil))
   521  		xtesting.NotPanic(t, func() { MustAutoProvide(providers...) })
   522  
   523  		o := _mc.MustGetByType(&O{}).(*O)
   524  		xtesting.Equal(t, o.X, "xxx")
   525  		xtesting.Equal(t, o.Y, int64(222))
   526  		xtesting.Equal(t, *o.F, F{})
   527  		xtesting.Equal(t, o.A.X, "sss")
   528  		xtesting.Equal(t, o.A.Y, "ttt")
   529  		xtesting.Equal(t, o.A.Z, int32(111))
   530  		xtesting.Equal(t, o.A.W, int64(222))
   531  		xtesting.Equal(t, o.A.B.X, int64(222))
   532  		xtesting.Equal(t, o.A.B.Y, int8(127))
   533  		xtesting.Equal(t, o.A.B.C, o.A.C)
   534  		xtesting.Equal(t, o.A.B.D.(*D).Y, int64(222))
   535  		xtesting.Equal(t, o.A.B.D.(*D).Z, "zzz")
   536  		xtesting.Equal(t, o.A.C.X, "ttt")
   537  		xtesting.Equal(t, o.A.C.Y, "abc")
   538  		xtesting.Equal(t, o.A.C.Z, uint64(789))
   539  		xtesting.Equal(t, o.A.C.E, o.A.E)
   540  		xtesting.Equal(t, *o.A.C.F, F{})
   541  		xtesting.Equal(t, o.A.D.X, int32(111))
   542  		xtesting.Equal(t, o.A.D.Y, int64(222))
   543  		xtesting.Equal(t, o.A.D.Z, "zzz2")
   544  		xtesting.Equal(t, o.A.D.C, o.A.C)
   545  		xtesting.Equal(t, o.A.E.(*E).X, "sss")
   546  		xtesting.Equal(t, o.A.E.(*E).Y, "ttt")
   547  		xtesting.Equal(t, o.A.E.(*E).Z, "abc")
   548  		xtesting.Equal(t, *o.A.E.(*E).F, F{})
   549  	})
   550  
   551  	t.Run("errors", func(t *testing.T) {
   552  		defer func() { _mc = NewModuleContainer() }()
   553  		SetLogger(DefaultLogger(LogSilent, nil, nil))
   554  		restore := func() { _mc.modules = map[mkey]interface{}{} }
   555  
   556  		type Dep1 struct {
   557  			X string `module:"x"`
   558  			Y string `module:"y"`
   559  		}
   560  		type Dep2 struct {
   561  			D *Dep2 `module:"~"`
   562  		}
   563  		type Dep3 struct {
   564  			D interface{} `module:"dep4"`
   565  		}
   566  		type Dep4 struct {
   567  			D *Dep3 `module:"~"`
   568  		}
   569  
   570  		restore()
   571  		xtesting.NotNil(t, AutoProvide(TypeProvider(&Dep1{}))) // module not found
   572  		restore()
   573  		xtesting.NotNil(t, AutoProvide(NameProvider("x", &Dep2{}))) // module not found
   574  		restore()
   575  		xtesting.NotNil(t, AutoProvide(TypeProvider(&Dep1{}), NameProvider("x", 0))) // type mismatch
   576  		restore()
   577  		xtesting.NotNil(t, AutoProvide(TypeProvider(&Dep1{}), NameProvider("x", 0), NameProvider("y", false))) // type mismatch, multi error
   578  		restore()
   579  		xtesting.NotNil(t, AutoProvide(TypeProvider(&Dep2{}))) // dependency cycle
   580  		restore()
   581  		xtesting.NotNil(t, AutoProvide(TypeProvider(&Dep3{}), NameProvider("dep4", &Dep4{}))) // dependency cycle
   582  
   583  		restore()
   584  		xtesting.Panic(t, func() { MustAutoProvide(TypeProvider(&Dep1{})) })
   585  		restore()
   586  		xtesting.Panic(t, func() { MustAutoProvide(NameProvider("x", &Dep2{})) })
   587  		restore()
   588  		xtesting.Panic(t, func() { MustAutoProvide(TypeProvider(&Dep1{}), NameProvider("x", 0)) })
   589  		restore()
   590  		xtesting.Panic(t, func() { MustAutoProvide(TypeProvider(&Dep1{}), NameProvider("x", 0), NameProvider("y", false)) })
   591  		restore()
   592  		xtesting.Panic(t, func() { MustAutoProvide(TypeProvider(&Dep2{})) })
   593  		restore()
   594  		xtesting.Panic(t, func() { MustAutoProvide(TypeProvider(&Dep3{}), NameProvider("dep4", &Dep4{})) })
   595  	})
   596  }
   597  
   598  func TestLogger(t *testing.T) {
   599  	xtesting.EqualValue(t, LogPrvName, 1)    // 00001
   600  	xtesting.EqualValue(t, LogPrvType, 2)    // 00010
   601  	xtesting.EqualValue(t, LogPrvIntf, 4)    // 00100
   602  	xtesting.EqualValue(t, LogInjField, 8)   // 01000
   603  	xtesting.EqualValue(t, LogInjFinish, 16) // 10000
   604  	xtesting.EqualValue(t, LogSilent, 0)     // 00000
   605  	xtesting.EqualValue(t, LogPrv, 7)        // 00111
   606  	xtesting.EqualValue(t, LogAll, 31)       // 11111
   607  
   608  	type testStruct struct {
   609  		unexported bool
   610  		WithoutTag bool
   611  		EmptyTag   bool `module:""`
   612  		IgnoreTag  bool `module:"-"`
   613  
   614  		Int    int         `module:"int"`
   615  		Uint   uint        `module:"uint"`
   616  		Float  float64     `module:"~"`
   617  		String string      `module:"~"`
   618  		Itf    interface{} `module:"~"`
   619  		Error  error       `module:"~"`
   620  	}
   621  
   622  	for _, tc := range []struct {
   623  		name         string
   624  		giveLevel    LogLevel
   625  		giveIgnore   bool
   626  		giveMismatch bool
   627  		giveCustom   bool
   628  	}{
   629  		{"LogSilent", LogSilent, false, false, false},
   630  		{"LogPrvName", LogPrvName, false, false, false},
   631  		{"LogPrvType", LogPrvType, false, false, false},
   632  		{"LogPrvIntf", LogPrvIntf, false, false, false},
   633  		{"LogPrvName | LogPrvType", LogPrvName | LogPrvType, false, false, false},
   634  		{"LogPrvType | LogPrvIntf", LogPrvType | LogPrvIntf, false, false, false},
   635  		{"LogPrvName | LogPrvIntf", LogPrvIntf | LogPrvName, false, false, false},
   636  		{"LogPrv", LogPrv, false, false, false},
   637  		{"LogInjField", LogInjField, false, false, false},
   638  		{"LogInjFinish with module not found", LogInjFinish, true, false, false},
   639  		{"LogInjFinish with cannot assign", LogInjFinish, false, true, false},
   640  		{"LogInjField | LogInjFinish", LogInjField | LogInjFinish, false, false, false},
   641  		{"LogAll", LogAll, false, false, false},
   642  		{"LogAll with custom function", LogAll, false, false, true},
   643  	} {
   644  		t.Run(tc.name, func(t *testing.T) {
   645  			mc := NewModuleContainer()
   646  			if !tc.giveCustom {
   647  				mc.SetLogger(DefaultLogger(tc.giveLevel, nil, nil))
   648  			} else {
   649  				mc.SetLogger(DefaultLogger(tc.giveLevel, func(moduleName, moduleType string) {
   650  					fmt.Printf("[Xmodule] Prv: %s <-- %s\n", moduleName, moduleType)
   651  				}, func(moduleName string, structName string, additional string) {
   652  					fmt.Printf("[Xmodule] Inj: %s --> %s %s\n", moduleName, structName, additional)
   653  				}))
   654  			}
   655  
   656  			// prv
   657  			if !tc.giveMismatch {
   658  				mc.ProvideByName("int", 1)
   659  			} else {
   660  				mc.ProvideByName("int", "1")
   661  			}
   662  			if !tc.giveIgnore {
   663  				mc.ProvideByName("uint", uint(1))
   664  				mc.ProvideByType(1.0)
   665  			}
   666  			mc.ProvideByType("test")
   667  			mc.ProvideByIntf((*interface{})(nil), struct{}{})
   668  			mc.ProvideByIntf((*error)(nil), errors.New("test"))
   669  
   670  			// inj
   671  			_ = mc.Inject(&testStruct{})
   672  		})
   673  	}
   674  }