Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make test failure source locations lazy #12260

Open
wants to merge 61 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
12632d6
wip
GregoryTravis Feb 5, 2025
007f068
nesting
GregoryTravis Feb 5, 2025
c4438e7
wip
GregoryTravis Feb 5, 2025
f926e8b
wip
GregoryTravis Feb 5, 2025
461f7b2
all in extensions and test
GregoryTravis Feb 5, 2025
39bcd9b
wip
GregoryTravis Feb 5, 2025
5c5a9bd
convert all
GregoryTravis Feb 5, 2025
1fd5911
all fts
GregoryTravis Feb 5, 2025
ff9c67a
no gsl
GregoryTravis Feb 5, 2025
ca2c0ce
wip
GregoryTravis Feb 5, 2025
10e6212
merge
GregoryTravis Feb 6, 2025
8f26d0a
simplify, no obj
GregoryTravis Feb 6, 2025
20072e9
wip
GregoryTravis Feb 6, 2025
b8afddf
wip
GregoryTravis Feb 6, 2025
75cfe92
fix signatures
GregoryTravis Feb 6, 2025
10814db
wip
GregoryTravis Feb 6, 2025
c3635b0
into Main
GregoryTravis Feb 6, 2025
c4cdca9
wip
GregoryTravis Feb 6, 2025
7fff45f
put gsl back for with_retries
GregoryTravis Feb 6, 2025
11ba0f8
gsl test back
GregoryTravis Feb 6, 2025
429fd69
wip
GregoryTravis Feb 6, 2025
2cdfdf9
wip
GregoryTravis Feb 6, 2025
e9d78e3
disambiguate
GregoryTravis Feb 6, 2025
c8af61b
unomitting
GregoryTravis Feb 6, 2025
9828302
more tests
GregoryTravis Feb 6, 2025
e524998
goofy but works
GregoryTravis Feb 7, 2025
0eb6018
fold
GregoryTravis Feb 7, 2025
02dd307
log
GregoryTravis Feb 7, 2025
26ed0f6
problems
GregoryTravis Feb 7, 2025
be6f652
Merge branch 'develop' into wip/gmt/12215-slow-stack-frame
GregoryTravis Feb 10, 2025
9c96e13
more us
GregoryTravis Feb 10, 2025
2725dc9
Merge branch 'develop' into wip/gmt/12215-slow-stack-frame
GregoryTravis Feb 11, 2025
023bb2b
mls
GregoryTravis Feb 11, 2025
0661925
no-arg test, fix 2-arg test
GregoryTravis Feb 11, 2025
5fe1222
0, 1, 2
GregoryTravis Feb 11, 2025
635e050
wrappers
GregoryTravis Feb 11, 2025
903a364
wip
GregoryTravis Feb 11, 2025
de153c1
rename
GregoryTravis Feb 11, 2025
c9db11c
wip
GregoryTravis Feb 11, 2025
3e1f4d2
wip
GregoryTravis Feb 11, 2025
fc7a125
type
GregoryTravis Feb 11, 2025
b7d9d60
docs
GregoryTravis Feb 11, 2025
9822e59
wip
GregoryTravis Feb 11, 2025
b2140bb
wip
GregoryTravis Feb 11, 2025
88115ce
wip
GregoryTravis Feb 11, 2025
049f263
unc
GregoryTravis Feb 11, 2025
b334d28
review
GregoryTravis Feb 12, 2025
551fa7b
Merge branch 'develop' into wip/gmt/12215-slow-stack-frame
GregoryTravis Feb 12, 2025
f0cdd00
wip
GregoryTravis Feb 12, 2025
cf7bc55
more lazy in Table_Tests Util.enso
GregoryTravis Feb 12, 2025
eb0553e
Move location spec to Test_Tests.
GregoryTravis Feb 12, 2025
260c482
fix MLS test maybe
GregoryTravis Feb 12, 2025
dec480c
fix with_retries
GregoryTravis Feb 12, 2025
cc3f27d
wip
GregoryTravis Feb 12, 2025
9e64f85
wip
GregoryTravis Feb 12, 2025
4b77e2b
Merge branch 'develop' into wip/gmt/12215-slow-stack-frame
GregoryTravis Feb 13, 2025
496c2bd
uncurry testers
GregoryTravis Feb 13, 2025
c75bc3f
uncurry testers
GregoryTravis Feb 13, 2025
d1bed32
uncurry testers
GregoryTravis Feb 13, 2025
d3e668c
remove debug
GregoryTravis Feb 13, 2025
82b40c0
wip
GregoryTravis Feb 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
258 changes: 97 additions & 161 deletions distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso

Large diffs are not rendered by default.

137 changes: 137 additions & 0 deletions distribution/lib/Standard/Test/0.0.0-dev/src/Frame_Hider.enso
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
from Standard.Base import all
import Standard.Base.Runtime.Ref.Ref

## PRIVATE
Copy link
Member

@JaroslavTulach JaroslavTulach Feb 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this module be private? Probably it couldn't be.


Frame_Hider is used in test utilities to hide uninteresting stack frames from
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name Frame_Hider feels "too strong" to me. The verb "to hide" feel negative. But we want users of our APIs to feel good!

Do you have some prior art for selecting such a name?

When thinking about an alternative a phase shadow stack come to my mind:

The shadow stack itself is a second, separate stack that "shadows" the program call stack.

Maybe "shadow stack" would be misleading as well, but "to shadow" certainly feels more positive than "to hide" ;-)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure if this is shadowing -- that implies a duplicate / parallel stack. What we are really doing is removing some frames from the stack before picking the top frame.

I originally called this Omitter, maybe that's less hostile? Frame_Omitter perhaps?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Frame_Tidy? Frame_Grooming? Frame_Refining? Frame_Cleansing?

Copy link
Member

@radeusgd radeusgd Feb 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be honest looking at all the possible names, Frame_Hider still seems most clear (IMHO) about what it is doing, I don't really share the feeling that it is 'negative' in any way...

It is an internal API anyway - mostly for library developers so the naming can probably be a bit less stringent.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Internal API would be private. E.g. no, this is not an internal API.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But it is less public than GUI-facing things.

It is marked as PRIVATE and meant to be used by library developers (advanced users) not by every user. There is some difference.

users when reporting the location of test failures.

In a test utility such as `Any.should_equal`, the failure of a test assertion
is accompanied by the location of the failure. The location should not be a
stack frame inside `Any.should_equal`, but rather should be at the location
that called `Any.should_equal`. This means identifying stack frames we don't
want to bother the user with, and omitting them when we're looking for the
correct stack frame to use.

The problem is more complicated when test utilities call other utilities, or
when test utilities invoke callbacks provided by user test code.

The `hide` and `unhide*` methods are simple wrappers. `get_top_stack_frame`
identifies these on the stack, and uses them to remove the uninteresting
frames from consderation.

A `hide` call should be wrapped around the body of every test utility.

An `unhide*` call should be wrapped around any call from a test utility to a
user-supplied callback, depending on the number of arguments. If one test
utility forwards a callback to another test utility, the callback should be
wrapped at the forward site as well.

! Partial Application

Partial application can prevent the `unhide*` methods from existing in the
stack trace, so test callbacks should have explicit parameters:

# Do not use partial application:
tester = expect_column_names ["A", "B"]
# Use explicit parameters:
tester t = expect_column_names ["A", "B"] t
! Tail Calls

Tail calls can result in changes to the stack that prevent
`get_top_stack_frame` from working properly. For this reason, uses of
`hide` and `unhide` should be within the tail call:

go i = Frame_Hider.hide <|
# ...
@Tail_Call go (i+1)
go 1
type Frame_Hider
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is Frame_Hider a type, when it doesn't have a constructor? Putting its static methods as module methods would work as well.

Copy link
Contributor Author

@GregoryTravis GregoryTravis Feb 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strangely, I am getting this error when I try this, but only in some tests -- other tests run correctly.

    - [FAILED] should work as shown in the doc examples [472ms]
        Reason: An unexpected panic was thrown: (Syntax_Error.Error 'Type for ResolvedType(Concrete(CompilerContext.Module{module=Module[Standard.Test.Frame_Hider]}),Type(Frame_Hider,Seq(),Seq(),false)) is null')
        at <enso> Util.expect_column_names<arg-0>(/Users/gmt/dev/enso/enso/test/Table_Tests/src/Common_Table_Operations/Util.enso:10:54-64)
        at <enso> Util.expect_column_names(/Users/gmt/dev/enso/enso/test/Table_Tests/src/Common_Table_Operations/Util.enso:10-13)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unexpected panic is an suspicious outcome - consider reporting it as a separate issue. If there is supposed to be an error, it shouldn't be unexpected panic. But yeah, such situations happen when we tease the compiler with unusual coding approaches.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After this PR, I'll create a task to remove the type and if it is still failing I'll report the bug.

## PRIVATE

Hides the stack frames of the contained code from `get_top_stack_frame`.

> Example
Wrap the body of a test utility to hide its stack frames.

Any.should_equal self that = Frame_Hider.hide <|
...
Error.throw ("Not equal at " + Frame_Hider.get_top_stack_frame)
hide ~action =
action

## PRIVATE

Unhides the stack frames of the call to the user-supplied callback
application.

> Example
Wrap a callback invocation to unhide its stack frames.

test_utility test_predicate x =
...
Frame_Hider.unhide (test_predicate x)
unhide ~action =
action

## PRIVATE

Unhides the stack frames of the call to the user-supplied callback
application, uncurried for one argument.

`unhide_1 f x` is Equivalent to `unhide (f x)`.

> Example
Wrap a callback invocation to unhide its stack frames.

test_utility test_predicate x =
...
Frame_Hider.unhide_1 test_predicate x
unhide_1 f x =
Copy link
Member

@JaroslavTulach JaroslavTulach Feb 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, this is so non-FPish!

I mean:

  • Enso does support currying
  • this kind constructs (Function1, Function2, etc.) are more likely to find in language that don't support currying (Java, Scala, Kotlin)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course I wanted to curry it, but it seems not to work; perhaps there is something I'm doing wrong.

Demonstrated below: for a two-argument function, only the version with two explicit arguments has wrap in its call stack:

foo a b =
    st = Runtime.get_stack_trace
    st.map IO.println
    IO.println "===="
    a + b

wrap_0 f =
    f

wrap_1 f x =
    f x

wrap_2 f x y =
    f x y

main =
    sp <| foo 1 2
    sp <| wrap_0 foo 1 2
    sp <| wrap_1 foo 1 2
    sp <| wrap_2 foo 1 2

Results:

M:dee wip/gmt/12215-slow-stack-frame$ g
(Stack_Trace_Element.Value 'dbbd.foo' (Source_Location dbbd.enso:126:10-32))
(Stack_Trace_Element.Value 'dbbd.main<arg-1>' (Source_Location dbbd.enso:141:11-17))
(Stack_Trace_Element.Value 'dbbd.main' (Source_Location dbbd.enso:141:5-17))
====
3
(Stack_Trace_Element.Value 'dbbd.foo' (Source_Location dbbd.enso:126:10-32))
(Stack_Trace_Element.Value 'dbbd.main<arg-1>' (Source_Location dbbd.enso:142:11-24))
(Stack_Trace_Element.Value 'dbbd.main' (Source_Location dbbd.enso:142:5-24))
====
3
(Stack_Trace_Element.Value 'dbbd.foo' (Source_Location dbbd.enso:126:10-32))
(Stack_Trace_Element.Value 'dbbd.main<arg-1>' (Source_Location dbbd.enso:143:11-24))
(Stack_Trace_Element.Value 'dbbd.main' (Source_Location dbbd.enso:143:5-24))
====
3
(Stack_Trace_Element.Value 'dbbd.foo' (Source_Location dbbd.enso:126:10-32))
(Stack_Trace_Element.Value 'dbbd.wrap_2' (Source_Location dbbd.enso:138:5-9))
(Stack_Trace_Element.Value 'dbbd.main<arg-1>' (Source_Location dbbd.enso:144:11-24))
(Stack_Trace_Element.Value 'dbbd.main' (Source_Location dbbd.enso:144:5-24))
====
3

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In addition to this unfortunate code, I have had to do this in some tests that use callbacks:

-            tester = expect_column_names ["A", "1980-01-01", "Column 1", "5.3", "True"]
+            tester t = expect_column_names ["A", "1980-01-01", "Column 1", "5.3", "True"] t
             problems = [Duplicate_Output_Column_Names.Error ["A", "A", "A"]]
             Problems.test_problem_handling action problems tester

(tester is invoked within an unhide call in test_problem_handling.)

It makes me sad, but it was necessary.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Following program:

from Standard.Base import all

sp = IO.println

foo a b =
    st = Runtime.get_stack_trace
    st.map IO.println
    IO.println "===="
    a + b

wrap_l ~a =
    a

main =
    sp <| foo 1 2
    sp <| wrap_l <| foo 1 2
    sp <| wrap_l <| foo 1 2
    sp <| wrap_l <| foo 1 2

does include wrap_l in all the invocations:

(Stack_Trace_Element.Value 'g.foo' (Source_Location g.enso:7:10-32))
(Stack_Trace_Element.Value 'g.main<arg-1>' (Source_Location g.enso:16:11-17))
(Stack_Trace_Element.Value 'g.main' (Source_Location g.enso:16:5-17))
====
3
(Stack_Trace_Element.Value 'g.foo' (Source_Location g.enso:7:10-32))
(Stack_Trace_Element.Value 'g.main<arg-1>' (Source_Location g.enso:17:21-27))
(Stack_Trace_Element.Value 'g.wrap_l' (Source_Location g.enso:13:5-5))
(Stack_Trace_Element.Value 'g.main<arg-1>' (Source_Location g.enso:17:11-27))
(Stack_Trace_Element.Value 'g.main' (Source_Location g.enso:17:5-27))
====
3
(Stack_Trace_Element.Value 'g.foo' (Source_Location g.enso:7:10-32))
(Stack_Trace_Element.Value 'g.main<arg-1>' (Source_Location g.enso:18:21-27))
(Stack_Trace_Element.Value 'g.wrap_l' (Source_Location g.enso:13:5-5))
(Stack_Trace_Element.Value 'g.main<arg-1>' (Source_Location g.enso:18:11-27))
(Stack_Trace_Element.Value 'g.main' (Source_Location g.enso:18:5-27))
====
3
(Stack_Trace_Element.Value 'g.foo' (Source_Location g.enso:7:10-32))
(Stack_Trace_Element.Value 'g.main<arg-1>' (Source_Location g.enso:19:21-27))
(Stack_Trace_Element.Value 'g.wrap_l' (Source_Location g.enso:13:5-5))
(Stack_Trace_Element.Value 'g.main<arg-1>' (Source_Location g.enso:19:11-27))
(Stack_Trace_Element.Value 'g.main' (Source_Location g.enso:19:5-27))
====
3

The only difference is that one has to use wrap_l <| instead of just wrap_l.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But for a callback, wrap_l is not included, as seen below. This is why I added unhide_1 and unhide_2 for -- the case when user-supplied callbacks are used to implement the value testers.

    sp <| (wrap_l <| foo 1) 2
(Stack_Trace_Element.Value 'jt.foo' (Source_Location jt.enso:6:10-32))
(Stack_Trace_Element.Value 'jt.main<arg-1>' (Source_Location jt.enso:18:11-29))
(Stack_Trace_Element.Value 'jt.main' (Source_Location jt.enso:18:5-29))
====
3

f x

## PRIVATE

Unhides the stack frames of the call to the user-supplied callback
application, uncurried for two arguments.

`unhide_1 f x y` is Equivalent to `unhide (f x y)`.

> Example
Wrap a callback invocation to unhide its stack frames.

test_utility test_predicate x y =
...
Frame_Hider.unhide_2 test_predicate x y
unhide_2 f x y =
f x y

## PRIVATE

Identify the correct top stack frame by skipping over the stack frames
inside test utilities.
get_top_stack_frame -> Text =
stack_trace = Runtime.get_stack_trace
reversed_stack_trace = stack_trace.reverse

## Iterate through stack frames from bottom to top, determining how many
`hide` calls each one is nested inside. Each `hide` frame increases
the nesting level by 1, and each `unhide` decreases it by one.

The stack frame we are looking for is the one before the last frame
that has a nesting level of 0 -- this is the most recent user-level
call, and the one that triggered the test assertion failure.

If we cannot find such a frame, we simply return the top of the
stack. (In this case, some test code is missing a call to `unhide*`.)
delta_for_frame frame =
name = frame.name
if name.starts_with "Frame_Hider.hide" then 1 else
if name.starts_with "Frame_Hider.unhide" then -1 else 0
nesting_levels = reversed_stack_trace.map delta_for_frame . running_fold 0 (+)
index =
i = nesting_levels.last_index_of 0
if i.is_nothing || i == 0 then 0 else i-1

top = reversed_stack_trace . at index . source_location
top.to_display_text
2 changes: 2 additions & 0 deletions distribution/lib/Standard/Test/0.0.0-dev/src/Main.enso
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export project.Extensions.should_succeed

export project.Faker.Faker

export project.Frame_Hider.Frame_Hider
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ech, probably the Frame_Hider couldn't be private.


export project.Problems

export project.Suite.Suite
Expand Down
63 changes: 30 additions & 33 deletions distribution/lib/Standard/Test/0.0.0-dev/src/Problems.enso
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from Standard.Base import all

from project import Test
import project.Frame_Hider.Frame_Hider
from project.Extensions import all

## Returns values of warnings attached to the value.
Expand All @@ -27,20 +28,20 @@ get_attached_warnings v =
duplicated and that will be accepted. If false (default), the warning count
has to match the counts in `expected_problems`.
test_problem_handling : (Problem_Behavior -> Any) -> Vector Any -> (Any -> Nothing) -> Boolean -> Boolean -> Nothing
test_problem_handling action expected_problems result_checker (unwrap_errors : Boolean = True) (ignore_warning_cardinality : Boolean = False) =
test_problem_handling action expected_problems result_checker (unwrap_errors : Boolean = True) (ignore_warning_cardinality : Boolean = False) = Frame_Hider.hide <|
unwrap_maybe error = if unwrap_errors then Error.unwrap error else error

error_checker error_result =
error_checker error_result = Frame_Hider.hide <|
first_problem = expected_problems.first
first_problem_type = Meta.type_of first_problem
error_result . should_fail_with first_problem_type unwrap_errors=unwrap_errors frames_to_skip=3
(unwrap_maybe error_result.catch) . should_equal first_problem frames_to_skip=3
warnings_checker warnings =
error_result . should_fail_with first_problem_type unwrap_errors=unwrap_errors
(unwrap_maybe error_result.catch) . should_equal first_problem
warnings_checker warnings = Frame_Hider.hide <|
Test.with_clue "The warnings were "+warnings.to_text+'.\n' <|
(if ignore_warning_cardinality then warnings.distinct else warnings)
. map unwrap_maybe
. should_equal_ignoring_order expected_problems frames_to_skip=8
test_advanced_problem_handling action error_checker warnings_checker result_checker frames_to_skip=1
. should_equal_ignoring_order expected_problems
test_advanced_problem_handling (Frame_Hider.unhide action) (Frame_Hider.unhide_1 error_checker) (Frame_Hider.unhide_1 warnings_checker) (Frame_Hider.unhide_1 result_checker)

## UNSTABLE
Tests how a specific operation behaves depending on the requested
Expand All @@ -58,31 +59,30 @@ test_problem_handling action expected_problems result_checker (unwrap_errors : B
- result_checker: A function which should verify that the result generated by
the action is correct. It does not return anything, instead it should use
the standard testing approach, like `x.should_equal y`.
test_advanced_problem_handling : (Problem_Behavior -> Any) -> (Any -> Nothing) -> (Vector Any -> Nothing) -> (Any -> Nothing) -> Integer -> Nothing
test_advanced_problem_handling action error_checker warnings_checker result_checker frames_to_skip=0 =
test_advanced_problem_handling : (Problem_Behavior -> Any) -> (Any -> Nothing) -> (Vector Any -> Nothing) -> (Any -> Nothing) -> Nothing
test_advanced_problem_handling action error_checker warnings_checker result_checker = Frame_Hider.hide <|
# First, we check the action ignoring any warnings.
result_ignoring = action Problem_Behavior.Ignore
result_checker result_ignoring
get_attached_warnings result_ignoring . should_equal [] frames_to_skip=frames_to_skip+1
result_ignoring = Frame_Hider.unhide_1 action Problem_Behavior.Ignore
Frame_Hider.unhide_1 result_checker result_ignoring
get_attached_warnings result_ignoring . should_equal []

# Then, we check the fail-on-first-error mode.
error_result = action Problem_Behavior.Report_Error
error_checker error_result
error_result = Frame_Hider.unhide_1 action Problem_Behavior.Report_Error
Frame_Hider.unhide_1 error_checker error_result

# Lastly, we check the report warnings mode and ensure that both the result is correct and the warnings are as expected.
result_warning = action Problem_Behavior.Report_Warning
result_checker result_warning
warnings_checker (get_attached_warnings result_warning)
result_warning = Frame_Hider.unhide_1 action Problem_Behavior.Report_Warning
Frame_Hider.unhide_1 result_checker result_warning
Frame_Hider.unhide_1 warnings_checker (get_attached_warnings result_warning)

## UNSTABLE
Checks if the provided value does not have any attached problems.
assume_no_problems result =
loc = Test.get_source_location 1
assume_no_problems result = Frame_Hider.hide <|
if result.is_error then
Test.fail "Expected the result to not be an error, but a dataflow error has been matched: "+result.catch.to_display_text+" (at "+loc+")." details=result.get_stack_trace_text
Test.fail "Expected the result to not be an error, but a dataflow error has been matched: "+result.catch.to_display_text+" (at "+Frame_Hider.get_top_stack_frame+")." details=result.get_stack_trace_text
warnings = get_attached_warnings result
if warnings.not_empty then
Test.fail "Expected the result to not contain any warnings, but it did: "+warnings.to_text+" (at "+loc+")."
Test.fail "Expected the result to not contain any warnings, but it did: "+warnings.to_text+" (at "+Frame_Hider.get_top_stack_frame+")."

## UNSTABLE
Checks if the provided value has a specific warning attached.
Expand All @@ -95,16 +95,15 @@ assume_no_problems result =
- unwrap_errors: If true, remove any wrapping errors from the result before
checking against the expected warning.
expect_warning : Any -> Any -> Boolean -> Nothing
expect_warning expected_warning result unwrap_errors=True =
expect_warning expected_warning result unwrap_errors=True = Frame_Hider.hide <|
unwrap_maybe error = if unwrap_errors then Error.unwrap error else error
loc = Test.get_source_location 1
if result.is_error then
Test.fail "Expected a warning "+expected_warning.to_text+", but a dataflow error has been matched: "+result.catch.to_display_text+" (at "+loc+")."
Test.fail "Expected a warning "+expected_warning.to_text+", but a dataflow error has been matched: "+result.catch.to_display_text+" (at "+Frame_Hider.get_top_stack_frame+")."
warnings = get_attached_warnings result . map unwrap_maybe
found = warnings.find if_missing=Nothing x->
(x == expected_warning) || (x.is_a expected_warning)
found.if_nothing <|
Test.fail "Expected the result to contain a warning: "+expected_warning.to_text+", but it did not. The warnings were "+warnings.short_display_text+' (at '+loc+').'
Test.fail "Expected the result to contain a warning: "+expected_warning.to_text+", but it did not. The warnings were "+warnings.short_display_text+' (at '+Frame_Hider.get_top_stack_frame+').'

## UNSTABLE
Checks if the provided value has a specific warning attached and if there are
Expand All @@ -119,30 +118,28 @@ expect_warning expected_warning result unwrap_errors=True =
- unwrap_errors: If true, remove any wrapping errors from the result before
checking against the expected warning.
expect_only_warning : Any -> Any -> Boolean -> Any
expect_only_warning expected_warning result unwrap_errors=True =
expect_only_warning expected_warning result unwrap_errors=True = Frame_Hider.hide <|
unwrap_maybe error = if unwrap_errors then Error.unwrap error else error
loc = Test.get_source_location 1
if result.is_error then
Test.fail "Expected only warning "+expected_warning.to_text+", but a dataflow error has been matched: "+result.catch.to_display_text+" (at "+loc+")."
Test.fail "Expected only warning "+expected_warning.to_text+", but a dataflow error has been matched: "+result.catch.to_display_text+" (at "+Frame_Hider.get_top_stack_frame+")."
warnings = get_attached_warnings result . map unwrap_maybe
is_expected x =
(x == expected_warning) || (x.is_a expected_warning)
found = warnings.find if_missing=Nothing is_expected
if found.is_nothing then
Test.fail "Expected the result to contain a warning: "+expected_warning.to_text+", but it did not. The warnings were "+warnings.short_display_text+' (at '+loc+').'
Test.fail "Expected the result to contain a warning: "+expected_warning.to_text+", but it did not. The warnings were "+warnings.short_display_text+' (at '+Frame_Hider.get_top_stack_frame+').'
invalid = warnings.filter x-> is_expected x . not
if invalid.not_empty then
Test.fail "Expected the result to contain only the warning: "+found.to_text+", but it also contained: "+invalid.to_text+' (at '+loc+').'
Test.fail "Expected the result to contain only the warning: "+found.to_text+", but it also contained: "+invalid.to_text+' (at '+Frame_Hider.get_top_stack_frame+').'
found

## UNSTABLE
Checks if the provided value does _not_ have a warning of the specified type.

It allows other warnings to be present also.
not_expect_warning : Any -> Any -> Nothing
not_expect_warning expected_warning_type result =
not_expect_warning expected_warning_type result = Frame_Hider.hide <|
warnings = get_attached_warnings result
found = warnings.find if_missing=Nothing x-> x.is_a expected_warning_type
if found.is_nothing.not then
loc = Test.get_source_location 3
Test.fail 'The result contained a warning it was not supposed to: '+found.to_text+' (at '+loc+').'
Test.fail 'The result contained a warning it was not supposed to: '+found.to_text+' (at '+Frame_Hider.get_top_stack_frame+').'
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from Standard.Base import all
import Standard.Base.Runtime.Ref.Ref

import project.Frame_Hider.Frame_Hider
import project.Spec_Result.Spec_Result
from project import Test
from project.Extensions import all
Expand All @@ -21,8 +22,7 @@ type Should_Reach
Nothing

## Checks if a point was reached in execution.
should_have_reached : Integer -> Spec_Result
should_have_reached self (frames_to_skip : Integer = 0) -> Spec_Result =
loc = Test.get_source_location 1+frames_to_skip
should_have_reached : Spec_Result
should_have_reached self -> Spec_Result = Frame_Hider.hide <|
if self.reached_ref.get then Spec_Result.Success else
Test.fail ("Did not reach Should_Reach (at "+loc+").")
Test.fail ("Did not reach Should_Reach (at "+Frame_Hider.get_top_stack_frame+").")
Loading
Loading