Skip to content

Commit 4400bb5

Browse files
committed
Use .bat for Windows version tests
1 parent 23ebb16 commit 4400bb5

File tree

3 files changed

+95
-66
lines changed

3 files changed

+95
-66
lines changed

src/main/kotlin/com/coder/gateway/settings/CoderSettings.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ open class CoderSettings(
2222
private val state: CoderSettingsState,
2323
// The location of the SSH config. Defaults to ~/.ssh/config.
2424
val sshConfigPath: Path = Path.of(System.getProperty("user.home")).resolve(".ssh/config"),
25-
// Env allows overriding the default environment.
25+
// Overrides the default environment (for tests).
2626
private val env: Environment = Environment(),
27+
// Overrides the default binary name (for tests).
28+
private val binaryName: String? = null,
2729
) {
2830
val tls = CoderTLSSettings(state)
2931
val enableDownloads: Boolean
@@ -68,10 +70,10 @@ open class CoderSettings(
6870
* To where the specified deployment should download the binary.
6971
*/
7072
fun binPath(url: URL, forceDownloadToData: Boolean = false): Path {
71-
val binaryName = getCoderCLIForOS(getOS(), getArch())
73+
val name = binaryName ?: getCoderCLIForOS(getOS(), getArch())
7274
val dir = if (forceDownloadToData || state.binaryDirectory.isBlank()) dataDir(url)
7375
else withHost(Path.of(expand(state.binaryDirectory)), url)
74-
return dir.resolve(binaryName).toAbsolutePath()
76+
return dir.resolve(name).toAbsolutePath()
7577
}
7678

7779
/**

src/test/kotlin/com/coder/gateway/cli/CoderCLIManagerTest.kt

Lines changed: 83 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -32,23 +32,34 @@ import kotlin.test.assertNotEquals
3232
import kotlin.test.assertTrue
3333

3434
internal class CoderCLIManagerTest {
35-
private fun mkbin(version: String): String {
36-
return listOf("#!/bin/sh", """echo '{"version": "$version"}'""")
37-
.joinToString("\n")
35+
/**
36+
* Return the contents of a script that contains the string.
37+
*/
38+
private fun mkbin(str: String): String {
39+
return if (getOS() == OS.WINDOWS) {
40+
// Must use a .bat extension for this to work.
41+
listOf("@echo off", str)
42+
} else {
43+
listOf("#!/bin/sh", str)
44+
}.joinToString(System.lineSeparator())
45+
}
46+
47+
/**
48+
* Return the contents of a script that outputs JSON containing the version.
49+
*/
50+
private fun mkbinVersion(version: String): String {
51+
return mkbin(echo("""{"version": "$version"}"""))
3852
}
3953

4054
private fun mockServer(errorCode: Int = 0, version: String? = null): Pair<HttpServer, URL> {
4155
val srv = HttpServer.create(InetSocketAddress(0), 0)
4256
srv.createContext("/") {exchange ->
4357
var code = HttpURLConnection.HTTP_OK
44-
// TODO: Is there some simple way to create an executable file on
45-
// Windows without having to execute something to generate said
46-
// executable or having to commit one to the repo?
47-
var response = mkbin(version ?: "${srv.address.port}.0.0")
58+
var response = mkbinVersion(version ?: "${srv.address.port}.0.0")
4859
val eTags = exchange.requestHeaders["If-None-Match"]
4960
if (exchange.requestURI.path == "/bin/override") {
5061
code = HttpURLConnection.HTTP_OK
51-
response = mkbin("0.0.0")
62+
response = mkbinVersion("0.0.0")
5263
} else if (!exchange.requestURI.path.startsWith("/bin/coder-")) {
5364
code = HttpURLConnection.HTTP_NOT_FOUND
5465
response = "not found"
@@ -159,22 +170,15 @@ internal class CoderCLIManagerTest {
159170

160171
assertEquals(true, ccm.download())
161172

162-
// The mock does not serve a binary that works on Windows so do not
163-
// actually execute. Checking the contents works just as well as proof
164-
// that the binary was correctly downloaded anyway.
165-
assertContains(ccm.localBinaryPath.toFile().readText(), url.port.toString())
166-
if (getOS() != OS.WINDOWS) {
167-
assertEquals(SemVer(url.port.toLong(), 0, 0), ccm.version())
168-
}
173+
assertEquals(SemVer(url.port.toLong(), 0, 0), ccm.version())
169174

170175
// It should skip the second attempt.
171176
assertEquals(false, ccm.download())
172177

173178
// Should use the source override.
174179
ccm = CoderCLIManager(url, CoderSettings(CoderSettingsState(
175180
binarySource = "/bin/override",
176-
dataDirectory = tmpdir.resolve("mock-cli").toString()))
177-
)
181+
dataDirectory = tmpdir.resolve("mock-cli").toString())))
178182

179183
assertEquals(true, ccm.download())
180184
assertContains(ccm.localBinaryPath.toFile().readText(), "0.0.0")
@@ -354,32 +358,52 @@ internal class CoderCLIManagerTest {
354358
}
355359
}
356360

357-
@Test
358-
fun testFailVersionParse() {
359-
if (getOS() == OS.WINDOWS) {
360-
return // Cannot execute mock binaries on Windows.
361+
/**
362+
* Return an echo command for the OS.
363+
*/
364+
private fun echo(str: String): String {
365+
return if (getOS() == OS.WINDOWS) {
366+
"echo $str"
367+
} else {
368+
"echo '$str'"
361369
}
370+
}
362371

372+
/**
373+
* Return an exit command for the OS.
374+
*/
375+
private fun exit(code: Number): String {
376+
return if (getOS() == OS.WINDOWS) {
377+
"exit /b $code"
378+
} else {
379+
"exit $code"
380+
}
381+
}
382+
383+
@Test
384+
fun testFailVersionParse() {
363385
val tests = mapOf(
364-
null to ProcessInitException::class,
365-
"""echo '{"foo": true, "baz": 1}'""" to MissingVersionException::class,
366-
"""echo '{"version: '""" to JsonSyntaxException::class,
367-
"""echo '{"version": "invalid"}'""" to InvalidVersionException::class,
368-
"exit 0" to MissingVersionException::class,
369-
"exit 1" to InvalidExitValueException::class,
386+
null to ProcessInitException::class,
387+
echo("""{"foo": true, "baz": 1}""") to MissingVersionException::class,
388+
echo("""{"version: """) to JsonSyntaxException::class,
389+
echo("""{"version": "invalid"}""") to InvalidVersionException::class,
390+
exit(0) to MissingVersionException::class,
391+
exit(1) to InvalidExitValueException::class,
370392
)
371393

372394
val ccm = CoderCLIManager(URL("https://test.coder.parse-fail.invalid"), CoderSettings(CoderSettingsState(
373-
binaryDirectory = tmpdir.resolve("bad-version").toString()))
374-
)
395+
binaryDirectory = tmpdir.resolve("bad-version").toString()),
396+
binaryName = "coder.bat"))
375397
ccm.localBinaryPath.parent.toFile().mkdirs()
376398

377399
tests.forEach {
378400
if (it.key == null) {
379401
ccm.localBinaryPath.toFile().deleteRecursively()
380402
} else {
381-
ccm.localBinaryPath.toFile().writeText("#!/bin/sh\n${it.key}")
382-
ccm.localBinaryPath.toFile().setExecutable(true)
403+
ccm.localBinaryPath.toFile().writeText(mkbin(it.key!!))
404+
if (getOS() != OS.WINDOWS) {
405+
ccm.localBinaryPath.toFile().setExecutable(true)
406+
}
383407
}
384408
assertFailsWith(
385409
exceptionClass = it.value,
@@ -389,39 +413,37 @@ internal class CoderCLIManagerTest {
389413

390414
@Test
391415
fun testMatchesVersion() {
392-
if (getOS() == OS.WINDOWS) {
393-
return
394-
}
395-
396416
val test = listOf(
397417
Triple(null, "v1.0.0", null),
398-
Triple("""echo '{"version": "v1.0.0"}'""", "v1.0.0", true),
399-
Triple("""echo '{"version": "v1.0.0"}'""", "v1.0.0-devel+b5b5b5b5", true),
400-
Triple("""echo '{"version": "v1.0.0-devel+b5b5b5b5"}'""", "v1.0.0-devel+b5b5b5b5", true),
401-
Triple("""echo '{"version": "v1.0.0-devel+b5b5b5b5"}'""", "v1.0.0", true),
402-
Triple("""echo '{"version": "v1.0.0-devel+b5b5b5b5"}'""", "v1.0.0-devel+c6c6c6c6", true),
403-
Triple("""echo '{"version": "v1.0.0-prod+b5b5b5b5"}'""", "v1.0.0-devel+b5b5b5b5", true),
404-
Triple("""echo '{"version": "v1.0.0"}'""", "v1.0.1", false),
405-
Triple("""echo '{"version": "v1.0.0"}'""", "v1.1.0", false),
406-
Triple("""echo '{"version": "v1.0.0"}'""", "v2.0.0", false),
407-
Triple("""echo '{"version": "v1.0.0"}'""", "v0.0.0", false),
408-
Triple("""echo '{"version": ""}'""", "v1.0.0", null),
409-
Triple("""echo '{"version": "v1.0.0"}'""", "", null),
410-
Triple("""echo '{"version'""", "v1.0.0", null),
411-
Triple("""exit 0""", "v1.0.0", null),
412-
Triple("""exit 1""", "v1.0.0", null))
418+
Triple(echo("""{"version": "v1.0.0"}"""), "v1.0.0", true),
419+
Triple(echo("""{"version": "v1.0.0"}"""), "v1.0.0-devel+b5b5b5b5", true),
420+
Triple(echo("""{"version": "v1.0.0-devel+b5b5b5b5"}"""), "v1.0.0-devel+b5b5b5b5", true),
421+
Triple(echo("""{"version": "v1.0.0-devel+b5b5b5b5"}"""), "v1.0.0", true),
422+
Triple(echo("""{"version": "v1.0.0-devel+b5b5b5b5"}"""), "v1.0.0-devel+c6c6c6c6", true),
423+
Triple(echo("""{"version": "v1.0.0-prod+b5b5b5b5"}"""), "v1.0.0-devel+b5b5b5b5", true),
424+
Triple(echo("""{"version": "v1.0.0"}"""), "v1.0.1", false),
425+
Triple(echo("""{"version": "v1.0.0"}"""), "v1.1.0", false),
426+
Triple(echo("""{"version": "v1.0.0"}"""), "v2.0.0", false),
427+
Triple(echo("""{"version": "v1.0.0"}"""), "v0.0.0", false),
428+
Triple(echo("""{"version": ""}"""), "v1.0.0", null),
429+
Triple(echo("""{"version": "v1.0.0"}"""), "", null),
430+
Triple(echo("""{"version"""), "v1.0.0", null),
431+
Triple(exit(0), "v1.0.0", null),
432+
Triple(exit(1), "v1.0.0", null))
413433

414434
val ccm = CoderCLIManager(URL("https://test.coder.matches-version.invalid"), CoderSettings(CoderSettingsState(
415-
binaryDirectory = tmpdir.resolve("matches-version").toString()))
416-
)
435+
binaryDirectory = tmpdir.resolve("matches-version").toString()),
436+
binaryName = "coder.bat"))
417437
ccm.localBinaryPath.parent.toFile().mkdirs()
418438

419439
test.forEach {
420440
if (it.first == null) {
421441
ccm.localBinaryPath.toFile().deleteRecursively()
422442
} else {
423-
ccm.localBinaryPath.toFile().writeText("#!/bin/sh\n${it.first}")
424-
ccm.localBinaryPath.toFile().setExecutable(true)
443+
ccm.localBinaryPath.toFile().writeText(mkbin(it.first!!))
444+
if (getOS() != OS.WINDOWS) {
445+
ccm.localBinaryPath.toFile().setExecutable(true)
446+
}
425447
}
426448

427449
assertEquals(it.third, ccm.matchesVersion(it.second), it.first)
@@ -446,7 +468,9 @@ internal class CoderCLIManagerTest {
446468
@Test
447469
fun testEnsureCLI() {
448470
if (getOS() == OS.WINDOWS) {
449-
return // Cannot execute mock binaries on Windows and setWritable() works differently.
471+
// TODO: setWritable() does not work the same way on Windows but we
472+
// should test what we can.
473+
return
450474
}
451475

452476
val tests = listOf(
@@ -490,7 +514,7 @@ internal class CoderCLIManagerTest {
490514
// Create a binary in the regular location.
491515
if (it.version != null) {
492516
settings.binPath(url).parent.toFile().mkdirs()
493-
settings.binPath(url).toFile().writeText(mkbin(it.version))
517+
settings.binPath(url).toFile().writeText(mkbinVersion(it.version))
494518
settings.binPath(url).toFile().setExecutable(true)
495519
}
496520

@@ -503,7 +527,7 @@ internal class CoderCLIManagerTest {
503527
// Create a binary in the fallback location.
504528
if (it.fallbackVersion != null) {
505529
settings.binPath(url, true).parent.toFile().mkdirs()
506-
settings.binPath(url, true).toFile().writeText(mkbin(it.fallbackVersion))
530+
settings.binPath(url, true).toFile().writeText(mkbinVersion(it.fallbackVersion))
507531
settings.binPath(url, true).toFile().setExecutable(true)
508532
}
509533

@@ -553,10 +577,6 @@ internal class CoderCLIManagerTest {
553577

554578
@Test
555579
fun testFeatures() {
556-
if (getOS() == OS.WINDOWS) {
557-
return // Cannot execute mock binaries on Windows.
558-
}
559-
560580
val tests = listOf(
561581
Pair("2.5.0", Features(true)),
562582
Pair("4.9.0", Features(true)),
@@ -567,8 +587,8 @@ internal class CoderCLIManagerTest {
567587
tests.forEach {
568588
val (srv, url) = mockServer(version = it.first)
569589
val ccm = CoderCLIManager(url, CoderSettings(CoderSettingsState(
570-
dataDirectory = tmpdir.resolve("features").toString()))
571-
)
590+
dataDirectory = tmpdir.resolve("features").toString()),
591+
binaryName = "coder.bat"))
572592
assertEquals(true, ccm.download())
573593
assertEquals(it.second, ccm.features, "version: ${it.first}")
574594

src/test/kotlin/com/coder/gateway/settings/CoderSettingsTest.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.coder.gateway.services.CoderSettingsState
44
import com.coder.gateway.util.OS
55
import com.coder.gateway.util.getOS
66
import com.coder.gateway.util.withPath
7+
import org.junit.Assert.assertNotEquals
78
import java.net.URL
89
import java.nio.file.Path
910
import kotlin.test.Test
@@ -84,6 +85,7 @@ internal class CoderSettingsTest {
8485
fun testBinPath() {
8586
val state = CoderSettingsState()
8687
val settings = CoderSettings(state)
88+
val settings2 = CoderSettings(state, binaryName = "foo-bar.baz")
8789
// The binary path should fall back to the data directory but that is
8890
// already tested in the data directory tests.
8991
val url = URL("http://localhost")
@@ -92,11 +94,16 @@ internal class CoderSettingsTest {
9294
state.binaryDirectory = "/tmp/coder-gateway-test/bin-dir"
9395
var expected = "/tmp/coder-gateway-test/bin-dir/localhost"
9496
assertEquals(Path.of(expected).toAbsolutePath(), settings.binPath(url).parent)
97+
assertEquals(Path.of(expected).toAbsolutePath(), settings2.binPath(url).parent)
9598

9699
// Second argument bypasses override.
97100
state.dataDirectory = "/tmp/coder-gateway-test/data-dir"
98101
expected = "/tmp/coder-gateway-test/data-dir/localhost"
99102
assertEquals(Path.of(expected).toAbsolutePath(), settings.binPath(url, true).parent)
103+
assertEquals(Path.of(expected).toAbsolutePath(), settings2.binPath(url, true).parent)
104+
105+
assertNotEquals("foo-bar.baz", settings.binPath(url).fileName)
106+
assertEquals("foo-bar.baz", settings2.binPath(url).fileName.toString())
100107
}
101108

102109
@Test

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy