diff options
author | H.J <thechairman@thechairman.info> | 2024-10-09 11:35:09 -0400 |
---|---|---|
committer | H.J <thechairman@thechairman.info> | 2024-10-09 11:35:09 -0400 |
commit | 6156a9ef7be4012063a042aafb4e9b0d7eadde8e (patch) | |
tree | 5d990ded3baba51ca3934b51994285f7d5915c34 /aoc2023/build/packages/adglent/src/showtime | |
parent | ef2ad0ee020b6754c230ae08f5979948b8db1350 (diff) | |
download | gleam_aoc-6156a9ef7be4012063a042aafb4e9b0d7eadde8e.tar.gz gleam_aoc-6156a9ef7be4012063a042aafb4e9b0d7eadde8e.zip |
cleanup
Diffstat (limited to 'aoc2023/build/packages/adglent/src/showtime')
15 files changed, 0 insertions, 1575 deletions
diff --git a/aoc2023/build/packages/adglent/src/showtime/internal/common/cli.gleam b/aoc2023/build/packages/adglent/src/showtime/internal/common/cli.gleam deleted file mode 100644 index 1c03211..0000000 --- a/aoc2023/build/packages/adglent/src/showtime/internal/common/cli.gleam +++ /dev/null @@ -1,5 +0,0 @@ -pub type Capture { - Yes - No - Mixed -} diff --git a/aoc2023/build/packages/adglent/src/showtime/internal/common/common_event_handler.gleam b/aoc2023/build/packages/adglent/src/showtime/internal/common/common_event_handler.gleam deleted file mode 100644 index b90af14..0000000 --- a/aoc2023/build/packages/adglent/src/showtime/internal/common/common_event_handler.gleam +++ /dev/null @@ -1,101 +0,0 @@ -import gleam/map.{type Map} -import showtime/internal/common/test_suite.{ - type TestEvent, type TestRun, CompletedTestRun, EndTest, EndTestRun, - EndTestSuite, OngoingTestRun, StartTest, StartTestRun, StartTestSuite, -} - -pub type TestState { - NotStarted - Running - Finished(num_modules: Int) -} - -pub type HandlerState { - HandlerState( - test_state: TestState, - num_done: Int, - events: Map(String, Map(String, TestRun)), - ) -} - -// This is the common event-handler (shared between erlang/JS targets) -// The main strategy is to collect the test-results in a map of maps: -// module_name -> -// test_name -> test_result -// It will also keep track of if it is running (i.e. did it receive the EndTestRun) -// so that the caller can determine when to print test-results -pub fn handle_event( - msg: TestEvent, - system_time: fn() -> Int, - state: HandlerState, -) { - let test_state = state.test_state - let num_done = state.num_done - let events = state.events - let #(updated_test_state, updated_num_done, updated_events) = case msg { - StartTestRun -> #(Running, num_done, events) - StartTestSuite(module) -> { - let maybe_module_events = map.get(events, module.name) - let new_events = case maybe_module_events { - Ok(_) -> events - Error(_) -> - events - |> map.insert(module.name, map.new()) - } - #(test_state, num_done, new_events) - } - StartTest(module, test) -> { - let current_time = system_time() - let maybe_module_events = map.get(events, module.name) - let new_events = case maybe_module_events { - Ok(module_events) -> { - let maybe_test_event = map.get(module_events, test.name) - case maybe_test_event { - Error(_) -> - events - |> map.insert( - module.name, - module_events - |> map.insert(test.name, OngoingTestRun(test, current_time)), - ) - Ok(_) -> events - } - } - Error(_) -> events - } - #(test_state, num_done, new_events) - } - EndTest(module, test, result) -> { - let current_time = system_time() - let maybe_module_events = map.get(events, module.name) - let new_events = case maybe_module_events { - Ok(module_events) -> { - let maybe_test_run = - module_events - |> map.get(test.name) - let updated_module_events = case maybe_test_run { - Ok(OngoingTestRun(test_function, started_at)) -> - module_events - |> map.insert( - test.name, - CompletedTestRun( - test_function, - current_time - started_at, - result, - ), - ) - Error(_) -> module_events - } - events - |> map.insert(module.name, updated_module_events) - } - Error(_) -> events - } - #(test_state, num_done, new_events) - } - EndTestSuite(_) -> #(test_state, num_done + 1, events) - EndTestRun(num_modules) -> #(Finished(num_modules), num_done, events) - _ -> #(Running, num_done, events) - } - HandlerState(updated_test_state, updated_num_done, updated_events) -} diff --git a/aoc2023/build/packages/adglent/src/showtime/internal/common/test_result.gleam b/aoc2023/build/packages/adglent/src/showtime/internal/common/test_result.gleam deleted file mode 100644 index a1d6bd9..0000000 --- a/aoc2023/build/packages/adglent/src/showtime/internal/common/test_result.gleam +++ /dev/null @@ -1,119 +0,0 @@ -import gleam/dynamic.{type Dynamic} -import gleam/map.{type Map} - -// These are all the types used for test-results -// NOTE: These are heavily used in the erlang/js ffi:s -// so any changes here are likely to break the ffi:s unless -// the corresponding change is introduced there -// -// Futhermore this has some erlang related names that should -// probably be cleaned up, but it is used by both the ffi:s - -// Currently only one reason, but could be possible to support -// more reasons in the future -pub type IgnoreReason { - Ignore -} - -// This is the return value from running the test-function -// or ignored if the test was ignored -pub type TestReturn { - TestFunctionReturn(value: Dynamic, output_buffer: List(String)) - Ignored(reason: IgnoreReason) -} - -// All data about an exception in the test function is captured -// in this type. -// This is also where the data about the assertions will end up (in reason) -pub type Exception { - ErlangException( - class: Class, - reason: Reason, - stacktrace: TraceList, - output_buffer: List(String), - ) -} - -// Alias for the test-result which is either a TestResult (passed test, ignored or a test-definition) -// or an Exception (failed test) -pub type TestResult = - Result(TestReturn, Exception) - -// Reason is either an assert equal (which is if the error was produced by gleeunit should) -// TODO: Add other asserts -// or it is a gleam error meaning that is was produced by showtime should -// TODO: Rename GleamError to ShowtimeError -pub type Reason { - AssertEqual(details: List(ReasonDetail)) - AssertNotEqual(details: List(ReasonDetail)) - AssertMatch(details: List(ReasonDetail)) - GleamError(details: GleamErrorDetail) - GleamAssert(value: Dynamic, line_no: Int) - GenericException(value: Dynamic) -} - -// ReasonDetail is the union-type used in erlang-exceptions where the reason -// is a list of such details -pub type ReasonDetail { - Module(name: String) - ReasonLine(line_no: Int) - Expression(expression: String) - Expected(value: Dynamic) - Value(value: Dynamic) - Pattern(pattern: String) -} - -// Gleam error detail is produced by showtime should and will hold all the information -// about the assertion (both expected and got) -pub type GleamErrorDetail { - LetAssert( - module: String, - function: String, - line_no: Int, - message: String, - value: Dynamic, - ) -} - -// Class is a part of standard erlang exceptions, but also used on js-side -// TODO: Extend to include a JS specific constructor -pub type Class { - ErlangError - Exit - Throw -} - -// The trace list is part of the standard erlang exception, but is also -// emulated on js-side. -// TODO: Maybe we need a js-version that contain some js-specific trace-elements -pub type TraceList { - TraceList(traces: List(Trace)) -} - -// Trace are the elements in the trace list in an erlang exception -// TODO: Maybe add a js-specific trace (since arity is not really a js attribute) -pub type Trace { - Trace(function: String, arity: Arity, extra_info: List(ExtraInfo)) - TraceModule( - module: String, - function: String, - arity: Arity, - extra_info: List(ExtraInfo), - ) -} - -// Extra info holds information about the file and line -// as well as some dynamic data in a map -// This is currently not used in the reporter -pub type ExtraInfo { - ErrorInfo(error_info: Map(Dynamic, Dynamic)) - File(filename: String) - Line(line_no: Int) -} - -// Arity is the erlang type for arity -// Can be either a number, or a list of arguments -pub type Arity { - Num(arity: Int) - ArgList(arg_list: List(Dynamic)) -} diff --git a/aoc2023/build/packages/adglent/src/showtime/internal/common/test_suite.gleam b/aoc2023/build/packages/adglent/src/showtime/internal/common/test_suite.gleam deleted file mode 100644 index eb58d64..0000000 --- a/aoc2023/build/packages/adglent/src/showtime/internal/common/test_suite.gleam +++ /dev/null @@ -1,63 +0,0 @@ -import gleam/option.{type Option} -import showtime/internal/common/test_result.{type TestResult} -import showtime/internal/common/cli.{type Capture} - -// The state (and result) of a test function -pub type TestRun { - OngoingTestRun(test_function: TestFunction, started_at: Int) - CompletedTestRun( - test_function: TestFunction, - total_time: Int, - result: TestResult, - ) -} - -// A test module (found by discovery) -pub type TestModule { - TestModule(name: String, path: Option(String)) -} - -// A test function -pub type TestFunction { - TestFunction(name: String) -} - -// A test suite is a test module together with the test functions -// that were collected from that module -pub type TestSuite { - TestSuite(module: TestModule, tests: List(TestFunction)) -} - -// Test event for the event handler -pub type TestEvent { - StartTestRun - StartTestSuite(test_module: TestModule) - StartTest(test_module: TestModule, test_function: TestFunction) - EndTest( - test_module: TestModule, - test_function: TestFunction, - result: TestResult, - ) - EndTestSuite(test_module: TestModule) - EndTestRun(num_modules: Int) -} - -// Interface for the module handler -pub type TestModuleHandler = - fn(TestModule) -> Nil - -// Interface for the event handler -pub type TestEventHandler = - fn(TestEvent) -> Nil - -// Interface for the module collector -pub type ModuleCollector = - fn(TestModuleHandler) -> List(TestModule) - -// Interface for the function collector -pub type TestFunctionCollector = - fn(TestModule) -> TestSuite - -// Interface for the test runner -pub type TestRunner = - fn(TestSuite, TestEventHandler, List(String), Capture) -> Nil diff --git a/aoc2023/build/packages/adglent/src/showtime/internal/erlang/discover.gleam b/aoc2023/build/packages/adglent/src/showtime/internal/erlang/discover.gleam deleted file mode 100644 index ecb752d..0000000 --- a/aoc2023/build/packages/adglent/src/showtime/internal/erlang/discover.gleam +++ /dev/null @@ -1,167 +0,0 @@ -@target(erlang) -import gleam/io -@target(erlang) -import gleam/dynamic.{type Dynamic} -@target(erlang) -import gleam/list -@target(erlang) -import gleam/string -@target(erlang) -import gleam/int -@target(erlang) -import gleam/option.{type Option, None, Some} -@target(erlang) -import gleam/erlang/atom.{type Atom} -@target(erlang) -import showtime/internal/common/test_suite.{ - type TestModule, type TestModuleHandler, type TestSuite, TestFunction, - TestModule, TestSuite, -} -import simplifile - -// Module collector for erlang -// Will search the test folder for files ending with _test and notify -// the module handler about each module it finds -@target(erlang) -pub fn collect_modules( - test_module_handler: TestModuleHandler, - only_modules: Option(List(String)), -) -> List(TestModule) { - collect_modules_in_folder("./test", test_module_handler, only_modules) -} - -@target(erlang) -fn collect_modules_in_folder( - path: String, - test_module_handler: TestModuleHandler, - only_modules: Option(List(String)), -) { - let module_prefix = get_module_prefix(path) - let assert Ok(files) = simplifile.read_directory(path) - let test_modules_in_folder = - files - |> list.filter(string.ends_with(_, "_test.gleam")) - |> list.filter_map(fn(test_module_file) { - let module_name = - module_prefix <> { - test_module_file - |> string.replace(".gleam", "") - } - case only_modules { - Some(only_modules_list) -> { - let module_in_list = - only_modules_list - |> list.any(fn(only_module_name) { - only_module_name == module_name - |> string.replace("@", "/") - }) - case module_in_list { - True -> { - let test_module = TestModule(module_name, Some(test_module_file)) - test_module_handler(test_module) - Ok(test_module) - } - - False -> Error(Nil) - } - } - None -> { - let test_module = TestModule(module_name, Some(test_module_file)) - test_module_handler(test_module) - Ok(test_module) - } - } - }) - let test_modules_in_subfolders = - files - |> list.map(fn(filename) { path <> "/" <> filename }) - |> list.filter(fn(file) { simplifile.is_directory(file) }) - |> list.fold( - [], - fn(modules, subfolder) { - modules - |> list.append(collect_modules_in_folder( - subfolder, - test_module_handler, - only_modules, - )) - }, - ) - test_modules_in_folder - |> list.append(test_modules_in_subfolders) -} - -@target(erlang) -fn get_module_prefix(path) { - let path_without_test = - path - |> string.replace("./test", "") - - let path_without_leading_slash = case - string.starts_with(path_without_test, "/") - { - True -> string.drop_left(path_without_test, 1) - False -> path_without_test - } - let module_prefix = - path_without_leading_slash - |> string.replace("/", "@") - case string.length(module_prefix) { - 0 -> module_prefix - _ -> module_prefix <> "@" - } -} - -// Test function collector for erlang -// Uses erlang `apply` to run `module_info` for the test module -// and collects all the exports ending with _test into a `TestSuite` -@target(erlang) -pub fn collect_test_functions(module: TestModule) -> TestSuite { - let test_functions: List(#(Atom, Int)) = - apply( - atom.create_from_string(module.name), - atom.create_from_string("module_info"), - [dynamic.from(atom.create_from_string("exports"))], - ) - |> dynamic.unsafe_coerce() - - let test_functions_filtered = - test_functions - |> list.map(fn(entry) { - let assert #(name, arity) = entry - #( - name - |> atom.to_string(), - arity, - ) - }) - |> list.filter_map(fn(entry) { - let assert #(name, arity) = entry - case string.ends_with(name, "_test") { - True -> - case arity { - 0 -> Ok(name) - _ -> { - io.println( - "WARNING: function \"" <> name <> "\" has arity: " <> int.to_string( - arity, - ) <> " - cannot be used as test (needs to be 0)", - ) - Error("Wrong arity") - } - } - False -> Error("Non matching name") - } - }) - |> list.filter(string.ends_with(_, "_test")) - |> list.map(fn(function_name) { TestFunction(function_name) }) - TestSuite(module, test_functions_filtered) -} - -@target(erlang) -@external(erlang, "erlang", "apply") -fn apply( - module module: Atom, - function function: Atom, - args args: List(Dynamic), -) -> Dynamic diff --git a/aoc2023/build/packages/adglent/src/showtime/internal/erlang/event_handler.gleam b/aoc2023/build/packages/adglent/src/showtime/internal/erlang/event_handler.gleam deleted file mode 100644 index 62a9caf..0000000 --- a/aoc2023/build/packages/adglent/src/showtime/internal/erlang/event_handler.gleam +++ /dev/null @@ -1,91 +0,0 @@ -@target(erlang) -import gleam/io -@target(erlang) -import gleam/otp/actor.{Continue, Stop} -@target(erlang) -import gleam/erlang/process.{type Subject, Normal} -@target(erlang) -import gleam/map -@target(erlang) -import showtime/internal/common/test_suite.{type TestEvent, EndTestRun} -@target(erlang) -import showtime/internal/common/common_event_handler.{ - Finished, HandlerState, NotStarted, handle_event, -} -@target(erlang) -import showtime/internal/reports/formatter.{create_test_report} -@target(erlang) -import gleam/erlang.{Millisecond} -@target(erlang) -import gleam/option.{None} - -@target(erlang) -type EventHandlerMessage { - EventHandlerMessage(test_event: TestEvent, reply_to: Subject(Int)) -} - -// Starts an actor that receives test events and forwards the to the event handler -// When handler updates the state to `Finished` the actor will wait until handler -// reports that all modules are done and the stop -@target(erlang) -pub fn start() { - let assert Ok(subject) = - actor.start( - #(NotStarted, 0, map.new()), - fn(msg: EventHandlerMessage, state) { - let EventHandlerMessage(test_event, reply_to) = msg - let #(test_state, num_done, events) = state - let updated_state = - handle_event( - test_event, - system_time, - HandlerState(test_state, num_done, events), - ) - case updated_state { - HandlerState(Finished(num_modules), num_done, events) if num_done == num_modules -> { - let #(test_report, num_failed) = create_test_report(events) - io.println(test_report) - process.send(reply_to, num_failed) - Stop(Normal) - } - HandlerState(test_state, num_done, events) -> - Continue(#(test_state, num_done, events), None) - } - }, - ) - let parent_subject = process.new_subject() - - let selector = - process.new_selector() - |> process.selecting(parent_subject, fn(x) { x }) - - // Returns a callback that can receive test events - fn(test_event: TestEvent) { - case test_event { - EndTestRun(..) -> { - // When EndTestRun has been received the callback will wait until the - // actor has stopped - // TODO: Use a timeout? - process.send(subject, EventHandlerMessage(test_event, parent_subject)) - let num_failed = process.select_forever(selector) - case num_failed > 0 { - True -> halt(1) - False -> halt(0) - } - } - - // Normally just send the test event to the actor - _ -> - process.send(subject, EventHandlerMessage(test_event, parent_subject)) - } - } -} - -@target(erlang) -@external(erlang, "erlang", "halt") -fn halt(a: Int) -> Nil - -@target(erlang) -fn system_time() { - erlang.system_time(Millisecond) -} diff --git a/aoc2023/build/packages/adglent/src/showtime/internal/erlang/module_handler.gleam b/aoc2023/build/packages/adglent/src/showtime/internal/erlang/module_handler.gleam deleted file mode 100644 index 88cc251..0000000 --- a/aoc2023/build/packages/adglent/src/showtime/internal/erlang/module_handler.gleam +++ /dev/null @@ -1,43 +0,0 @@ -@target(erlang) -import gleam/otp/actor.{Continue} -@target(erlang) -import gleam/erlang/process -@target(erlang) -import showtime/internal/common/test_suite.{ - type TestEventHandler, type TestFunctionCollector, type TestModule, - type TestRunner, EndTestSuite, StartTestSuite, -} -@target(erlang) -import showtime/internal/common/cli.{type Capture} -@target(erlang) -import gleam/option.{None} - -@target(erlang) -pub fn start( - test_event_handler: TestEventHandler, - test_function_collector: TestFunctionCollector, - run_test_suite: TestRunner, - ignore_tags: List(String), - capture: Capture, -) { - let assert Ok(subject) = - actor.start( - Nil, - fn(module: TestModule, state) { - process.start( - fn() { - let test_suite = test_function_collector(module) - test_event_handler(StartTestSuite(module)) - run_test_suite(test_suite, test_event_handler, ignore_tags, capture) - test_event_handler(EndTestSuite(module)) - }, - False, - ) - Continue(state, None) - }, - ) - fn(test_module: TestModule) { - process.send(subject, test_module) - Nil - } -} diff --git a/aoc2023/build/packages/adglent/src/showtime/internal/erlang/runner.gleam b/aoc2023/build/packages/adglent/src/showtime/internal/erlang/runner.gleam deleted file mode 100644 index ebbf426..0000000 --- a/aoc2023/build/packages/adglent/src/showtime/internal/erlang/runner.gleam +++ /dev/null @@ -1,59 +0,0 @@ -@target(erlang) -import gleam/list -@target(erlang) -import gleam/erlang/atom.{type Atom} -@target(erlang) -import showtime/internal/common/test_suite.{ - type TestEventHandler, type TestSuite, EndTest, StartTest, -} -@target(erlang) -import showtime/internal/common/test_result.{type TestResult} -@target(erlang) -import showtime/internal/common/cli.{type Capture} - -// Runs all tests in a test suite -@target(erlang) -pub fn run_test_suite( - test_suite: TestSuite, - test_event_handler: TestEventHandler, - ignore_tags: List(String), - capture: Capture, -) { - test_suite.tests - |> list.each(fn(test) { - test_event_handler(StartTest(test_suite.module, test)) - let result = - run_test(test_suite.module.name, test.name, ignore_tags, capture) - test_event_handler(EndTest(test_suite.module, test, result)) - }) -} - -// Wrapper around the ffi function that converts names to atoms -@target(erlang) -pub fn run_test( - module_name: String, - test_name: String, - ignore_tags: List(String), - capture: Capture, -) -> TestResult { - let result = - run_test_ffi( - atom.create_from_string(module_name), - atom.create_from_string(test_name), - ignore_tags, - capture, - ) - result -} - -// Calls ffi for running a test function -// The ffi will take care of mapping the result and exception to the data-types -// used in gleam -@target(erlang) -@external(erlang, "showtime_ffi", "run_test") -fn run_test_ffi( - module module: Atom, - function function: Atom, - ignore_tags ignore_tags: List(String), - capture capture: Capture, -) -> TestResult diff --git a/aoc2023/build/packages/adglent/src/showtime/internal/reports/compare.gleam b/aoc2023/build/packages/adglent/src/showtime/internal/reports/compare.gleam deleted file mode 100644 index 5ccddee..0000000 --- a/aoc2023/build/packages/adglent/src/showtime/internal/reports/compare.gleam +++ /dev/null @@ -1,42 +0,0 @@ -import gleam/dynamic.{type Dynamic} -import gleam/string -import showtime/internal/reports/styles.{expected_highlight, got_highlight} -import gap.{compare_lists, compare_strings} -import gap/styling.{from_comparison, highlight, to_styled_comparison} - -pub fn compare(expected: Dynamic, got: Dynamic) -> #(String, String) { - let expected_as_list = - expected - |> dynamic.list(dynamic.dynamic) - let got_as_list = - got - |> dynamic.list(dynamic.dynamic) - let expected_as_string = - expected - |> dynamic.string() - let got_as_string = - got - |> dynamic.string() - case expected_as_list, got_as_list, expected_as_string, got_as_string { - Ok(expected_list), Ok(got_list), _, _ -> { - let comparison = - compare_lists(expected_list, got_list) - |> from_comparison() - |> highlight(expected_highlight, got_highlight, fn(item) { item }) - |> to_styled_comparison() - #(comparison.first, comparison.second) - } - _, _, Ok(expected_string), Ok(got_string) -> { - let comparison = - compare_strings(expected_string, got_string) - |> from_comparison() - |> highlight(expected_highlight, got_highlight, fn(item) { item }) - |> to_styled_comparison() - #(comparison.first, comparison.second) - } - _, _, _, _ -> #( - expected_highlight(string.inspect(expected)), - got_highlight(string.inspect(got)), - ) - } -} diff --git a/aoc2023/build/packages/adglent/src/showtime/internal/reports/formatter.gleam b/aoc2023/build/packages/adglent/src/showtime/internal/reports/formatter.gleam deleted file mode 100644 index 8c1a6ac..0000000 --- a/aoc2023/build/packages/adglent/src/showtime/internal/reports/formatter.gleam +++ /dev/null @@ -1,480 +0,0 @@ -import gleam/io -import gleam/int -import gleam/list -import gleam/string -import gleam/option.{type Option, None, Some} -import gleam/map.{type Map} -import gleam/dynamic.{type Dynamic} -import showtime/internal/common/test_result.{ - type GleamErrorDetail, type ReasonDetail, type Trace, AssertEqual, AssertMatch, - AssertNotEqual, Expected, Expression, GenericException, GleamAssert, - GleamError, Ignored, LetAssert, Pattern, Trace, TraceModule, Value, -} -import showtime/internal/common/test_suite.{type TestRun, CompletedTestRun} -import showtime/tests/should.{type Assertion, Eq, Fail, IsError, IsOk, NotEq} -import showtime/internal/reports/styles.{ - error_style, expected_highlight, failed_style, function_style, got_highlight, - heading_style, ignored_style, not_style, passed_style, stacktrace_style, -} -import showtime/internal/reports/compare.{compare} -import showtime/internal/reports/table.{ - AlignLeft, AlignLeftOverflow, AlignRight, Content, Separator, StyledContent, - Table, align_table, to_string, -} -import showtime/tests/meta.{type Meta} - -type GleeUnitAssertionType { - GleeUnitAssertEqual(message: String) - GleeUnitAssertNotEqual(message: String) - GleeUnitAssertMatch(message: String) -} - -type ModuleAndTest { - ModuleAndTestRun(module_name: String, test_run: TestRun) -} - -type UnifiedError { - UnifiedError( - meta: Option(Meta), - reason: String, - message: String, - expected: String, - got: String, - line: Option(Int), - stacktrace: List(Trace), - ) -} - -pub fn create_test_report(test_results: Map(String, Map(String, TestRun))) { - let all_test_runs = - test_results - |> map.values() - |> list.flat_map(map.values) - let failed_test_runs = - test_results - |> map.to_list() - |> list.flat_map(fn(entry) { - let #(module_name, test_module_results) = entry - test_module_results - |> map.values() - |> list.filter_map(fn(test_run) { - case test_run { - CompletedTestRun(_test_function, _, result) -> - case result { - Error(_) -> Ok(ModuleAndTestRun(module_name, test_run)) - Ok(Ignored(_)) -> Error(Nil) - Ok(_) -> Error(Nil) - } - _ -> { - test_run - |> io.debug() - Error(Nil) - } - } - }) - }) - - let ignored_test_runs = - test_results - |> map.to_list() - |> list.flat_map(fn(entry) { - let #(module_name, test_module_results) = entry - test_module_results - |> map.values() - |> list.filter_map(fn(test_run) { - case test_run { - CompletedTestRun(test_function, _, result) -> - case result { - Ok(Ignored(reason)) -> - Ok(#(module_name <> "." <> test_function.name, reason)) - _ -> Error(Nil) - } - _ -> Error(Nil) - } - }) - }) - - let failed_tests_report = - failed_test_runs - |> list.filter_map(fn(module_and_test_run) { - case module_and_test_run.test_run { - CompletedTestRun(test_function, _total_time, result) -> - case result { - Error(exception) -> - case exception.reason { - AssertEqual(reason_details) -> - Ok(format_reason( - erlang_error_to_unified( - reason_details, - GleeUnitAssertEqual("Assert equal"), - exception.stacktrace.traces, - ), - module_and_test_run.module_name, - test_function.name, - exception.output_buffer, - )) - AssertNotEqual(reason_details) -> - Ok(format_reason( - erlang_error_to_unified( - reason_details, - GleeUnitAssertNotEqual("Assert not equal"), - exception.stacktrace.traces, - ), - module_and_test_run.module_name, - test_function.name, - exception.output_buffer, - )) - AssertMatch(reason_details) -> - Ok(format_reason( - erlang_error_to_unified( - reason_details, - GleeUnitAssertMatch("Assert match"), - exception.stacktrace.traces, - ), - module_and_test_run.module_name, - test_function.name, - exception.output_buffer, - )) - GleamError(reason) -> - Ok(format_reason( - gleam_error_to_unified(reason, exception.stacktrace.traces), - module_and_test_run.module_name, - test_function.name, - exception.output_buffer, - )) - // GleamAssert(value) -> Error(Nil) - GleamAssert(value, line_no) -> - Ok(format_reason( - UnifiedError( - None, - "gleam assert", - "Assert failed", - "Patterns should match", - error_style(string.inspect(value)), - Some(line_no), - exception.stacktrace.traces, - ), - module_and_test_run.module_name, - test_function.name, - exception.output_buffer, - )) - GenericException(value) -> - Ok(format_reason( - UnifiedError( - None, - "generic exception", - "Test function threw an exception", - "Exception in test function", - error_style(string.inspect(value)), - None, - exception.stacktrace.traces, - ), - module_and_test_run.module_name, - test_function.name, - exception.output_buffer, - )) - other -> { - io.println("Other: " <> string.inspect(other)) - panic - Error(Nil) - } - } - _ -> Error(Nil) - } - _ -> Error(Nil) - } - }) - |> list.fold([], fn(rows, test_rows) { list.append(rows, test_rows) }) - - let all_test_execution_time_reports = - all_test_runs - |> list.filter_map(fn(test_run) { - case test_run { - CompletedTestRun(test_function, total_time, _) -> - Ok(test_function.name <> ": " <> int.to_string(total_time) <> " ms") - _ -> Error(Nil) - } - }) - let _execution_times_report = - all_test_execution_time_reports - |> string.join("\n") - - let all_tests_count = - all_test_runs - |> list.length() - let ignored_tests_count = - ignored_test_runs - |> list.length() - let failed_tests_count = - failed_test_runs - |> list.length() - - let passed = - passed_style( - int.to_string(all_tests_count - failed_tests_count - ignored_tests_count) <> " passed", - ) - let failed = failed_style(int.to_string(failed_tests_count) <> " failed") - let ignored = case ignored_tests_count { - 0 -> "" - _ -> ", " <> ignored_style(int.to_string(ignored_tests_count) <> " ignored") - } - - let failed_tests_table = - Table(None, failed_tests_report) - |> align_table() - |> to_string() - - let test_report = - "\n" <> failed_tests_table <> "\n" <> passed <> ", " <> failed <> ignored - #(test_report, failed_tests_count) -} - -fn erlang_error_to_unified( - error_details: List(ReasonDetail), - assertion_type: GleeUnitAssertionType, - stacktrace: List(Trace), -) { - error_details - |> list.fold( - UnifiedError( - None, - "not_set", - assertion_type.message, - "", - "", - None, - stacktrace, - ), - fn(unified, reason) { - case reason { - Expression(expression) -> UnifiedError(..unified, reason: expression) - Expected(value) -> - case assertion_type { - GleeUnitAssertEqual(_messaged) -> - UnifiedError( - ..unified, - expected: expected_highlight(string.inspect(value)), - ) - _ -> unified - } - Value(value) -> - case assertion_type { - GleeUnitAssertNotEqual(_message) -> - UnifiedError( - ..unified, - expected: not_style("not ") <> string.inspect(value), - got: got_highlight(string.inspect(value)), - ) - _ -> - UnifiedError(..unified, got: got_highlight(string.inspect(value))) - } - Pattern(pattern) -> - case pattern { - "{ ok , _ }" -> - UnifiedError(..unified, expected: expected_highlight("Ok(_)")) - "{ error , _ }" -> - UnifiedError(..unified, expected: expected_highlight("Error(_)")) - _ -> unified - } - _ -> unified - } - }, - ) -} - -fn gleam_error_to_unified( - gleam_error: GleamErrorDetail, - stacktrace: List(Trace), -) -> UnifiedError { - case gleam_error { - LetAssert(_module, _function, _line_no, _message, value) -> { - let result: Result(Dynamic, Assertion(Dynamic, Dynamic)) = - dynamic.unsafe_coerce(value) - let assert Error(assertion) = result - case assertion { - Eq(got, expected, meta) -> { - let #(expected, got) = compare(expected, got) - UnifiedError( - meta, - "assert", - "Assert equal", - expected, - got, - None, - stacktrace, - ) - } - NotEq(got, expected, meta) -> - UnifiedError( - meta, - "assert", - "Assert not equal", - not_style("not ") <> string.inspect(expected), - string.inspect(got), - None, - stacktrace, - ) - IsOk(got, meta) -> - UnifiedError( - meta, - "assert", - "Assert is Ok", - expected_highlight("Ok(_)"), - got_highlight(string.inspect(got)), - None, - stacktrace, - ) - IsError(got, meta) -> - UnifiedError( - meta, - "assert", - "Assert is Ok", - expected_highlight("Error(_)"), - got_highlight(string.inspect(got)), - None, - stacktrace, - ) - Fail(meta) -> - UnifiedError( - meta, - "assert", - "Assert is Ok", - got_highlight("should.fail()"), - got_highlight("N/A - test always expected to fail"), - None, - stacktrace, - ) - } - } - } -} - -fn format_reason( - error: UnifiedError, - module: String, - function: String, - output_buffer: List(String), -) { - let meta = case error.meta { - Some(meta) -> - Some([ - AlignRight(StyledContent(heading_style("Description")), 2), - Separator(": "), - AlignLeft(Content(meta.description), 0), - ]) - - None -> None - } - - let stacktrace = - error.stacktrace - |> list.map(fn(trace) { - case trace { - Trace(function, _, _) if function == "" -> "(anonymous)" - TraceModule(module, function, _, _) if function == "" -> - module <> "." <> "(anonymous)" - Trace(function, _, _) -> function - TraceModule(module, function, _, _) -> module <> "." <> function - } - }) - let stacktrace_rows = case stacktrace { - [] -> [] - [first, ..rest] -> { - let first_row = - Some([ - AlignRight(StyledContent(heading_style("Stacktrace")), 2), - Separator(": "), - AlignLeft(StyledContent(stacktrace_style(first)), 0), - ]) - let rest_rows = - rest - |> list.map(fn(row) { - Some([ - AlignRight(Content(""), 2), - Separator(" "), - AlignLeft(StyledContent(stacktrace_style(row)), 0), - ]) - }) - [first_row, ..rest_rows] - } - } - - let output_rows = case - output_buffer - |> list.reverse() - |> list.map(fn(row) { string.trim_right(row) }) - { - [] -> [] - [first, ..rest] -> { - let first_row = - Some([ - AlignRight(StyledContent(heading_style("Output")), 2), - Separator(": "), - AlignLeftOverflow(StyledContent(stacktrace_style(first)), 0), - ]) - let rest_rows = - rest - |> list.map(fn(row) { - Some([ - AlignRight(Content(""), 2), - Separator(" "), - AlignLeftOverflow(StyledContent(stacktrace_style(row)), 0), - ]) - }) - [first_row, ..rest_rows] - } - } - - let line = - error.line - |> option.map(fn(line) { ":" <> int.to_string(line) }) - |> option.unwrap("") - - let arrow = - string.join( - list.repeat( - "-", - string.length(module) + 1 + { - string.length(function) + string.length(line) - } / 2, - ), - "", - ) <> "⌄" - let standard_table_rows = [ - Some([ - AlignRight(StyledContent(error_style("Failed")), 2), - Separator(": "), - AlignLeft(Content(arrow), 0), - ]), - Some([ - AlignRight(StyledContent(heading_style("Test")), 2), - Separator(": "), - AlignLeft( - StyledContent(module <> "." <> function_style(function <> line)), - 0, - ), - ]), - meta, - Some([ - AlignRight(StyledContent(heading_style("Expected")), 2), - Separator(": "), - AlignLeftOverflow(StyledContent(error.expected), 0), - ]), - Some([ - AlignRight(StyledContent(heading_style("Got")), 2), - Separator(": "), - AlignLeftOverflow(StyledContent(error.got), 0), - ]), - ] - standard_table_rows - |> list.append(stacktrace_rows) - |> list.append(output_rows) - |> list.append([ - Some([ - AlignRight(Content(""), 0), - AlignRight(Content(""), 0), - AlignRight(Content(""), 0), - ]), - ]) - |> list.filter_map(fn(row) { option.to_result(row, Nil) }) -} diff --git a/aoc2023/build/packages/adglent/src/showtime/internal/reports/styles.gleam b/aoc2023/build/packages/adglent/src/showtime/internal/reports/styles.gleam deleted file mode 100644 index b051dd3..0000000 --- a/aoc2023/build/packages/adglent/src/showtime/internal/reports/styles.gleam +++ /dev/null @@ -1,84 +0,0 @@ -import gleam_community/ansi -import gleam/list -import gleam/string -import gleam/bit_array - -pub fn passed_style(text) { - bold_green(text) -} - -pub fn failed_style(text) { - bold_red(text) -} - -pub fn ignored_style(text) { - bold_yellow(text) -} - -pub fn error_style(text) { - bold_red(text) -} - -pub fn expected_highlight(text) { - bold_green(text) -} - -pub fn got_highlight(text) { - bold_red(text) -} - -pub fn not_style(text) { - ansi.bold(text) -} - -pub fn module_style(text: String) { - ansi.cyan(text) -} - -pub fn heading_style(text: String) { - ansi.cyan(text) -} - -pub fn function_style(text: String) { - bold_cyan(text) -} - -pub fn stacktrace_style(text: String) { - text -} - -fn bold_red(text: String) { - ansi.bold(ansi.red(text)) -} - -fn bold_green(text) { - ansi.bold(ansi.green(text)) -} - -fn bold_yellow(text) { - ansi.bold(ansi.yellow(text)) -} - -fn bold_cyan(text) { - ansi.bold(ansi.cyan(text)) -} - -pub fn strip_style(text) { - let #(new_text, _) = - text - |> string.to_graphemes() - |> list.fold( - #("", False), - fn(acc, char) { - let #(str, removing) = acc - let bit_char = bit_array.from_string(char) - case bit_char, removing { - <<0x1b>>, _ -> #(str, True) - <<0x6d>>, True -> #(str, False) - _, True -> #(str, True) - _, False -> #(str <> char, False) - } - }, - ) - new_text -} diff --git a/aoc2023/build/packages/adglent/src/showtime/internal/reports/table.gleam b/aoc2023/build/packages/adglent/src/showtime/internal/reports/table.gleam deleted file mode 100644 index f8bc00c..0000000 --- a/aoc2023/build/packages/adglent/src/showtime/internal/reports/table.gleam +++ /dev/null @@ -1,148 +0,0 @@ -import gleam/list -import gleam/string -import gleam/int -import gleam/option.{type Option} -import showtime/internal/reports/styles.{strip_style} - -pub type Content { - Content(unstyled_text: String) - StyledContent(styled_text: String) -} - -pub type Col { - AlignRight(content: Content, margin: Int) - AlignLeft(content: Content, margin: Int) - AlignRightOverflow(content: Content, margin: Int) - AlignLeftOverflow(content: Content, margin: Int) - Separator(char: String) - Aligned(content: String) -} - -pub type Table { - Table(header: Option(String), rows: List(List(Col))) -} - -pub fn to_string(table: Table) -> String { - let rows = - table.rows - |> list.map(fn(row) { - row - |> list.filter_map(fn(col) { - case col { - Separator(char) -> Ok(char) - Aligned(content) -> Ok(content) - _ -> Error(Nil) - } - }) - |> string.join("") - }) - |> string.join("\n") - let header = - table.header - |> option.map(fn(header) { header <> "\n" }) - |> option.unwrap("") - header <> rows -} - -pub fn align_table(table: Table) -> Table { - let cols = - table.rows - |> list.transpose() - let col_width = - cols - |> list.map(fn(col) { - col - |> list.map(fn(content) { - case content { - AlignRight(Content(unstyled), _) -> unstyled - AlignRight(StyledContent(styled), _) -> strip_style(styled) - AlignLeft(Content(unstyled), _) -> unstyled - AlignLeft(StyledContent(styled), _) -> strip_style(styled) - AlignLeftOverflow(_, _) -> "" - AlignRightOverflow(_, _) -> "" - Separator(char) -> char - Aligned(content) -> content - } - }) - |> list.fold(0, fn(max, str) { int.max(max, string.length(str)) }) - }) - let aligned_col = - cols - |> list.zip(col_width) - |> list.map(fn(col_and_width) { - let #(col, width) = col_and_width - col - |> list.map(fn(content) { - case content { - AlignRight(Content(unstyled), margin) -> - Aligned(pad_left( - unstyled, - width + margin - string.length(unstyled), - " ", - )) - AlignRight(StyledContent(styled), margin) -> - Aligned(pad_left( - styled, - width + margin - string.length(strip_style(styled)), - " ", - )) - AlignRightOverflow(Content(unstyled), margin) -> - Aligned(pad_left( - unstyled, - width + margin - string.length(unstyled), - " ", - )) - AlignRightOverflow(StyledContent(styled), margin) -> - Aligned(pad_left( - styled, - width + margin - string.length(strip_style(styled)), - " ", - )) - AlignLeft(Content(unstyled), margin) -> - Aligned(pad_right( - unstyled, - width + margin - string.length(unstyled), - " ", - )) - AlignLeft(StyledContent(styled), margin) -> - Aligned(pad_right( - styled, - width + margin - string.length(strip_style(styled)), - " ", - )) - AlignLeftOverflow(Content(unstyled), margin) -> - Aligned(pad_right( - unstyled, - width + margin - string.length(unstyled), - " ", - )) - AlignLeftOverflow(StyledContent(styled), margin) -> - Aligned(pad_right( - styled, - width + margin - string.length(strip_style(styled)), - " ", - )) - Separator(char) -> Separator(char) - Aligned(content) -> Aligned(content) - } - }) - }) - let aligned_rows = - aligned_col - |> list.transpose() - Table(..table, rows: aligned_rows) -} - -fn pad_left(str: String, num: Int, char: String) { - let padding = - list.repeat(char, num) - |> string.join("") - padding <> str -} - -fn pad_right(str: String, num: Int, char: String) { - let padding = - list.repeat(char, num) - |> string.join("") - str <> padding -} diff --git a/aoc2023/build/packages/adglent/src/showtime/tests/meta.gleam b/aoc2023/build/packages/adglent/src/showtime/tests/meta.gleam deleted file mode 100644 index cbba414..0000000 --- a/aoc2023/build/packages/adglent/src/showtime/tests/meta.gleam +++ /dev/null @@ -1,3 +0,0 @@ -pub type Meta { - Meta(description: String, tags: List(String)) -} diff --git a/aoc2023/build/packages/adglent/src/showtime/tests/should.gleam b/aoc2023/build/packages/adglent/src/showtime/tests/should.gleam deleted file mode 100644 index 71578c7..0000000 --- a/aoc2023/build/packages/adglent/src/showtime/tests/should.gleam +++ /dev/null @@ -1,113 +0,0 @@ -import gleam/option.{type Option, None, Some} -import showtime/tests/meta.{type Meta} - -pub type Assertion(t, e) { - Eq(a: t, b: t, meta: Option(Meta)) - NotEq(a: t, b: t, meta: Option(Meta)) - IsOk(a: Result(t, e), meta: Option(Meta)) - IsError(a: Result(t, e), meta: Option(Meta)) - Fail(meta: Option(Meta)) -} - -pub fn equal(a: t, b: t) { - evaluate(Eq(a, b, None)) -} - -pub fn equal_meta(a: t, b: t, meta: Meta) { - evaluate(Eq(a, b, Some(meta))) -} - -pub fn not_equal(a: t, b: t) { - evaluate(NotEq(a, b, None)) -} - -pub fn not_equal_meta(a: t, b: t, meta: Meta) { - evaluate(NotEq(a, b, Some(meta))) -} - -pub fn be_ok(a: Result(o, e)) { - evaluate(IsOk(a, None)) - let assert Ok(value) = a - value -} - -pub fn be_ok_meta(a: Result(o, e), meta: Meta) { - evaluate(IsOk(a, Some(meta))) -} - -pub fn be_error(a: Result(o, e)) { - evaluate(IsError(a, None)) - let assert Error(value) = a - value -} - -pub fn be_error_meta(a: Result(o, e), meta: Meta) { - evaluate(IsError(a, Some(meta))) -} - -pub fn fail() { - evaluate(Fail(None)) -} - -pub fn fail_meta(meta: Meta) { - evaluate(Fail(Some(meta))) -} - -pub fn be_true(a: Bool) { - a - |> equal(True) -} - -pub fn be_true_meta(a: Bool, meta: Meta) { - a - |> equal_meta(True, meta) -} - -pub fn be_false(a: Bool) { - a - |> equal(False) -} - -pub fn be_false_meta(a: Bool, meta: Meta) { - a - |> equal_meta(False, meta) -} - -@external(erlang, "showtime_ffi", "gleam_error") -fn gleam_error(value: Result(Nil, Assertion(a, b))) -> Nil - -pub fn evaluate(assertion) -> Nil { - case assertion { - Eq(a, b, _meta) -> - case a == b { - True -> Nil - False -> { - gleam_error(Error(assertion)) - } - } - NotEq(a, b, _meta) -> - case a != b { - True -> Nil - False -> { - gleam_error(Error(assertion)) - } - } - IsOk(a, _meta) -> - case a { - Ok(_) -> Nil - Error(_) -> { - gleam_error(Error(assertion)) - } - } - IsError(a, _meta) -> - case a { - Error(_) -> Nil - Ok(_) -> { - gleam_error(Error(assertion)) - } - } - Fail(_meta) -> { - gleam_error(Error(assertion)) - } - } -} diff --git a/aoc2023/build/packages/adglent/src/showtime/tests/test.gleam b/aoc2023/build/packages/adglent/src/showtime/tests/test.gleam deleted file mode 100644 index 730f943..0000000 --- a/aoc2023/build/packages/adglent/src/showtime/tests/test.gleam +++ /dev/null @@ -1,57 +0,0 @@ -import showtime/tests/should -import showtime/tests/meta.{type Meta} -import gleam/io - -pub type Test { - Test(meta: Meta, test_function: fn() -> Nil) -} - -pub type MetaShould(t) { - MetaShould(equal: fn(t, t) -> Nil, not_equal: fn(t, t) -> Nil) -} - -pub fn test(meta: Meta, test_function: fn(Meta) -> Nil) { - Test(meta, fn() { test_function(meta) }) -} - -pub fn with_meta(meta: Meta, test_function: fn(MetaShould(a)) -> Nil) { - Test( - meta, - fn() { - test_function(MetaShould( - fn(a, b) { equal(a, b, meta) }, - fn(a, b) { not_equal(a, b, meta) }, - )) - }, - ) -} - -pub fn equal(a: t, b: t, meta: Meta) { - io.debug(a) - io.debug(b) - should.equal_meta(a, b, meta) -} - -pub fn not_equal(a: t, b: t, meta: Meta) { - should.equal_meta(a, b, meta) -} - -pub fn be_ok(a: Result(o, e), meta: Meta) { - should.be_ok_meta(a, meta) -} - -pub fn be_error(a: Result(o, e), meta: Meta) { - should.be_error_meta(a, meta) -} - -pub fn fail(meta: Meta) { - should.fail_meta(meta) -} - -pub fn be_true(a: Bool, meta: Meta) { - should.be_true_meta(a, meta) -} - -pub fn be_false(a: Bool, meta: Meta) { - should.be_false_meta(a, meta) -} |