turbo-editors/turbo-golopublic Fork 0
v1.0.1
Commits
Clone
git clone https://git.rickub.com/turbo-editors/turbo-golo.git
git clone ssh://git@rickub.com/turbo-editors/turbo-golo.git

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

profile_test.go · 226 lines · 7.4 KBGo Blame HistoryRaw
📦 Turbo Golo d710c1b k33g 16h ago1package gololang_test
2
3import (
4 "slices"
5 "strings"
6 "testing"
7
8 "rickub.com/turbo-editors/turbo-core/profile"
9 "rickub.com/turbo-editors/turbo-core/syntax"
10
11 "rickub.com/turbo-editors/turbo-golo/internal/gololang"
12)
13
14// fixedMenuHotKeys are the hot keys turbo-core's own menus take. The toolchain
15// menu may not claim one of them, or one of the two would be unreachable from
16// the keyboard and nothing would say so.
17var fixedMenuHotKeys = []rune{'F', 'E', 'S', 'R', 'C', 'O', 'W', 'N', 'H'}
18
19func TestProfileNamesTheEditor(t *testing.T) {
20 p := gololang.Profile()
21
22 if p.Name != "Turbo Golo" {
23 t.Errorf("Name = %q, want %q", p.Name, "Turbo Golo")
24 }
25 if p.Slug != "turbo-golo" {
26 t.Errorf("Slug = %q, want %q", p.Slug, "turbo-golo")
27 }
28 // The About box and the status bar read this out. It names the language,
29 // not the implementation: Golo is what the files are written in, and
30 // GoloScript is the binary the editor talks to.
31 if p.Language != "Golo" {
32 t.Errorf("Language = %q, want %q", p.Language, "Golo")
33 }
34}
35
36func TestSlugDerivesEveryPath(t *testing.T) {
37 p := gololang.Profile()
38
39 if got := p.ProjectDir(); got != ".turbo-golo" {
40 t.Errorf("ProjectDir() = %q, want %q", got, ".turbo-golo")
41 }
42 for name, got := range map[string]string{
43 "DirEnvVar": p.DirEnvVar(),
44 "ThemeDirEnvVar": p.ThemeDirEnvVar(),
45 "SnippetDirEnvVar": p.SnippetDirEnvVar(),
46 } {
47 if !strings.HasPrefix(got, "TURBO_GOLO_") {
48 t.Errorf("%s() = %q, want a TURBO_GOLO_ prefix", name, got)
49 }
50 }
51}
52
53func TestThemeDirFollowsItsEnvironmentVariable(t *testing.T) {
54 p := gololang.Profile()
55 want := t.TempDir()
56 t.Setenv(p.ThemeDirEnvVar(), want)
57
58 if got := p.ThemeDir(); got != want {
59 t.Errorf("ThemeDir() = %q, want %q", got, want)
60 }
61}
62
63func TestToolsMenuHotKeyClashesWithNoFixedMenu(t *testing.T) {
64 label := gololang.Profile().ToolsMenu
65
66 hotKey, ok := hotKeyOf(label)
67 if !ok {
68 t.Fatalf("ToolsMenu = %q, which marks no hot key between tildes", label)
69 }
70 if slices.Contains(fixedMenuHotKeys, hotKey) {
71 t.Errorf("ToolsMenu hot key %q is already taken by a fixed menu", hotKey)
72 }
73 if got := strings.ReplaceAll(label, "~", ""); got != "Golo" {
74 t.Errorf("ToolsMenu reads %q once the tildes are removed, want %q", got, "Golo")
75 }
76}
77
78// hotKeyOf returns the upper-case letter a menu label marks between tildes.
79func hotKeyOf(label string) (rune, bool) {
80 open := strings.Index(label, "~")
81 if open < 0 {
82 return 0, false
83 }
84 rest := label[open+1:]
85 shut := strings.Index(rest, "~")
86 if shut != 1 {
87 return 0, false
88 }
89 return []rune(strings.ToUpper(rest))[0], true
90}
91
92func TestThereAreNoRootMarkers(t *testing.T) {
93 // Golo has no project manifest, so there is nothing to walk up towards.
94 // With no markers app.ProjectRoot hands the server the directory of the
95 // file being edited, which is what main_test.go checks from the other
96 // side. A marker appearing here would be a claim about the language that
97 // the language does not make.
98 if got := gololang.Profile().RootMarkers; len(got) != 0 {
99 t.Errorf("RootMarkers = %v, want none", got)
100 }
101}
102
103func TestServerIsTheInterpreterInLSPMode(t *testing.T) {
104 server := gololang.Profile().Server
105
106 if server.Command != "golo" {
107 t.Errorf("Server.Command = %q, want %q", server.Command, "golo")
108 }
109 // golo with no argument starts a REPL that reads standard input as Golo,
110 // which the editor would see as a server that never answers.
111 if !slices.Equal(server.Args, []string{"lsp"}) {
112 t.Errorf("Server.Args = %v, want exactly [lsp]", server.Args)
113 }
114 if server.InstallHint == "" {
115 t.Error("Server.InstallHint is empty; a missing server would say nothing useful")
116 }
117 // It is shown on the status bar, so it has to fit on a narrow line.
118 if len(server.InstallHint) > 72 {
119 t.Errorf("InstallHint is %d characters, too long for a status bar", len(server.InstallHint))
120 }
121}
122
123func TestServerArgsCannotBeAppendedToByACaller(t *testing.T) {
124 first := gololang.ServerArgs()
125 first = append(first, "--nonsense")
126
127 if second := gololang.ServerArgs(); slices.Contains(second, "--nonsense") {
128 t.Errorf("ServerArgs() = %v after a caller appended to an earlier result", second)
129 }
130}
131
132func TestServerIsLookedForWhereGoloScriptInstallsIt(t *testing.T) {
133 // GoloScript's install.sh copies golo, gogolo and wagolo into
134 // /usr/local/bin. It is on PATH on nearly every machine, and the editor
135 // searches it anyway for the one where it is not.
136 dirs := gololang.Profile().Server.Dirs
137
138 if !slices.Contains(dirs, "/usr/local/bin") {
139 t.Errorf("Server.Dirs = %v, want it to contain /usr/local/bin", dirs)
140 }
141 if len(dirs) != 1 {
142 t.Errorf("Server.Dirs = %v, want the one directory the installer writes into and no guesses", dirs)
143 }
144}
145
146func TestProfileHandsOutAFreshCopyEveryTime(t *testing.T) {
147 // A caller that appends to Server.Dirs must not be appending to the
148 // package's one copy, or the next caller would inherit its guess.
149 first := gololang.Profile()
150 first.Server.Dirs = append(first.Server.Dirs, "/somewhere/else")
151
152 if second := gololang.Profile(); slices.Contains(second.Server.Dirs, "/somewhere/else") {
153 t.Errorf("Profile().Server.Dirs = %v after a caller appended to an earlier result", second.Server.Dirs)
154 }
155}
156
157func TestTemplatesAreAllFilledIn(t *testing.T) {
158 templates := gololang.Profile().Templates
159
160 for name, template := range map[string]string{
161 "Settings": templates.Settings,
162 "Snippets": templates.Snippets,
163 "Tools": templates.Tools,
164 } {
165 if template == "" {
166 t.Errorf("Templates.%s is empty; the editor would offer to write nothing", name)
167 }
168 }
169}
170
171func TestRegisterTeachesTheLibraryGolo(t *testing.T) {
172 gololang.Register()
173
174 if !slices.Contains(syntax.Registered(), gololang.Language) {
175 t.Fatalf("Registered() = %v, want it to contain %q", syntax.Registered(), gololang.Language)
176 }
177
178 for _, path := range []string{"main.golo", "lib/strings_test.golo", "DEEP/nested/x.GOLO"} {
179 if got := syntax.LanguageOf(path, ""); got != gololang.Language {
180 t.Errorf("LanguageOf(%q) = %q, want %q", path, got, gololang.Language)
181 }
182 }
183}
184
185func TestAShebangNamingGoloIsGolo(t *testing.T) {
186 // A script run as a command has no extension and opens with a line the
187 // interpreter reads as a comment. The first line is what identifies it.
188 gololang.Register()
189
190 for _, firstLine := range []string{"#!/usr/bin/env golo", "#!/usr/local/bin/golo"} {
191 if got := syntax.LanguageOf("run-me", firstLine); got != gololang.Language {
192 t.Errorf("LanguageOf(%q) = %q, want %q", firstLine, got, gololang.Language)
193 }
194 }
195}
196
197func TestRegisterLeavesOtherFilesAlone(t *testing.T) {
198 gololang.Register()
199
200 cases := map[string]syntax.Language{
201 "README.md": syntax.LanguageMarkdown,
202 "Dockerfile": syntax.LanguageDockerfile,
203 "main.py": syntax.LanguageNone,
204 "main.mbt": syntax.LanguageNone,
205 // A golo file's neighbour with a different extension is not Golo
206 // however Golo-flavoured its name.
207 "golo.toml": syntax.LanguageTOML,
208 }
209 for path, want := range cases {
210 if got := syntax.LanguageOf(path, ""); got != want {
211 t.Errorf("LanguageOf(%q) = %q, want %q", path, got, want)
212 }
213 }
214 // A shebang naming another interpreter is not Golo either.
215 if got := syntax.LanguageOf("script", "#!/usr/bin/env python3"); got == gololang.Language {
216 t.Errorf("LanguageOf with a python shebang = %q, want anything but Golo", got)
217 }
218}
219
220// A profile is a literal, so this example is the whole of how one is used.
221func ExampleProfile() {
222 gololang.Register()
223
224 var p profile.Profile = gololang.Profile()
225 _ = p.ProjectDir() // ".turbo-golo"
226}