@@ -6,29 +6,54 @@ import spock.lang.Unroll
66
77import java.nio.file.Files
88import java.nio.file.Path
9+ import java.nio.file.attribute.*
910
1011@Unroll
1112class PathExtensionsTest extends Specification {
1213 @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" )
14+ private Path tmpdir = Path . of(System . getProperty(" java.io.tmpdir" )). resolve(" coder-gateway-test/path-extensions/" )
15+
16+ private void setPermissions (Path file ) {
17+ AclFileAttributeView view = Files . getFileAttributeView(file, AclFileAttributeView . class)
18+ UserPrincipal user = view. getOwner()
19+ AclEntry entry = AclEntry . newBuilder()
20+ .setType(AclEntryType . DENY )
21+ .setPrincipal(user)
22+ .setPermissions(AclEntryPermission . WRITE_DATA )
23+ .build();
24+ List<AclEntry > acl = view. getAcl()
25+ acl. set(0 , entry)
26+ view. setAcl(acl)
27+ }
1828
1929 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()
30+ // Clean up from the last run, if any.
31+ tmpdir. toFile(). deleteDir()
32+
33+ // Push out the test files.
34+ for (String dir in [" read-only-dir" , " no-permissions-dir" ]) {
35+ Files . createDirectories(tmpdir. resolve(dir))
36+ tmpdir. resolve(dir). resolve(" file" ). toFile(). write(" " )
37+ }
38+ for (String file in [" read-only-file" , " writable-file" , " no-permissions-file" ]) {
39+ tmpdir. resolve(file). toFile(). write(" " )
40+ }
41+
42+ // On Windows `File.setWritable()` only sets read-only, not permissions
43+ // so on other platforms "read-only" is the same as "no permissions".
44+ tmpdir. resolve(" read-only-file" ). toFile(). setWritable(false )
45+ tmpdir. resolve(" read-only-dir" ). toFile(). setWritable(false )
46+
47+ // Create files without actual write permissions on Windows (not just
48+ // read-only). On other platforms this is the same as above.
49+ tmpdir. resolve(" no-permissions-dir/file" ). toFile(). write(" " )
50+ if (System . getProperty(" os.name" ). toLowerCase(). contains(" windows" )) {
51+ setPermissions(tmpdir. resolve(" no-permissions-file" ))
52+ setPermissions(tmpdir. resolve(" no-permissions-dir" ))
53+ } else {
54+ tmpdir. resolve(" no-permissions-file" ). toFile(). setWritable(false )
55+ tmpdir. resolve(" no-permissions-dir" ). toFile(). setWritable(false )
2656 }
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 )
3257 }
3358
3459 def " canCreateDirectory" () {
@@ -39,20 +64,33 @@ class PathExtensionsTest extends Specification {
3964
4065 where :
4166 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
67+ // A file is not valid for directory creation regardless of writability.
68+ tmpdir. resolve(" read-only-file" ) | false
69+ tmpdir. resolve(" read-only-file/nested/under/file" ) | false
70+ tmpdir. resolve(" writable-file" ) | false
71+ tmpdir. resolve(" writable-file/nested/under/file" ) | false
72+ tmpdir. resolve(" read-only-dir/file" ) | false
73+ tmpdir. resolve(" no-permissions-dir/file" ) | false
74+
75+ // Window: can create under read-only directories.
76+ tmpdir. resolve(" read-only-dir" ) | System . getProperty(" os.name" ). toLowerCase(). contains(" windows" )
77+ tmpdir. resolve(" read-only-dir/nested/under/dir" ) | System . getProperty(" os.name" ). toLowerCase(). contains(" windows" )
78+
79+ // Cannot create under a directory without permissions.
80+ tmpdir. resolve(" no-permissions-dir" ) | false
81+ tmpdir. resolve(" no-permissions-dir/nested/under/dir" ) | false
82+
83+ // Can create under a writable directory.
5284 tmpdir | true
53- tmpdir. resolve(" some/nested/non-existent/path" ) | true
85+ tmpdir. resolve(" ./foo/bar/../../coder-gateway-test/path-extensions" ) | true
86+ tmpdir. resolve(" nested/under/dir" ) | true
5487 tmpdir. resolve(" with space" ) | true
88+
89+ // Config/data directories should be fine.
5590 CoderCLIManager . getConfigDir() | true
5691 CoderCLIManager . getDataDir() | true
92+
93+ // Relative paths can work as well.
94+ Path . of(" relative/to/project" ) | true
5795 }
5896}
0 commit comments