-
-
Notifications
You must be signed in to change notification settings - Fork 35
check-compile-time-exn implementation for compile time exception testing #114
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
check-compile-time-exn implementation for compile time exception testing #114
Conversation
The documentation should specify the contract for The documentation however should specify that the second operand is expanded in the expression context. |
Note that I reviewed the PR directly from Github, so I haven't actually tested it yet. One thing I concern is whether it reports a correct source location when a test fails. You might want to take a look at |
Sounds great, thanks so much for the detailed review! Will fix these things up and report back! |
New commit resolves documentation recommendations and adds source location to failed tests. |
I would expect a form named
You can run the expression by applying the resulting thunk (if one is returned), but I'm not sure the expression needs to be run. If it is run, and if it raises a run time exception, should that be caught? (If so, it still should not be confused with a compile-time exception.) |
@@ -176,6 +176,27 @@ entirely. | |||
] | |||
} | |||
|
|||
@defform[(check-compile-time-exn exn-predicate body) | |||
#:contracts ([exn-predicate (or/c (-> any/c any/c) regexp?)] | |||
[body (-> any/c)]) |
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.
Sorry if I was being unclear. I think:
1.body
should be renamed to expr
; and
2. It should not be in #:contracts
.
For the first one, there's a convention to use the name body
as described here. Because this operand only allows an expression, it should be named expr
instead.
For the second one, I would hope that it is possible to write (check-compile-time-exn #rx"foo" 1)
, but 1
would not satisfy (-> any/c)
as it's not a function. Switching from (-> any/c)
to any/c
wouldn't work either because (I would hope that) it's possible to write (check-compile-time-exn #rx"foo" (values 1 2 3))
, but again, (values 1 2 3)
would not satisfy any/c
. So just leave it out from #:contracts
.
@rmculpepper @sorawee Thanks for the feedback! Will play around with this and get back with some fixes! |
API feedback:
(check-compile-time-exn exn:fail:syntax?
(begin
(thing)
(other-thing)
(more-things))) ...but I'd like to omit the
(test-case "hypothetical static type checker"
(define e
(check-compile-time-exn
(: foo String)
(define foo 42)))
(check-pred exn:fail:syntax:type? e)
(check-equal? (exn:fail:syntax:type-expected-type e) String))
(define e
(check-compile-time-exn
(define good 42)
(define bad)
(define also-good 42)))
(check-pred exn:fail:syntax? e)
(check-syntax-error-source e #'(define bad)) Also, thank you so much for working on this! I think it will make a huge difference in the quality of everyone's macros. |
Thanks for the Great feedback for API design!! I'm working on some of the other feedback and will definitely add these to it, as I totally agree with you on these different points. Should have an update with a more detailed response to these points and the others in the PR ASAP! |
@jackfirth: what context should these forms be expanded in? One of your examples ends with a define statement, which is illegal in both internal definition context and expression context. Perhaps you want the behavior of |
@sorawee Expanding the forms inside a |
Yeah that’s how I interpreted as there could be multiple statements that
each are checked for compile time errors, last statement checked need not
be an expression like block
On Mon, Jan 6, 2020 at 12:07 AM sorawee ***@***.***> wrote:
@jackfirth <https://github.com/jackfirth>: what context should these
forms be expanded in? One of your examples ends with a define statement,
which is illegal in both internal definition context and expression
context. Perhaps you want the behavior of block
<https://docs.racket-lang.org/reference/block.html>? Which, for the
purpose of compile-time expansion, is equivalent to (lambda () body ...
(void))?
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#114?email_source=notifications&email_token=AAMRJXMR3HSIXGURFR6HUELQ4LDCNA5CNFSM4KCU2ZL2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEIEQOLQ#issuecomment-571017006>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAMRJXNHWTPF27RMZK4IWOTQ4LDCNANCNFSM4KCU2ZLQ>
.
--
*Thomas McHugh*
*Northwestern University*
Undergraduate Student
Email: [email protected]
Phone: 847-922-4801
|
In that case, I would document
with a contract for |
Do we want to require that there is at least one |
Just to repeat what I said earlier, I think it shouldn't accept a predicate. Agreed on naming the body forms
Definitely. Only use case I can think of for allowing zero would be for a macro that expands into a use of |
@sorawee yeah I think that makes sense, I can't think of a point to having no defn-or-expr so requiring makes sense |
I agree with @jackfirth with having it without a predicate, returning the exception makes sense. Do we think it makes sense to return |
@tommymchugh If no exception is raised, the check should fail entirely. Failed checks raise exceptions, so it shouldn't need to return anything at all. |
@jackfirth Oh yeah thats right, sounds good to me |
@sorawee's updated docs would be Accounting for removal of predicate and return type of exception unless failure. I also think scrapping |
I don't think the expression should be evaluated at all, so even if it would throw a runtime error that shouldn't matter. For example this: (define e
(check-compile-time-exn
(error "runtime kaboom!")
(define compile-time-kaboom!))) ...should succeed and (tets-case "compile-time exception test"
(define was-run? (box #f))
(check-compile-time-exn (set-box! was-run? #t)) ...should fail (because |
So just to summarize mainly for my notes while I'm implementing here's overview of what I got. API Design Spec Add: Checks for compile time exceptions in a Add: Checks that there are no compile time exceptions. Fails if there are any found. Does not run so does not check for runtime exceptions. Add:
Confirms that stx-expr is the defn-or-expr causing the resulting exception from check-compile-time-exn |
Design spec is a great idea! Here's my nitpicks:
(test-case "raising a non-exception value at compile-time"
(define v
(check-compile-time-exn
(define-syntax foo (raise "just a plain string!"))))
(check-pred string? v)
(check-equal? v "just a plain string!"))
(test-case "manual syntax error test"
(define e
(make-exn:fail:syntax
"I made this error all by myself!"
(current-continuation-marks)
(list #'(uh-oh-spaghettios))))
(check-syntax-error-source e #'(uh-oh-spaghettios)))
|
Just pushing this to update everyone, have compile time only checking working. Next steps for me are:
|
Example:
|
@@ -173,5 +177,11 @@ | |||
(define-shortcut (test-exn pred thunk) | |||
(check-exn pred thunk)) | |||
|
|||
(define-shortcut (test-compile-time-exn pred thunk) |
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 think these shortcut forms should be skipped, actually. They won't work quite right for check-compile-time-exn
, since it accepts a variable number of body forms instead of a single thunk. The shortcuts really only work for checks that behave much more like functions: notice there isn't a test-match
shortcut either, since check-match
is similarly unusual.
(define-syntax (check-compile-time-exn stx) | ||
(syntax-parse stx | ||
[(_ body ...+) | ||
(with-syntax ([loc (datum->syntax #f 'loc stx)]) |
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.
You can use the #:with
pattern directive to simplify this a bit:
(syntax-parse stx
[(_ body ...+)
#:with loc (datum->syntax #f 'loc stx)
(syntax/loc stx ...)])
|
||
(define-syntax (check-compile-time-exn stx) | ||
(syntax-parse stx | ||
[(_ body ...+) |
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.
You can use the expr
syntax class to prevent body
from matching bare keywords:
(syntax-parse stx
[(_ body:expr ...+)
...])
That way, a usage like (check-compile-time-exn #:foo)
won't be allowed.
@@ -139,7 +142,7 @@ | |||
(procedure-arity-includes? thunk 0)) | |||
(raise-arguments-error name "thunk must be a procedure that accepts 0 arguments" "thunk" thunk))) | |||
|
|||
(define-check (check-exn raw-pred thunk) | |||
(define-check (check-exn-helper raw-pred thunk location) |
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 think it would be better to not try and reuse the check-exn
code. Checks are kind of hard to reuse, since they're sensitive to the source location of their usage site and they have some slightly whacky semantics. Plus there's no need to try and reuse the regex/predicate parts of check-exn
.
@@ -434,6 +435,20 @@ | |||
(check-exn exn:fail:contract? | |||
(lambda () | |||
(check-not-exn (lambda (x) x))))) | |||
|
|||
;; Verify compile time exceptions are now |
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.
Here's some more test cases:
(test-case "check-compile-time-exn should allow multiple body forms"
(check-compile-time-exn
(define foo 1)
(define bar 2)
(define kaboom)))
(test-case "check-compile-time-exn should allow raising non-exception values"
(check-compile-time-exn
(define-syntax foo (raise "not an exception"))))
(test-case "check-compile-time-exn should not evaluate its body"
(define evaluated? (box #f))
(check-compile-time-exn
(set-box! evaluated? #t)
(define kaboom))
(check-false (unbox evaluated?)))
(test-case "check-compile-time-exn should return the raised value"
(define raised-value (check-compile-time-exn (define-syntax foo (raise 42)))
(check-equal? raised-value 42))
Another very minor nit: note that indentations are off in some of your code. You might want to use DrRacket or |
Thanks for the comments! Will have some solid time this weekend to work on this so will keep you all updated! |
We just switched from Travis to GHA, so you should discard any changes to |
@jackfirth reading this again, I disagree that That is to say, @tommymchugh do you still want to continue working on the PR? -- I notice that you deleted your forked repo. This is a great feature addition, so if you don't want to continue, would you mind if someone else takes it over? And in any case, thank you for your work so far! |
Happy to take this on. I have a new year's resolution to get back to some
of the open source work I had that piled up during the pandemic :). I'll
reassociate myself with this PR and write up the changes.
…On Sat, Jan 2, 2021 at 10:46 AM sorawee ***@***.***> wrote:
@jackfirth <https://github.com/jackfirth> reading this again, I disagree
that check-compile-time-exn should return an exception. The name check-
suggests that it should be a check form -- an assertion that the test
should pass. If you want it to return an exception, then perhaps the name
with-compile-time-exn is more appropriate?
That is to say, rackunit would provide check-compile-time-exn (which
accepts a predicate), and perhaps there should be rackunit/util that
provides with-compile-time-exn.
@tommymchugh <https://github.com/tommymchugh> do you still want to
continue working on the PR? -- I notice that you deleted your forked repo.
This is a great feature addition, so if you don't want to continue, would
you mind if someone else takes it over? And in any case, thank you for your
work so far!
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#114 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAMRJXKNB3TNOWGWRMFUXQDSX5EXXANCNFSM4KCU2ZLQ>
.
|
Resolves racket/racket#2996
Adds:
check-compile-time-exn
check-syntax-exn
check-not-compile-time-exn
check-not-syntax-exn
As some interest was expressed in providing handy wrappers for syntax/macro-testing's
convert-compile-time-error
andconvert-syntax-error
for thunk procedures in check-exn and check-not-exn.Uses define-syntax instead of define-check for definition as wrappers are ignored when using define-check for expression.
Note: also adds
|| true
to the Travis yaml file to allow the build to continue running the test suite even though there seems to be an existing dependency bug in rackunit-gui before this PR. Can remove if needed.