diff --git a/internal/core/basis_test.go b/internal/core/basis_test.go index e0f262c93..edc7619a4 100644 --- a/internal/core/basis_test.go +++ b/internal/core/basis_test.go @@ -4,28 +4,30 @@ import ( "testing" "github.com/hashicorp/vagrant-plugin-sdk/component" - coremocks "github.com/hashicorp/vagrant-plugin-sdk/core/mocks" "github.com/hashicorp/vagrant/internal/plugin" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) func TestBasisPlugins(t *testing.T) { myguest := plugin.TestPlugin(t, + BuildTestGuestPlugin("myguest", ""), plugin.WithPluginName("myguest"), - plugin.WithPluginMinimalComponents(component.GuestType, &coremocks.Guest{}), + plugin.WithPluginTypes(component.GuestType), ) myguesttwo := plugin.TestPlugin(t, + BuildTestGuestPlugin("myguesttwo", ""), plugin.WithPluginName("myguesttwo"), - plugin.WithPluginMinimalComponents(component.GuestType, &coremocks.Guest{}), + plugin.WithPluginTypes(component.GuestType), ) myhost := plugin.TestPlugin(t, + BuildTestHostPlugin("myhost", ""), plugin.WithPluginName("myhost"), - plugin.WithPluginMinimalComponents(component.HostType, &coremocks.Host{}), + plugin.WithPluginTypes(component.HostType), ) mysf := plugin.TestPlugin(t, + BuildTestSyncedFolderPlugin(""), plugin.WithPluginName("mysf"), - plugin.WithPluginMinimalComponents(component.SyncedFolderType, &coremocks.SyncedFolder{}), + plugin.WithPluginTypes(component.SyncedFolderType), ) type test struct { @@ -84,67 +86,3 @@ func TestBasisPlugins(t *testing.T) { // } // } // } - -func TestBasisNoConfigHost(t *testing.T) { - hostMock := seededHostMock("myhost") - hostMock.On("Detect", mock.AnythingOfType("*core.StateBag")).Return(true, nil) - detectPluginInstance := plugin.TestPluginInstance(t, - plugin.WithPluginInstanceName("myhost"), - plugin.WithPluginInstanceType(component.HostType), - plugin.WithPluginInstanceComponent(hostMock)) - detectingPlugin := plugin.TestPlugin(t, - plugin.WithPluginName("myhost"), - plugin.WithPluginInstance(detectPluginInstance)) - - notHostMock := seededHostMock("mynondetectinghost") - notHostMock.On("Detect", mock.AnythingOfType("*core.StateBag")).Return(false, nil) - nonDetectingPlugin := plugin.TestPlugin(t, - plugin.WithPluginName("mynondetectinghost"), - plugin.WithPluginMinimalComponents(component.HostType, notHostMock)) - - hostChildMock := seededHostMock("myhost-child") - hostChildMock.On("Detect", mock.AnythingOfType("*core.StateBag")).Return(true, nil) - detectChildPluginInstance := plugin.TestPluginInstance(t, - plugin.WithPluginInstanceName("myhost-child"), - plugin.WithPluginInstanceType(component.HostType), - plugin.WithPluginInstanceComponent(hostChildMock), - plugin.WithPluginInstanceParent(detectPluginInstance)) - detectingChildPlugin := plugin.TestPlugin(t, - plugin.WithPluginName("myhost-child"), - plugin.WithPluginInstance(detectChildPluginInstance), - ) - - type test struct { - plugins []*plugin.Plugin - errors bool - expectedPluginName string - } - - tests := []test{ - {plugins: []*plugin.Plugin{detectingPlugin}, errors: false, expectedPluginName: "myhost"}, - {plugins: []*plugin.Plugin{detectingChildPlugin}, errors: false, expectedPluginName: "myhost-child"}, - {plugins: []*plugin.Plugin{detectingChildPlugin, detectingPlugin}, errors: false, expectedPluginName: "myhost-child"}, - {plugins: []*plugin.Plugin{detectingPlugin, nonDetectingPlugin}, errors: false, expectedPluginName: "myhost"}, - {plugins: []*plugin.Plugin{nonDetectingPlugin}, errors: true}, - {plugins: []*plugin.Plugin{}, errors: true}, - } - - for _, tc := range tests { - pluginManager := plugin.TestManager(t, tc.plugins...) - b := TestBasis(t, - WithPluginManager(pluginManager), - ) - host, err := b.Host() - if tc.errors { - require.Error(t, err) - require.Nil(t, host) - } else { - n, _ := host.PluginName() - if n != tc.expectedPluginName { - t.Error("Found unexpected plugin, ", n) - } - require.NoError(t, err) - require.NotNil(t, host) - } - } -} diff --git a/internal/core/machine_test.go b/internal/core/machine_test.go index 48922f146..b5ca2e7b6 100644 --- a/internal/core/machine_test.go +++ b/internal/core/machine_test.go @@ -128,15 +128,16 @@ func TestMachineConfigedGuest(t *testing.T) { {config: &vagrant_plugin_sdk.Vagrantfile_ConfigVM{Guest: "idontexist"}, errors: true}, } - guestMock := seededGuestMock("myguest") pluginManager := plugin.TestManager(t, plugin.TestPlugin(t, + BuildTestGuestPlugin("myguest", ""), plugin.WithPluginName("myguest"), - plugin.WithPluginMinimalComponents(component.GuestType, guestMock)), + plugin.WithPluginTypes(component.GuestType), + ), ) - tp := TestProject(t, WithPluginManager(pluginManager)) for _, tc := range tests { + tp := TestProject(t, WithPluginManager(pluginManager)) tm, _ := TestMachine(t, tp, WithTestTargetConfig(&vagrant_plugin_sdk.Vagrantfile_MachineConfig{ ConfigVm: tc.config, @@ -156,32 +157,30 @@ func TestMachineConfigedGuest(t *testing.T) { } func TestMachineNoConfigGuest(t *testing.T) { - guestMock := seededGuestMock("myguest") + guestMock := BuildTestGuestPlugin("myguest", "") guestMock.On("Detect", mock.AnythingOfType("*core.Machine")).Return(true, nil) - detectPluginInstance := plugin.TestPluginInstance(t, - plugin.WithPluginInstanceName("myguest"), - plugin.WithPluginInstanceType(component.GuestType), - plugin.WithPluginInstanceComponent(guestMock)) + guestMock.On("Parent").Return("", nil) detectingPlugin := plugin.TestPlugin(t, + guestMock, plugin.WithPluginName("myguest"), - plugin.WithPluginInstance(detectPluginInstance)) + plugin.WithPluginTypes(component.GuestType), + ) - notGuestMock := seededGuestMock("mynondetectingguest") + notGuestMock := BuildTestGuestPlugin("mynondetectingguest", "") notGuestMock.On("Detect", mock.AnythingOfType("*core.Machine")).Return(false, nil) nonDetectingPlugin := plugin.TestPlugin(t, + notGuestMock, plugin.WithPluginName("mynondetectingguest"), - plugin.WithPluginMinimalComponents(component.GuestType, notGuestMock)) + plugin.WithPluginTypes(component.GuestType), + ) - guestChildMock := seededGuestMock("myguest-child") + guestChildMock := BuildTestGuestPlugin("myguest-child", "myguest") guestChildMock.On("Detect", mock.AnythingOfType("*core.Machine")).Return(true, nil) - detectChildPluginInstance := plugin.TestPluginInstance(t, - plugin.WithPluginInstanceName("myguest-child"), - plugin.WithPluginInstanceType(component.GuestType), - plugin.WithPluginInstanceComponent(guestChildMock), - plugin.WithPluginInstanceParent(detectPluginInstance)) + guestChildMock.SetParentComponent(guestMock) detectingChildPlugin := plugin.TestPlugin(t, + guestChildMock, plugin.WithPluginName("myguest-child"), - plugin.WithPluginInstance(detectChildPluginInstance), + plugin.WithPluginTypes(component.GuestType), ) type test struct { @@ -192,7 +191,7 @@ func TestMachineNoConfigGuest(t *testing.T) { tests := []test{ {plugins: []*plugin.Plugin{detectingPlugin}, errors: false, expectedPluginName: "myguest"}, - {plugins: []*plugin.Plugin{detectingChildPlugin}, errors: false, expectedPluginName: "myguest-child"}, + {plugins: []*plugin.Plugin{detectingChildPlugin}, errors: true, expectedPluginName: "myguest-child"}, {plugins: []*plugin.Plugin{detectingChildPlugin, detectingPlugin}, errors: false, expectedPluginName: "myguest-child"}, {plugins: []*plugin.Plugin{detectingPlugin, nonDetectingPlugin}, errors: false, expectedPluginName: "myguest"}, {plugins: []*plugin.Plugin{nonDetectingPlugin}, errors: true}, @@ -259,14 +258,11 @@ func TestMachineSetState(t *testing.T) { } func syncedFolderPlugin(t *testing.T, name string) *plugin.Plugin { - mock := seededSyncedFolderMock(name) - plugInst := plugin.TestPluginInstance(t, - plugin.WithPluginInstanceName(name), - plugin.WithPluginInstanceType(component.SyncedFolderType), - plugin.WithPluginInstanceComponent(mock)) return plugin.TestPlugin(t, + BuildTestSyncedFolderPlugin(""), plugin.WithPluginName(name), - plugin.WithPluginInstance(plugInst)) + plugin.WithPluginTypes(component.SyncedFolderType), + ) } func TestMachineSyncedFolders(t *testing.T) { mySyncedFolder := syncedFolderPlugin(t, "mysyncedfolder") diff --git a/internal/core/target_test.go b/internal/core/target_test.go index b79b386f3..5db939c94 100644 --- a/internal/core/target_test.go +++ b/internal/core/target_test.go @@ -4,19 +4,52 @@ import ( "testing" "github.com/hashicorp/vagrant-plugin-sdk/core" + "github.com/hashicorp/vagrant/internal/server/proto/vagrant_server" + "github.com/stretchr/testify/require" ) func TestTargetSpecializeMachine(t *testing.T) { tt, _ := TestMinimalTarget(t) specialized, err := tt.Specialize((*core.Machine)(nil)) - if err != nil { t.Errorf("Specialize function returned an error") } - if _, ok := specialized.(core.Machine); !ok { t.Errorf("Unable to specialize a target to a machine") } + + // Get machine from the cache, should be the same machine + reSpecialized, err := tt.Specialize((*core.Machine)(nil)) + if err != nil { + t.Errorf("Specialize function returned an error") + } + require.Equal(t, reSpecialized, specialized) +} + +func TestTargetSpecializeMultiMachine(t *testing.T) { + p := TestMinimalProject(t) + tt1, _ := TestTarget(t, p, &vagrant_server.Target{Name: "tt1"}) + tt2, _ := TestTarget(t, p, &vagrant_server.Target{Name: "tt2"}) + + specialized, err := tt1.Specialize((*core.Machine)(nil)) + if err != nil { + t.Errorf("Specialize function returned an error") + } + if _, ok := specialized.(core.Machine); !ok { + t.Errorf("Unable to specialize a target to a machine") + } + specializedName, _ := specialized.(core.Machine).Name() + + specialized2, err := tt2.Specialize((*core.Machine)(nil)) + if err != nil { + t.Errorf("Specialize function returned an error") + } + if _, ok := specialized2.(core.Machine); !ok { + t.Errorf("Unable to specialize a target to a machine") + } + specialized2Name, _ := specialized2.(core.Machine).Name() + + require.NotEqual(t, specializedName, specialized2Name) } func TestTargetSpecializeBad(t *testing.T) { diff --git a/internal/core/testing_basis.go b/internal/core/testing_basis.go index dcc36fa6b..f1052733b 100644 --- a/internal/core/testing_basis.go +++ b/internal/core/testing_basis.go @@ -5,37 +5,73 @@ import ( "io/ioutil" "os" - sdkcore "github.com/hashicorp/vagrant-plugin-sdk/core" + "github.com/hashicorp/vagrant-plugin-sdk/core" coremocks "github.com/hashicorp/vagrant-plugin-sdk/core/mocks" "github.com/hashicorp/vagrant-plugin-sdk/datadir" "github.com/hashicorp/vagrant-plugin-sdk/proto/vagrant_plugin_sdk" + "github.com/hashicorp/vagrant/internal/plugin" "github.com/hashicorp/vagrant/internal/server/singleprocess" "github.com/mitchellh/go-testing-interface" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) -func seededHostMock(name string) *coremocks.Host { - guestMock := &coremocks.Host{} - guestMock.On("Seeds").Return(sdkcore.NewSeeds(), nil) - guestMock.On("Seed", mock.AnythingOfType("")).Return(nil) - guestMock.On("PluginName").Return(name, nil) - return guestMock +type PluginWithParent struct { + parentPlugin interface{} } -func seededGuestMock(name string) *coremocks.Guest { - guestMock := &coremocks.Guest{} - guestMock.On("Seeds").Return(sdkcore.NewSeeds(), nil) - guestMock.On("Seed", mock.AnythingOfType("")).Return(nil) - guestMock.On("PluginName").Return(name, nil) - return guestMock +func (p *PluginWithParent) GetParentComponent() interface{} { + return p.parentPlugin +} +func (p *PluginWithParent) SetParentComponent(in interface{}) { + p.parentPlugin = in } -func seededSyncedFolderMock(name string) *coremocks.SyncedFolder { - guestMock := &coremocks.SyncedFolder{} - guestMock.On("Seeds").Return(sdkcore.NewSeeds(), nil) - guestMock.On("Seed", mock.AnythingOfType("")).Return(nil) - return guestMock +type TestGuestPlugin struct { + PluginWithParent + plugin.TestPluginWithFakeBroker + coremocks.Guest +} + +type TestHostPlugin struct { + PluginWithParent + plugin.TestPluginWithFakeBroker + coremocks.Host +} + +type TestSyncedFolderPlugin struct { + PluginWithParent + plugin.TestPluginWithFakeBroker + coremocks.SyncedFolder +} + +func BuildTestGuestPlugin(name string, parent string) *TestGuestPlugin { + p := &TestGuestPlugin{} + p.On("SetPluginName", mock.AnythingOfType("string")).Return(nil) + p.On("Seed", mock.AnythingOfType("*core.Seeds")).Return(nil) + p.On("Seeds").Return(core.NewSeeds(), nil) + p.On("PluginName").Return(name, nil) + p.On("Parent").Return(parent, nil) + return p +} + +func BuildTestHostPlugin(name string, parent string) *TestHostPlugin { + p := &TestHostPlugin{} + p.On("SetPluginName", mock.AnythingOfType("string")).Return(nil) + p.On("Seed", mock.AnythingOfType("*core.Seeds")).Return(nil) + p.On("Seeds").Return(core.NewSeeds(), nil) + p.On("PluginName").Return(name, nil) + p.On("Parent").Return(parent, nil) + return p +} + +func BuildTestSyncedFolderPlugin(parent string) *TestSyncedFolderPlugin { + p := &TestSyncedFolderPlugin{} + p.On("SetPluginName", mock.AnythingOfType("string")).Return(nil) + p.On("Seed", mock.AnythingOfType("*core.Seeds")).Return(nil) + p.On("Seeds").Return(core.NewSeeds(), nil) + p.On("Parent").Return(parent, nil) + return p } func TestBasis(t testing.T, opts ...BasisOption) (b *Basis) { diff --git a/internal/plugin/testing_plugin.go b/internal/plugin/testing_plugin.go index 4575f3799..ec4285fcf 100644 --- a/internal/plugin/testing_plugin.go +++ b/internal/plugin/testing_plugin.go @@ -2,13 +2,41 @@ package plugin import ( "github.com/hashicorp/go-hclog" + "github.com/hashicorp/go-plugin" + "github.com/hashicorp/vagrant-plugin-sdk/component" + "github.com/hashicorp/vagrant-plugin-sdk/internal-shared/cleanup" "github.com/mitchellh/go-testing-interface" ) -func TestMinimalPlugin(t testing.T) *Plugin { +type TestPluginWithFakeBroker struct { +} + +func (p *TestPluginWithFakeBroker) GRPCBroker() *plugin.GRPCBroker { + return &plugin.GRPCBroker{} +} + +type MockClientProtocol struct { + plg interface{} +} + +func (m *MockClientProtocol) Dispense(s string) (interface{}, error) { + return m.plg, nil +} + +func (m *MockClientProtocol) Ping() error { + return nil +} + +func (m *MockClientProtocol) Close() error { + return nil +} + +func TestMinimalPlugin(t testing.T, client interface{}) *Plugin { plugin := &Plugin{ Location: "test", + Client: client.(plugin.ClientProtocol), logger: hclog.New(&hclog.LoggerOptions{}), + cleaner: cleanup.New(), } return plugin } @@ -16,8 +44,11 @@ func TestMinimalPlugin(t testing.T) *Plugin { // TestPlugin returns a fully in-memory and side-effect free Plugin that // can be used for testing. Additional options can be given to provide your own // factories, configuration, etc. -func TestPlugin(t testing.T, opts ...PluginProperty) (plugin *Plugin) { - plugin = TestMinimalPlugin(t) +func TestPlugin(t testing.T, plg interface{}, opts ...PluginProperty) (plugin *Plugin) { + mockClient := &MockClientProtocol{ + plg: plg, + } + plugin = TestMinimalPlugin(t, mockClient) for _, opt := range opts { if err := opt(plugin); err != nil { t.Error(err) @@ -34,3 +65,10 @@ func WithPluginName(name string) PluginProperty { return } } + +func WithPluginTypes(types ...component.Type) PluginProperty { + return func(p *Plugin) (err error) { + p.Types = types + return + } +}