From e0f4ce3754d538a67a24e5ccdcbfa7bad990d690 Mon Sep 17 00:00:00 2001 From: Yaruk Asghar Date: Thu, 10 Sep 2026 13:14:41 -0700 Subject: [PATCH] tstest/integration: stop skipping five tests on Windows Updates #20750 Signed-off-by: Yaruk Asghar --- tstest/integration/integration.go | 6 ++++++ tstest/integration/integration_test.go | 21 ++++----------------- tstest/integration/service_notwindows.go | 4 ++++ tstest/integration/service_windows.go | 9 ++++++--- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/tstest/integration/integration.go b/tstest/integration/integration.go index 162bbb0ee..b4eed9a2b 100644 --- a/tstest/integration/integration.go +++ b/tstest/integration/integration.go @@ -597,6 +597,8 @@ type TestNode struct { allowUpdates bool tunMode bool // TUN rather than userspace networking + svcStarted bool // whether this node has started its Windows service before + mu sync.Mutex onLogLine []func([]byte) lc *local.Client @@ -887,6 +889,10 @@ type waitResult struct { d.Process.Kill() <-done } + if d.svc != nil { + // Uninstall so the node can start a fresh service; its state dir persists. + d.svc.uninstallService() + } } // awaitTailscaledRunnable tries to run `tailscaled --version` until it diff --git a/tstest/integration/integration_test.go b/tstest/integration/integration_test.go index 385d4965a..3d2f7b70f 100644 --- a/tstest/integration/integration_test.go +++ b/tstest/integration/integration_test.go @@ -206,9 +206,6 @@ func TestExpectedFeaturesLinked(t *testing.T) { } func TestCollectPanic(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("has a Windows panic-capture race; see #20443") - } tstest.Parallel(t) env := NewTestEnv(t) n := NewTestNode(t, env) @@ -848,16 +845,14 @@ func(control *testcontrol.Server) { } func TestConfigFileAuthKey(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("--config is unsupported by the Windows service; see #20871") - } t.Parallel() const authKey = "opensesame" env := NewTestEnv(t, ConfigureControl(func(control *testcontrol.Server) { control.RequireAuthKey = authKey })) - n1 := NewTestNode(t, env) + // A userspace node, because the Windows service can't be passed --config. + n1 := NewTestNode(t, env, TUNMode(false)) n1.configFile = filepath.Join(n1.dir, "config.json") authKeyFile := filepath.Join(n1.dir, "my-auth-key") must.Do(os.WriteFile(authKeyFile, fmt.Appendf(nil, "%s\n", authKey), 0666)) @@ -1274,9 +1269,6 @@ func TestC2NPingRequest(t *testing.T) { // Issue 2434: when "down" (WantRunning false), tailscaled shouldn't // be connected to control. func TestNoControlConnWhenDown(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("restarting the daemon with preserved state needs harness support; see #20750") - } tstest.Parallel(t) env := NewTestEnv(t) n1 := NewTestNode(t, env) @@ -1644,9 +1636,6 @@ func TestAutoUpdateDefaults_cap(t *testing.T) { testAutoUpdateDefaults(t, true) // useCap is whether to use NodeAttrDefaultAutoUpdate (as opposed to the old // DeprecatedDefaultAutoUpdate top-level MapResponse field). func testAutoUpdateDefaults(t *testing.T, useCap bool) { - if runtime.GOOS == "windows" { - t.Skip("multiple nodes need the userspace-peer harness; see #20711") - } t.Cleanup(feature.HookCanAutoUpdate.SetForTest(func() bool { return true })) env := NewTestEnv(t) @@ -2128,9 +2117,6 @@ func TestNetstackUDPLoopback(t *testing.T) { } func TestEncryptStateMigration(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("--encrypt-state is unsupported by the Windows service; see #20872") - } if !hostinfo.New().TPM.Present() { t.Skip("TPM not available") } @@ -2139,7 +2125,8 @@ func TestEncryptStateMigration(t *testing.T) { } tstest.Parallel(t) env := NewTestEnv(t) - n := NewTestNode(t, env) + // A userspace node, because runNode reads the state file from the test's own dir. + n := NewTestNode(t, env, TUNMode(false)) runNode := func(t *testing.T, wantStateKeys []string) { t.Helper() diff --git a/tstest/integration/service_notwindows.go b/tstest/integration/service_notwindows.go index 8874b31b5..bc9cd79ad 100644 --- a/tstest/integration/service_notwindows.go +++ b/tstest/integration/service_notwindows.go @@ -16,3 +16,7 @@ func (n *TestNode) startWindowsServiceDaemon() *Daemon { func (n *TestNode) stopService() { n.env.t.Fatal("Windows service daemon is only supported on Windows") } + +func (n *TestNode) uninstallService() { + n.env.t.Fatal("Windows service daemon is only supported on Windows") +} diff --git a/tstest/integration/service_windows.go b/tstest/integration/service_windows.go index 47b0b8ed3..50e5dc479 100644 --- a/tstest/integration/service_windows.go +++ b/tstest/integration/service_windows.go @@ -40,7 +40,11 @@ func (n *TestNode) startWindowsServiceDaemon() *Daemon { t.Fatal("existing Tailscale service found; run only on a disposable/CI machine") } - n.cleanupServiceState() + // Only on a node's first start: a restart keeps its state, and so its identity. + if !n.svcStarted { + n.cleanupServiceState() + } + n.svcStarted = true stageWintun(t, filepath.Dir(n.env.daemon)) n.writeServiceEnvFile() @@ -48,8 +52,7 @@ func (n *TestNode) startWindowsServiceDaemon() *Daemon { t.Fatalf("install-system-daemon: %v\n%s", err, out) } var proc *os.Process - // Teardown: stop, wait for the process to exit so it releases the files below, - // uninstall, then wipe state for the next test. + // Safety net for tests that never call MustCleanShutdown, plus the final state wipe. t.Cleanup(func() { n.stopService() if proc != nil {