From 314de1701bc145cf3824844b956413f1b63fb01f Mon Sep 17 00:00:00 2001 From: Carlos Nihelton Date: Wed, 4 Sep 2024 12:44:22 -0300 Subject: [PATCH] More deamon tests leveraging netwatcher I made a separate test function with a single case for the expected scenario where there is no WSL adapter until it eventually shows up. The test is not a perfect mimic, but it proves a new .address file is written after the emulated system signal is delivered. It's setup is a bit more involving and distinct from other cases found in TestServeWSLIP, thus I preferred to make it in its own test function. --- windows-agent/internal/daemon/daemon_test.go | 67 +++++++++++++++++++- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/windows-agent/internal/daemon/daemon_test.go b/windows-agent/internal/daemon/daemon_test.go index 2825678db..cc249d55a 100644 --- a/windows-agent/internal/daemon/daemon_test.go +++ b/windows-agent/internal/daemon/daemon_test.go @@ -2,6 +2,7 @@ package daemon_test import ( "context" + "errors" "io/fs" "net" "os" @@ -14,6 +15,7 @@ import ( "github.com/canonical/ubuntu-pro-for-wsl/windows-agent/internal/daemon" "github.com/canonical/ubuntu-pro-for-wsl/windows-agent/internal/daemon/daemontestutils" "github.com/canonical/ubuntu-pro-for-wsl/windows-agent/internal/daemon/testdata/grpctestservice" + "github.com/canonical/ubuntu-pro-for-wsl/windows-agent/internal/netwatcher" "github.com/canonical/ubuntu-pro-for-wsl/windows-agent/internal/netwatcher/netwatchertestutils" "github.com/stretchr/testify/require" "google.golang.org/grpc" @@ -265,9 +267,8 @@ func TestRestart(t *testing.T) { } } +//nolint:tparallel // Cannot make test parallel because we play with the default netwatcher global options. func TestServeWSLIP(t *testing.T) { - t.Parallel() - registerer := func(context.Context, bool) *grpc.Server { return grpc.NewServer() } @@ -275,6 +276,7 @@ func TestServeWSLIP(t *testing.T) { testcases := map[string]struct { netmode string withAdapters daemontestutils.MockIPAdaptersState + subscribeErr error wantErr bool }{ @@ -288,11 +290,19 @@ func TestServeWSLIP(t *testing.T) { "When listing adapters requires too much memory": {withAdapters: daemontestutils.RequiresTooMuchMem}, "When there is no Hyper-V adapter the list": {withAdapters: daemontestutils.NoHyperVAdapterInList}, "When retrieving adapters information fails": {withAdapters: daemontestutils.MockError}, + + // Should wantErr? + "When the WSL IP cannot be found and monitoring network fails": {withAdapters: daemontestutils.NoHyperVAdapterInList, subscribeErr: errors.New("mock error")}, } + //nolint:tparallel // Cannot make test parallel because we play with the default netwatcher global options. for name, tc := range testcases { t.Run(name, func(t *testing.T) { - t.Parallel() + if tc.subscribeErr != nil { + netwatchertestutils.SetDefaultNetAdaptersAPIProviderToMock(t, func() (netwatcher.NetAdaptersAPI, error) { + return nil, tc.subscribeErr + }) + } addrDir := t.TempDir() // Very lenient timeout because we either expect Serve to fail immediately or we stop it manually. @@ -345,6 +355,57 @@ func TestServeWSLIP(t *testing.T) { } } +//nolint:parallel // Cannot make test parallel because we play with the default netwatcher global options. +func TestAddingWSLAdapterRestarts(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + addrDir := t.TempDir() + + registerer := func(context.Context, bool) *grpc.Server { + server := grpc.NewServer() + var service testGRPCService + grpctestservice.RegisterTestServiceServer(server, service) + return server + } + + d := daemon.New(ctx, registerer, addrDir) + + systemNotification := make(chan error) + defer close(systemNotification) + netwatchertestutils.SetDefaultNetAdaptersAPIProviderToMock(t, netwatchertestutils.NetAdapterMockAPIWithAddedWSL(systemNotification)) + mock := daemontestutils.NewHostIPConfigMock(daemontestutils.NoHyperVAdapterInList) + + serveErr := make(chan error, 1) + go func() { + serveErr <- d.Serve(ctx, daemon.WithMockedGetAdapterAddresses(mock)) + close(serveErr) + }() + + addrPath := filepath.Join(addrDir, common.ListeningPortFileName) + + var err error + requireWaitPathExists(t, addrPath, "Serve should create an address file") + addrSt, err := os.Stat(addrPath) + require.NoError(t, err, "Address file should be readable") + + // Now we know the GRPC server has started serving. Let's emulate the OS triggering a notification. + systemNotification <- nil + + // d.Serve() shouldn't have exitted with an error yet at this point. + select { + case err := <-serveErr: + require.NoError(t, err, "Restart should not have caused Serve() to exit with an error") + case <-time.After(200 * time.Millisecond): + // proceed. + } + + requireWaitPathExists(t, addrPath, "Serve should create an address file") + // Contents could be the same without our control, thus best to check the file time. + newAddrSt, err := os.Stat(addrPath) + require.NoError(t, err, "Address file should be readable") + require.NotEqual(t, addrSt.ModTime(), newAddrSt.ModTime(), "Address file should be overwritten after Restart") +} + func TestServeError(t *testing.T) { t.Parallel()