diff --git a/dune-workspace b/dune-workspace new file mode 100644 index 0000000..a017b55 --- /dev/null +++ b/dune-workspace @@ -0,0 +1,2 @@ +(lang dune 3.12) +(instrument_with bisect_ppx) \ No newline at end of file diff --git a/flake.nix b/flake.nix index 99af3a7..46b9ebb 100644 --- a/flake.nix +++ b/flake.nix @@ -30,6 +30,8 @@ inputsFrom = [ self'.packages.default ]; packages = [ pkgs.just + pkgs.ocamlPackages.alcotest + pkgs.ocamlPackages.bisect_ppx pkgs.ocamlPackages.dune_3 pkgs.ocamlPackages.findlib pkgs.ocamlPackages.ocaml diff --git a/lib/dune b/lib/dune index 6e33744..e268194 100644 --- a/lib/dune +++ b/lib/dune @@ -1,5 +1,13 @@ (library (name lib) (public_name flake_env.lib) - (libraries core core_unix core_unix.filename_unix core_unix.sys_unix ppx_yojson_conv re sha) - (preprocess (pps ppx_yojson_conv ppx_jane ppx_inline_test))) \ No newline at end of file + (libraries + core + core_unix + core_unix.filename_unix + core_unix.sys_unix + ppx_yojson_conv + re + sha) + (instrumentation (backend bisect_ppx)) + (preprocess (pps ppx_yojson_conv ppx_jane ))) \ No newline at end of file diff --git a/tests/dune b/tests/dune new file mode 100644 index 0000000..9440d3f --- /dev/null +++ b/tests/dune @@ -0,0 +1,13 @@ +(tests + (names + flake_env_test_versions + flake_env_test_watches + flake_env_test_util) + (deps spit_version.sh) + (libraries + lib + alcotest + core + core_unix + core_unix.sys_unix + core_unix.filename_unix)) \ No newline at end of file diff --git a/tests/flake_env_test_util.re b/tests/flake_env_test_util.re new file mode 100644 index 0000000..6f8e11f --- /dev/null +++ b/tests/flake_env_test_util.re @@ -0,0 +1,159 @@ +open Core; +module Unix = Core_unix; + +open Lib.Util; + +let _pp_exit_or_signal = (pp_fmt, e) => + Fmt.pf(pp_fmt, "%s", Unix.Exit_or_signal.to_string_hum(e)); +let _exit_or_signal_eq = (a, b) => Unix.Exit_or_signal.compare(a, b) == 0; +let testable_exit_or_signal = + Alcotest.testable(_pp_exit_or_signal, _exit_or_signal_eq); + +let _syst_to_bool = + fun + | `Yes => true + | _ => false; + +let check_exit_or_signal = + Alcotest.(check(Alcotest.pair(testable_exit_or_signal, string))); +let check_string = Alcotest.(check(string)); +let check_bool = Alcotest.(check(bool)); +let check_get_args = + Alcotest.( + check( + Alcotest.result(Alcotest.triple(string, string, list(string)), unit), + ) + ); + +let test_run_process_success = () => + check_exit_or_signal( + "Returns expected", + (Ok(), ""), + run_process("true", []), + ); + +let test_run_process_failure = () => + check_exit_or_signal( + "Returns expected", + (Error(`Exit_non_zero(1)), ""), + run_process("false", []), + ); + +let test_run_process_stdout = () => + check_exit_or_signal( + "Returns expected", + (Ok(), "echoed\n"), + run_process("echo", ["echoed"]), + ); + +let test_hash_one = () => { + check_string( + "Hash matches", + hash_files([|"../LICENSE"|]), + "b43cf2e824eb66ba0e8f939c08072a8e307b5e5f", + ); +}; + +let test_hash_multiple = () => { + check_string( + "Hash matches", + hash_files([|"../LICENSE", "../LICENSE"|]), + "08304d8baeed02722f81252952b00f6ac011ce0c", + ); +}; + +let test_hash_filters_nonexistent = () => { + check_string( + "Hash matches", + hash_files([|"../LICENSE", "FOOBARBAZ"|]), + "b43cf2e824eb66ba0e8f939c08072a8e307b5e5f", + ); +}; + +let test_rmrf_file = () => { + let tmp_file_name = Filename_unix.temp_file("test", "txt"); + rmrf(tmp_file_name); + + check_bool( + "File removed", + false, + _syst_to_bool(Sys_unix.is_file(tmp_file_name)), + ); +}; + +let test_rmrf_dir = () => { + let temp_dir_name = Filename_unix.temp_dir("test", "d"); + let _ = Filename_unix.temp_file(~in_dir=temp_dir_name, "test", "txt"); + + rmrf(temp_dir_name); + + check_bool( + "File removed", + false, + _syst_to_bool(Sys_unix.file_exists(temp_dir_name)), + ); +}; + +let test_get_args_simple = () => { + check_get_args( + "Parses successfully", + Ok(("foo", "bar", ["oof", "rab", "zab"])), + get_args([|"000", "foo", "bar", "oof", "rab", "zab"|]), + ); +}; + +let test_get_args_just_enough = () => { + check_get_args( + "Parses just enough args", + Ok(("foo", "bar", [])), + get_args([|"000", "foo", "bar"|]), + ); +}; + +let test_get_args_error = () => { + check_get_args("Errors on too few args", Error(), get_args([|"000", "111"|])) +}; + +let () = + Alcotest.( + run( + "Watches", + [ + ( + "run_process", + [ + test_case("Capture's Stdout", `Quick, test_run_process_stdout), + test_case("Success", `Quick, test_run_process_success), + test_case("Failure", `Quick, test_run_process_failure), + ], + ), + ( + "hash_files", + [ + test_case("Hashes one file", `Quick, test_hash_one), + test_case("Hashes multiple files", `Quick, test_hash_multiple), + test_case( + "Filters non-existent", + `Quick, + test_hash_filters_nonexistent, + ), + ], + ), + ( + "rmrf helper", + [ + test_case("Removes file", `Quick, test_rmrf_file), + test_case("Removes dir", `Quick, test_rmrf_dir), + ], + ), + ( + "get_args", + [ + test_case("Parses Args", `Quick, test_get_args_simple), + test_case("Parses just enough args", `Quick, test_get_args_just_enough), + test_case("Handles too few args", `Quick, test_get_args_error) + ], + ), + ], + ) + ); diff --git a/tests/flake_env_test_versions.re b/tests/flake_env_test_versions.re new file mode 100644 index 0000000..5b06a48 --- /dev/null +++ b/tests/flake_env_test_versions.re @@ -0,0 +1,174 @@ +open Lib; + +let pprint = (pp_fmt, version) => { + Versions.( + Fmt.pf( + pp_fmt, + "{ major: %d, minor: %d, point: %d }", + version.major, + version.minor, + version.point, + ) + ); +}; +let testable_version = + Alcotest.testable(pprint, (a, b) => Versions.compare(a, b) == 0); + +let check_version = + Alcotest.(check(Alcotest.result(testable_version, string))); + +let test_compare_equal = () => { + let a = Versions.init(1, 0, 0); + Alcotest.(check(int))("equal", 0, Versions.compare(a, a)); +}; + +let test_compare_first_major_greater = () => { + let a = Versions.init(2, 0, 0); + let b = Versions.init(1, 0, 0); + Alcotest.(check(int))("First major greater", 1, Versions.compare(a, b)); +}; + +let test_compare_first_minor_greater = () => { + let a = Versions.init(1, 1, 0); + let b = Versions.init(1, 0, 0); + Alcotest.(check(int))("First minor greater", 1, Versions.compare(a, b)); +}; + +let test_compare_first_point_greater = () => { + let a = Versions.init(1, 0, 1); + let b = Versions.init(1, 0, 0); + Alcotest.(check(int))("First point greater", 1, Versions.compare(a, b)); +}; + +let test_compare_second_major_greater = () => { + let a = Versions.init(1, 0, 0); + let b = Versions.init(2, 0, 0); + Alcotest.(check(int))("Second major greater", -1, Versions.compare(a, b)); +}; + +let test_compare_second_minor_greater = () => { + let a = Versions.init(1, 0, 0); + let b = Versions.init(1, 1, 0); + Alcotest.(check(int))("Second minor greater", -1, Versions.compare(a, b)); +}; + +let test_compare_second_point_greater = () => { + let a = Versions.init(1, 0, 0); + let b = Versions.init(1, 0, 1); + Alcotest.(check(int))("Second point greater", -1, Versions.compare(a, b)); +}; + +let test_ine_cur_newer = () => { + let a = Ok(Versions.init(2, 0, 0)); + let b = Versions.init(1, 0, 0); + let ine = Versions.is_new_enough(a, b); + Alcotest.(check(bool))("Curr newer", true, ine |> Result.get_ok); +}; + +let test_ine_cur_older = () => { + let a = Ok(Versions.init(1, 0, 0)); + let b = Versions.init(2, 0, 0); + let ine = Versions.is_new_enough(a, b); + Alcotest.(check(bool))("Curr older", false, ine |> Result.get_ok); +}; + +let test_ine_cur_equal = () => { + let a = Versions.init(1, 0, 0); + let ine = Versions.is_new_enough(Ok(a), a); + Alcotest.(check(bool))("Curr equal", true, ine |> Result.get_ok); +}; + +let test_ine_error = () => { + let a = Error("foobarbaz"); + let ine = Versions.is_new_enough(a, Versions.init(1, 0, 0)); + Alcotest.(check(Alcotest.result(bool, string)))( + "Error bubbled", + Error("foobarbaz"), + ine, + ); +}; + +let test_in_direnv_true = () => { + Core_unix.putenv(~key="direnv", ~data="direnv"); + Alcotest.(check(bool))("In direnv", true, Versions.in_direnv()); +}; + +let test_in_direnv_false = () => { + Core_unix.unsetenv("direnv"); + Alcotest.(check(bool))("Not in direnv", false, Versions.in_direnv()); +}; + +let test_extract_version_number_success = () => { + let result = Versions.extract_version_number("../tests/spit_version.sh"); + check_version("Versions", Ok(Versions.init(1, 1, 1)), result); +}; + +// TODO: Test: +// * extract_version_number: impure, don't know how to get a concrete version number to test against +// * preflight_versions? impure, but m +let () = + Alcotest.( + run( + "Versions", + [ + ( + "compare", + [ + test_case("Versions Equal", `Quick, test_compare_equal), + test_case( + "First Major Greater", + `Quick, + test_compare_first_major_greater, + ), + test_case( + "First Minor Greater", + `Quick, + test_compare_first_minor_greater, + ), + test_case( + "First Point Greater", + `Quick, + test_compare_first_point_greater, + ), + test_case( + "Second Major Greater", + `Quick, + test_compare_second_major_greater, + ), + test_case( + "Second Minor Greater", + `Quick, + test_compare_second_minor_greater, + ), + test_case( + "Second Point Greater", + `Quick, + test_compare_second_point_greater, + ), + ], + ), + ( + "is_new_enough", + [ + test_case("Curr Newer", `Quick, test_ine_cur_newer), + test_case("Curr Older", `Quick, test_ine_cur_older), + test_case("Curr Equal", `Quick, test_ine_cur_equal), + test_case("Error", `Quick, test_ine_error), + ], + ), + ( + "in_direnv", + [ + test_case("true", `Quick, test_in_direnv_true), + test_case("false", `Quick, test_in_direnv_false), + ], + ), + ( + "extract_version_number", + [ + test_case("success", `Quick, test_extract_version_number_success), + ], + ), + ], + ) + ); diff --git a/tests/flake_env_test_watches.re b/tests/flake_env_test_watches.re new file mode 100644 index 0000000..38d4226 --- /dev/null +++ b/tests/flake_env_test_watches.re @@ -0,0 +1,41 @@ +open Lib.Watches; + +let test_get_path_removes_prefix = () => { + let input = `Assoc([ + ("path", `String("aaaaaaaaaaabbbbb")) + ]); + Alcotest.(check(string))("Prefix removed", "bbbbb", get_path(input)) +}; + +let test_get_paths_from_doc = () => { + let input = `Assoc([ + ("path", `String("aaaaaaaaaaabbbbb")), + ("inputs", `Assoc([ + ("foo", `Assoc([ + ("path", `String("aaaaaaaaaaaccccc")), + ("inputs", `Assoc([ + ("bar", `Assoc([ + ("path", `String("aaaaaaaaaaaddddd")), + ("inputs", `Assoc([])) + ])) + ])) + ])) + ])) + ]); + Alcotest.(check(list(string)))("Gathers all inputs", ["bbbbb", "ccccc", "ddddd"], get_paths_from_doc(input, [])) +}; + +let () = + Alcotest.( + run( + "Watches", + [("get_path", + [ + test_case("Removes prefix", `Quick, test_get_path_removes_prefix), + ]), + ("get_paths_from_doc", + [ + test_case("Collects all paths", `Quick, test_get_paths_from_doc), + ]) + ]), +); diff --git a/tests/spit_version.sh b/tests/spit_version.sh new file mode 100755 index 0000000..b2eee26 --- /dev/null +++ b/tests/spit_version.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +echo "1.1.1";