@@ -6,29 +6,56 @@ import spock.lang.Unroll
66
77import java.nio.file.Files
88import java.nio.file.Path
9+ import java.nio.file.attribute.AclEntry
10+ import java.nio.file.attribute.AclEntryPermission
11+ import java.nio.file.attribute.AclEntryType
12+ import java.nio.file.attribute.AclFileAttributeView
913
1014@Unroll
1115class PathExtensionsTest extends Specification {
1216 @Shared
13- private Path tmpdir = Path . of(System . getProperty(" java.io.tmpdir" ))
14- @Shared
15- private Path unwritableFile = tmpdir. resolve(" coder-gateway-test/path-extensions/unwritable/file" )
16- @Shared
17- private Path writableFile = tmpdir. resolve(" coder-gateway-test/path-extensions/writable-file" )
17+ private Path tmpdir = Path . of(System . getProperty(" java.io.tmpdir" )). resolve(" coder-gateway-test/path-extensions/" )
18+
19+ private void setWindowsPermissions (Path path ) {
20+ AclFileAttributeView view = Files . getFileAttributeView(path, AclFileAttributeView . class)
21+ AclEntry entry = AclEntry . newBuilder()
22+ .setType(AclEntryType . DENY )
23+ .setPrincipal(view. getOwner())
24+ .setPermissions(AclEntryPermission . WRITE_DATA )
25+ .build()
26+ List<AclEntry > acl = view. getAcl()
27+ acl. set(0 , entry)
28+ view. setAcl(acl)
29+ }
1830
1931 void setupSpec () {
20- // TODO: On Windows setWritable() only sets read-only; how do we set
21- // actual permissions? Initially I tried an existing dir like WINDIR
22- // which worked locally but in CI that is writable for some reason.
23- if (unwritableFile. parent. toFile(). exists()) {
24- unwritableFile. parent. toFile(). setWritable(true )
25- unwritableFile. parent. toFile(). deleteDir()
32+ // Clean up from the last run, if any.
33+ tmpdir. toFile(). deleteDir()
34+
35+ // Push out the test files.
36+ for (String dir in [" read-only-dir" , " no-permissions-dir" ]) {
37+ Files . createDirectories(tmpdir. resolve(dir))
38+ tmpdir. resolve(dir). resolve(" file" ). toFile(). write(" " )
39+ }
40+ for (String file in [" read-only-file" , " writable-file" , " no-permissions-file" ]) {
41+ tmpdir. resolve(file). toFile(). write(" " )
42+ }
43+
44+ // On Windows `File.setWritable()` only sets read-only, not permissions
45+ // so on other platforms "read-only" is the same as "no permissions".
46+ tmpdir. resolve(" read-only-file" ). toFile(). setWritable(false )
47+ tmpdir. resolve(" read-only-dir" ). toFile(). setWritable(false )
48+
49+ // Create files without actual write permissions on Windows (not just
50+ // read-only). On other platforms this is the same as above.
51+ tmpdir. resolve(" no-permissions-dir/file" ). toFile(). write(" " )
52+ if (System . getProperty(" os.name" ). toLowerCase(). contains(" windows" )) {
53+ setWindowsPermissions(tmpdir. resolve(" no-permissions-file" ))
54+ setWindowsPermissions(tmpdir. resolve(" no-permissions-dir" ))
55+ } else {
56+ tmpdir. resolve(" no-permissions-file" ). toFile(). setWritable(false )
57+ tmpdir. resolve(" no-permissions-dir" ). toFile(). setWritable(false )
2658 }
27- Files . createDirectories(unwritableFile. parent)
28- unwritableFile. toFile(). write(" text" )
29- writableFile. toFile(). write(" text" )
30- unwritableFile. toFile(). setWritable(false )
31- unwritableFile. parent. toFile(). setWritable(false )
3259 }
3360
3461 def " canCreateDirectory" () {
@@ -39,20 +66,33 @@ class PathExtensionsTest extends Specification {
3966
4067 where :
4168 path | expected
42- unwritableFile | false
43- unwritableFile. resolve(" probably/nonexistent" ) | false
44- // TODO: Java reports read-only directories on Windows as writable.
45- unwritableFile. parent. resolve(" probably/nonexistent" ) | System . getProperty(" os.name" ). toLowerCase(). contains(" windows" )
46- writableFile | false
47- writableFile. parent | true
48- writableFile. resolve(" nested/under/file" ) | false
49- writableFile. parent. resolve(" nested/under/dir" ) | true
50- Path . of(" relative to project" ) | true
51- tmpdir. resolve(" ./foo/bar/../../coder-gateway-test/path-extensions" ) | true
69+ // A file is not valid for directory creation regardless of writability.
70+ tmpdir. resolve(" read-only-file" ) | false
71+ tmpdir. resolve(" read-only-file/nested/under/file" ) | false
72+ tmpdir. resolve(" writable-file" ) | false
73+ tmpdir. resolve(" writable-file/nested/under/file" ) | false
74+ tmpdir. resolve(" read-only-dir/file" ) | false
75+ tmpdir. resolve(" no-permissions-dir/file" ) | false
76+
77+ // Windows: can create under read-only directories.
78+ tmpdir. resolve(" read-only-dir" ) | System . getProperty(" os.name" ). toLowerCase(). contains(" windows" )
79+ tmpdir. resolve(" read-only-dir/nested/under/dir" ) | System . getProperty(" os.name" ). toLowerCase(). contains(" windows" )
80+
81+ // Cannot create under a directory without permissions.
82+ tmpdir. resolve(" no-permissions-dir" ) | false
83+ tmpdir. resolve(" no-permissions-dir/nested/under/dir" ) | false
84+
85+ // Can create under a writable directory.
5286 tmpdir | true
53- tmpdir. resolve(" some/nested/non-existent/path" ) | true
87+ tmpdir. resolve(" ./foo/bar/../../coder-gateway-test/path-extensions" ) | true
88+ tmpdir. resolve(" nested/under/dir" ) | true
5489 tmpdir. resolve(" with space" ) | true
90+
91+ // Config/data directories should be fine.
5592 CoderCLIManager . getConfigDir() | true
5693 CoderCLIManager . getDataDir() | true
94+
95+ // Relative paths can work as well.
96+ Path . of(" relative/to/project" ) | true
5797 }
5898}
0 commit comments