diff --git a/src/file/list_dir.odin b/src/file/list_dir.odin index 7b96dde..996cfb5 100644 --- a/src/file/list_dir.odin +++ b/src/file/list_dir.odin @@ -4,6 +4,7 @@ import "base:runtime" import "core:fmt" import "core:os" import "core:path/filepath" +import "core:sort" import "core:strings" // ----------------------------------------- @@ -98,12 +99,14 @@ list_dir_queue :: proc( path := pop(queue) defer delete(path, queue^.allocator) - f := os.open(path, {.Read}) or_return - defer os.close(f) - - entries := os.read_all_directory(f, allocator) or_return + entries := os.read_all_directory_by_path(path, allocator) or_return defer os.file_info_slice_delete(entries, allocator) + // Improve determinism, as entries are explicitly returned unsorted by os.read_dir + sort.quick_sort_proc(entries, proc(a, b: os.File_Info) -> int { + return strings.compare(a.name, b.name) + }) + // Reserve space for new entries, minor waste as directories are also counted. reserve(result, len(result) + len(entries)) diff --git a/test.sh b/test.sh index e4c2dd7..2cfadd0 100755 --- a/test.sh +++ b/test.sh @@ -3,7 +3,7 @@ mkdir -p build odin test tests/ -collection:src=src -out:build/tests.bin \ - -define:ODIN_TEST_THREADS=1 \ + -define:ODIN_TEST_THREADS=0 \ -define:ODIN_TEST_TRACK_MEMORY=false \ -define:ODIN_TEST_RANDOM_SEED=1 \ "$@" diff --git a/tests/blabla.odin b/tests/blabla.odin deleted file mode 100644 index cbe2068..0000000 --- a/tests/blabla.odin +++ /dev/null @@ -1,25 +0,0 @@ -package tests - -import "core:os" -import "core:testing" - -import "src:file" - -@(test) -single_tiny_file :: proc(t: ^testing.T) { - // Arrange - - create_file(t, "single_tiny_file", "hello world!") - - // Act - - wd, err := os.get_working_directory(context.allocator) - entries, err2 := file.list_dir_recursive_by_path(TEST_DIR, wd) - pack(t, 750) - delete_path(t, "") - read := read(t, "single_tiny_file") - - // Assert - - testing.expect_value(t, read, "hello world!") -} diff --git a/tests/helpers.odin b/tests/helpers.odin index e45ad55..44724f7 100644 --- a/tests/helpers.odin +++ b/tests/helpers.odin @@ -1,23 +1,78 @@ package tests +import "core:fmt" import "core:os" import "core:testing" import "src:chunks" import "src:file" -TEST_DIR :: "./build/tests" -TEST_OUTPUT_DIR :: "./build/out" +TEST_INPUT_DIR :: "./build/tests/in" +TEST_OUTPUT_DIR :: "./build/tests/out" // ----------------------------------------- +Test_State :: struct { + input_dir: string, + output_dir: string, +} + +// ----------------------------------------- + +test_state_init :: proc( + t: ^testing.T, + allocator := context.allocator, +) -> Test_State { + ensure_dir(t, TEST_INPUT_DIR) + ensure_dir(t, TEST_OUTPUT_DIR) + + temp_input_path, err := os.mkdir_temp(TEST_INPUT_DIR, "", allocator) + testing.expect(t, err == nil, os.error_string(err)) + + temp_output_path, err2 := os.mkdir_temp(TEST_OUTPUT_DIR, "", allocator) + testing.expect(t, err2 == nil, os.error_string(err2)) + + return Test_State { + input_dir = temp_input_path, + output_dir = temp_output_path, + } +} + +test_state_destroy :: proc( + ts: ^Test_State, + t: ^testing.T, + allocator := context.allocator, +) { + if os.exists(ts.input_dir) { + err := os.remove_all(ts.input_dir) + testing.expect(t, err == nil, os.error_string(err)) + } + delete(ts.input_dir, allocator) + + if os.exists(ts.output_dir) { + err := os.remove_all(ts.output_dir) + testing.expect(t, err == nil, os.error_string(err)) + } + delete(ts.output_dir, allocator) +} + +// ----------------------------------------- + +ensure_dir :: proc(t: ^testing.T, dir: string) { + if !os.exists(dir) { + err := os.make_directory_all(dir) + testing.expect(t, err == nil, os.error_string(err)) + } +} + create_file :: proc( + ts: ^Test_State, t: ^testing.T, path: string, content: string, allocator := context.allocator, ) { - relpath, err := os.join_path({TEST_DIR, path}, allocator) + relpath, err := os.join_path({ts.input_dir, path}, allocator) testing.expect(t, err == nil, os.error_string(err)) defer delete(relpath, allocator) @@ -36,36 +91,39 @@ create_file :: proc( } delete_path :: proc( + ts: ^Test_State, t: ^testing.T, path: string, allocator := context.allocator, ) { - relpath, err := os.join_path({TEST_DIR, path}, allocator) + relpath, err := os.join_path({ts.input_dir, path}, allocator) defer delete(relpath, allocator) testing.expect(t, err == nil, os.error_string(err)) - err2 := os.remove_all(relpath) - testing.expect(t, err2 == nil, os.error_string(err2)) + if os.exists(relpath) { + err2 := os.remove_all(relpath) + testing.expect(t, err2 == nil, os.error_string(err2)) + } } -pack :: proc(t: ^testing.T, size: u64) { +pack :: proc(ts: ^Test_State, t: ^testing.T, size: u64) { wd, err := os.get_working_directory(context.allocator) testing.expect(t, err == nil, os.error_string(err)) - entries, err2 := file.list_dir_recursive_by_path(TEST_DIR, wd) + entries, err2 := file.list_dir_recursive_by_path(ts.input_dir, wd) testing.expect(t, err2 == nil, file.format_error(err2)) w := chunks.writer_init(cast(u64)len(entries)) defer chunks.writer_destroy(&w) - if os.exists(TEST_OUTPUT_DIR) { - err3 := os.remove_all(TEST_OUTPUT_DIR) + if os.exists(ts.output_dir) { + err3 := os.remove_all(ts.output_dir) testing.expect(t, err3 == nil, os.error_string(err3)) } - err4 := os.make_directory_all(TEST_OUTPUT_DIR) // fails if dir exists + err4 := os.make_directory_all(ts.output_dir) // fails if dir exists testing.expect(t, err4 == nil, os.error_string(err4)) - output_file, err5 := os.open(TEST_OUTPUT_DIR, {.Read}) + output_file, err5 := os.open(ts.output_dir, {.Read}) testing.expect(t, err5 == nil, os.error_string(err5)) defer os.close(output_file) @@ -77,15 +135,16 @@ pack :: proc(t: ^testing.T, size: u64) { } read :: proc( + ts: ^Test_State, t: ^testing.T, path: string, allocator := context.allocator, ) -> string { - relpath, err := os.join_path({TEST_DIR, path}, allocator) + relpath, err := os.join_path({ts.input_dir, path}, allocator) testing.expect(t, err == nil, os.error_string(err)) defer delete(relpath, allocator) - file, err2 := os.open(TEST_OUTPUT_DIR) + file, err2 := os.open(ts.output_dir) testing.expect(t, err2 == nil, os.error_string(err2)) defer os.close(file) diff --git a/tests/tests.odin b/tests/tests.odin new file mode 100644 index 0000000..64e5de3 --- /dev/null +++ b/tests/tests.odin @@ -0,0 +1,118 @@ +package tests + +import "core:testing" + +@(test) +single_tiny_file_read_from_chunk :: proc(t: ^testing.T) { + // Arrange + + ts := test_state_init(t) + defer test_state_destroy(&ts, t) + + delete_path(&ts, t, "") + create_file(&ts, t, "single_tiny_file", "hello world!") + pack(&ts, t, 750) + delete_path(&ts, t, "") + + // Act + + read := read(&ts, t, "single_tiny_file") + + // Assert + + testing.expect_value(t, read, "hello world!") +} + +@(test) +single_file_single_chunk_read_from_path :: proc(t: ^testing.T) { + // Arrange + + ts := test_state_init(t) + defer test_state_destroy(&ts, t) + + delete_path(&ts, t, "") + create_file(&ts, t, "single_tiny_file", "hello world!") + pack(&ts, t, 750) + delete_path(&ts, t, "") + create_file(&ts, t, "single_tiny_file", "hello world from path!") + + // Act + + read := read(&ts, t, "single_tiny_file") + + // Assert + + testing.expect_value(t, read, "hello world from path!") +} + +@(test) +multiple_files_single_chunk_read_from_chunk :: proc(t: ^testing.T) { + // Arrange + + ts := test_state_init(t) + defer test_state_destroy(&ts, t) + + delete_path(&ts, t, "") + create_file(&ts, t, "a", "hello a!") + create_file(&ts, t, "b", "hello b!") + create_file(&ts, t, "c", "hello c!") + pack(&ts, t, 1750) + delete_path(&ts, t, "") + + // Act + + a := read(&ts, t, "a") + b := read(&ts, t, "b") + c := read(&ts, t, "c") + + // Assert + + testing.expect_value(t, a, "hello a!") + testing.expect_value(t, b, "hello b!") + testing.expect_value(t, c, "hello c!") +} + +@(test) +multiple_files_single_chunk_read_from_path :: proc(t: ^testing.T) { + // Arrange + + ts := test_state_init(t) + defer test_state_destroy(&ts, t) + + delete_path(&ts, t, "") + create_file(&ts, t, "a", "hello a!") + create_file(&ts, t, "b", "hello b!") + create_file(&ts, t, "c", "hello c!") + pack(&ts, t, 1750) + delete_path(&ts, t, "") + create_file(&ts, t, "a", "hello a from path!") + create_file(&ts, t, "b", "hello b from path!") + create_file(&ts, t, "c", "hello c from path!") + + // Act + + a := read(&ts, t, "a") + b := read(&ts, t, "b") + c := read(&ts, t, "c") + + // Assert + + testing.expect_value(t, a, "hello a from path!") + testing.expect_value(t, b, "hello b from path!") + testing.expect_value(t, c, "hello c from path!") +} + +// @(test) +// blabla :: proc(t: ^testing.T) { +// // Arrange +// +// // Act +// +// // Assert +// } + +// TODO: +// v multiple files (path and chunk find) +// - nested file (path and chunk find) +// - not found in path +// - not found in path + chunk