diff --git a/TODOS.md b/TODOS.md index d126285..da3991d 100644 --- a/TODOS.md +++ b/TODOS.md @@ -1,6 +1,6 @@ # TODOs -1. Commands are still leaking. (Do 15. first) +1. Commands are still leaking. (Do 13. first) 2. Add color flag and support non colored output. @@ -8,31 +8,29 @@ 4. Generate md and man pages again. -6. Consistently ignore allocator errors +5. Consistently ignore allocator errors -7. Check for prealloc opportunities. i.e. `make([dynamic]string)` -> `make([dynamic]string, 5)`. +6. Check for prealloc opportunities. i.e. `make([dynamic]string)` -> `make([dynamic]string, 5)`. -8. Add a text filter to the multi_select. +7. Add a text filter to the multi_select. -9. Add tests for untested commands. +8. Add tests for untested commands. -10. add --format -f flag to commands that draw tables. +9. add --format -f flag to commands that draw tables. -11. Replace `testing.expect` calls with `testing.expect_value` calls where appropriate. +10. procedures should be ordered by use, main at the top, then in the order they are called from main. -12. procedures should be ordered by use, main at the top, then in the order they are called from main. +11. Shell completion -13. Shell completion +12. Bring back windows support / cross-compilation. -14. Bring back windows support / cross-compilation. +13. Test all cmds / terminal branches. -15. Test all cmds / terminal branches. +14. Fix error messages to use fmt.eprintf (stderr) instead of fmt.printf (stdout) -16. Fix error messages to use fmt.eprintf (stderr) instead of fmt.printf (stdout) +15. Pass allocator to findr? -17. Pass allocator to findr? - -18. Update `read_wire_string` to use a slice. +16. Update `read_wire_string` to use a slice. ## Double-check AI output diff --git a/cli_test.odin b/cli_test.odin index 37a2dd4..56c2e58 100644 --- a/cli_test.odin +++ b/cli_test.odin @@ -123,7 +123,7 @@ test_command_help_unknown :: proc(t: ^testing.T) { testing.expect(t, !ok, "write_command_help(\"nonexistent\") should return false") text := strings.to_string(b) - testing.expect(t, len(text) == 0, "text should be empty for unknown command") + testing.expect_value(t, len(text), 0) } @(test) @@ -236,9 +236,9 @@ test_parse_args_positional :: proc(t: ^testing.T) { defer delete_command(&cmd) testing.expect(t, ok, "should succeed") - testing.expect(t, cmd.name == "backup") - testing.expect(t, len(cmd.args) == 1) - testing.expect(t, cmd.args[0] == "/project/.env") + testing.expect_value(t, cmd.name, "backup") + testing.expect_value(t, len(cmd.args), 1) + testing.expect_value(t, cmd.args[0], "/project/.env") } @(test) @@ -248,7 +248,7 @@ test_parse_args_long_flag_with_value :: proc(t: ^testing.T) { if !ok do return defer delete_command(&cmd) - testing.expect(t, cmd.flags["config"] == "x.json") + testing.expect_value(t, cmd.flags["config"], "x.json") } @(test) @@ -258,7 +258,7 @@ test_parse_args_short_flag_with_value :: proc(t: ^testing.T) { if !ok do return defer delete_command(&cmd) - testing.expect(t, cmd.flags["c"] == "x.json") + testing.expect_value(t, cmd.flags["c"], "x.json") } @(test) @@ -268,7 +268,7 @@ test_parse_args_long_bool_flag :: proc(t: ^testing.T) { if !ok do return defer delete_command(&cmd) - testing.expect(t, cmd.bool_set["force"] == true) + testing.expect_value(t, cmd.bool_set["force"], true) } @(test) @@ -278,7 +278,7 @@ test_parse_args_short_bool_flag :: proc(t: ^testing.T) { if !ok do return defer delete_command(&cmd) - testing.expect(t, cmd.bool_set["l"] == true) + testing.expect_value(t, cmd.bool_set["l"], true) } @(test) @@ -288,9 +288,9 @@ test_parse_args_multiple_positionals :: proc(t: ^testing.T) { if !ok do return defer delete_command(&cmd) - testing.expect(t, len(cmd.args) == 2) - testing.expect(t, cmd.args[0] == "a") - testing.expect(t, cmd.args[1] == "b") + testing.expect_value(t, len(cmd.args), 2) + testing.expect_value(t, cmd.args[0], "a") + testing.expect_value(t, cmd.args[1], "b") } @(test) @@ -300,9 +300,9 @@ test_parse_args_mixed_flags_and_positionals :: proc(t: ^testing.T) { if !ok do return defer delete_command(&cmd) - testing.expect(t, cmd.bool_set["force"] == true) - testing.expect(t, len(cmd.args) == 1) - testing.expect(t, cmd.args[0] == "/project/.env") + testing.expect_value(t, cmd.bool_set["force"], true) + testing.expect_value(t, len(cmd.args), 1) + testing.expect_value(t, cmd.args[0], "/project/.env") } @(test) @@ -318,10 +318,10 @@ test_parse_args_flag_then_positional_then_flag :: proc(t: ^testing.T) { defer delete_command(&cmd) testing.expect(t, ok, "should succeed") - testing.expect(t, cmd.bool_set["force"] == true) - testing.expect(t, cmd.bool_set["verbose"] == true) - testing.expect(t, len(cmd.args) == 1) - testing.expect(t, cmd.args[0] == "a.env") + testing.expect_value(t, cmd.bool_set["force"], true) + testing.expect_value(t, cmd.bool_set["verbose"], true) + testing.expect_value(t, len(cmd.args), 1) + testing.expect_value(t, cmd.args[0], "a.env") } @(test) @@ -333,11 +333,7 @@ test_parse_args_config_file_long_flag :: proc(t: ^testing.T) { if !ok do return defer delete_command(&cmd) - testing.expect( - t, - cmd.config_path == "/custom/config.json", - "config_path should be set from --config-file", - ) + testing.expect_value(t, cmd.config_path, "/custom/config.json") } @(test) @@ -347,11 +343,7 @@ test_parse_args_config_file_short_flag :: proc(t: ^testing.T) { if !ok do return defer delete_command(&cmd) - testing.expect( - t, - cmd.config_path == "/custom/config.json", - "config_path should be set from -c", - ) + testing.expect_value(t, cmd.config_path, "/custom/config.json") } @(test) diff --git a/cmd_check_test.odin b/cmd_check_test.odin index cbe16aa..f25e7c7 100644 --- a/cmd_check_test.odin +++ b/cmd_check_test.odin @@ -1,7 +1,6 @@ #+test package main -import "core:fmt" import "core:testing" @(test) @@ -10,13 +9,9 @@ test_find_unbacked_finds_missing :: proc(t: ^testing.T) { db := []EnvFile{{path = "/a/.env"}, {path = "/b/.env"}} result := find_unbacked(local, db[:]) - testing.expect(t, len(result) == 1, fmt.tprintf("expected 1 unbacked, got %d", len(result))) + testing.expect_value(t, len(result), 1) if len(result) > 0 { - testing.expect( - t, - result[0] == "/c/.env", - fmt.tprintf("expected /c/.env, got %s", result[0]), - ) + testing.expect_value(t, result[0], "/c/.env") } } @@ -26,7 +21,7 @@ test_find_unbacked_all_backed :: proc(t: ^testing.T) { db := []EnvFile{{path = "/a/.env"}, {path = "/b/.env"}} result := find_unbacked(local, db[:]) - testing.expect(t, len(result) == 0, fmt.tprintf("expected 0 unbacked, got %d", len(result))) + testing.expect_value(t, len(result), 0) } @(test) @@ -35,7 +30,7 @@ test_find_unbacked_no_local :: proc(t: ^testing.T) { db := []EnvFile{{path = "/a/.env"}} result := find_unbacked(local, db[:]) - testing.expect(t, len(result) == 0, fmt.tprintf("expected 0 unbacked, got %d", len(result))) + testing.expect_value(t, len(result), 0) } @(test) @@ -44,6 +39,6 @@ test_find_unbacked_none_backed :: proc(t: ^testing.T) { db: []EnvFile result := find_unbacked(local, db[:]) - testing.expect(t, len(result) == 2, fmt.tprintf("expected 2 unbacked, got %d", len(result))) + testing.expect_value(t, len(result), 2) } diff --git a/cmd_list_test.odin b/cmd_list_test.odin index f9fff31..ee453e2 100644 --- a/cmd_list_test.odin +++ b/cmd_list_test.odin @@ -11,9 +11,9 @@ test_filepath_base_equals_rel :: proc(t: ^testing.T) { for path in cases { dir := filepath.dir(path) rel, rel_err := filepath.rel(dir, path, context.temp_allocator) - testing.expect(t, rel_err == nil, "filepath.rel returned an error") + testing.expect_value(t, rel_err, nil) base := filepath.base(path) - testing.expect(t, rel == base, "filepath.rel(dir, path) should equal filepath.base(path)") + testing.expect_value(t, rel, base) } } diff --git a/config_test.odin b/config_test.odin index 3eae639..c48cf9f 100644 --- a/config_test.odin +++ b/config_test.odin @@ -16,13 +16,9 @@ test_new_config_single_key :: proc(t: ^testing.T) { cfg := new_config(paths) defer delete_config(&cfg) - testing.expect(t, len(cfg.keys) == 1, "should have 1 key") - testing.expect(t, cfg.keys[0].private == "/home/user/.ssh/id_ed25519", "Private path mismatch") - testing.expect( - t, - cfg.keys[0].public == "/home/user/.ssh/id_ed25519.pub", - "Public path mismatch", - ) + testing.expect_value(t, len(cfg.keys), 1) + testing.expect_value(t, cfg.keys[0].private, "/home/user/.ssh/id_ed25519") + testing.expect_value(t, cfg.keys[0].public, "/home/user/.ssh/id_ed25519.pub") } @(test) @@ -31,9 +27,9 @@ test_new_config_multiple_keys :: proc(t: ^testing.T) { cfg := new_config(paths) defer delete_config(&cfg) - testing.expect(t, len(cfg.keys) == 2, "should have 2 keys") - testing.expect(t, cfg.keys[0].private == "/home/user/.ssh/id_ed25519") - testing.expect(t, cfg.keys[1].private == "/home/user/.ssh/id_rsa") + testing.expect_value(t, len(cfg.keys), 2) + testing.expect_value(t, cfg.keys[0].private, "/home/user/.ssh/id_ed25519") + testing.expect_value(t, cfg.keys[1].private, "/home/user/.ssh/id_rsa") } @(test) @@ -42,7 +38,7 @@ test_new_config_empty_keys :: proc(t: ^testing.T) { cfg := new_config(paths) defer delete_config(&cfg) - testing.expect(t, len(cfg.keys) == 0, "should have 0 keys") + testing.expect_value(t, len(cfg.keys), 0) } @(test) @@ -51,10 +47,10 @@ test_new_config_scan_defaults :: proc(t: ^testing.T) { cfg := new_config(paths) defer delete_config(&cfg) - testing.expect(t, cfg.scan_config.matcher == "\\.env", "matcher should be \\.env") - testing.expect(t, len(cfg.scan_config.exclude) == 4, "should have 4 exclude patterns") - testing.expect(t, len(cfg.scan_config.include) == 1, "should have 1 include path") - testing.expect(t, cfg.scan_config.include[0] == "~", "include should be ~") + testing.expect_value(t, cfg.scan_config.matcher, "\\.env") + testing.expect_value(t, len(cfg.scan_config.exclude), 4) + testing.expect_value(t, len(cfg.scan_config.include), 1) + testing.expect_value(t, cfg.scan_config.include[0], "~") } @(test) @@ -65,7 +61,7 @@ test_new_config_exclude_patterns :: proc(t: ^testing.T) { expected := []string{"*\\.envrc", "\\.local/", "node_modules", "vendor"} for i in 0 ..< len(expected) { - testing.expect(t, cfg.scan_config.exclude[i] == expected[i]) + testing.expect_value(t, cfg.scan_config.exclude[i], expected[i]) } } @@ -75,7 +71,7 @@ test_save_load_config_roundtrip :: proc(t: ^testing.T) { defer os.remove_all(base) cfgPath, err := filepath.join([]string{base, "config.json"}, context.temp_allocator) - testing.expect(t, err == nil, "cfgPath should build successfully") + testing.expect_value(t, err, nil) cfg := new_config([]string{"/home/user/.ssh/id_ed25519"}, cfgPath) defer delete_config(&cfg) @@ -87,13 +83,13 @@ test_save_load_config_roundtrip :: proc(t: ^testing.T) { if !ok do return defer delete_config(&loaded) - testing.expect(t, len(loaded.keys) == 1, "should have 1 key") - testing.expect(t, loaded.keys[0].private == "/home/user/.ssh/id_ed25519") - testing.expect(t, loaded.keys[0].public == "/home/user/.ssh/id_ed25519.pub") - testing.expect(t, loaded.scan_config.matcher == "\\.env") - testing.expect(t, len(loaded.scan_config.exclude) == 4) - testing.expect(t, len(loaded.scan_config.include) == 1) - testing.expect(t, loaded.scan_config.include[0] == "~") + testing.expect_value(t, len(loaded.keys), 1) + testing.expect_value(t, loaded.keys[0].private, "/home/user/.ssh/id_ed25519") + testing.expect_value(t, loaded.keys[0].public, "/home/user/.ssh/id_ed25519.pub") + testing.expect_value(t, loaded.scan_config.matcher, "\\.env") + testing.expect_value(t, len(loaded.scan_config.exclude), 4) + testing.expect_value(t, len(loaded.scan_config.include), 1) + testing.expect_value(t, loaded.scan_config.include[0], "~") } @(test) @@ -108,7 +104,7 @@ test_save_config_no_clobber :: proc(t: ^testing.T) { defer os.remove_all(base) cfgPath, err := filepath.join([]string{base, "config.json"}, context.temp_allocator) - testing.expect(t, err == nil, "cfgPath should build successfully") + testing.expect_value(t, err, nil) cfg := new_config([]string{"/home/user/.ssh/key1"}, cfgPath) defer delete_config(&cfg) @@ -125,7 +121,7 @@ test_save_config_force_overwrites :: proc(t: ^testing.T) { defer os.remove_all(base) cfgPath, err := filepath.join([]string{base, "config.json"}, context.temp_allocator) - testing.expect(t, err == nil, "cfgPath should build successfully") + testing.expect_value(t, err, nil) cfg := new_config([]string{"/home/user/.ssh/key1"}, cfgPath) defer delete_config(&cfg) @@ -140,12 +136,8 @@ test_save_config_force_overwrites :: proc(t: ^testing.T) { if !ok do return defer delete_config(&loaded) - testing.expect(t, len(loaded.keys) == 1, "should have 1 key") - testing.expect( - t, - loaded.keys[0].private == "/home/user/.ssh/key2", - "should be the overwritten key", - ) + testing.expect_value(t, len(loaded.keys), 1) + testing.expect_value(t, loaded.keys[0].private, "/home/user/.ssh/key2") } @(test) @@ -190,7 +182,7 @@ test_search_paths_expands_tilde :: proc(t: ^testing.T) { paths := search_paths(cfg, context.temp_allocator) - testing.expect(t, len(paths) == 1, "should have 1 path") + testing.expect_value(t, len(paths), 1) if len(paths) > 0 { testing.expectf( t, diff --git a/crypto_test.odin b/crypto_test.odin index 7faf2ae..d6c7404 100644 --- a/crypto_test.odin +++ b/crypto_test.odin @@ -27,13 +27,10 @@ test_encrypt_decrypt_roundtrip :: proc(t: ^testing.T) { testing.expect(t, dec_ok, "decryption should succeed") defer delete(decrypted) - testing.expect( - t, - len(decrypted) == len(original), - fmt.tprintf("expected %d bytes, got %d", len(original), len(decrypted)), - ) + testing.expect_value(t, len(decrypted), len(original)) + // TODO: Should this be a loop? for i in 0 ..< len(original) { - testing.expect(t, decrypted[i] == original[i], fmt.tprintf("byte mismatch at index %d", i)) + testing.expect_value(t, decrypted[i], original[i]) } } @@ -56,16 +53,8 @@ test_encrypt_decrypt_multi_recipient :: proc(t: ^testing.T) { defer delete(decrypted2) for i in 0 ..< len(original) { - testing.expect( - t, - decrypted1[i] == original[i], - fmt.tprintf("key1: byte mismatch at %d", i), - ) - testing.expect( - t, - decrypted2[i] == original[i], - fmt.tprintf("key2: byte mismatch at %d", i), - ) + testing.expect_value(t, decrypted1[i], original[i]) + testing.expect_value(t, decrypted2[i], original[i]) } } @@ -96,7 +85,7 @@ test_encrypt_empty_plaintext :: proc(t: ^testing.T) { testing.expect(t, dec_ok, "decryption should succeed") defer delete(decrypted) - testing.expect(t, len(decrypted) == 0, "decrypted empty data should be empty") + testing.expect_value(t, len(decrypted), 0) } @(test) @@ -113,8 +102,9 @@ test_recipient_can_decrypt_senders_data :: proc(t: ^testing.T) { testing.expect(t, dec_ok, "second recipient should decrypt without the sender key present") defer delete(decrypted) + // TODO: Should this be a loop? for i in 0 ..< len(original) { - testing.expect(t, decrypted[i] == original[i], fmt.tprintf("byte mismatch at %d", i)) + testing.expect_value(t, decrypted[i], original[i]) } } @@ -128,9 +118,9 @@ test_ciphertext_has_magic :: proc(t: ^testing.T) { defer delete(encrypted) testing.expect(t, len(encrypted) >= 4, "ciphertext should have at least 4 bytes") - testing.expect(t, encrypted[0] == u8('E'), "magic byte 0") - testing.expect(t, encrypted[1] == u8('N'), "magic byte 1") - testing.expect(t, encrypted[2] == u8('V'), "magic byte 2") - testing.expect(t, encrypted[3] == u8('R'), "magic byte 3") + testing.expect_value(t, encrypted[0], u8('E')) + testing.expect_value(t, encrypted[1], u8('N')) + testing.expect_value(t, encrypted[2], u8('V')) + testing.expect_value(t, encrypted[3], u8('R')) } diff --git a/db_integration_test.odin b/db_integration_test.odin index 64aeacd..a5cadf0 100644 --- a/db_integration_test.odin +++ b/db_integration_test.odin @@ -67,10 +67,10 @@ test_encrypt_decrypt_sqlite_roundtrip :: proc(t: ^testing.T) { defer delete(encrypted) testing.expect(t, len(encrypted) >= HEADER_SIZE, "ciphertext should have header") - testing.expect(t, encrypted[0] == u8('E'), "magic byte 0") - testing.expect(t, encrypted[1] == u8('N'), "magic byte 1") - testing.expect(t, encrypted[2] == u8('V'), "magic byte 2") - testing.expect(t, encrypted[3] == u8('R'), "magic byte 3") + testing.expect_value(t, encrypted[0], u8('E')) + testing.expect_value(t, encrypted[1], u8('N')) + testing.expect_value(t, encrypted[2], u8('V')) + testing.expect_value(t, encrypted[3], u8('R')) plaintext, dec_ok := decrypt(encrypted, cfg.keys[:]) testing.expect(t, dec_ok, "decryption should succeed") @@ -142,7 +142,7 @@ test_encrypt_write_read_decrypt :: proc(t: ^testing.T) { } defer delete(plaintext) - testing.expect(t, len(plaintext) == len(sqlite_data), "size mismatch after file round-trip") + testing.expect_value(t, len(plaintext), len(sqlite_data)) } @(test) @@ -189,7 +189,7 @@ test_decrypt_then_deserialize_sqlite :: proc(t: ^testing.T) { copy(buf[:len(plaintext)], plaintext) rc = sqlite.deserialize(mem_db, "main", buf, n, n, {.FREEONCLOSE, .RESIZEABLE}) - testing.expect(t, rc == sqlite.OK, "deserialize should succeed") + testing.expect_value(t, rc, sqlite.OK) if rc != sqlite.OK { sqlite.free(buf) return @@ -198,14 +198,14 @@ test_decrypt_then_deserialize_sqlite :: proc(t: ^testing.T) { sql: cstring = "SELECT path FROM envr_env_files" stmt: sqlite.Stmt rc = sqlite.prepare_v2(mem_db, sql, -1, &stmt, nil) - testing.expect(t, rc == sqlite.OK, "prepare failed") + testing.expect_value(t, rc, sqlite.OK) if rc != sqlite.OK { return } defer sqlite.finalize(stmt) rc = sqlite.step(stmt) - testing.expect(t, rc == sqlite.ROW, "expected at least one row") + testing.expect_value(t, rc, sqlite.ROW) if rc == sqlite.ROW { path := string(sqlite.column_text(stmt, 0)) testing.expect(t, len(path) > 0, "path should not be empty") @@ -275,15 +275,7 @@ test_full_db_cycle :: proc(t: ^testing.T) { } defer delete(plaintext2) - testing.expect( - t, - len(plaintext2) == len(original_data), - fmt.tprintf( - "double round-trip size mismatch: expected %d, got %d", - len(original_data), - len(plaintext2), - ), - ) + testing.expect_value(t, len(plaintext2), len(original_data)) os.remove(data_path) os.remove(envr_dir_path) @@ -317,7 +309,7 @@ test_ssh_key_parse_from_fixtures :: proc(t: ^testing.T) { return } - testing.expect(t, len(x25519_pairs) == 1, "should have 1 x25519 keypair") + testing.expect_value(t, len(x25519_pairs), 1) } @(test) @@ -327,7 +319,7 @@ test_config_load_with_fixture_key :: proc(t: ^testing.T) { delete(cfg.keys) } - testing.expect(t, len(cfg.keys) == 1, "should have 1 key") + testing.expect_value(t, len(cfg.keys), 1) key := cfg.keys[0] diff --git a/db_test.odin b/db_test.odin index 682b509..7807a4b 100644 --- a/db_test.odin +++ b/db_test.odin @@ -81,7 +81,7 @@ test_db_insert_or_replace :: proc(t: ^testing.T) { results, list_ok := db_list(&db) testing.expect(t, list_ok, "list should succeed") - testing.expect(t, len(results) == 1, "should have 1 row, not 2") + testing.expect_value(t, len(results), 1) fetched, fetch_ok := db_fetch(&db, "/project/.env") testing.expect(t, fetch_ok, "fetch should succeed") @@ -149,7 +149,7 @@ test_db_list_empty :: proc(t: ^testing.T) { results, list_ok := db_list(&db) testing.expect(t, list_ok, "list should succeed on empty db") - testing.expect(t, len(results) == 0, "should have 0 rows") + testing.expect_value(t, len(results), 0) } @(test) @@ -276,11 +276,11 @@ test_get_git_remotes_single :: proc(t: ^testing.T) { config_content := "[core]\n\trepositoryformatversion = 0\n[remote \"origin\"]\n\turl = git@github.com:user/repo.git\n\tfetch = +refs/heads/*:refs/remotes/origin/*\n" config_path := fmt.tprintf("%s/config", git_dir) err := os.write_entire_file(config_path, transmute([]u8)config_content) - testing.expect(t, err == nil, "should write .git/config") + testing.expect_value(t, err, nil) remotes := get_git_remotes(base, context.temp_allocator) - testing.expect(t, len(remotes) == 1, "should find 1 remote") + testing.expect_value(t, len(remotes), 1) if len(remotes) != 1 do return testing.expect_value(t, remotes[0], "git@github.com:user/repo.git") } @@ -296,11 +296,11 @@ test_get_git_remotes_multiple :: proc(t: ^testing.T) { config_content := "[remote \"origin\"]\n\turl = git@github.com:user/repo.git\n[remote \"upstream\"]\n\turl = https://gitlab.com/upstream/repo.git\n" config_path := fmt.tprintf("%s/config", git_dir) err := os.write_entire_file(config_path, transmute([]u8)config_content) - testing.expect(t, err == nil, "should write .git/config") + testing.expect_value(t, err, nil) remotes := get_git_remotes(base, context.temp_allocator) - testing.expect(t, len(remotes) == 2, "should find 2 remotes") + testing.expect_value(t, len(remotes), 2) } @(test) @@ -310,7 +310,7 @@ test_get_git_remotes_no_config :: proc(t: ^testing.T) { remotes := get_git_remotes(base, context.temp_allocator) - testing.expect(t, len(remotes) == 0, "should return empty when no .git/config") + testing.expect_value(t, len(remotes), 0) } @(test) @@ -324,11 +324,11 @@ test_get_git_remotes_no_remotes :: proc(t: ^testing.T) { config_content := "[core]\n\trepositoryformatversion = 0\n\tbare = false\n" config_path := fmt.tprintf("%s/config", git_dir) err := os.write_entire_file(config_path, transmute([]u8)config_content) - testing.expect(t, err == nil, "should write .git/config") + testing.expect_value(t, err, nil) remotes := get_git_remotes(base, context.temp_allocator) - testing.expect(t, len(remotes) == 0, "should return empty when no remote sections") + testing.expect_value(t, len(remotes), 0) } @(test) @@ -338,7 +338,7 @@ test_new_env_file :: proc(t: ^testing.T) { env_path := fmt.tprintf("%s/.env", base) err := os.write_entire_file(env_path, "SECRET=value\n") - testing.expect(t, err == nil, ".env file should exists") + testing.expect_value(t, err, nil) file, ok := new_env_file(env_path) testing.expect(t, ok, "new_env_file should succeed") @@ -350,8 +350,8 @@ test_new_env_file :: proc(t: ^testing.T) { testing.expect(t, filepath.is_abs(file.path), "path should be absolute") testing.expect(t, strings.has_suffix(file.path, "/.env"), "path should end with /.env") - testing.expect(t, file.contents == "SECRET=value\n", "contents mismatch") - testing.expect(t, len(file.sha256) == 64, "sha256 should be 64 hex chars") + testing.expect_value(t, file.contents, "SECRET=value\n") + testing.expect_value(t, len(file.sha256), 64) } @(test) @@ -366,7 +366,7 @@ test_closing_db_has_no_leaks :: proc(t: ^testing.T) { defer os.remove_all(base) cfg_path, err := filepath.join([]string{base, "config.json"}, context.temp_allocator) - testing.expect(t, err == nil, "cfgPath should build successfully") + testing.expect_value(t, err, nil) { cfg := new_config([]string{"fixtures/keys/insecure-test-key"}, cfg_path) @@ -385,7 +385,7 @@ test_open_existing_db_has_no_leaks :: proc(t: ^testing.T) { defer os.remove_all(base) cfg_path, err := filepath.join([]string{base, "config.json"}, context.temp_allocator) - testing.expect(t, err == nil, "cfgPath should build successfully") + testing.expect_value(t, err, nil) { cfg := new_config([]string{"fixtures/keys/insecure-test-key"}, cfg_path) @@ -422,7 +422,7 @@ test_db_sync_noop :: proc(t: ^testing.T) { env_path := fmt.tprintf("%s/.env", base) content := "KEY=value\n" write_err := os.write_entire_file(env_path, transmute([]u8)content) - testing.expect(t, write_err == nil, "should write .env file") + testing.expect_value(t, write_err, nil) digest := hash.hash_bytes( hash.Algorithm.SHA256, @@ -441,8 +441,8 @@ test_db_sync_noop :: proc(t: ^testing.T) { db_insert(&db, f) result, sync_err := db_sync(&db, &f) - testing.expect(t, sync_err == .None, "sync should not error") - testing.expect(t, result == {}, "should be noop") + testing.expect_value(t, sync_err, SyncError.None) + testing.expect_value(t, result, nil) } @(test) @@ -453,7 +453,7 @@ test_db_sync_backed_up :: proc(t: ^testing.T) { env_path := fmt.tprintf("%s/.env", base) changed_content := "KEY=changed\n" write_err := os.write_entire_file(env_path, transmute([]u8)changed_content) - testing.expect(t, write_err == nil, "should write .env file") + testing.expect_value(t, write_err, nil) db, ok := db_init() testing.expect(t, ok, "failed to create test db") @@ -464,7 +464,7 @@ test_db_sync_backed_up :: proc(t: ^testing.T) { db_insert(&db, f) result, sync_err := db_sync(&db, &f) - testing.expect(t, sync_err == .None, "sync should not error") + testing.expect_value(t, sync_err, SyncError.None) testing.expect(t, .BackedUp in result, "should be backed up") } @@ -485,11 +485,11 @@ test_db_sync_restored :: proc(t: ^testing.T) { db_insert(&db, f) result, err := db_sync(&db, &f) - testing.expect(t, err == .None, "sync should not error") + testing.expect_value(t, err, SyncError.None) testing.expect(t, .Restored in result, "should be restored") data, read_err := os.read_entire_file_from_path(env_path, context.temp_allocator) - testing.expect(t, read_err == nil, "file should exist after restore") + testing.expect_value(t, read_err, nil) if read_err == nil { testing.expect_value(t, string(data), "SECRET=value") } @@ -522,7 +522,7 @@ test_db_sync_moved :: proc(t: ^testing.T) { config_content := "[remote \"origin\"]\n\turl = git@github.com:user/repo.git\n" config_path := fmt.tprintf("%s/config", git_dir) write_err := os.write_entire_file(config_path, transmute([]u8)config_content) - testing.expect(t, write_err == nil, "should write .git/config") + testing.expect_value(t, write_err, nil) db, ok := db_init() testing.expect(t, ok, "failed to create test db") @@ -540,7 +540,7 @@ test_db_sync_moved :: proc(t: ^testing.T) { testing.expect(t, db_insert(&db, f), "insert should succeed") result, err := db_sync(&db, &f) - testing.expect(t, err == .None, "sync should not error") + testing.expect_value(t, err, SyncError.None) if err != .None do return testing.expect(t, .DirUpdated in result, "should have DirUpdated flag") testing.expect(t, .Restored in result, "should have Restored flag") diff --git a/scan_test.odin b/scan_test.odin index 26bf55c..19e0701 100644 --- a/scan_test.odin +++ b/scan_test.odin @@ -83,6 +83,6 @@ test_scan_path_empty_dir :: proc(t: ^testing.T) { results, ok := scan_path(base, cfg) defer delete(results) testing.expect(t, ok, "scan_path should succeed") - testing.expect(t, len(results) == 0, fmt.tprintf("expected 0 results, got %d", len(results))) + testing.expect_value(t, len(results), 0) } diff --git a/ssh_test.odin b/ssh_test.odin index 4ee1d63..ea7b3d6 100644 --- a/ssh_test.odin +++ b/ssh_test.odin @@ -46,15 +46,7 @@ test_private_key_pub_matches_public_key :: proc(t: ^testing.T) { kp, priv_ok := parse_ssh_private_key(TEST_KEY_DIR + "/test_ed25519") testing.expect(t, priv_ok, "expected private key to parse") - testing.expect( - t, - pub_from_pub == kp.Public, - fmt.tprintf( - "public key mismatch:\n from .pub: %v\n from priv: %v", - pub_from_pub, - kp.Public, - ), - ) + testing.expect_value(t, pub_from_pub, kp.Public) } @(test) @@ -64,12 +56,11 @@ test_read_wire_string :: proc(t: ^testing.T) { s, ok := read_wire_string(data, &offset) testing.expect(t, ok, "expected read_wire_string to succeed") - testing.expect(t, s == "hello", fmt.tprintf("expected 'hello', got %q", s)) - testing.expect(t, offset == 9, fmt.tprintf("expected offset 9, got %d", offset)) + testing.expect_value(t, s, "hello") + testing.expect_value(t, offset, 9) s2, ok2 := read_wire_string(data, &offset) testing.expect(t, ok2, "expected second read to succeed") - testing.expect(t, s2 == "", "expected empty string") + testing.expect_value(t, s2, "") } - diff --git a/table.odin b/table.odin index fb4558f..5d3f186 100644 --- a/table.odin +++ b/table.odin @@ -18,21 +18,18 @@ decorations := table.Decorations { "─", } -ansi_aware_width :: proc(str: string) -> int { - #no_bounds_check { - width := 0 - i := 0 - for i < len(str) { - if i + 1 < len(str) && str[i] == 0x1b && str[i + 1] == '[' { - i += 2 - for i < len(str) {c := str[i]; i += 1; if c >= 0x40 && c <= 0x7E {break}} - } else { - width += 1 - i += 1 - } +ansi_aware_width :: proc(str: string) -> int #no_bounds_check { + width := 0 + for i := 0; i < len(str); { + if i + 1 < len(str) && str[i] == 0x1b && str[i + 1] == '[' { + i += 2 + for i < len(str) {c := str[i]; i += 1; if c >= 0x40 && c <= 0x7E {break}} + } else { + width += 1 + i += 1 } - return width } + return width } write_borderless_table :: proc(w: io.Writer, t: ^table.Table) {