treewide: use Fatal in tests instead of Error

Error doesn't return the goroutine, but these cases are final and should end the test case.

Change-Id: I9d87e268b56acd7d1ff5883bb82bf3d74c309176
Reviewed-on: https://review.monogon.dev/c/monogon/+/4044
Reviewed-by: Jan Schär <jan@monogon.tech>
Tested-by: Jenkins CI
diff --git a/cloud/agent/takeover/e2e/main_test.go b/cloud/agent/takeover/e2e/main_test.go
index 756eb40..5387f92 100644
--- a/cloud/agent/takeover/e2e/main_test.go
+++ b/cloud/agent/takeover/e2e/main_test.go
@@ -208,7 +208,7 @@
 	switch res := resp.Result.(type) {
 	case *api.TakeoverResponse_Success:
 		if res.Success.InitMessage.BmaasEndpoint != init.BmaasEndpoint {
-			t.Error("InitMessage not passed through properly")
+			t.Fatal("InitMessage not passed through properly")
 		}
 	case *api.TakeoverResponse_Error:
 		t.Fatalf("takeover returned error: %v", res.Error.Message)
diff --git a/go/algorithm/toposort/toposort_test.go b/go/algorithm/toposort/toposort_test.go
index 22ca914..d848f25 100644
--- a/go/algorithm/toposort/toposort_test.go
+++ b/go/algorithm/toposort/toposort_test.go
@@ -130,7 +130,7 @@
 			return
 		}
 		if err != nil {
-			t.Error(err)
+			t.Fatal(err)
 		}
 		validateSolution(t, g, solution)
 
@@ -140,7 +140,7 @@
 			return
 		}
 		if err != nil {
-			t.Error(err)
+			t.Fatal(err)
 		}
 		validateSolution(t, g, detSolution)
 	})
diff --git a/metropolis/installer/test/run_test.go b/metropolis/installer/test/run_test.go
index 3c8f1a6..5008be0 100644
--- a/metropolis/installer/test/run_test.go
+++ b/metropolis/installer/test/run_test.go
@@ -166,7 +166,7 @@
 	// ESP contents are in order.
 	image, err := diskfs.OpenWithMode(installerImage, diskfs.ReadOnly)
 	if err != nil {
-		t.Errorf("Couldn't open the installer image at %q: %s", installerImage, err.Error())
+		t.Fatalf("Couldn't open the installer image at %q: %s", installerImage, err)
 	}
 	// Verify that GPT exists.
 	ti, err := image.GetPartitionTable()
@@ -174,22 +174,22 @@
 		t.Fatalf("Couldn't read the installer image partition table: %s", err)
 	}
 	if ti.Type() != "gpt" {
-		t.Error("Couldn't verify that the installer image contains a GPT.")
+		t.Fatal("Couldn't verify that the installer image contains a GPT.")
 	}
 	// Check that the first partition is likely to be a valid ESP.
 	pi := ti.GetPartitions()
 	esp := (pi[0]).(*gpt.Partition)
 	if esp.Start == 0 || esp.End == 0 {
-		t.Error("The installer's ESP GPT entry looks off.")
+		t.Fatal("The installer's ESP GPT entry looks off.")
 	}
 	// Verify that the image contains only one partition.
 	second := (pi[1]).(*gpt.Partition)
 	if second.Name != "" || second.Start != 0 || second.End != 0 {
-		t.Error("It appears the installer image contains more than one partition.")
+		t.Fatal("It appears the installer image contains more than one partition.")
 	}
 	// Verify the ESP contents.
 	if err := checkEspContents(image); err != nil {
-		t.Error(err.Error())
+		t.Fatal(err)
 	}
 }
 
@@ -203,7 +203,7 @@
 	expectedOutput := "couldn't find a suitable block device"
 	result, err := runQemuWithInstaller(ctx, nil, expectedOutput)
 	if err != nil {
-		t.Error(err.Error())
+		t.Fatal(err)
 	}
 	if !result {
 		t.Errorf("QEMU didn't produce the expected output %q", expectedOutput)
@@ -219,17 +219,17 @@
 	imagePath, err := getStorage(64)
 	defer os.Remove(imagePath)
 	if err != nil {
-		t.Error(err.Error())
+		t.Fatal(err)
 	}
 
 	// Run QEMU. Expect the installer to fail with a predefined error string.
 	expectedOutput := "couldn't find a suitable block device"
 	result, err := runQemuWithInstaller(ctx, qemuDriveParam(imagePath), expectedOutput)
 	if err != nil {
-		t.Error(err.Error())
+		t.Fatal(err)
 	}
 	if !result {
-		t.Errorf("QEMU didn't produce the expected output %q", expectedOutput)
+		t.Fatalf("QEMU didn't produce the expected output %q", expectedOutput)
 	}
 }
 
@@ -244,23 +244,23 @@
 	storagePath, err := getStorage(4096*2 + 384 + 128 + 2)
 	defer os.Remove(storagePath)
 	if err != nil {
-		t.Error(err.Error())
+		t.Fatal(err)
 	}
 
 	// Run QEMU. Expect the installer to succeed.
 	expectedOutput := "Installation completed"
 	result, err := runQemuWithInstaller(ctx, qemuDriveParam(storagePath), expectedOutput)
 	if err != nil {
-		t.Error(err.Error())
+		t.Fatal(err)
 	}
 	if !result {
-		t.Errorf("QEMU didn't produce the expected output %q", expectedOutput)
+		t.Fatalf("QEMU didn't produce the expected output %q", expectedOutput)
 	}
 
 	// Verify the resulting node image. Check whether the node GPT was created.
 	storage, err := diskfs.OpenWithMode(storagePath, diskfs.ReadOnly)
 	if err != nil {
-		t.Errorf("Couldn't open the resulting node image at %q: %s", storagePath, err.Error())
+		t.Fatalf("Couldn't open the resulting node image at %q: %s", storagePath, err)
 	}
 	// Verify that GPT exists.
 	ti, err := storage.GetPartitionTable()
@@ -268,37 +268,37 @@
 		t.Fatalf("Couldn't read the installer image partition table: %s", err)
 	}
 	if ti.Type() != "gpt" {
-		t.Error("Couldn't verify that the resulting node image contains a GPT.")
+		t.Fatal("Couldn't verify that the resulting node image contains a GPT.")
 	}
 	// Check that the first partition is likely to be a valid ESP.
 	pi := ti.GetPartitions()
 	esp := (pi[0]).(*gpt.Partition)
 	if esp.Name != osimage.ESPLabel || esp.Start == 0 || esp.End == 0 {
-		t.Error("The node's ESP GPT entry looks off.")
+		t.Fatal("The node's ESP GPT entry looks off.")
 	}
 	// Verify the system partition's GPT entry.
 	system := (pi[1]).(*gpt.Partition)
 	if system.Name != osimage.SystemALabel || system.Start == 0 || system.End == 0 {
-		t.Error("The node's system partition GPT entry looks off.")
+		t.Fatal("The node's system partition GPT entry looks off.")
 	}
 	// Verify the system partition's GPT entry.
 	systemB := (pi[2]).(*gpt.Partition)
 	if systemB.Name != osimage.SystemBLabel || systemB.Start == 0 || systemB.End == 0 {
-		t.Error("The node's system partition GPT entry looks off.")
+		t.Fatal("The node's system partition GPT entry looks off.")
 	}
 	// Verify the data partition's GPT entry.
 	data := (pi[3]).(*gpt.Partition)
 	if data.Name != osimage.DataLabel || data.Start == 0 || data.End == 0 {
-		t.Errorf("The node's data partition GPT entry looks off: %+v", data)
+		t.Fatalf("The node's data partition GPT entry looks off: %+v", data)
 	}
 	// Verify that there are no more partitions.
 	fourth := (pi[4]).(*gpt.Partition)
 	if fourth.Name != "" || fourth.Start != 0 || fourth.End != 0 {
-		t.Error("The resulting node image contains more partitions than expected.")
+		t.Fatal("The resulting node image contains more partitions than expected.")
 	}
 	// Verify the ESP contents.
 	if err := checkEspContents(storage); err != nil {
-		t.Error(err.Error())
+		t.Fatal(err)
 	}
 	storage.File.Close()
 	// Run QEMU again. Expect TestOS to launch successfully.
@@ -306,9 +306,9 @@
 	time.Sleep(time.Second)
 	result, err = runQemu(ctx, qemuDriveParam(storagePath), expectedOutput)
 	if err != nil {
-		t.Error(err.Error())
+		t.Fatal(err)
 	}
 	if !result {
-		t.Errorf("QEMU didn't produce the expected output %q", expectedOutput)
+		t.Fatalf("QEMU didn't produce the expected output %q", expectedOutput)
 	}
 }
diff --git a/metropolis/node/core/clusternet/clusternet_test.go b/metropolis/node/core/clusternet/clusternet_test.go
index 317c409..9747e63 100644
--- a/metropolis/node/core/clusternet/clusternet_test.go
+++ b/metropolis/node/core/clusternet/clusternet_test.go
@@ -157,8 +157,7 @@
 				break
 			}
 			if time.Now().After(deadline) {
-				t.Error(err)
-				return
+				t.Fatal(err)
 			}
 		}
 
diff --git a/metropolis/node/core/curator/impl_leader_test.go b/metropolis/node/core/curator/impl_leader_test.go
index e593bbd..cd61e54 100644
--- a/metropolis/node/core/curator/impl_leader_test.go
+++ b/metropolis/node/core/curator/impl_leader_test.go
@@ -1682,11 +1682,11 @@
 		},
 	})
 	if err != nil {
-		t.Error(err)
+		t.Fatal(err)
 	}
 	ev, err := w.Recv()
 	if err != nil {
-		t.Error(err)
+		t.Fatal(err)
 	}
 	cn := ev.Nodes[0].Clusternet
 	if want, got := "GaNXuc/yl8IaXduX6PQ+ZxIG4HtBACubHrRI7rqfA20=", cn.WireguardPubkey; want != got {
diff --git a/metropolis/node/core/network/dhcp4c/dhcpc_test.go b/metropolis/node/core/network/dhcp4c/dhcpc_test.go
index 45be1a4..57361fe 100644
--- a/metropolis/node/core/network/dhcp4c/dhcpc_test.go
+++ b/metropolis/node/core/network/dhcp4c/dhcpc_test.go
@@ -232,7 +232,7 @@
 	p.bmt.sendPackets(terribleOffer, offer)
 
 	if err := p.c.runState(context.Background()); err != nil {
-		t.Error(err)
+		t.Fatal(err)
 	}
 	assert.Equal(t, stateRequesting, p.c.state, "DHCP client didn't process offer")
 	assert.Equal(t, testIP, p.c.offer.YourIPAddr, "DHCP client requested invalid offer")
@@ -257,7 +257,7 @@
 	}
 
 	if err := p.c.runState(context.Background()); err != nil {
-		t.Error(err)
+		t.Fatal(err)
 	}
 	assert.Equal(t, stateBound, p.c.state, "DHCP client didn't process offer")
 	assert.Equal(t, testIP, p.c.lease.YourIPAddr, "DHCP client requested invalid offer")
@@ -278,7 +278,7 @@
 	for i := 0; i < 10; i++ {
 		p.bmt.sendPackets()
 		if err := p.c.runState(context.Background()); err != nil {
-			t.Error(err)
+			t.Fatal(err)
 		}
 		assert.Equal(t, dhcpv4.MessageTypeRequest, p.bmt.sentPacket.MessageType(), "Invalid message type for requesting")
 		if p.c.state == stateDiscovering {
@@ -312,7 +312,7 @@
 	}
 	p.bmt.sendPackets(offer)
 	if err := p.c.runState(context.Background()); err != nil {
-		t.Error(err)
+		t.Fatal(err)
 	}
 	assert.Equal(t, stateBound, p.c.state, "DHCP client didn't process offer")
 	assert.Equal(t, testIP, p.c.lease.YourIPAddr, "DHCP client requested invalid offer")
@@ -350,7 +350,7 @@
 
 	p.ft.Advance(5*time.Second - 5*time.Millisecond)
 	if err := p.c.runState(context.Background()); err != nil {
-		t.Error(err)
+		t.Fatal(err)
 	}
 	// We cannot intercept time.After so we just advance the clock by the time slept
 	p.ft.Advance(5 * time.Millisecond)
@@ -364,7 +364,7 @@
 		return nil
 	}
 	if err := p.c.runState(context.Background()); err != nil {
-		t.Error(err)
+		t.Fatal(err)
 	}
 	assert.Equal(t, stateBound, p.c.state, "DHCP client didn't renew")
 	assert.Equal(t, p.ft.Now().Add(leaseTime), p.c.leaseDeadline, "lease deadline not updated")
@@ -401,7 +401,7 @@
 	for i := 0; i < 10; i++ {
 		p.umt.sendPackets()
 		if err := p.c.runState(context.Background()); err != nil {
-			t.Error(err)
+			t.Fatal(err)
 		}
 		assert.Equal(t, dhcpv4.MessageTypeRequest, p.umt.sentPacket.MessageType(), "Invalid message type for renewal")
 		p.ft.time = p.umt.setDeadline
@@ -438,7 +438,7 @@
 
 	p.ft.Advance(9 * time.Second)
 	if err := p.c.runState(context.Background()); err != nil {
-		t.Error(err)
+		t.Fatal(err)
 	}
 	assert.Equal(t, dhcpv4.MessageTypeRequest, p.bmt.sentPacket.MessageType(), "DHCP rebind sent invalid message type")
 	assert.Equal(t, stateRebinding, p.c.state, "DHCP client transferred out of rebinding state without trigger")
@@ -452,7 +452,7 @@
 		return nil
 	}
 	if err := p.c.runState(context.Background()); err != nil {
-		t.Error(err)
+		t.Fatal(err)
 	}
 	assert.Equal(t, dhcpv4.MessageTypeRequest, p.bmt.sentPacket.MessageType())
 	assert.Equal(t, stateBound, p.c.state, "DHCP client didn't go back to bound")
@@ -484,7 +484,7 @@
 		p.bmt.sendPackets()
 		p.bmt.sentPacket = nil
 		if err := p.c.runState(context.Background()); err != nil {
-			t.Error(err)
+			t.Fatal(err)
 		}
 		if p.c.state == stateDiscovering {
 			assert.Nil(t, p.bmt.sentPacket)
diff --git a/metropolis/node/kubernetes/networkpolicy/networkpolicy_test.go b/metropolis/node/kubernetes/networkpolicy/networkpolicy_test.go
index 22cf569..f32dc37 100644
--- a/metropolis/node/kubernetes/networkpolicy/networkpolicy_test.go
+++ b/metropolis/node/kubernetes/networkpolicy/networkpolicy_test.go
@@ -810,21 +810,18 @@
 			recorder := &testRecorder{t: t}
 			nft, err := nftctrl.New(recorder, podIfaceGroup)
 			if err != nil {
-				t.Errorf("Failed to create nftctrl: %v", err)
-				return
+				t.Fatalf("Failed to create nftctrl: %v", err)
 			}
 			defer nft.Close()
 			kubernetes.nft = nft
 
 			if err := kubernetes.initializeNft(); err != nil {
-				t.Errorf("nftctrl initialization failed: %v", err)
-				return
+				t.Fatalf("nftctrl initialization failed: %v", err)
 			}
 
 			result := interpreter.ExecuteTestCase(testCase)
 			if result.Err != nil {
-				t.Error(result.Err)
-				return
+				t.Fatal(result.Err)
 			}
 			if !result.Passed(ignoreLoopback) {
 				printer.PrintTestCaseResult(result)
@@ -863,8 +860,7 @@
 			recorder := &testRecorder{t: t}
 			nft, err := nftctrl.New(recorder, podIfaceGroup)
 			if err != nil {
-				t.Errorf("Failed to create nftctrl: %v", err)
-				return
+				t.Fatalf("Failed to create nftctrl: %v", err)
 			}
 			defer nft.Close()
 
@@ -874,13 +870,11 @@
 			}
 
 			if err := testCase.init(testCaseState, nft); err != nil {
-				t.Errorf("initialization failed: %v", err)
-				return
+				t.Fatalf("initialization failed: %v", err)
 			}
 
 			if err := nft.Flush(); err != nil {
-				t.Errorf("flush failed: %v", err)
-				return
+				t.Fatalf("flush failed: %v", err)
 			}
 
 			parsedPolicy := matcher.BuildNetworkPolicies(true, testCaseState.Policies)
diff --git a/osbase/bringup/test/run_test.go b/osbase/bringup/test/run_test.go
index 70003cb..085b875 100644
--- a/osbase/bringup/test/run_test.go
+++ b/osbase/bringup/test/run_test.go
@@ -69,7 +69,7 @@
 	expectedOutput := "_BRINGUP_LAUNCH_SUCCESS_"
 	result, err := runQemu(ctx, extraArgs, expectedOutput)
 	if err != nil {
-		t.Error(err.Error())
+		t.Error(err)
 	}
 	if !result {
 		t.Errorf("QEMU didn't produce the expected output %q", expectedOutput)
@@ -85,7 +85,7 @@
 	expectedOutput := "root runnable paniced"
 	result, err := runQemu(ctx, extraArgs, expectedOutput)
 	if err != nil {
-		t.Error(err.Error())
+		t.Error(err)
 	}
 	if !result {
 		t.Errorf("QEMU didn't produce the expected output %q", expectedOutput)
@@ -102,7 +102,7 @@
 	expectedOutput := "this is an error"
 	result, err := runQemu(ctx, extraArgs, expectedOutput)
 	if err != nil {
-		t.Error(err.Error())
+		t.Error(err)
 	}
 	if !result {
 		t.Errorf("QEMU didn't produce the expected output %q", expectedOutput)
diff --git a/osbase/erofs/erofs_test.go b/osbase/erofs/erofs_test.go
index cc02047..7ab4f51 100644
--- a/osbase/erofs/erofs_test.go
+++ b/osbase/erofs/erofs_test.go
@@ -251,7 +251,7 @@
 			}
 			_ = file.Close()
 			if err := os.MkdirAll("/test", 0755); err != nil {
-				t.Error(err)
+				t.Fatal(err)
 			}
 			if err := unix.Mount("/dev/ram0", "/test", "erofs", unix.MS_NOEXEC|unix.MS_NODEV, ""); err != nil {
 				t.Fatal(err)
diff --git a/osbase/fat32/linux_test.go b/osbase/fat32/linux_test.go
index 002ac2c..29699a4 100644
--- a/osbase/fat32/linux_test.go
+++ b/osbase/fat32/linux_test.go
@@ -262,14 +262,16 @@
 			}
 			_ = file.Close()
 			if err := os.MkdirAll("/dut", 0755); err != nil {
-				t.Error(err)
+				t.Fatal(err)
 			}
 			// TODO(lorenz): Set CONFIG_FAT_DEFAULT_UTF8 for Monogon Kernel
 			if err := unix.Mount("/dev/ram0", "/dut", "vfat", unix.MS_NOEXEC|unix.MS_NODEV, "utf8=1"); err != nil {
 				t.Fatal(err)
 			}
 			defer unix.Unmount("/dut", 0)
-			test.validate(t)
+			if err := test.validate(t); err != nil {
+				t.Fatal(err)
+			}
 		})
 
 	}
diff --git a/osbase/fsquota/fsquota_test.go b/osbase/fsquota/fsquota_test.go
index 97cf8bc..2850b4f 100644
--- a/osbase/fsquota/fsquota_test.go
+++ b/osbase/fsquota/fsquota_test.go
@@ -45,7 +45,7 @@
 		t.Fatal(err, string(out))
 	}
 	if err := os.Mkdir("/test", 0755); err != nil {
-		t.Error(err)
+		t.Fatal(err)
 	}
 
 	if err := unix.Mount("/dev/ram0", "/test", "xfs", unix.MS_NOEXEC|unix.MS_NODEV, "prjquota"); err != nil {
diff --git a/osbase/kmod/manager_test.go b/osbase/kmod/manager_test.go
index 2169703..05f7826 100644
--- a/osbase/kmod/manager_test.go
+++ b/osbase/kmod/manager_test.go
@@ -19,22 +19,22 @@
 	}
 	t.Run("LoadExampleModule", func(t *testing.T) {
 		if err := mgr.LoadModule("r8169"); err != nil {
-			t.Error(err)
+			t.Fatal(err)
 		}
 		if _, err := os.Stat("/sys/module/r8169"); err != nil {
-			t.Error("module load returned success, but module not in sysfs")
+			t.Fatal("module load returned success, but module not in sysfs")
 		}
 	})
 	t.Run("LoadNonexistentModule", func(t *testing.T) {
 		err := mgr.LoadModule("definitelynomodule")
 		var notFoundErr *ErrNotFound
 		if !errors.As(err, &notFoundErr) {
-			t.Errorf("expected ErrNotFound, got %v", err)
+			t.Fatalf("expected ErrNotFound, got %v", err)
 		}
 	})
 	t.Run("LoadModuleTwice", func(t *testing.T) {
 		if err := mgr.LoadModule("r8169"); err != nil {
-			t.Error(err)
+			t.Fatal(err)
 		}
 	})
 	// TODO(lorenz): Should test loading dependencies here, but we currently
@@ -42,10 +42,10 @@
 	// just for this.
 	t.Run("LoadDeviceModule", func(t *testing.T) {
 		if err := mgr.LoadModulesForDevice("pci:v00008086d00001591sv00001043sd000085F0bc02sc00i00"); err != nil {
-			t.Error(err)
+			t.Fatal(err)
 		}
 		if _, err := os.Stat("/sys/module/ice"); err != nil {
-			t.Error("module load returned success, but module not in sysfs")
+			t.Fatalf("module load returned success, but module not in sysfs")
 		}
 	})
 }
diff --git a/osbase/kmod/radix_test.go b/osbase/kmod/radix_test.go
index 6185beb..fa31f67 100644
--- a/osbase/kmod/radix_test.go
+++ b/osbase/kmod/radix_test.go
@@ -45,7 +45,7 @@
 			}
 			diff := cmp.Diff(c.expectedNodes, out, protocmp.Transform())
 			if diff != "" {
-				t.Error(diff)
+				t.Fatal(diff)
 			}
 		})
 	}
@@ -56,18 +56,18 @@
 		Type: kmodpb.RadixNode_TYPE_LITERAL,
 	}
 	if err := AddPattern(root, "usb:v0B95p1790d*dc*dsc*dp*icFFiscFFip00in*", 2); err != nil {
-		t.Error(err)
+		t.Fatal(err)
 	}
 	if err := AddPattern(root, "usb:v0B95p178Ad*dc*dsc*dp*icFFiscFFip00in*", 3); err != nil {
-		t.Error(err)
+		t.Fatal(err)
 	}
 	if err := AddPattern(root, "acpi*:PNP0C14:*", 10); err != nil {
-		t.Error(err)
+		t.Fatal(err)
 	}
 	matches := make(map[uint32]bool)
 	lookupModulesRec(root, "acpi:PNP0C14:asdf", matches)
 	if !matches[10] {
-		t.Error("value should match pattern 10")
+		t.Fatal("value should match pattern 10")
 	}
 }
 
diff --git a/osbase/pstore/pstore_test.go b/osbase/pstore/pstore_test.go
index d4024e6..36423f3 100644
--- a/osbase/pstore/pstore_test.go
+++ b/osbase/pstore/pstore_test.go
@@ -110,10 +110,10 @@
 			dumps, err := getKmsgDumpsFromFS(c.inputFS)
 			switch {
 			case err == nil && c.expectErr:
-				t.Error("Expected error, but got none")
+				t.Fatal("Expected error, but got none")
 				return
 			case err != nil && !c.expectErr:
-				t.Errorf("Got unexpected error: %v", err)
+				t.Fatalf("Got unexpected error: %v", err)
 				return
 			case err != nil && c.expectErr:
 				// Got expected error