Initial commit
This commit is contained in:
commit
a6272848f9
379 changed files with 74829 additions and 0 deletions
191
build/packages/gleeunit/LICENCE
Normal file
191
build/packages/gleeunit/LICENCE
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright 2021, Louis Pilfold <louis@lpil.uk>.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
44
build/packages/gleeunit/README.md
Normal file
44
build/packages/gleeunit/README.md
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# gleeunit
|
||||
|
||||
A simple test runner for Gleam, using EUnit on Erlang and a custom runner on JS.
|
||||
|
||||
[](https://hex.pm/packages/gleeunit)
|
||||
[](https://hexdocs.pm/gleeunit/)
|
||||
|
||||
|
||||
```sh
|
||||
gleam add gleeunit@1 --dev
|
||||
```
|
||||
```gleam
|
||||
// In test/yourapp_test.gleam
|
||||
import gleeunit
|
||||
|
||||
pub fn main() {
|
||||
gleeunit.main()
|
||||
}
|
||||
```
|
||||
|
||||
Now any public function with a name ending in `_test` in the `test` directory
|
||||
will be found and run as a test.
|
||||
|
||||
```gleam
|
||||
pub fn some_function_test() {
|
||||
assert some_function() == "Hello!"
|
||||
}
|
||||
```
|
||||
|
||||
Run the tests by entering `gleam test` in the command line.
|
||||
|
||||
### Deno
|
||||
|
||||
If using the Deno JavaScript runtime, you will need to add the following to your
|
||||
`gleam.toml`.
|
||||
|
||||
```toml
|
||||
[javascript.deno]
|
||||
allow_read = [
|
||||
"gleam.toml",
|
||||
"test",
|
||||
"build",
|
||||
]
|
||||
```
|
||||
16
build/packages/gleeunit/gleam.toml
Normal file
16
build/packages/gleeunit/gleam.toml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
name = "gleeunit"
|
||||
version = "1.9.0"
|
||||
licences = ["Apache-2.0"]
|
||||
description = "A simple test runner for Gleam, using EUnit on Erlang"
|
||||
repository = { type = "github", user = "lpil", repo = "gleeunit" }
|
||||
links = [{ title = "Sponsor", href = "https://github.com/sponsors/lpil" }]
|
||||
gleam = ">= 1.13.0"
|
||||
|
||||
[javascript.deno]
|
||||
allow_read = ["gleam.toml", "test", "build"]
|
||||
|
||||
[dependencies]
|
||||
gleam_stdlib = ">= 0.60.0 and < 1.0.0"
|
||||
|
||||
[dev-dependencies]
|
||||
testhelper = { "path" = "./testhelper" }
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
-record(assert, {
|
||||
start :: integer(),
|
||||
'end' :: integer(),
|
||||
expression_start :: integer(),
|
||||
kind :: gleeunit@internal@gleam_panic:assert_kind()
|
||||
}).
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
-record(asserted_expression, {
|
||||
start :: integer(),
|
||||
'end' :: integer(),
|
||||
kind :: gleeunit@internal@gleam_panic:expression_kind()
|
||||
}).
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
-record(binary_operator, {
|
||||
operator :: binary(),
|
||||
left :: gleeunit@internal@gleam_panic:asserted_expression(),
|
||||
right :: gleeunit@internal@gleam_panic:asserted_expression()
|
||||
}).
|
||||
|
|
@ -0,0 +1 @@
|
|||
-record(expression, {value :: gleam@dynamic:dynamic_()}).
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
-record(function_call, {
|
||||
arguments :: list(gleeunit@internal@gleam_panic:asserted_expression())
|
||||
}).
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
-record(gleam_panic, {
|
||||
message :: binary(),
|
||||
file :: binary(),
|
||||
module :: binary(),
|
||||
function :: binary(),
|
||||
line :: integer(),
|
||||
kind :: gleeunit@internal@gleam_panic:panic_kind()
|
||||
}).
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
-record(let_assert, {
|
||||
start :: integer(),
|
||||
'end' :: integer(),
|
||||
pattern_start :: integer(),
|
||||
pattern_end :: integer(),
|
||||
value :: gleam@dynamic:dynamic_()
|
||||
}).
|
||||
|
|
@ -0,0 +1 @@
|
|||
-record(literal, {value :: gleam@dynamic:dynamic_()}).
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
-record(other_expression, {
|
||||
expression :: gleeunit@internal@gleam_panic:asserted_expression()
|
||||
}).
|
||||
|
|
@ -0,0 +1 @@
|
|||
-record(state, {passed :: integer(), failed :: integer(), skipped :: integer()}).
|
||||
16
build/packages/gleeunit/src/gleeunit.app.src
Normal file
16
build/packages/gleeunit/src/gleeunit.app.src
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{application, gleeunit, [
|
||||
{vsn, "1.9.0"},
|
||||
{applications, [gleam_stdlib]},
|
||||
{description, "A simple test runner for Gleam, using EUnit on Erlang"},
|
||||
{modules, [erlang_test_module,
|
||||
gleeunit,
|
||||
gleeunit@@main,
|
||||
gleeunit@internal@gleam_panic,
|
||||
gleeunit@internal@reporting,
|
||||
gleeunit@should,
|
||||
gleeunit_ffi,
|
||||
gleeunit_gleam_panic_ffi,
|
||||
gleeunit_progress,
|
||||
gleeunit_test_ffi]},
|
||||
{registered, []}
|
||||
]}.
|
||||
89
build/packages/gleeunit/src/gleeunit.erl
Normal file
89
build/packages/gleeunit/src/gleeunit.erl
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
-module(gleeunit).
|
||||
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
|
||||
-define(FILEPATH, "src/gleeunit.gleam").
|
||||
-export([main/0]).
|
||||
-export_type([atom_/0, encoding/0, report_module_name/0, gleeunit_progress_option/0, eunit_option/0]).
|
||||
|
||||
-if(?OTP_RELEASE >= 27).
|
||||
-define(MODULEDOC(Str), -moduledoc(Str)).
|
||||
-define(DOC(Str), -doc(Str)).
|
||||
-else.
|
||||
-define(MODULEDOC(Str), -compile([])).
|
||||
-define(DOC(Str), -compile([])).
|
||||
-endif.
|
||||
|
||||
-type atom_() :: any().
|
||||
|
||||
-type encoding() :: utf8.
|
||||
|
||||
-type report_module_name() :: gleeunit_progress.
|
||||
|
||||
-type gleeunit_progress_option() :: {colored, boolean()}.
|
||||
|
||||
-type eunit_option() :: verbose |
|
||||
no_tty |
|
||||
{report, {report_module_name(), list(gleeunit_progress_option())}} |
|
||||
{scale_timeouts, integer()}.
|
||||
|
||||
-file("src/gleeunit.gleam", 42).
|
||||
-spec gleam_to_erlang_module_name(binary()) -> binary().
|
||||
gleam_to_erlang_module_name(Path) ->
|
||||
case gleam_stdlib:string_ends_with(Path, <<".gleam"/utf8>>) of
|
||||
true ->
|
||||
_pipe = Path,
|
||||
_pipe@1 = gleam@string:replace(
|
||||
_pipe,
|
||||
<<".gleam"/utf8>>,
|
||||
<<""/utf8>>
|
||||
),
|
||||
gleam@string:replace(_pipe@1, <<"/"/utf8>>, <<"@"/utf8>>);
|
||||
|
||||
false ->
|
||||
_pipe@2 = Path,
|
||||
_pipe@3 = gleam@string:split(_pipe@2, <<"/"/utf8>>),
|
||||
_pipe@4 = gleam@list:last(_pipe@3),
|
||||
_pipe@5 = gleam@result:unwrap(_pipe@4, Path),
|
||||
gleam@string:replace(_pipe@5, <<".erl"/utf8>>, <<""/utf8>>)
|
||||
end.
|
||||
|
||||
-file("src/gleeunit.gleam", 18).
|
||||
-spec do_main() -> nil.
|
||||
do_main() ->
|
||||
Options = [verbose,
|
||||
no_tty,
|
||||
{report, {gleeunit_progress, [{colored, true}]}},
|
||||
{scale_timeouts, 10}],
|
||||
Result = begin
|
||||
_pipe = gleeunit_ffi:find_files(
|
||||
<<"**/*.{erl,gleam}"/utf8>>,
|
||||
<<"test"/utf8>>
|
||||
),
|
||||
_pipe@1 = gleam@list:map(_pipe, fun gleam_to_erlang_module_name/1),
|
||||
_pipe@2 = gleam@list:map(
|
||||
_pipe@1,
|
||||
fun(_capture) -> erlang:binary_to_atom(_capture, utf8) end
|
||||
),
|
||||
gleeunit_ffi:run_eunit(_pipe@2, Options)
|
||||
end,
|
||||
Code = case Result of
|
||||
{ok, _} ->
|
||||
0;
|
||||
|
||||
{error, _} ->
|
||||
1
|
||||
end,
|
||||
erlang:halt(Code).
|
||||
|
||||
-file("src/gleeunit.gleam", 13).
|
||||
?DOC(
|
||||
" Find and run all test functions for the current project using Erlang's EUnit\n"
|
||||
" test framework, or a custom JavaScript test runner.\n"
|
||||
"\n"
|
||||
" Any Erlang or Gleam function in the `test` directory with a name ending in\n"
|
||||
" `_test` is considered a test function and will be run.\n"
|
||||
"\n"
|
||||
" A test that panics is considered a failure.\n"
|
||||
).
|
||||
-spec main() -> nil.
|
||||
main() ->
|
||||
do_main().
|
||||
86
build/packages/gleeunit/src/gleeunit.gleam
Normal file
86
build/packages/gleeunit/src/gleeunit.gleam
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
import gleam/list
|
||||
import gleam/result
|
||||
import gleam/string
|
||||
|
||||
/// Find and run all test functions for the current project using Erlang's EUnit
|
||||
/// test framework, or a custom JavaScript test runner.
|
||||
///
|
||||
/// Any Erlang or Gleam function in the `test` directory with a name ending in
|
||||
/// `_test` is considered a test function and will be run.
|
||||
///
|
||||
/// A test that panics is considered a failure.
|
||||
///
|
||||
pub fn main() -> Nil {
|
||||
do_main()
|
||||
}
|
||||
|
||||
@external(javascript, "./gleeunit_ffi.mjs", "main")
|
||||
fn do_main() -> Nil {
|
||||
let options = [
|
||||
Verbose,
|
||||
NoTty,
|
||||
Report(#(GleeunitProgress, [Colored(True)])),
|
||||
ScaleTimeouts(10),
|
||||
]
|
||||
|
||||
let result =
|
||||
find_files(matching: "**/*.{erl,gleam}", in: "test")
|
||||
|> list.map(gleam_to_erlang_module_name)
|
||||
|> list.map(dangerously_convert_string_to_atom(_, Utf8))
|
||||
|> run_eunit(options)
|
||||
|
||||
let code = case result {
|
||||
Ok(_) -> 0
|
||||
Error(_) -> 1
|
||||
}
|
||||
halt(code)
|
||||
}
|
||||
|
||||
@external(erlang, "erlang", "halt")
|
||||
fn halt(a: Int) -> Nil
|
||||
|
||||
fn gleam_to_erlang_module_name(path: String) -> String {
|
||||
case string.ends_with(path, ".gleam") {
|
||||
True ->
|
||||
path
|
||||
|> string.replace(".gleam", "")
|
||||
|> string.replace("/", "@")
|
||||
|
||||
False ->
|
||||
path
|
||||
|> string.split("/")
|
||||
|> list.last
|
||||
|> result.unwrap(path)
|
||||
|> string.replace(".erl", "")
|
||||
}
|
||||
}
|
||||
|
||||
@external(erlang, "gleeunit_ffi", "find_files")
|
||||
fn find_files(matching matching: String, in in: String) -> List(String)
|
||||
|
||||
type Atom
|
||||
|
||||
type Encoding {
|
||||
Utf8
|
||||
}
|
||||
|
||||
@external(erlang, "erlang", "binary_to_atom")
|
||||
fn dangerously_convert_string_to_atom(a: String, b: Encoding) -> Atom
|
||||
|
||||
type ReportModuleName {
|
||||
GleeunitProgress
|
||||
}
|
||||
|
||||
type GleeunitProgressOption {
|
||||
Colored(Bool)
|
||||
}
|
||||
|
||||
type EunitOption {
|
||||
Verbose
|
||||
NoTty
|
||||
Report(#(ReportModuleName, List(GleeunitProgressOption)))
|
||||
ScaleTimeouts(Int)
|
||||
}
|
||||
|
||||
@external(erlang, "gleeunit_ffi", "run_eunit")
|
||||
fn run_eunit(a: List(Atom), b: List(EunitOption)) -> Result(Nil, a)
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
import gleam/dynamic
|
||||
|
||||
pub type GleamPanic {
|
||||
GleamPanic(
|
||||
message: String,
|
||||
file: String,
|
||||
module: String,
|
||||
function: String,
|
||||
line: Int,
|
||||
kind: PanicKind,
|
||||
)
|
||||
}
|
||||
|
||||
pub type PanicKind {
|
||||
Todo
|
||||
Panic
|
||||
LetAssert(
|
||||
start: Int,
|
||||
end: Int,
|
||||
pattern_start: Int,
|
||||
pattern_end: Int,
|
||||
value: dynamic.Dynamic,
|
||||
)
|
||||
Assert(start: Int, end: Int, expression_start: Int, kind: AssertKind)
|
||||
}
|
||||
|
||||
pub type AssertKind {
|
||||
BinaryOperator(
|
||||
operator: String,
|
||||
left: AssertedExpression,
|
||||
right: AssertedExpression,
|
||||
)
|
||||
FunctionCall(arguments: List(AssertedExpression))
|
||||
OtherExpression(expression: AssertedExpression)
|
||||
}
|
||||
|
||||
pub type AssertedExpression {
|
||||
AssertedExpression(start: Int, end: Int, kind: ExpressionKind)
|
||||
}
|
||||
|
||||
pub type ExpressionKind {
|
||||
Literal(value: dynamic.Dynamic)
|
||||
Expression(value: dynamic.Dynamic)
|
||||
Unevaluated
|
||||
}
|
||||
|
||||
@external(erlang, "gleeunit_gleam_panic_ffi", "from_dynamic")
|
||||
@external(javascript, "./gleeunit_gleam_panic_ffi.mjs", "from_dynamic")
|
||||
pub fn from_dynamic(data: dynamic.Dynamic) -> Result(GleamPanic, Nil)
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
-module(gleeunit_gleam_panic_ffi).
|
||||
-export([from_dynamic/1]).
|
||||
|
||||
from_dynamic(#{
|
||||
gleam_error := assert,
|
||||
start := Start,
|
||||
'end' := End,
|
||||
expression_start := EStart
|
||||
} = E) ->
|
||||
wrap(E, {assert, Start, End, EStart, assert_kind(E)});
|
||||
from_dynamic(#{
|
||||
gleam_error := let_assert,
|
||||
start := Start,
|
||||
'end' := End,
|
||||
pattern_start := PStart,
|
||||
pattern_end := PEnd,
|
||||
value := Value
|
||||
} = E) ->
|
||||
wrap(E, {let_assert, Start, End, PStart, PEnd, Value});
|
||||
from_dynamic(#{gleam_error := panic} = E) ->
|
||||
wrap(E, panic);
|
||||
from_dynamic(#{gleam_error := todo} = E) ->
|
||||
wrap(E, todo);
|
||||
from_dynamic(_) ->
|
||||
{error, nil}.
|
||||
|
||||
assert_kind(#{kind := binary_operator, left := L, right := R, operator := O}) ->
|
||||
{binary_operator, atom_to_binary(O), expression(L), expression(R)};
|
||||
assert_kind(#{kind := function_call, arguments := Arguments}) ->
|
||||
{function_call, lists:map(fun expression/1, Arguments)};
|
||||
assert_kind(#{kind := expression, expression := Expression}) ->
|
||||
{other_expression, expression(Expression)}.
|
||||
|
||||
expression(#{start := S, 'end' := E, kind := literal, value := Value}) ->
|
||||
{asserted_expression, S, E, {literal, Value}};
|
||||
expression(#{start := S, 'end' := E, kind := expression, value := Value}) ->
|
||||
{asserted_expression, S, E, {expression, Value}};
|
||||
expression(#{start := S, 'end' := E, kind := unevaluated}) ->
|
||||
{asserted_expression, S, E, unevaluated}.
|
||||
|
||||
wrap(#{
|
||||
gleam_error := _,
|
||||
file := File,
|
||||
message := Message,
|
||||
module := Module,
|
||||
function := Function,
|
||||
line := Line
|
||||
}, Kind) ->
|
||||
{ok, {gleam_panic, Message, File, Module, Function, Line, Kind}}.
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
import { Result$Ok, Result$Error, List$Empty, List$NonEmpty } from "../../gleam.mjs";
|
||||
import {
|
||||
GleamPanic$GleamPanic,
|
||||
PanicKind$Todo,
|
||||
PanicKind$Panic,
|
||||
PanicKind$LetAssert,
|
||||
PanicKind$Assert,
|
||||
AssertKind$BinaryOperator,
|
||||
AssertKind$FunctionCall,
|
||||
AssertKind$OtherExpression,
|
||||
AssertedExpression$AssertedExpression,
|
||||
ExpressionKind$Literal,
|
||||
ExpressionKind$Expression,
|
||||
ExpressionKind$Unevaluated,
|
||||
} from "./gleam_panic.mjs";
|
||||
|
||||
export function from_dynamic(error) {
|
||||
if (!(error instanceof globalThis.Error) || !error.gleam_error) {
|
||||
return Result$Error(undefined);
|
||||
}
|
||||
|
||||
if (error.gleam_error === "todo") {
|
||||
return wrap(error, PanicKind$Todo());
|
||||
}
|
||||
|
||||
if (error.gleam_error === "panic") {
|
||||
return wrap(error, PanicKind$Panic());
|
||||
}
|
||||
|
||||
if (error.gleam_error === "let_assert") {
|
||||
let kind = PanicKind$LetAssert(
|
||||
error.start,
|
||||
error.end,
|
||||
error.pattern_start,
|
||||
error.pattern_end,
|
||||
error.value,
|
||||
);
|
||||
return wrap(error, kind);
|
||||
}
|
||||
|
||||
if (error.gleam_error === "assert") {
|
||||
let kind = PanicKind$Assert(
|
||||
error.start,
|
||||
error.end,
|
||||
error.expression_start,
|
||||
assert_kind(error),
|
||||
);
|
||||
return wrap(error, kind);
|
||||
}
|
||||
|
||||
return Result$Error(undefined);
|
||||
}
|
||||
|
||||
function assert_kind(error) {
|
||||
if (error.kind == "binary_operator") {
|
||||
return AssertKind$BinaryOperator(
|
||||
error.operator,
|
||||
expression(error.left),
|
||||
expression(error.right),
|
||||
);
|
||||
}
|
||||
|
||||
if (error.kind == "function_call") {
|
||||
let list = List$Empty();
|
||||
let i = error.arguments.length;
|
||||
while (i--) {
|
||||
list = List$NonEmpty(expression(error.arguments[i]), list);
|
||||
}
|
||||
return AssertKind$FunctionCall(list);
|
||||
}
|
||||
|
||||
return AssertKind$OtherExpression(expression(error.expression));
|
||||
}
|
||||
|
||||
function expression(data) {
|
||||
const expression = AssertedExpression$AssertedExpression(data.start, data.end, undefined);
|
||||
if (data.kind == "literal") {
|
||||
expression.kind = ExpressionKind$Literal(data.value);
|
||||
} else if (data.kind == "expression") {
|
||||
expression.kind = ExpressionKind$Expression(data.value);
|
||||
} else {
|
||||
expression.kind = ExpressionKind$Unevaluated();
|
||||
}
|
||||
return expression;
|
||||
}
|
||||
|
||||
function wrap(e, kind) {
|
||||
return Result$Ok(
|
||||
GleamPanic$GleamPanic(e.message, e.file, e.module, e.function, e.line, kind),
|
||||
);
|
||||
}
|
||||
240
build/packages/gleeunit/src/gleeunit/internal/reporting.gleam
Normal file
240
build/packages/gleeunit/src/gleeunit/internal/reporting.gleam
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
import gleam/bit_array
|
||||
import gleam/dynamic
|
||||
import gleam/int
|
||||
import gleam/io
|
||||
import gleam/list
|
||||
import gleam/option.{type Option}
|
||||
import gleam/result
|
||||
import gleam/string
|
||||
import gleeunit/internal/gleam_panic.{type GleamPanic}
|
||||
|
||||
pub type State {
|
||||
State(passed: Int, failed: Int, skipped: Int)
|
||||
}
|
||||
|
||||
pub fn new_state() -> State {
|
||||
State(passed: 0, failed: 0, skipped: 0)
|
||||
}
|
||||
|
||||
pub fn finished(state: State) -> Int {
|
||||
case state {
|
||||
State(passed: 0, failed: 0, skipped: 0) -> {
|
||||
io.println("\nNo tests found!")
|
||||
1
|
||||
}
|
||||
State(failed: 0, skipped: 0, ..) -> {
|
||||
let message =
|
||||
"\n" <> int.to_string(state.passed) <> " passed, no failures"
|
||||
io.println(green(message))
|
||||
0
|
||||
}
|
||||
State(skipped: 0, ..) -> {
|
||||
let message =
|
||||
"\n"
|
||||
<> int.to_string(state.passed)
|
||||
<> " passed, "
|
||||
<> int.to_string(state.failed)
|
||||
<> " failures"
|
||||
io.println(red(message))
|
||||
1
|
||||
}
|
||||
State(failed: 0, ..) -> {
|
||||
let message =
|
||||
"\n"
|
||||
<> int.to_string(state.passed)
|
||||
<> " passed, 0 failures, "
|
||||
<> int.to_string(state.skipped)
|
||||
<> " skipped"
|
||||
io.println(yellow(message))
|
||||
1
|
||||
}
|
||||
State(..) -> {
|
||||
let message =
|
||||
"\n"
|
||||
<> int.to_string(state.passed)
|
||||
<> " passed, "
|
||||
<> int.to_string(state.failed)
|
||||
<> " failures, "
|
||||
<> int.to_string(state.skipped)
|
||||
<> " skipped"
|
||||
io.println(red(message))
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn test_passed(state: State) -> State {
|
||||
io.print(green("."))
|
||||
State(..state, passed: state.passed + 1)
|
||||
}
|
||||
|
||||
pub fn test_failed(
|
||||
state: State,
|
||||
module: String,
|
||||
function: String,
|
||||
error: dynamic.Dynamic,
|
||||
) -> State {
|
||||
let message = case gleam_panic.from_dynamic(error) {
|
||||
Ok(error) -> {
|
||||
let src = option.from_result(read_file(error.file))
|
||||
format_gleam_error(error, module, function, src)
|
||||
}
|
||||
Error(_) -> format_unknown(module, function, error)
|
||||
}
|
||||
|
||||
io.print("\n" <> message)
|
||||
State(..state, failed: state.failed + 1)
|
||||
}
|
||||
|
||||
pub fn eunit_missing() -> Result(never, Nil) {
|
||||
let message = bold(red("Error")) <> ": EUnit libraries not found.
|
||||
|
||||
Your Erlang installation seems to be incomplete. If you installed Erlang using
|
||||
a package manager ensure that you have installed the full Erlang
|
||||
distribution instead of a stripped-down version.
|
||||
"
|
||||
io.print_error(message)
|
||||
Error(Nil)
|
||||
}
|
||||
|
||||
fn format_unknown(
|
||||
module: String,
|
||||
function: String,
|
||||
error: dynamic.Dynamic,
|
||||
) -> String {
|
||||
string.concat([
|
||||
grey(module <> "." <> function) <> "\n",
|
||||
"An unexpected error occurred:\n",
|
||||
"\n",
|
||||
" " <> string.inspect(error) <> "\n",
|
||||
])
|
||||
}
|
||||
|
||||
fn format_gleam_error(
|
||||
error: GleamPanic,
|
||||
module: String,
|
||||
function: String,
|
||||
src: Option(BitArray),
|
||||
) -> String {
|
||||
let location = grey(error.file <> ":" <> int.to_string(error.line))
|
||||
|
||||
case error.kind {
|
||||
gleam_panic.Panic -> {
|
||||
string.concat([
|
||||
bold(red("panic")) <> " " <> location <> "\n",
|
||||
cyan(" test") <> ": " <> module <> "." <> function <> "\n",
|
||||
cyan(" info") <> ": " <> error.message <> "\n",
|
||||
])
|
||||
}
|
||||
|
||||
gleam_panic.Todo -> {
|
||||
string.concat([
|
||||
bold(yellow("todo")) <> " " <> location <> "\n",
|
||||
cyan(" test") <> ": " <> module <> "." <> function <> "\n",
|
||||
cyan(" info") <> ": " <> error.message <> "\n",
|
||||
])
|
||||
}
|
||||
|
||||
gleam_panic.Assert(start:, end:, kind:, ..) -> {
|
||||
string.concat([
|
||||
bold(red("assert")) <> " " <> location <> "\n",
|
||||
cyan(" test") <> ": " <> module <> "." <> function <> "\n",
|
||||
code_snippet(src, start, end),
|
||||
assert_info(kind),
|
||||
cyan(" info") <> ": " <> error.message <> "\n",
|
||||
])
|
||||
}
|
||||
|
||||
gleam_panic.LetAssert(start:, end:, value:, ..) -> {
|
||||
string.concat([
|
||||
bold(red("let assert")) <> " " <> location <> "\n",
|
||||
cyan(" test") <> ": " <> module <> "." <> function <> "\n",
|
||||
code_snippet(src, start, end),
|
||||
cyan("value") <> ": " <> string.inspect(value) <> "\n",
|
||||
cyan(" info") <> ": " <> error.message <> "\n",
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_info(kind: gleam_panic.AssertKind) -> String {
|
||||
case kind {
|
||||
gleam_panic.BinaryOperator(left:, right:, ..) -> {
|
||||
string.concat([assert_value(" left", left), assert_value("right", right)])
|
||||
}
|
||||
|
||||
gleam_panic.FunctionCall(arguments:) -> {
|
||||
arguments
|
||||
|> list.index_map(fn(e, i) {
|
||||
let number = string.pad_start(int.to_string(i), 5, " ")
|
||||
assert_value(number, e)
|
||||
})
|
||||
|> string.concat
|
||||
}
|
||||
|
||||
gleam_panic.OtherExpression(..) -> ""
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_value(name: String, value: gleam_panic.AssertedExpression) -> String {
|
||||
cyan(name) <> ": " <> inspect_value(value) <> "\n"
|
||||
}
|
||||
|
||||
fn inspect_value(value: gleam_panic.AssertedExpression) -> String {
|
||||
case value.kind {
|
||||
gleam_panic.Unevaluated -> grey("unevaluated")
|
||||
gleam_panic.Literal(..) -> grey("literal")
|
||||
gleam_panic.Expression(value:) -> string.inspect(value)
|
||||
}
|
||||
}
|
||||
|
||||
fn code_snippet(src: Option(BitArray), start: Int, end: Int) -> String {
|
||||
{
|
||||
use src <- result.try(option.to_result(src, Nil))
|
||||
use snippet <- result.try(bit_array.slice(src, start, end - start))
|
||||
use snippet <- result.try(bit_array.to_string(snippet))
|
||||
let snippet = cyan(" code") <> ": " <> snippet <> "\n"
|
||||
Ok(snippet)
|
||||
}
|
||||
|> result.unwrap("")
|
||||
}
|
||||
|
||||
pub fn test_skipped(state: State, module: String, function: String) -> State {
|
||||
io.print("\n" <> module <> "." <> function <> yellow(" skipped"))
|
||||
State(..state, skipped: state.skipped + 1)
|
||||
}
|
||||
|
||||
fn bold(text: String) -> String {
|
||||
"\u{001b}[1m" <> text <> "\u{001b}[22m"
|
||||
}
|
||||
|
||||
fn cyan(text: String) -> String {
|
||||
"\u{001b}[36m" <> text <> "\u{001b}[39m"
|
||||
}
|
||||
|
||||
fn yellow(text: String) -> String {
|
||||
"\u{001b}[33m" <> text <> "\u{001b}[39m"
|
||||
}
|
||||
|
||||
fn green(text: String) -> String {
|
||||
"\u{001b}[32m" <> text <> "\u{001b}[39m"
|
||||
}
|
||||
|
||||
fn red(text: String) -> String {
|
||||
"\u{001b}[31m" <> text <> "\u{001b}[39m"
|
||||
}
|
||||
|
||||
fn grey(text: String) -> String {
|
||||
"\u{001b}[90m" <> text <> "\u{001b}[39m"
|
||||
}
|
||||
|
||||
@external(erlang, "file", "read_file")
|
||||
fn read_file(path: String) -> Result(BitArray, dynamic.Dynamic) {
|
||||
case read_file_text(path) {
|
||||
Ok(text) -> Ok(bit_array.from_string(text))
|
||||
Error(e) -> Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
@external(javascript, "../../gleeunit_ffi.mjs", "read_file")
|
||||
fn read_file_text(path: String) -> Result(String, dynamic.Dynamic)
|
||||
72
build/packages/gleeunit/src/gleeunit/should.gleam
Normal file
72
build/packages/gleeunit/src/gleeunit/should.gleam
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
//// Use the `assert` keyword instead of this module.
|
||||
|
||||
import gleam/option.{type Option, None, Some}
|
||||
import gleam/string
|
||||
|
||||
pub fn equal(a: t, b: t) -> Nil {
|
||||
case a == b {
|
||||
True -> Nil
|
||||
_ ->
|
||||
panic as string.concat([
|
||||
"\n",
|
||||
string.inspect(a),
|
||||
"\nshould equal\n",
|
||||
string.inspect(b),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn not_equal(a: t, b: t) -> Nil {
|
||||
case a != b {
|
||||
True -> Nil
|
||||
_ ->
|
||||
panic as string.concat([
|
||||
"\n",
|
||||
string.inspect(a),
|
||||
"\nshould not equal\n",
|
||||
string.inspect(b),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn be_ok(a: Result(a, e)) -> a {
|
||||
case a {
|
||||
Ok(value) -> value
|
||||
_ -> panic as string.concat(["\n", string.inspect(a), "\nshould be ok"])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn be_error(a: Result(a, e)) -> e {
|
||||
case a {
|
||||
Error(error) -> error
|
||||
_ -> panic as string.concat(["\n", string.inspect(a), "\nshould be error"])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn be_some(a: Option(a)) -> a {
|
||||
case a {
|
||||
Some(value) -> value
|
||||
_ -> panic as string.concat(["\n", string.inspect(a), "\nshould be some"])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn be_none(a: Option(a)) -> Nil {
|
||||
case a {
|
||||
None -> Nil
|
||||
_ -> panic as string.concat(["\n", string.inspect(a), "\nshould be none"])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn be_true(actual: Bool) -> Nil {
|
||||
actual
|
||||
|> equal(True)
|
||||
}
|
||||
|
||||
pub fn be_false(actual: Bool) -> Nil {
|
||||
actual
|
||||
|> equal(False)
|
||||
}
|
||||
|
||||
pub fn fail() -> Nil {
|
||||
be_true(False)
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
-module(gleeunit@internal@gleam_panic).
|
||||
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
|
||||
-define(FILEPATH, "src/gleeunit/internal/gleam_panic.gleam").
|
||||
-export([from_dynamic/1]).
|
||||
-export_type([gleam_panic/0, panic_kind/0, assert_kind/0, asserted_expression/0, expression_kind/0]).
|
||||
|
||||
-if(?OTP_RELEASE >= 27).
|
||||
-define(MODULEDOC(Str), -moduledoc(Str)).
|
||||
-define(DOC(Str), -doc(Str)).
|
||||
-else.
|
||||
-define(MODULEDOC(Str), -compile([])).
|
||||
-define(DOC(Str), -compile([])).
|
||||
-endif.
|
||||
|
||||
?MODULEDOC(false).
|
||||
|
||||
-type gleam_panic() :: {gleam_panic,
|
||||
binary(),
|
||||
binary(),
|
||||
binary(),
|
||||
binary(),
|
||||
integer(),
|
||||
panic_kind()}.
|
||||
|
||||
-type panic_kind() :: todo |
|
||||
panic |
|
||||
{let_assert,
|
||||
integer(),
|
||||
integer(),
|
||||
integer(),
|
||||
integer(),
|
||||
gleam@dynamic:dynamic_()} |
|
||||
{assert, integer(), integer(), integer(), assert_kind()}.
|
||||
|
||||
-type assert_kind() :: {binary_operator,
|
||||
binary(),
|
||||
asserted_expression(),
|
||||
asserted_expression()} |
|
||||
{function_call, list(asserted_expression())} |
|
||||
{other_expression, asserted_expression()}.
|
||||
|
||||
-type asserted_expression() :: {asserted_expression,
|
||||
integer(),
|
||||
integer(),
|
||||
expression_kind()}.
|
||||
|
||||
-type expression_kind() :: {literal, gleam@dynamic:dynamic_()} |
|
||||
{expression, gleam@dynamic:dynamic_()} |
|
||||
unevaluated.
|
||||
|
||||
-file("src/gleeunit/internal/gleam_panic.gleam", 49).
|
||||
?DOC(false).
|
||||
-spec from_dynamic(gleam@dynamic:dynamic_()) -> {ok, gleam_panic()} |
|
||||
{error, nil}.
|
||||
from_dynamic(Data) ->
|
||||
gleeunit_gleam_panic_ffi:from_dynamic(Data).
|
||||
343
build/packages/gleeunit/src/gleeunit@internal@reporting.erl
Normal file
343
build/packages/gleeunit/src/gleeunit@internal@reporting.erl
Normal file
|
|
@ -0,0 +1,343 @@
|
|||
-module(gleeunit@internal@reporting).
|
||||
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
|
||||
-define(FILEPATH, "src/gleeunit/internal/reporting.gleam").
|
||||
-export([new_state/0, test_skipped/3, test_passed/1, finished/1, eunit_missing/0, test_failed/4]).
|
||||
-export_type([state/0]).
|
||||
|
||||
-if(?OTP_RELEASE >= 27).
|
||||
-define(MODULEDOC(Str), -moduledoc(Str)).
|
||||
-define(DOC(Str), -doc(Str)).
|
||||
-else.
|
||||
-define(MODULEDOC(Str), -compile([])).
|
||||
-define(DOC(Str), -compile([])).
|
||||
-endif.
|
||||
|
||||
?MODULEDOC(false).
|
||||
|
||||
-type state() :: {state, integer(), integer(), integer()}.
|
||||
|
||||
-file("src/gleeunit/internal/reporting.gleam", 15).
|
||||
?DOC(false).
|
||||
-spec new_state() -> state().
|
||||
new_state() ->
|
||||
{state, 0, 0, 0}.
|
||||
|
||||
-file("src/gleeunit/internal/reporting.gleam", 207).
|
||||
?DOC(false).
|
||||
-spec bold(binary()) -> binary().
|
||||
bold(Text) ->
|
||||
<<<<"\x{001b}[1m"/utf8, Text/binary>>/binary, "\x{001b}[22m"/utf8>>.
|
||||
|
||||
-file("src/gleeunit/internal/reporting.gleam", 211).
|
||||
?DOC(false).
|
||||
-spec cyan(binary()) -> binary().
|
||||
cyan(Text) ->
|
||||
<<<<"\x{001b}[36m"/utf8, Text/binary>>/binary, "\x{001b}[39m"/utf8>>.
|
||||
|
||||
-file("src/gleeunit/internal/reporting.gleam", 191).
|
||||
?DOC(false).
|
||||
-spec code_snippet(gleam@option:option(bitstring()), integer(), integer()) -> binary().
|
||||
code_snippet(Src, Start, End) ->
|
||||
_pipe = begin
|
||||
gleam@result:'try'(
|
||||
gleam@option:to_result(Src, nil),
|
||||
fun(Src@1) ->
|
||||
gleam@result:'try'(
|
||||
gleam_stdlib:bit_array_slice(Src@1, Start, End - Start),
|
||||
fun(Snippet) ->
|
||||
gleam@result:'try'(
|
||||
gleam@bit_array:to_string(Snippet),
|
||||
fun(Snippet@1) ->
|
||||
Snippet@2 = <<<<<<(cyan(<<" code"/utf8>>))/binary,
|
||||
": "/utf8>>/binary,
|
||||
Snippet@1/binary>>/binary,
|
||||
"\n"/utf8>>,
|
||||
{ok, Snippet@2}
|
||||
end
|
||||
)
|
||||
end
|
||||
)
|
||||
end
|
||||
)
|
||||
end,
|
||||
gleam@result:unwrap(_pipe, <<""/utf8>>).
|
||||
|
||||
-file("src/gleeunit/internal/reporting.gleam", 215).
|
||||
?DOC(false).
|
||||
-spec yellow(binary()) -> binary().
|
||||
yellow(Text) ->
|
||||
<<<<"\x{001b}[33m"/utf8, Text/binary>>/binary, "\x{001b}[39m"/utf8>>.
|
||||
|
||||
-file("src/gleeunit/internal/reporting.gleam", 202).
|
||||
?DOC(false).
|
||||
-spec test_skipped(state(), binary(), binary()) -> state().
|
||||
test_skipped(State, Module, Function) ->
|
||||
gleam_stdlib:print(
|
||||
<<<<<<<<"\n"/utf8, Module/binary>>/binary, "."/utf8>>/binary,
|
||||
Function/binary>>/binary,
|
||||
(yellow(<<" skipped"/utf8>>))/binary>>
|
||||
),
|
||||
{state,
|
||||
erlang:element(2, State),
|
||||
erlang:element(3, State),
|
||||
erlang:element(4, State) + 1}.
|
||||
|
||||
-file("src/gleeunit/internal/reporting.gleam", 219).
|
||||
?DOC(false).
|
||||
-spec green(binary()) -> binary().
|
||||
green(Text) ->
|
||||
<<<<"\x{001b}[32m"/utf8, Text/binary>>/binary, "\x{001b}[39m"/utf8>>.
|
||||
|
||||
-file("src/gleeunit/internal/reporting.gleam", 66).
|
||||
?DOC(false).
|
||||
-spec test_passed(state()) -> state().
|
||||
test_passed(State) ->
|
||||
gleam_stdlib:print(green(<<"."/utf8>>)),
|
||||
{state,
|
||||
erlang:element(2, State) + 1,
|
||||
erlang:element(3, State),
|
||||
erlang:element(4, State)}.
|
||||
|
||||
-file("src/gleeunit/internal/reporting.gleam", 223).
|
||||
?DOC(false).
|
||||
-spec red(binary()) -> binary().
|
||||
red(Text) ->
|
||||
<<<<"\x{001b}[31m"/utf8, Text/binary>>/binary, "\x{001b}[39m"/utf8>>.
|
||||
|
||||
-file("src/gleeunit/internal/reporting.gleam", 19).
|
||||
?DOC(false).
|
||||
-spec finished(state()) -> integer().
|
||||
finished(State) ->
|
||||
case State of
|
||||
{state, 0, 0, 0} ->
|
||||
gleam_stdlib:println(<<"\nNo tests found!"/utf8>>),
|
||||
1;
|
||||
|
||||
{state, _, 0, 0} ->
|
||||
Message = <<<<"\n"/utf8,
|
||||
(erlang:integer_to_binary(erlang:element(2, State)))/binary>>/binary,
|
||||
" passed, no failures"/utf8>>,
|
||||
gleam_stdlib:println(green(Message)),
|
||||
0;
|
||||
|
||||
{state, _, _, 0} ->
|
||||
Message@1 = <<<<<<<<"\n"/utf8,
|
||||
(erlang:integer_to_binary(erlang:element(2, State)))/binary>>/binary,
|
||||
" passed, "/utf8>>/binary,
|
||||
(erlang:integer_to_binary(erlang:element(3, State)))/binary>>/binary,
|
||||
" failures"/utf8>>,
|
||||
gleam_stdlib:println(red(Message@1)),
|
||||
1;
|
||||
|
||||
{state, _, 0, _} ->
|
||||
Message@2 = <<<<<<<<"\n"/utf8,
|
||||
(erlang:integer_to_binary(erlang:element(2, State)))/binary>>/binary,
|
||||
" passed, 0 failures, "/utf8>>/binary,
|
||||
(erlang:integer_to_binary(erlang:element(4, State)))/binary>>/binary,
|
||||
" skipped"/utf8>>,
|
||||
gleam_stdlib:println(yellow(Message@2)),
|
||||
1;
|
||||
|
||||
{state, _, _, _} ->
|
||||
Message@3 = <<<<<<<<<<<<"\n"/utf8,
|
||||
(erlang:integer_to_binary(
|
||||
erlang:element(2, State)
|
||||
))/binary>>/binary,
|
||||
" passed, "/utf8>>/binary,
|
||||
(erlang:integer_to_binary(erlang:element(3, State)))/binary>>/binary,
|
||||
" failures, "/utf8>>/binary,
|
||||
(erlang:integer_to_binary(erlang:element(4, State)))/binary>>/binary,
|
||||
" skipped"/utf8>>,
|
||||
gleam_stdlib:println(red(Message@3)),
|
||||
1
|
||||
end.
|
||||
|
||||
-file("src/gleeunit/internal/reporting.gleam", 89).
|
||||
?DOC(false).
|
||||
-spec eunit_missing() -> {ok, any()} | {error, nil}.
|
||||
eunit_missing() ->
|
||||
Message = <<(bold(red(<<"Error"/utf8>>)))/binary,
|
||||
": EUnit libraries not found.
|
||||
|
||||
Your Erlang installation seems to be incomplete. If you installed Erlang using
|
||||
a package manager ensure that you have installed the full Erlang
|
||||
distribution instead of a stripped-down version.
|
||||
"/utf8>>,
|
||||
gleam_stdlib:print_error(Message),
|
||||
{error, nil}.
|
||||
|
||||
-file("src/gleeunit/internal/reporting.gleam", 227).
|
||||
?DOC(false).
|
||||
-spec grey(binary()) -> binary().
|
||||
grey(Text) ->
|
||||
<<<<"\x{001b}[90m"/utf8, Text/binary>>/binary, "\x{001b}[39m"/utf8>>.
|
||||
|
||||
-file("src/gleeunit/internal/reporting.gleam", 100).
|
||||
?DOC(false).
|
||||
-spec format_unknown(binary(), binary(), gleam@dynamic:dynamic_()) -> binary().
|
||||
format_unknown(Module, Function, Error) ->
|
||||
erlang:list_to_binary(
|
||||
[<<(grey(<<<<Module/binary, "."/utf8>>/binary, Function/binary>>))/binary,
|
||||
"\n"/utf8>>,
|
||||
<<"An unexpected error occurred:\n"/utf8>>,
|
||||
<<"\n"/utf8>>,
|
||||
<<<<" "/utf8, (gleam@string:inspect(Error))/binary>>/binary,
|
||||
"\n"/utf8>>]
|
||||
).
|
||||
|
||||
-file("src/gleeunit/internal/reporting.gleam", 183).
|
||||
?DOC(false).
|
||||
-spec inspect_value(gleeunit@internal@gleam_panic:asserted_expression()) -> binary().
|
||||
inspect_value(Value) ->
|
||||
case erlang:element(4, Value) of
|
||||
unevaluated ->
|
||||
grey(<<"unevaluated"/utf8>>);
|
||||
|
||||
{literal, _} ->
|
||||
grey(<<"literal"/utf8>>);
|
||||
|
||||
{expression, Value@1} ->
|
||||
gleam@string:inspect(Value@1)
|
||||
end.
|
||||
|
||||
-file("src/gleeunit/internal/reporting.gleam", 179).
|
||||
?DOC(false).
|
||||
-spec assert_value(
|
||||
binary(),
|
||||
gleeunit@internal@gleam_panic:asserted_expression()
|
||||
) -> binary().
|
||||
assert_value(Name, Value) ->
|
||||
<<<<<<(cyan(Name))/binary, ": "/utf8>>/binary,
|
||||
(inspect_value(Value))/binary>>/binary,
|
||||
"\n"/utf8>>.
|
||||
|
||||
-file("src/gleeunit/internal/reporting.gleam", 160).
|
||||
?DOC(false).
|
||||
-spec assert_info(gleeunit@internal@gleam_panic:assert_kind()) -> binary().
|
||||
assert_info(Kind) ->
|
||||
case Kind of
|
||||
{binary_operator, _, Left, Right} ->
|
||||
erlang:list_to_binary(
|
||||
[assert_value(<<" left"/utf8>>, Left),
|
||||
assert_value(<<"right"/utf8>>, Right)]
|
||||
);
|
||||
|
||||
{function_call, Arguments} ->
|
||||
_pipe = Arguments,
|
||||
_pipe@1 = gleam@list:index_map(
|
||||
_pipe,
|
||||
fun(E, I) ->
|
||||
Number = gleam@string:pad_start(
|
||||
erlang:integer_to_binary(I),
|
||||
5,
|
||||
<<" "/utf8>>
|
||||
),
|
||||
assert_value(Number, E)
|
||||
end
|
||||
),
|
||||
erlang:list_to_binary(_pipe@1);
|
||||
|
||||
{other_expression, _} ->
|
||||
<<""/utf8>>
|
||||
end.
|
||||
|
||||
-file("src/gleeunit/internal/reporting.gleam", 113).
|
||||
?DOC(false).
|
||||
-spec format_gleam_error(
|
||||
gleeunit@internal@gleam_panic:gleam_panic(),
|
||||
binary(),
|
||||
binary(),
|
||||
gleam@option:option(bitstring())
|
||||
) -> binary().
|
||||
format_gleam_error(Error, Module, Function, Src) ->
|
||||
Location = grey(
|
||||
<<<<(erlang:element(3, Error))/binary, ":"/utf8>>/binary,
|
||||
(erlang:integer_to_binary(erlang:element(6, Error)))/binary>>
|
||||
),
|
||||
case erlang:element(7, Error) of
|
||||
panic ->
|
||||
erlang:list_to_binary(
|
||||
[<<<<<<(bold(red(<<"panic"/utf8>>)))/binary, " "/utf8>>/binary,
|
||||
Location/binary>>/binary,
|
||||
"\n"/utf8>>,
|
||||
<<<<<<<<<<(cyan(<<" test"/utf8>>))/binary, ": "/utf8>>/binary,
|
||||
Module/binary>>/binary,
|
||||
"."/utf8>>/binary,
|
||||
Function/binary>>/binary,
|
||||
"\n"/utf8>>,
|
||||
<<<<<<(cyan(<<" info"/utf8>>))/binary, ": "/utf8>>/binary,
|
||||
(erlang:element(2, Error))/binary>>/binary,
|
||||
"\n"/utf8>>]
|
||||
);
|
||||
|
||||
todo ->
|
||||
erlang:list_to_binary(
|
||||
[<<<<<<(bold(yellow(<<"todo"/utf8>>)))/binary, " "/utf8>>/binary,
|
||||
Location/binary>>/binary,
|
||||
"\n"/utf8>>,
|
||||
<<<<<<<<<<(cyan(<<" test"/utf8>>))/binary, ": "/utf8>>/binary,
|
||||
Module/binary>>/binary,
|
||||
"."/utf8>>/binary,
|
||||
Function/binary>>/binary,
|
||||
"\n"/utf8>>,
|
||||
<<<<<<(cyan(<<" info"/utf8>>))/binary, ": "/utf8>>/binary,
|
||||
(erlang:element(2, Error))/binary>>/binary,
|
||||
"\n"/utf8>>]
|
||||
);
|
||||
|
||||
{assert, Start, End, _, Kind} ->
|
||||
erlang:list_to_binary(
|
||||
[<<<<<<(bold(red(<<"assert"/utf8>>)))/binary, " "/utf8>>/binary,
|
||||
Location/binary>>/binary,
|
||||
"\n"/utf8>>,
|
||||
<<<<<<<<<<(cyan(<<" test"/utf8>>))/binary, ": "/utf8>>/binary,
|
||||
Module/binary>>/binary,
|
||||
"."/utf8>>/binary,
|
||||
Function/binary>>/binary,
|
||||
"\n"/utf8>>,
|
||||
code_snippet(Src, Start, End),
|
||||
assert_info(Kind),
|
||||
<<<<<<(cyan(<<" info"/utf8>>))/binary, ": "/utf8>>/binary,
|
||||
(erlang:element(2, Error))/binary>>/binary,
|
||||
"\n"/utf8>>]
|
||||
);
|
||||
|
||||
{let_assert, Start@1, End@1, _, _, Value} ->
|
||||
erlang:list_to_binary(
|
||||
[<<<<<<(bold(red(<<"let assert"/utf8>>)))/binary, " "/utf8>>/binary,
|
||||
Location/binary>>/binary,
|
||||
"\n"/utf8>>,
|
||||
<<<<<<<<<<(cyan(<<" test"/utf8>>))/binary, ": "/utf8>>/binary,
|
||||
Module/binary>>/binary,
|
||||
"."/utf8>>/binary,
|
||||
Function/binary>>/binary,
|
||||
"\n"/utf8>>,
|
||||
code_snippet(Src, Start@1, End@1),
|
||||
<<<<<<(cyan(<<"value"/utf8>>))/binary, ": "/utf8>>/binary,
|
||||
(gleam@string:inspect(Value))/binary>>/binary,
|
||||
"\n"/utf8>>,
|
||||
<<<<<<(cyan(<<" info"/utf8>>))/binary, ": "/utf8>>/binary,
|
||||
(erlang:element(2, Error))/binary>>/binary,
|
||||
"\n"/utf8>>]
|
||||
)
|
||||
end.
|
||||
|
||||
-file("src/gleeunit/internal/reporting.gleam", 71).
|
||||
?DOC(false).
|
||||
-spec test_failed(state(), binary(), binary(), gleam@dynamic:dynamic_()) -> state().
|
||||
test_failed(State, Module, Function, Error) ->
|
||||
Message = case gleeunit_gleam_panic_ffi:from_dynamic(Error) of
|
||||
{ok, Error@1} ->
|
||||
Src = gleam@option:from_result(
|
||||
file:read_file(erlang:element(3, Error@1))
|
||||
),
|
||||
format_gleam_error(Error@1, Module, Function, Src);
|
||||
|
||||
{error, _} ->
|
||||
format_unknown(Module, Function, Error)
|
||||
end,
|
||||
gleam_stdlib:print(<<"\n"/utf8, Message/binary>>),
|
||||
{state,
|
||||
erlang:element(2, State),
|
||||
erlang:element(3, State) + 1,
|
||||
erlang:element(4, State)}.
|
||||
153
build/packages/gleeunit/src/gleeunit@should.erl
Normal file
153
build/packages/gleeunit/src/gleeunit@should.erl
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
-module(gleeunit@should).
|
||||
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
|
||||
-define(FILEPATH, "src/gleeunit/should.gleam").
|
||||
-export([equal/2, not_equal/2, be_ok/1, be_error/1, be_some/1, be_none/1, be_true/1, be_false/1, fail/0]).
|
||||
|
||||
-if(?OTP_RELEASE >= 27).
|
||||
-define(MODULEDOC(Str), -moduledoc(Str)).
|
||||
-define(DOC(Str), -doc(Str)).
|
||||
-else.
|
||||
-define(MODULEDOC(Str), -compile([])).
|
||||
-define(DOC(Str), -compile([])).
|
||||
-endif.
|
||||
|
||||
?MODULEDOC(" Use the `assert` keyword instead of this module.\n").
|
||||
|
||||
-file("src/gleeunit/should.gleam", 6).
|
||||
-spec equal(DOF, DOF) -> nil.
|
||||
equal(A, B) ->
|
||||
case A =:= B of
|
||||
true ->
|
||||
nil;
|
||||
|
||||
_ ->
|
||||
erlang:error(#{gleam_error => panic,
|
||||
message => erlang:list_to_binary(
|
||||
[<<"\n"/utf8>>,
|
||||
gleam@string:inspect(A),
|
||||
<<"\nshould equal\n"/utf8>>,
|
||||
gleam@string:inspect(B)]
|
||||
),
|
||||
file => <<?FILEPATH/utf8>>,
|
||||
module => <<"gleeunit/should"/utf8>>,
|
||||
function => <<"equal"/utf8>>,
|
||||
line => 10})
|
||||
end.
|
||||
|
||||
-file("src/gleeunit/should.gleam", 19).
|
||||
-spec not_equal(DOG, DOG) -> nil.
|
||||
not_equal(A, B) ->
|
||||
case A /= B of
|
||||
true ->
|
||||
nil;
|
||||
|
||||
_ ->
|
||||
erlang:error(#{gleam_error => panic,
|
||||
message => erlang:list_to_binary(
|
||||
[<<"\n"/utf8>>,
|
||||
gleam@string:inspect(A),
|
||||
<<"\nshould not equal\n"/utf8>>,
|
||||
gleam@string:inspect(B)]
|
||||
),
|
||||
file => <<?FILEPATH/utf8>>,
|
||||
module => <<"gleeunit/should"/utf8>>,
|
||||
function => <<"not_equal"/utf8>>,
|
||||
line => 23})
|
||||
end.
|
||||
|
||||
-file("src/gleeunit/should.gleam", 32).
|
||||
-spec be_ok({ok, DOH} | {error, any()}) -> DOH.
|
||||
be_ok(A) ->
|
||||
case A of
|
||||
{ok, Value} ->
|
||||
Value;
|
||||
|
||||
_ ->
|
||||
erlang:error(#{gleam_error => panic,
|
||||
message => erlang:list_to_binary(
|
||||
[<<"\n"/utf8>>,
|
||||
gleam@string:inspect(A),
|
||||
<<"\nshould be ok"/utf8>>]
|
||||
),
|
||||
file => <<?FILEPATH/utf8>>,
|
||||
module => <<"gleeunit/should"/utf8>>,
|
||||
function => <<"be_ok"/utf8>>,
|
||||
line => 35})
|
||||
end.
|
||||
|
||||
-file("src/gleeunit/should.gleam", 39).
|
||||
-spec be_error({ok, any()} | {error, DOM}) -> DOM.
|
||||
be_error(A) ->
|
||||
case A of
|
||||
{error, Error} ->
|
||||
Error;
|
||||
|
||||
_ ->
|
||||
erlang:error(#{gleam_error => panic,
|
||||
message => erlang:list_to_binary(
|
||||
[<<"\n"/utf8>>,
|
||||
gleam@string:inspect(A),
|
||||
<<"\nshould be error"/utf8>>]
|
||||
),
|
||||
file => <<?FILEPATH/utf8>>,
|
||||
module => <<"gleeunit/should"/utf8>>,
|
||||
function => <<"be_error"/utf8>>,
|
||||
line => 42})
|
||||
end.
|
||||
|
||||
-file("src/gleeunit/should.gleam", 46).
|
||||
-spec be_some(gleam@option:option(DOP)) -> DOP.
|
||||
be_some(A) ->
|
||||
case A of
|
||||
{some, Value} ->
|
||||
Value;
|
||||
|
||||
_ ->
|
||||
erlang:error(#{gleam_error => panic,
|
||||
message => erlang:list_to_binary(
|
||||
[<<"\n"/utf8>>,
|
||||
gleam@string:inspect(A),
|
||||
<<"\nshould be some"/utf8>>]
|
||||
),
|
||||
file => <<?FILEPATH/utf8>>,
|
||||
module => <<"gleeunit/should"/utf8>>,
|
||||
function => <<"be_some"/utf8>>,
|
||||
line => 49})
|
||||
end.
|
||||
|
||||
-file("src/gleeunit/should.gleam", 53).
|
||||
-spec be_none(gleam@option:option(any())) -> nil.
|
||||
be_none(A) ->
|
||||
case A of
|
||||
none ->
|
||||
nil;
|
||||
|
||||
_ ->
|
||||
erlang:error(#{gleam_error => panic,
|
||||
message => erlang:list_to_binary(
|
||||
[<<"\n"/utf8>>,
|
||||
gleam@string:inspect(A),
|
||||
<<"\nshould be none"/utf8>>]
|
||||
),
|
||||
file => <<?FILEPATH/utf8>>,
|
||||
module => <<"gleeunit/should"/utf8>>,
|
||||
function => <<"be_none"/utf8>>,
|
||||
line => 56})
|
||||
end.
|
||||
|
||||
-file("src/gleeunit/should.gleam", 60).
|
||||
-spec be_true(boolean()) -> nil.
|
||||
be_true(Actual) ->
|
||||
_pipe = Actual,
|
||||
equal(_pipe, true).
|
||||
|
||||
-file("src/gleeunit/should.gleam", 65).
|
||||
-spec be_false(boolean()) -> nil.
|
||||
be_false(Actual) ->
|
||||
_pipe = Actual,
|
||||
equal(_pipe, false).
|
||||
|
||||
-file("src/gleeunit/should.gleam", 70).
|
||||
-spec fail() -> nil.
|
||||
fail() ->
|
||||
be_true(false).
|
||||
21
build/packages/gleeunit/src/gleeunit_ffi.erl
Normal file
21
build/packages/gleeunit/src/gleeunit_ffi.erl
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
-module(gleeunit_ffi).
|
||||
|
||||
-export([find_files/2, run_eunit/2]).
|
||||
|
||||
find_files(Pattern, In) ->
|
||||
Results = filelib:wildcard(binary_to_list(Pattern), binary_to_list(In)),
|
||||
lists:map(fun list_to_binary/1, Results).
|
||||
|
||||
run_eunit(Tests, Options) ->
|
||||
case code:which(eunit) of
|
||||
non_existing ->
|
||||
gleeunit@internal@reporting:eunit_missing();
|
||||
|
||||
_ ->
|
||||
case eunit:test(Tests, Options) of
|
||||
ok -> {ok, nil};
|
||||
error -> {error, nil};
|
||||
{error, Term} -> {error, Term}
|
||||
end
|
||||
end.
|
||||
|
||||
100
build/packages/gleeunit/src/gleeunit_ffi.mjs
Normal file
100
build/packages/gleeunit/src/gleeunit_ffi.mjs
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
import { readFileSync } from "node:fs";
|
||||
import { Result$Ok, Result$Error } from "./gleam.mjs";
|
||||
import * as reporting from "./gleeunit/internal/reporting.mjs";
|
||||
|
||||
export function read_file(path) {
|
||||
try {
|
||||
return Result$Ok(readFileSync(path));
|
||||
} catch {
|
||||
return Result$Error(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
async function* gleamFiles(directory) {
|
||||
for (let entry of await read_dir(directory)) {
|
||||
let path = join_path(directory, entry);
|
||||
if (path.endsWith(".gleam")) {
|
||||
yield path;
|
||||
} else {
|
||||
try {
|
||||
yield* gleamFiles(path);
|
||||
} catch (error) {
|
||||
// Could not read directory, assume it's a file
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function readRootPackageName() {
|
||||
let toml = await async_read_file("gleam.toml", "utf-8");
|
||||
for (let line of toml.split("\n")) {
|
||||
let matches = line.match(/\s*name\s*=\s*"([a-z][a-z0-9_]*)"/); // Match regexp in compiler-cli/src/new.rs in validate_name()
|
||||
if (matches) return matches[1];
|
||||
}
|
||||
throw new Error("Could not determine package name from gleam.toml");
|
||||
}
|
||||
|
||||
export async function main() {
|
||||
let state = reporting.new_state();
|
||||
|
||||
let packageName = await readRootPackageName();
|
||||
let dist = `../${packageName}/`;
|
||||
|
||||
for await (let path of await gleamFiles("test")) {
|
||||
let js_path = path.slice("test/".length).replace(".gleam", ".mjs");
|
||||
let module = await import(join_path(dist, js_path));
|
||||
for (let fnName of Object.keys(module)) {
|
||||
if (!fnName.endsWith("_test")) continue;
|
||||
try {
|
||||
await module[fnName]();
|
||||
state = reporting.test_passed(state);
|
||||
} catch (error) {
|
||||
let moduleName = js_path.slice(0, -4);
|
||||
state = reporting.test_failed(state, moduleName, fnName, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const status = reporting.finished(state);
|
||||
exit(status);
|
||||
}
|
||||
|
||||
export function crash(message) {
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
function exit(code) {
|
||||
if (globalThis.Deno) {
|
||||
Deno.exit(code);
|
||||
} else {
|
||||
process.exit(code);
|
||||
}
|
||||
}
|
||||
|
||||
async function read_dir(path) {
|
||||
if (globalThis.Deno) {
|
||||
let items = [];
|
||||
for await (let item of Deno.readDir(path, { withFileTypes: true })) {
|
||||
items.push(item.name);
|
||||
}
|
||||
return items;
|
||||
} else {
|
||||
let { readdir } = await import("node:fs/promises");
|
||||
return readdir(path);
|
||||
}
|
||||
}
|
||||
|
||||
function join_path(a, b) {
|
||||
if (a.endsWith("/")) return a + b;
|
||||
return a + "/" + b;
|
||||
}
|
||||
|
||||
async function async_read_file(path) {
|
||||
if (globalThis.Deno) {
|
||||
return Deno.readTextFile(path);
|
||||
} else {
|
||||
let { readFile } = await import("node:fs/promises");
|
||||
let contents = await readFile(path);
|
||||
return contents.toString();
|
||||
}
|
||||
}
|
||||
72
build/packages/gleeunit/src/gleeunit_progress.erl
Normal file
72
build/packages/gleeunit/src/gleeunit_progress.erl
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
%% A formatter adapted from Sean Cribb's https://github.com/seancribbs/eunit_formatters
|
||||
|
||||
-module(gleeunit_progress).
|
||||
-define(NOTEST, true).
|
||||
|
||||
%% eunit_listener callbacks
|
||||
-export([
|
||||
init/1, handle_begin/3, handle_end/3, handle_cancel/3, terminate/2,
|
||||
start/0, start/1
|
||||
]).
|
||||
|
||||
-define(reporting, gleeunit@internal@reporting).
|
||||
|
||||
start() ->
|
||||
start([]).
|
||||
|
||||
start(Options) ->
|
||||
eunit_listener:start(?MODULE, Options).
|
||||
|
||||
init(_Options) ->
|
||||
?reporting:new_state().
|
||||
|
||||
handle_begin(_test_or_group, _data, State) ->
|
||||
State.
|
||||
|
||||
handle_end(group, _data, State) ->
|
||||
State;
|
||||
handle_end(test, Data, State) ->
|
||||
{AtomModule, AtomFunction, _Arity} = proplists:get_value(source, Data),
|
||||
Module = erlang:atom_to_binary(AtomModule),
|
||||
Function = erlang:atom_to_binary(AtomFunction),
|
||||
|
||||
% EUnit swallows stdout, so print it to make debugging easier.
|
||||
case proplists:get_value(output, Data) of
|
||||
undefined -> ok;
|
||||
<<>> -> ok;
|
||||
Out -> gleam@io:print(Out)
|
||||
end,
|
||||
|
||||
case proplists:get_value(status, Data) of
|
||||
ok ->
|
||||
?reporting:test_passed(State);
|
||||
{skipped, _Reason} ->
|
||||
?reporting:test_skipped(State, Module, Function);
|
||||
{error, {_, Exception, _Stack}} ->
|
||||
?reporting:test_failed(State, Module, Function, Exception)
|
||||
end.
|
||||
|
||||
|
||||
handle_cancel(_test_or_group, Data, State) ->
|
||||
?reporting:test_failed(State, <<"gleeunit">>, <<"main">>, Data).
|
||||
|
||||
terminate({ok, _Data}, State) ->
|
||||
?reporting:finished(State),
|
||||
ok;
|
||||
terminate({error, Reason}, State) ->
|
||||
?reporting:finished(State),
|
||||
io:fwrite("
|
||||
Eunit failed:
|
||||
|
||||
~80p
|
||||
|
||||
This is probably a bug in gleeunit. Please report it.
|
||||
", [Reason]),
|
||||
sync_end(error).
|
||||
|
||||
sync_end(Result) ->
|
||||
receive
|
||||
{stop, Reference, ReplyTo} ->
|
||||
ReplyTo ! {result, Reference, Result},
|
||||
ok
|
||||
end.
|
||||
Loading…
Add table
Add a link
Reference in a new issue