-
Notifications
You must be signed in to change notification settings - Fork 328
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
base: develop
Are you sure you want to change the base?
Conversation
from Standard.Base import all | ||
import Standard.Base.Runtime.Ref.Ref | ||
|
||
## PRIVATE |
There was a problem hiding this comment.
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.
@@ -27,6 +27,8 @@ export project.Extensions.should_succeed | |||
|
|||
export project.Faker.Faker | |||
|
|||
export project.Frame_Hider.Frame_Hider |
There was a problem hiding this comment.
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
.
test_utility test_predicate x = | ||
... | ||
Frame_Hider.unhide_1 test_predicate x | ||
unhide_1 f x = |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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
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. | ||
type Frame_Hider |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 | ||
|
||
Frame_Hider is used in test utilities to hide uninteresting stack frames from |
There was a problem hiding this comment.
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" ;-)
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is an API?
Everything somebody else can depend on!
The PRIVATE
comment is mostly irrelevant in deciding whether someone can depend on the Frame_Hider
name or not. It is just a comment.
There is some difference.
Even users of the IDE can depend and use the Frame_Hider
. The PRIVATE
comment just makes it harder for them to find it. But we have no way to know whether or not they found it!
The only way to make sure someone cannot (easily) depend on an element in a Standard.Test
library is to use private
keyword.
Once a check for Standard.Test
API stability is in, the "difference" will be clear. PRIVATE
comment is unimportant from API perspective.
@@ -703,8 +703,8 @@ add_specs suite_builder = | |||
actual.column_names . should_equal ["col0", "col_between", "col1"] | |||
|
|||
suite_builder.group "Use First Row As Names" group_builder-> | |||
expect_column_names names table = | |||
table.columns . map .name . should_equal names frames_to_skip=2 | |||
expect_column_names names table = Frame_Hider.hide <| |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, removing frames_to_skip=2
is certainly an improvement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Opinions inline...
|
||
## PRIVATE | ||
|
||
Frame_Hider is used in test utilities to hide uninteresting stack frames from |
There was a problem hiding this comment.
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
?
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. | ||
type Frame_Hider |
There was a problem hiding this comment.
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.
test_utility test_predicate x = | ||
... | ||
Frame_Hider.unhide_1 test_predicate x | ||
unhide_1 f x = |
There was a problem hiding this comment.
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
.
Every time I am executing |
Just waiting on an engine approval. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- There may be things I don't fully adore present in this PR
- I have expressed my opinion in a previous review
- However none of my opinions is critical enough to prevent the integration
- If you are OK maintaining the API, go on and integrate
- Approving from engine point of view
Replaces eager calls to
Test.get_source_location
to lazy calls toFrame_Hider.get_top_stack_frame
, resulting in a 2x speedup forBase_Tests
.Test utilities wrap their contents in
Frame_Hider.hide
, and wrap callbacks inFrame_Hider.unhide
. These marker frames are used to skip test utility frames to find the real user-level location of the test failure.The
frames_to_skip
parameter is no longer needed, since the number of frames to skip is computed dynamically on test failure.Checklist
Please ensure that the following checklist has been satisfied before submitting the PR:
Scala,
Java,
TypeScript,
and
Rust
style guides. In case you are using a language not listed above, follow the Rust style guide.
or the Snowflake database integration, a run of the Extra Tests has been scheduled.