Exemplo n.º 1
0
func TestMultiHandleServiceGenerator(t *testing.T) {
	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()

	handles := make([]*handletest.MockHandle, 1000)

	var mh MultiHandle
	for i := range handles {
		handle := handletest.NewMockHandle(mockCtrl)
		handles[i] = handle
		mh = append(mh, handle)

		// only odd handles have a ServiceGenerator
		if i%2 == 0 {
			handle.EXPECT().ServiceGenerator().Return(nil)
			continue
		}

		handle.EXPECT().ServiceGenerator().Return(
			handletest.NewMockServiceGenerator(mockCtrl))
	}

	assert.NotNil(t, mh.ServiceGenerator())
}
Exemplo n.º 2
0
func TestGenerate(t *testing.T) {
	var (
		ts compile.TypeSpec = &compile.TypedefSpec{
			Name:   "Timestamp",
			File:   testdata(t, "thrift/common/bar.thrift"),
			Target: &compile.I64Spec{},
		}
		ts2 compile.TypeSpec = &compile.TypedefSpec{
			Name:   "Timestamp",
			File:   testdata(t, "thrift/foo.thrift"),
			Target: ts,
		}
	)

	ts2, err := ts2.Link(compile.EmptyScope("bar"))
	require.NoError(t, err)

	ts, err = ts.Link(compile.EmptyScope("bar"))
	require.NoError(t, err)

	module := &compile.Module{
		Name:       "foo",
		ThriftPath: testdata(t, "thrift/foo.thrift"),
		Includes: map[string]*compile.IncludedModule{
			"bar": {
				Name: "bar",
				Module: &compile.Module{
					Name:       "bar",
					ThriftPath: testdata(t, "thrift/common/bar.thrift"),
					Types:      map[string]compile.TypeSpec{"Timestamp": ts},
				},
			},
		},
		Types: map[string]compile.TypeSpec{"Timestamp": ts2},
	}

	tests := []struct {
		desc      string
		noRecurse bool
		getPlugin func(*gomock.Controller) plugin.Handle

		wantFiles []string
		wantError string
	}{
		{
			desc:      "nil plugin; no recurse",
			noRecurse: true,
			wantFiles: []string{"foo/types.go"},
		},
		{
			desc: "nil plugin; recurse",
			wantFiles: []string{
				"foo/types.go",
				"common/bar/types.go",
			},
		},
		{
			desc: "no service generator",
			getPlugin: func(mockCtrl *gomock.Controller) plugin.Handle {
				handle := handletest.NewMockHandle(mockCtrl)
				handle.EXPECT().ServiceGenerator().Return(nil)
				return handle
			},
			wantFiles: []string{
				"foo/types.go",
				"common/bar/types.go",
			},
		},
		{
			desc: "empty plugin",
			getPlugin: func(mockCtrl *gomock.Controller) plugin.Handle {
				return plugin.EmptyHandle
			},
			wantFiles: []string{
				"foo/types.go",
				"common/bar/types.go",
			},
		},
		{
			desc: "ServiceGenerator plugin",
			getPlugin: func(mockCtrl *gomock.Controller) plugin.Handle {
				sgen := handletest.NewMockServiceGenerator(mockCtrl)
				sgen.EXPECT().Generate(gomock.Any()).
					Return(&api.GenerateServiceResponse{
						Files: map[string][]byte{
							"foo.txt":    []byte("hello world\n"),
							"bar/baz.go": []byte("package bar\n"),
						},
					}, nil)

				handle := handletest.NewMockHandle(mockCtrl)
				handle.EXPECT().ServiceGenerator().Return(sgen)
				return handle
			},
			wantFiles: []string{
				"foo/types.go",
				"common/bar/types.go",
				"foo.txt",
				"bar/baz.go",
			},
		},
		{
			desc: "ServiceGenerator plugin conflict",
			getPlugin: func(mockCtrl *gomock.Controller) plugin.Handle {
				sgen := handletest.NewMockServiceGenerator(mockCtrl)
				sgen.EXPECT().Generate(gomock.Any()).
					Return(&api.GenerateServiceResponse{
						Files: map[string][]byte{
							"common/bar/types.go": []byte("hulk smash"),
						},
					}, nil)

				handle := handletest.NewMockHandle(mockCtrl)
				handle.EXPECT().ServiceGenerator().Return(sgen)
				return handle
			},
			wantError: `file generation conflict: multiple sources are trying to write to "common/bar/types.go"`,
		},
		{
			desc: "ServiceGenerator plugin error",
			getPlugin: func(mockCtrl *gomock.Controller) plugin.Handle {
				sgen := handletest.NewMockServiceGenerator(mockCtrl)
				sgen.EXPECT().Generate(gomock.Any()).Return(nil, errors.New("great sadness"))

				handle := handletest.NewMockHandle(mockCtrl)
				handle.EXPECT().ServiceGenerator().Return(sgen)
				return handle
			},
			wantError: `great sadness`,
		},
	}

	for _, tt := range tests {
		func() {
			mockCtrl := gomock.NewController(t)
			defer mockCtrl.Finish()

			outputDir, err := ioutil.TempDir(os.TempDir(), "test-generate-recurse")
			require.NoError(t, err)
			defer os.RemoveAll(outputDir)

			var p plugin.Handle
			if tt.getPlugin != nil {
				p = tt.getPlugin(mockCtrl)
			}

			err = Generate(module, &Options{
				OutputDir:     outputDir,
				PackagePrefix: "go.uber.org/thriftrw/gen/testdata",
				ThriftRoot:    testdata(t, "thrift"),
				Plugin:        p,
				NoRecurse:     tt.noRecurse,
			})
			if tt.wantError != "" {
				assert.Contains(t, err.Error(), tt.wantError)
				return
			}

			if assert.NoError(t, err, tt.desc) {
				for _, f := range tt.wantFiles {
					_, err = os.Stat(filepath.Join(outputDir, f))
					assert.NoError(t, err, tt.desc)
				}
			}
		}()
	}
}
Exemplo n.º 3
0
func TestMultiServiceGeneratorGenerate(t *testing.T) {
	type response struct {
		success *api.GenerateServiceResponse
		failure error
	}

	tests := []struct {
		desc string

		// list of responses from different service generators
		responses []response

		// final expected response or errors
		wantResponse    *api.GenerateServiceResponse
		wantErrors      []string
		wantOneOfErrors []string
		// both, wantErrors and wantOneOfErrors may be set. All errors in
		// wantErrors must be present, but only one or more of the errors in
		// wantOneOfErrors must be present.
	}{
		{
			desc: "no conflicts; no errors",
			responses: []response{
				{success: &api.GenerateServiceResponse{Files: map[string][]byte{
					"foo/a.go": {1, 2, 3},
					"foo/b.go": {4, 5, 6},
				}}},
				{success: &api.GenerateServiceResponse{Files: map[string][]byte{
					"foo/c.go": {7, 8, 9},
					"foo/d.go": {1, 2, 3},
				}}},
				{success: &api.GenerateServiceResponse{Files: map[string][]byte{
				// no files
				}}},
				{success: &api.GenerateServiceResponse{Files: map[string][]byte{
					"foo/keyvalue/e.go": {4, 5, 6},
				}}},
			},
			wantResponse: &api.GenerateServiceResponse{Files: map[string][]byte{
				"foo/a.go":          {1, 2, 3},
				"foo/b.go":          {4, 5, 6},
				"foo/c.go":          {7, 8, 9},
				"foo/d.go":          {1, 2, 3},
				"foo/keyvalue/e.go": {4, 5, 6},
			}},
		},
		{
			desc: "no conflicts; with errors",
			responses: []response{
				{failure: errors.New("foo: great sadness")},
				{success: &api.GenerateServiceResponse{Files: map[string][]byte{
					"foo/a.go": {1, 2, 3},
				}}},
				{success: &api.GenerateServiceResponse{Files: map[string][]byte{
					"foo/b.go": {4, 5, 6},
				}}},
				{failure: errors.New("bar: great sadness")},
			},
			wantErrors: []string{
				`foo: great sadness`,
				`bar: great sadness`,
			},
		},
		{
			desc: "conflicts",
			responses: []response{
				{success: &api.GenerateServiceResponse{Files: map[string][]byte{
					"foo/a.go": {1, 2, 3},
					"foo/b.go": {4, 5, 6},
				}}},
				{success: &api.GenerateServiceResponse{Files: map[string][]byte{
					"foo/c.go": {7, 8, 9},
					"foo/b.go": {1, 2, 3},
				}}},
			},
			wantErrors: []string{`plugin conflict: cannot write file "foo/b.go" for plugin`},
			wantOneOfErrors: []string{
				`plugin "plugin-1" already wrote to that file`,
				`plugin "plugin-0" already wrote to that file`,
			},
		},
	}

	req := &api.GenerateServiceRequest{
		RootServices: []api.ServiceID{1},
		Services: map[api.ServiceID]*api.Service{
			1: {
				Name:       "KeyValue",
				ThriftName: "KeyValue",
				Functions:  []*api.Function{},
				ModuleID:   api.ModuleID(1),
			},
		},
		Modules: map[api.ModuleID]*api.Module{
			1: {
				ImportPath: "go.uber.org/thriftrw/foo",
				Directory:  "foo",
			},
		},
	}

	for _, tt := range tests {
		func() {
			mockCtrl := gomock.NewController(t)
			defer mockCtrl.Finish()

			var msg MultiServiceGenerator
			for i, res := range tt.responses {
				handle := handletest.NewMockHandle(mockCtrl)
				handle.EXPECT().Name().Return(fmt.Sprintf("plugin-%d", i)).AnyTimes()

				sg := handletest.NewMockServiceGenerator(mockCtrl)
				msg = append(msg, sg)
				sg.EXPECT().Generate(req).Return(res.success, res.failure)
				sg.EXPECT().Handle().Return(handle).AnyTimes()
			}

			res, err := msg.Generate(req)
			if len(tt.wantErrors) > 0 || len(tt.wantOneOfErrors) > 0 {
				if !assert.Error(t, err, tt.desc) {
					return
				}

				for _, errMsg := range tt.wantErrors {
					assert.Contains(t, err.Error(), errMsg, tt.desc)
				}

				matches := len(tt.wantOneOfErrors) == 0
				for _, errMsg := range tt.wantOneOfErrors {
					if strings.Contains(err.Error(), errMsg) {
						matches = true
						break
					}
				}

				assert.True(t, matches, "expected %v to contain one of %v", err, tt.wantOneOfErrors)
			} else {
				assert.Equal(t, tt.wantResponse, res, tt.desc)
			}
		}()
	}
}