Skip to content

Commit 7d6dda8

Browse files
committed
test: add unit tests for IO (link, fileInfo, chmod, permissions)
10 tests covering link creation, hardlink count, inode detection, chmod permissions, FileNotFoundException on missing paths, and permission constant values. setStdoutStderrTo() is excluded from unit tests because dup2() redirects the JVM's stdout/stderr, breaking surefire communication. Refs: CO-3493
1 parent 14288db commit 7d6dda8

1 file changed

Lines changed: 144 additions & 0 deletions

File tree

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
// SPDX-FileCopyrightText: 2025 Zextras <https://www.zextras.com>
2+
//
3+
// SPDX-License-Identifier: GPL-2.0-only
4+
5+
package com.zimbra.znative;
6+
7+
import static org.junit.jupiter.api.Assertions.*;
8+
9+
import java.io.FileNotFoundException;
10+
import java.io.IOException;
11+
import java.nio.file.Files;
12+
import java.nio.file.Path;
13+
import java.nio.file.attribute.PosixFilePermission;
14+
import org.junit.jupiter.api.Test;
15+
import org.junit.jupiter.api.io.TempDir;
16+
17+
class IOTest {
18+
19+
@TempDir Path tempDir;
20+
21+
@Test
22+
void link_createsHardLink() throws IOException {
23+
Path src = tempDir.resolve("source.txt");
24+
Files.writeString(src, "hello");
25+
26+
String destPath = tempDir.resolve("dest.txt").toString();
27+
IO.link(src.toString(), destPath);
28+
29+
assertEquals("hello", Files.readString(Path.of(destPath)));
30+
// Hard link means same inode
31+
assertEquals(
32+
Files.getAttribute(src, "unix:ino"),
33+
Files.getAttribute(Path.of(destPath), "unix:ino"));
34+
}
35+
36+
@Test
37+
void link_throwsFileNotFoundForMissingSrc() {
38+
String missing = tempDir.resolve("nonexistent").toString();
39+
String dest = tempDir.resolve("dest").toString();
40+
41+
assertThrows(FileNotFoundException.class, () -> IO.link(missing, dest));
42+
}
43+
44+
@Test
45+
void link_throwsIOExceptionForExistingDest() throws IOException {
46+
Path src = tempDir.resolve("src.txt");
47+
Path dest = tempDir.resolve("dest.txt");
48+
Files.writeString(src, "a");
49+
Files.writeString(dest, "b");
50+
51+
assertThrows(IOException.class, () -> IO.link(src.toString(), dest.toString()));
52+
}
53+
54+
@Test
55+
void fileInfo_returnsInodeSizeAndLinkCount() throws IOException {
56+
Path file = tempDir.resolve("test.txt");
57+
Files.writeString(file, "content");
58+
59+
IO.FileInfo info = IO.fileInfo(file.toString());
60+
61+
assertNotNull(info);
62+
assertTrue(info.getInodeNum() > 0);
63+
assertEquals(7, info.getSize()); // "content" is 7 bytes
64+
assertEquals(1, info.getLinkCount());
65+
}
66+
67+
@Test
68+
void fileInfo_linkCountIncreasesAfterHardLink() throws IOException {
69+
Path file = tempDir.resolve("original.txt");
70+
Files.writeString(file, "data");
71+
72+
IO.FileInfo before = IO.fileInfo(file.toString());
73+
assertEquals(1, before.getLinkCount());
74+
75+
IO.link(file.toString(), tempDir.resolve("linked.txt").toString());
76+
77+
IO.FileInfo after = IO.fileInfo(file.toString());
78+
assertEquals(2, after.getLinkCount());
79+
assertEquals(before.getInodeNum(), after.getInodeNum());
80+
}
81+
82+
@Test
83+
void fileInfo_throwsFileNotFoundForMissingPath() {
84+
String missing = tempDir.resolve("nonexistent").toString();
85+
assertThrows(FileNotFoundException.class, () -> IO.fileInfo(missing));
86+
}
87+
88+
@Test
89+
void linkCount_returnsCorrectCount() throws IOException {
90+
Path file = tempDir.resolve("file.txt");
91+
Files.writeString(file, "x");
92+
93+
assertEquals(1, IO.linkCount(file.toString()));
94+
95+
IO.link(file.toString(), tempDir.resolve("link1.txt").toString());
96+
assertEquals(2, IO.linkCount(file.toString()));
97+
98+
IO.link(file.toString(), tempDir.resolve("link2.txt").toString());
99+
assertEquals(3, IO.linkCount(file.toString()));
100+
}
101+
102+
@Test
103+
void chmod_setsPermissions() throws IOException {
104+
Path file = tempDir.resolve("perms.txt");
105+
Files.writeString(file, "test");
106+
107+
IO.chmod(file.toString(), IO.S_IRUSR | IO.S_IWUSR);
108+
109+
var perms = Files.getPosixFilePermissions(file);
110+
assertTrue(perms.contains(PosixFilePermission.OWNER_READ));
111+
assertTrue(perms.contains(PosixFilePermission.OWNER_WRITE));
112+
assertFalse(perms.contains(PosixFilePermission.OWNER_EXECUTE));
113+
assertFalse(perms.contains(PosixFilePermission.GROUP_READ));
114+
assertFalse(perms.contains(PosixFilePermission.OTHERS_READ));
115+
}
116+
117+
@Test
118+
void chmod_throwsFileNotFoundForMissingPath() {
119+
String missing = tempDir.resolve("nonexistent").toString();
120+
assertThrows(FileNotFoundException.class, () -> IO.chmod(missing, IO.S_IRUSR));
121+
}
122+
123+
// setStdoutStderrTo() tests are intentionally excluded:
124+
// dup2() redirects stdout/stderr for the entire JVM process, which breaks
125+
// surefire's communication pipe and causes "forked VM terminated" errors.
126+
// This function is only used by MailboxServer output rotation and is tested
127+
// in production via systemd service startup.
128+
129+
@Test
130+
void permissionConstants_haveCorrectValues() {
131+
assertEquals(0400, IO.S_IRUSR);
132+
assertEquals(0200, IO.S_IWUSR);
133+
assertEquals(0100, IO.S_IXUSR);
134+
assertEquals(0040, IO.S_IRGRP);
135+
assertEquals(0020, IO.S_IWGRP);
136+
assertEquals(0010, IO.S_IXGRP);
137+
assertEquals(0004, IO.S_IROTH);
138+
assertEquals(0002, IO.S_IWOTH);
139+
assertEquals(0001, IO.S_IXOTH);
140+
assertEquals(04000, IO.S_ISUID);
141+
assertEquals(02000, IO.S_ISGID);
142+
assertEquals(01000, IO.S_ISVTX);
143+
}
144+
}

0 commit comments

Comments
 (0)