@@ -32,23 +32,34 @@ import kotlin.test.assertNotEquals
3232import kotlin.test.assertTrue
3333
3434internal 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
0 commit comments