turbo-editors/turbo-jspublic Fork 0
v1.0.0
Commits
Clone
git clone https://git.rickub.com/turbo-editors/turbo-js.git
git clone ssh://git@rickub.com/turbo-editors/turbo-js.git

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

version_check_test.go · 174 lines · 5.9 KBGo Blame HistoryRaw
📦 Turbo JS 91999d1 k33g 12h ago1package main
2
3import (
4 "os"
5 "os/exec"
6 "path/filepath"
7 "strings"
8 "testing"
9)
10
11// Linker flags are a string, and a wrong one is not an error: `-X` naming a
12// symbol that does not exist links happily and stamps nothing, so the binary
13// falls back to whatever the Go build system knows and reports a version the
14// build never meant. Nothing but running the binary catches that, which is why
15// `make build` runs it and why these tests do too.
16
17// buildStampedWith compiles the editor with the flags `make ldflags` produces
18// for a version, and returns the path to the binary.
19func buildStampedWith(t *testing.T, version string) string {
20 t.Helper()
21
22 flags, err := exec.Command("make", "--no-print-directory", "ldflags", "VERSION="+version).Output()
23 if err != nil {
24 t.Fatalf("make ldflags VERSION=%s: %v", version, err)
25 }
26
27 binary := filepath.Join(t.TempDir(), "turbo-js")
28 build := exec.Command("go", "build", "-ldflags", strings.TrimSpace(string(flags)), "-o", binary, ".")
29 if out, err := build.CombinedOutput(); err != nil {
30 t.Fatalf("building with those flags failed: %v\n%s", err, out)
31 }
32 return binary
33}
34
35// checkVersion runs the build's version check and returns what it said and
36// whether it was satisfied.
37func checkVersion(t *testing.T, args ...string) (string, bool) {
38 t.Helper()
39
40 out, err := exec.Command("./scripts/check-version.sh", args...).CombinedOutput()
41 return string(out), err == nil
42}
43
44func TestTheVersionCheckAcceptsTheVersionTheBuildStamped(t *testing.T) {
45 binary := buildStampedWith(t, "v9.9.9")
46
47 out, ok := checkVersion(t, binary, "v9.9.9")
48 if !ok {
49 t.Fatalf("the check refused a correctly stamped binary:\n%s", out)
50 }
51 if !strings.Contains(out, "9.9.9") {
52 t.Errorf("the check reported %q, want it to name the version", out)
53 }
54}
55
56func TestTheVersionCheckRefusesAVersionThatMerelyContainsTheRightOne(t *testing.T) {
57 // The reason this is an equality test and not a grep: "0.2.0" is a
58 // substring of "10.2.0", so a substring check passes a release that ships
59 // a binary naming an entirely different version.
60 binary := buildStampedWith(t, "v10.2.0")
61
62 out, ok := checkVersion(t, binary, "v0.2.0")
63 if ok {
64 t.Fatalf("the check accepted 10.2.0 as 0.2.0:\n%s", out)
65 }
66 if !strings.Contains(out, "10.2.0") {
67 t.Errorf("the failure does not say what the binary actually reports:\n%s", out)
68 }
69}
70
71func TestTheVersionCheckRefusesAStampThatNeverReachedTheLinker(t *testing.T) {
72 // The failure this exists for. `-X` naming a symbol that is not there is
73 // not an error: the binary links, runs, and reports the wrong thing.
74 binary := filepath.Join(t.TempDir(), "turbo-js")
75 build := exec.Command("go", "build",
76 "-ldflags", "-X 'rickub.com/turbo-editors/turbo-core/version.stampX=v9.9.9'",
77 "-o", binary, ".")
78 if out, err := build.CombinedOutput(); err != nil {
79 t.Fatalf("the build with a misspelt -X failed, so there is nothing to catch: %v\n%s", err, out)
80 }
81
82 out, ok := checkVersion(t, binary, "v9.9.9")
83 if ok {
84 t.Fatalf("the check accepted a binary nothing was stamped into:\n%s", out)
85 }
86}
87
88func TestTheVersionCheckRefusesABinaryThatDoesNotRun(t *testing.T) {
89 binary := filepath.Join(t.TempDir(), "turbo-js")
90 if err := os.WriteFile(binary, []byte("#!/bin/sh\nexit 1\n"), 0o755); err != nil {
91 t.Fatalf("cannot write the stand-in: %v", err)
92 }
93
94 if out, ok := checkVersion(t, binary, "v9.9.9"); ok {
95 t.Fatalf("the check accepted a binary that does not run:\n%s", out)
96 }
97}
98
99func TestTheVersionCheckNeedsSomethingToCheck(t *testing.T) {
100 if out, ok := checkVersion(t); ok {
101 t.Fatalf("the check accepted no arguments at all:\n%s", out)
102 }
103 if out, ok := checkVersion(t, filepath.Join(t.TempDir(), "not-there")); ok {
104 t.Fatalf("the check accepted a path with no binary at it:\n%s", out)
105 }
106}
107
108func TestAnUnstampedBuildIsAcceptedButAnUnnameableOneIsNot(t *testing.T) {
109 // Installing from a tarball has no git checkout to describe, so there is no
110 // version to expect. The claim left is that *some* source named it.
111 binary := filepath.Join(t.TempDir(), "turbo-js")
112 build := exec.Command("go", "build", "-o", binary, ".")
113 if out, err := build.CombinedOutput(); err != nil {
114 t.Fatalf("an unstamped build failed: %v\n%s", err, out)
115 }
116
117 out, ok := checkVersion(t, binary)
118 if !ok {
119 t.Fatalf("the check refused an unstamped build, which is a legitimate one:\n%s", out)
120 }
121 if strings.Contains(out, "unknown") {
122 t.Errorf("the binary cannot name its version and the check passed anyway:\n%s", out)
123 }
124}
125
126func TestTheBuildTargetChecksWhatItStamped(t *testing.T) {
127 // The wiring, not the script: a check nothing calls protects nothing.
128 makefile, err := os.ReadFile("Makefile")
129 if err != nil {
130 t.Fatalf("cannot read the Makefile: %v", err)
131 }
132 recipe := buildRecipe(t, string(makefile))
133
134 if !strings.Contains(recipe, "check-version.sh") {
135 t.Errorf("the build target never checks the version it stamped:\n%s", recipe)
136 }
137}
138
139func TestTheInstallerChecksWhatItStampedBeforeItInstalls(t *testing.T) {
140 // Before, not after: a binary that cannot name its own version must never
141 // replace one that can.
142 script, err := os.ReadFile("scripts/install.sh")
143 if err != nil {
144 t.Fatalf("cannot read the installer: %v", err)
145 }
146 text := string(script)
147
148 check := strings.Index(text, "check-version.sh")
149 install := strings.Index(text, "mv -f")
150 switch {
151 case check < 0:
152 t.Fatal("the installer never checks the version it stamped")
153 case install < 0:
154 t.Fatal("the installer no longer installs by rename; this test is out of date")
155 case check > install:
156 t.Error("the installer checks the version after installing, so a bad build replaces a good one")
157 }
158}
159
160// buildRecipe returns the lines of the Makefile's build target.
161func buildRecipe(t *testing.T, makefile string) string {
162 t.Helper()
163
164 start := strings.Index(makefile, "\nbuild:")
165 if start < 0 {
166 t.Fatal("the Makefile has no build target")
167 }
168 rest := makefile[start+1:]
169 end := strings.Index(rest, "\n\n")
170 if end < 0 {
171 end = len(rest)
172 }
173 return rest[:end]
174}