turbo-editors/turbo-moonbitpublic Fork 0
v1.0.2
Commits
Clone
git clone https://git.rickub.com/turbo-editors/turbo-moonbit.git
git clone ssh://git@rickub.com/turbo-editors/turbo-moonbit.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

📦 Turbo MoonBit cc1f595 · on v1.0.2 · k33g · 10h ago
version_check_test.go · 174 lines · 5.9 KBGo Blame HistoryRaw
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
package main

import (
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"
)

// Linker flags are a string, and a wrong one is not an error: `-X` naming a
// symbol that does not exist links happily and stamps nothing, so the binary
// falls back to whatever the Go build system knows and reports a version the
// build never meant. Nothing but running the binary catches that, which is why
// `make build` runs it and why these tests do too.

// buildStampedWith compiles the editor with the flags `make ldflags` produces
// for a version, and returns the path to the binary.
func buildStampedWith(t *testing.T, version string) string {
	t.Helper()

	flags, err := exec.Command("make", "--no-print-directory", "ldflags", "VERSION="+version).Output()
	if err != nil {
		t.Fatalf("make ldflags VERSION=%s: %v", version, err)
	}

	binary := filepath.Join(t.TempDir(), "turbo-moonbit")
	build := exec.Command("go", "build", "-ldflags", strings.TrimSpace(string(flags)), "-o", binary, ".")
	if out, err := build.CombinedOutput(); err != nil {
		t.Fatalf("building with those flags failed: %v\n%s", err, out)
	}
	return binary
}

// checkVersion runs the build's version check and returns what it said and
// whether it was satisfied.
func checkVersion(t *testing.T, args ...string) (string, bool) {
	t.Helper()

	out, err := exec.Command("./scripts/check-version.sh", args...).CombinedOutput()
	return string(out), err == nil
}

func TestTheVersionCheckAcceptsTheVersionTheBuildStamped(t *testing.T) {
	binary := buildStampedWith(t, "v9.9.9")

	out, ok := checkVersion(t, binary, "v9.9.9")
	if !ok {
		t.Fatalf("the check refused a correctly stamped binary:\n%s", out)
	}
	if !strings.Contains(out, "9.9.9") {
		t.Errorf("the check reported %q, want it to name the version", out)
	}
}

func TestTheVersionCheckRefusesAVersionThatMerelyContainsTheRightOne(t *testing.T) {
	// The reason this is an equality test and not a grep: "0.2.0" is a
	// substring of "10.2.0", so a substring check passes a release that ships
	// a binary naming an entirely different version.
	binary := buildStampedWith(t, "v10.2.0")

	out, ok := checkVersion(t, binary, "v0.2.0")
	if ok {
		t.Fatalf("the check accepted 10.2.0 as 0.2.0:\n%s", out)
	}
	if !strings.Contains(out, "10.2.0") {
		t.Errorf("the failure does not say what the binary actually reports:\n%s", out)
	}
}

func TestTheVersionCheckRefusesAStampThatNeverReachedTheLinker(t *testing.T) {
	// The failure this exists for. `-X` naming a symbol that is not there is
	// not an error: the binary links, runs, and reports the wrong thing.
	binary := filepath.Join(t.TempDir(), "turbo-moonbit")
	build := exec.Command("go", "build",
		"-ldflags", "-X 'rickub.com/turbo-editors/turbo-core/version.stampX=v9.9.9'",
		"-o", binary, ".")
	if out, err := build.CombinedOutput(); err != nil {
		t.Fatalf("the build with a misspelt -X failed, so there is nothing to catch: %v\n%s", err, out)
	}

	out, ok := checkVersion(t, binary, "v9.9.9")
	if ok {
		t.Fatalf("the check accepted a binary nothing was stamped into:\n%s", out)
	}
}

func TestTheVersionCheckRefusesABinaryThatDoesNotRun(t *testing.T) {
	binary := filepath.Join(t.TempDir(), "turbo-moonbit")
	if err := os.WriteFile(binary, []byte("#!/bin/sh\nexit 1\n"), 0o755); err != nil {
		t.Fatalf("cannot write the stand-in: %v", err)
	}

	if out, ok := checkVersion(t, binary, "v9.9.9"); ok {
		t.Fatalf("the check accepted a binary that does not run:\n%s", out)
	}
}

func TestTheVersionCheckNeedsSomethingToCheck(t *testing.T) {
	if out, ok := checkVersion(t); ok {
		t.Fatalf("the check accepted no arguments at all:\n%s", out)
	}
	if out, ok := checkVersion(t, filepath.Join(t.TempDir(), "not-there")); ok {
		t.Fatalf("the check accepted a path with no binary at it:\n%s", out)
	}
}

func TestAnUnstampedBuildIsAcceptedButAnUnnameableOneIsNot(t *testing.T) {
	// Installing from a tarball has no git checkout to describe, so there is no
	// version to expect. The claim left is that *some* source named it.
	binary := filepath.Join(t.TempDir(), "turbo-moonbit")
	build := exec.Command("go", "build", "-o", binary, ".")
	if out, err := build.CombinedOutput(); err != nil {
		t.Fatalf("an unstamped build failed: %v\n%s", err, out)
	}

	out, ok := checkVersion(t, binary)
	if !ok {
		t.Fatalf("the check refused an unstamped build, which is a legitimate one:\n%s", out)
	}
	if strings.Contains(out, "unknown") {
		t.Errorf("the binary cannot name its version and the check passed anyway:\n%s", out)
	}
}

func TestTheBuildTargetChecksWhatItStamped(t *testing.T) {
	// The wiring, not the script: a check nothing calls protects nothing.
	makefile, err := os.ReadFile("Makefile")
	if err != nil {
		t.Fatalf("cannot read the Makefile: %v", err)
	}
	recipe := buildRecipe(t, string(makefile))

	if !strings.Contains(recipe, "check-version.sh") {
		t.Errorf("the build target never checks the version it stamped:\n%s", recipe)
	}
}

func TestTheInstallerChecksWhatItStampedBeforeItInstalls(t *testing.T) {
	// Before, not after: a binary that cannot name its own version must never
	// replace one that can.
	script, err := os.ReadFile("scripts/install.sh")
	if err != nil {
		t.Fatalf("cannot read the installer: %v", err)
	}
	text := string(script)

	check := strings.Index(text, "check-version.sh")
	install := strings.Index(text, "mv -f")
	switch {
	case check < 0:
		t.Fatal("the installer never checks the version it stamped")
	case install < 0:
		t.Fatal("the installer no longer installs by rename; this test is out of date")
	case check > install:
		t.Error("the installer checks the version after installing, so a bad build replaces a good one")
	}
}

// buildRecipe returns the lines of the Makefile's build target.
func buildRecipe(t *testing.T, makefile string) string {
	t.Helper()

	start := strings.Index(makefile, "\nbuild:")
	if start < 0 {
		t.Fatal("the Makefile has no build target")
	}
	rest := makefile[start+1:]
	end := strings.Index(rest, "\n\n")
	if end < 0 {
		end = len(rest)
	}
	return rest[:end]
}