Skip to content

Commit ffc9376

Browse files
committed
test untested path funs
1 parent 7181255 commit ffc9376

2 files changed

Lines changed: 371 additions & 14 deletions

File tree

crates/roc_command/src/lib.rs

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -92,23 +92,12 @@ pub fn command_status(roc_cmd: &Command) -> RocResult<i32, roc_io_error::IOErr>
9292

9393
// Status of the child process, successful/exit code/killed by signal
9494
fn from_exit_status(status: std::process::ExitStatus) -> RocResult<i32, roc_io_error::IOErr> {
95-
if status.success() {
96-
RocResult::ok(0)
97-
} else {
98-
match status.code() {
99-
Some(code) => non_zero_exit(code),
100-
None => killed_by_signal(),
101-
}
95+
match status.code() {
96+
Some(code) => RocResult::ok(code),
97+
None => killed_by_signal(),
10298
}
10399
}
104100

105-
fn non_zero_exit(code: i32) -> RocResult<i32, roc_io_error::IOErr> {
106-
RocResult::err(roc_io_error::IOErr {
107-
tag: roc_io_error::IOErrTag::Other,
108-
msg: format!("Non-zero exit code: {}", code).as_str().into(),
109-
})
110-
}
111-
112101
// If no exit code is returned, the process was terminated by a signal.
113102
fn killed_by_signal() -> RocResult<i32, roc_io_error::IOErr> {
114103
RocResult::err(roc_io_error::IOErr {

tests/path.roc

Lines changed: 368 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,368 @@
1+
app [main!] {
2+
pf: platform "../platform/main.roc",
3+
json: "https://github.com/lukewilliamboswell/roc-json/releases/download/0.13.0/RqendgZw5e1RsQa3kFhgtnMP8efWoqGRsAvubx4-zus.tar.br",
4+
}
5+
6+
import pf.Stdout
7+
import pf.Stderr
8+
import pf.Path
9+
import pf.Arg exposing [Arg]
10+
import pf.Cmd
11+
import json.Json
12+
13+
main! : List Arg => Result {} _
14+
main! = |_args|
15+
Stdout.line!(
16+
"""
17+
Testing Path functions...
18+
This will create and manipulate test files and directories in the current directory.
19+
20+
"""
21+
)?
22+
23+
# Test path creation
24+
test_path_creation!({})?
25+
26+
# Test file operations
27+
test_file_operations!({})?
28+
29+
# Test directory operations
30+
test_directory_operations!({})?
31+
32+
# Test hard link creation
33+
test_hard_link!({})?
34+
35+
Stdout.line!("\nAll Path function tests completed! ✓")
36+
37+
test_path_creation! : {} => Result {} _
38+
test_path_creation! = |{}|
39+
Stdout.line!("Testing Path.from_bytes and Path.with_extension:")?
40+
41+
# Test Path.from_bytes
42+
path_bytes = [116, 101, 115, 116, 95, 112, 97, 116, 104] # "test_path" in bytes
43+
path_from_bytes = Path.from_bytes(path_bytes)
44+
expected_str = "test_path"
45+
actual_str = Path.display(path_from_bytes)
46+
47+
# Test Path.with_extension
48+
base_path = Path.from_str("test_file")
49+
path_with_ext = Path.with_extension(base_path, "txt")
50+
51+
path_with_dot = Path.from_str("test_file.")
52+
path_dot_ext = Path.with_extension(path_with_dot, "json")
53+
54+
path_replace_ext = Path.from_str("test_file.old")
55+
path_new_ext = Path.with_extension(path_replace_ext, "new")
56+
57+
Stdout.line!(
58+
"""
59+
Created path from bytes: ${Path.display(path_from_bytes)}
60+
Path.from_bytes result matches expected: ${Inspect.to_str(actual_str == expected_str)}
61+
Path with extension: ${Path.display(path_with_ext)}
62+
Extension added correctly: ${Inspect.to_str(Path.display(path_with_ext) == "test_file.txt")}
63+
Path with dot and extension: ${Path.display(path_dot_ext)}
64+
Extension after dot: ${Inspect.to_str(Path.display(path_dot_ext) == "test_file.json")}
65+
Path with replaced extension: ${Path.display(path_new_ext)}
66+
Extension replaced: ${Inspect.to_str(Path.display(path_new_ext) == "test_file.new")}
67+
"""
68+
)?
69+
70+
Ok({})
71+
72+
test_file_operations! : {} => Result {} _
73+
test_file_operations! = |{}|
74+
Stdout.line!("\nTesting Path file operations:")?
75+
76+
# Test Path.write_bytes! and Path.read_bytes!
77+
test_bytes = [72, 101, 108, 108, 111, 44, 32, 80, 97, 116, 104, 33] # "Hello, Path!" in bytes
78+
bytes_path = Path.from_str("test_path_bytes.txt")
79+
Path.write_bytes!(test_bytes, bytes_path)?
80+
81+
# Verify file exists using ls
82+
ls_output = Cmd.new("ls") |> Cmd.args(["-la", "test_path_bytes.txt"]) |> Cmd.output!()
83+
ls_stdout = Str.from_utf8(ls_output.stdout) ? |_| LsInvalidUtf8
84+
85+
read_bytes = Path.read_bytes!(bytes_path)?
86+
87+
Stdout.line!(
88+
"""
89+
File created: ${ls_stdout}
90+
Bytes written: ${Inspect.to_str(test_bytes)}
91+
Bytes read: ${Inspect.to_str(read_bytes)}
92+
Bytes match: ${Inspect.to_str(test_bytes == read_bytes)}
93+
"""
94+
)?
95+
96+
# Test Path.write_utf8! and Path.read_utf8!
97+
utf8_content = "Hello from Path module! 🚀"
98+
utf8_path = Path.from_str("test_path_utf8.txt")
99+
Path.write_utf8!(utf8_content, utf8_path)?
100+
101+
# Check file content with cat
102+
cat_output = Cmd.new("cat") |> Cmd.args(["test_path_utf8.txt"]) |> Cmd.output!()
103+
cat_stdout = Str.from_utf8(cat_output.stdout) ? |_| CatInvalidUtf8
104+
105+
read_utf8 = Path.read_utf8!(utf8_path)?
106+
107+
Stdout.line!(
108+
"""
109+
File content via cat: ${cat_stdout}
110+
UTF-8 written: ${utf8_content}
111+
UTF-8 read: ${read_utf8}
112+
UTF-8 content matches: ${Inspect.to_str(utf8_content == read_utf8)}
113+
"""
114+
)?
115+
116+
# Test Path.write! with JSON encoding
117+
json_data = { message: "Path test", numbers: [1, 2, 3] }
118+
json_path = Path.from_str("test_path_json.json")
119+
Path.write!(json_data, json_path, Json.utf8)?
120+
121+
json_content = Path.read_utf8!(json_path)?
122+
123+
# Verify it's valid JSON by checking it contains expected fields
124+
contains_message = Str.contains(json_content, "\"message\"")
125+
contains_numbers = Str.contains(json_content, "\"numbers\"")
126+
127+
Stdout.line!(
128+
"""
129+
JSON content: ${json_content}
130+
JSON contains 'message' field: ${Inspect.to_str(contains_message)}
131+
JSON contains 'numbers' field: ${Inspect.to_str(contains_numbers)}
132+
"""
133+
)?
134+
135+
# Test Path.delete!
136+
delete_path = Path.from_str("test_to_delete.txt")
137+
Path.write_utf8!("This file will be deleted", delete_path)?
138+
139+
# Verify file exists before deletion
140+
ls_before = Cmd.new("ls") |> Cmd.args(["test_to_delete.txt"]) |> Cmd.output!()
141+
142+
Stdout.line!("hey")?
143+
144+
Path.delete!(delete_path) ? |err| DeleteFailed(err)
145+
146+
Stdout.line!("yo")?
147+
148+
# Verify file is gone after deletion
149+
ls_after = Cmd.new("ls") |> Cmd.args(["test_to_delete.txt"]) |> Cmd.output!()
150+
151+
Stdout.line!("after")?
152+
153+
Stdout.line!(
154+
"""
155+
File exists before delete: ${Inspect.to_str(ls_before.status? == 0)}
156+
File exists after delete: ${Inspect.to_str(ls_after.status? == 0)}
157+
Successfully deleted test file
158+
"""
159+
)?
160+
161+
Ok({})
162+
163+
test_directory_operations! : {} => Result {} _
164+
test_directory_operations! = |{}|
165+
Stdout.line!("\nTesting Path directory operations:")?
166+
167+
# Test Path.create_dir!
168+
single_dir = Path.from_str("test_single_dir")
169+
Path.create_dir!(single_dir)?
170+
171+
# Verify directory exists
172+
ls_dir = Cmd.new("ls") |> Cmd.args(["-ld", "test_single_dir"]) |> Cmd.output!()
173+
ls_dir_stdout = Str.from_utf8(ls_dir.stdout) ? |_| LsDirInvalidUtf8
174+
is_dir = Str.starts_with(ls_dir_stdout, "d")
175+
176+
Stdout.line!(
177+
"""
178+
Created directory: ${ls_dir_stdout}
179+
Is directory (starts with 'd'): ${Inspect.to_str(is_dir)}
180+
"""
181+
)?
182+
183+
# Test Path.create_all! (nested directories)
184+
nested_dir = Path.from_str("test_parent/test_child/test_grandchild")
185+
Path.create_all!(nested_dir)?
186+
187+
# Verify nested structure with tree or find
188+
find_output = Cmd.new("find") |> Cmd.args(["test_parent", "-type", "d"]) |> Cmd.output!()
189+
find_stdout = Str.from_utf8(find_output.stdout) ? |_| FindInvalidUtf8
190+
191+
# Count directories created
192+
dir_count = Str.split_on(find_stdout, "\n") |> List.len
193+
194+
Stdout.line!(
195+
"""
196+
Nested directory structure:
197+
${find_stdout}
198+
Number of directories created: ${Num.to_str(dir_count - 1)}
199+
"""
200+
)?
201+
202+
# Create some files in the directory for testing
203+
Path.write_utf8!("File 1", Path.from_str("test_single_dir/file1.txt"))?
204+
Path.write_utf8!("File 2", Path.from_str("test_single_dir/file2.txt"))?
205+
Path.create_dir!(Path.from_str("test_single_dir/subdir"))?
206+
207+
# List directory contents
208+
ls_contents = Cmd.new("ls") |> Cmd.args(["-la", "test_single_dir"]) |> Cmd.output!()
209+
ls_contents_stdout = Str.from_utf8(ls_contents.stdout) ? |_| LsContentsInvalidUtf8
210+
211+
Stdout.line!(
212+
"""
213+
Directory contents:
214+
${ls_contents_stdout}
215+
"""
216+
)?
217+
218+
# Test Path.delete_empty!
219+
empty_dir = Path.from_str("test_empty_dir")
220+
Path.create_dir!(empty_dir)?
221+
222+
# Verify it exists
223+
ls_empty_before = Cmd.new("ls") |> Cmd.args(["-ld", "test_empty_dir"]) |> Cmd.output!()
224+
225+
Path.delete_empty!(empty_dir)?
226+
227+
# Verify it's gone
228+
ls_empty_after = Cmd.new("ls") |> Cmd.args(["-ld", "test_empty_dir"]) |> Cmd.output!()
229+
230+
Stdout.line!(
231+
"""
232+
Empty dir exists before delete: ${Inspect.to_str(ls_empty_before.status? == 0)}
233+
Empty dir exists after delete: ${Inspect.to_str(ls_empty_after.status? == 0)}
234+
"""
235+
)?
236+
237+
# Test Path.delete_all!
238+
# First show what we're about to delete
239+
du_output = Cmd.new("du") |> Cmd.args(["-sh", "test_parent"]) |> Cmd.output!()
240+
du_stdout = Str.from_utf8(du_output.stdout) ? |_| DuInvalidUtf8
241+
242+
Path.delete_all!(Path.from_str("test_parent"))?
243+
244+
# Verify it's gone
245+
ls_parent_after = Cmd.new("ls") |> Cmd.args(["test_parent"]) |> Cmd.output!()
246+
247+
Stdout.line!(
248+
"""
249+
Size before delete_all: ${du_stdout}
250+
Parent dir exists after delete_all: ${Inspect.to_str(ls_parent_after.status? == 0)}
251+
Recursively deleted directory tree
252+
"""
253+
)?
254+
255+
# Clean up remaining test directory
256+
Path.delete_all!(single_dir)?
257+
258+
Ok({})
259+
260+
test_hard_link! : {} => Result {} _
261+
test_hard_link! = |{}|
262+
Stdout.line!("\nTesting Path.hard_link!:")?
263+
264+
# Create original file
265+
original_path = Path.from_str("test_path_original.txt")
266+
Path.write_utf8!("Original content for Path hard link test", original_path)?
267+
268+
# Get original file stats
269+
stat_before = Cmd.new("stat") |> Cmd.args(["-c", "%h", "test_path_original.txt"]) |> Cmd.output!()
270+
links_before = Str.from_utf8(stat_before.stdout) ? |_| StatBeforeInvalidUtf8
271+
272+
# Create hard link
273+
link_path = Path.from_str("test_path_hardlink.txt")
274+
when Path.hard_link!(original_path, link_path) is
275+
Ok({}) ->
276+
# Get link count after
277+
stat_after = Cmd.new("stat") |> Cmd.args(["-c", "%h", "test_path_original.txt"]) |> Cmd.output!()
278+
links_after = Str.from_utf8(stat_after.stdout) ? |_| StatAfterInvalidUtf8
279+
280+
# Verify both files exist and have same content
281+
original_content = Path.read_utf8!(original_path)?
282+
link_content = Path.read_utf8!(link_path)?
283+
284+
Stdout.line!(
285+
"""
286+
Successfully created hard link
287+
Hard link count before: ${Str.trim(links_before)}
288+
Hard link count after: ${Str.trim(links_after)}
289+
Original content: ${original_content}
290+
Link content: ${link_content}
291+
Content matches: ${Inspect.to_str(original_content == link_content)}
292+
"""
293+
)?
294+
295+
# Check inodes are the same
296+
ls_li_output =
297+
Cmd.new("ls")
298+
|> Cmd.args(["-li", "test_path_original.txt", "test_path_hardlink.txt"])
299+
|> Cmd.output!()
300+
301+
ls_li_stdout_utf8 = Str.from_utf8(ls_li_output.stdout) ? |_| LsLiInvalidUtf8
302+
303+
inodes =
304+
Str.split_on(ls_li_stdout_utf8, "\n")
305+
|> List.map(|line|
306+
Str.split_on(line, " ")
307+
|> List.take_first(1)
308+
)
309+
310+
first_inode = List.get(inodes, 0) ? |_| FirstInodeNotFound
311+
second_inode = List.get(inodes, 1) ? |_| SecondInodeNotFound
312+
313+
Stdout.line!(
314+
"""
315+
Inode information:
316+
${ls_li_stdout_utf8}
317+
First file inode: ${Inspect.to_str(first_inode)}
318+
Second file inode: ${Inspect.to_str(second_inode)}
319+
Inodes are equal: ${Inspect.to_str(first_inode == second_inode)}
320+
"""
321+
)?
322+
323+
Err(err) ->
324+
Stderr.line!("Hard link creation failed: ${Inspect.to_str(err)}")?
325+
326+
# Clean up test files
327+
cleanup_test_files!({})
328+
329+
cleanup_test_files! : {} => Result {} _
330+
cleanup_test_files! = |{}|
331+
Stdout.line!("\nCleaning up test files...")?
332+
333+
test_files = [
334+
"test_path_bytes.txt",
335+
"test_path_utf8.txt",
336+
"test_path_json.json",
337+
"test_path_original.txt",
338+
"test_path_hardlink.txt"
339+
]
340+
341+
# Show files before cleanup
342+
ls_before_cleanup = Cmd.new("ls") |> Cmd.args(["-la"] |> List.concat(test_files)) |> Cmd.output!()
343+
344+
if ls_before_cleanup.status? == 0 then
345+
cleanup_stdout = Str.from_utf8(ls_before_cleanup.stdout) ? |_| CleanupInvalidUtf8
346+
347+
Stdout.line!(
348+
"""
349+
Files to clean up:
350+
${cleanup_stdout}
351+
"""
352+
)?
353+
354+
List.for_each_try!(test_files, |filename|
355+
Path.delete!(Path.from_str(filename))
356+
)?
357+
358+
# Verify cleanup
359+
ls_after_cleanup = Cmd.new("ls") |> Cmd.args(test_files) |> Cmd.output!()
360+
361+
Stdout.line!(
362+
"""
363+
Files remaining after cleanup: ${Inspect.to_str(ls_after_cleanup.status? == 0)}
364+
Deleted all test files.
365+
"""
366+
)
367+
else
368+
Stderr.line!("Error listing files before cleanup: `ls -la ...` exited with non-zero exit code:\n\t${Inspect.to_str(ls_before_cleanup)}")

0 commit comments

Comments
 (0)