greghendershott / racket-mode

Emacs major and minor modes for Racket: edit, REPL, check-syntax, debug, profile, packages, and more.
https://www.racket-mode.com/
GNU General Public License v3.0
681 stars 93 forks source link

No check-expect test output #316

Closed ghost closed 4 years ago

ghost commented 6 years ago

Dr. Racket produces an eval result telling me the expected result does not match my (check-expect)s. Racket-mode produces any empty result.

#lang htdp/bsl
(require 2htdp/image)

(define (double n) 0)
(check-expect (double 3) 6)
(check-expect (double 4.2) 8.4)
greghendershott commented 6 years ago

It is a goal for racket-mode to support #lang mechanisms described in Creating Languages.

Is htdp/bsl's check-expect using one of those to work with DrRacket, or something else/special? I think the latter, but I'm not sure. I don't think I'll be able to take a look at this soon; maybe someone else who sees this, could?

Full disclosure: The HTDP langs have not been a high priority for racket-mode. They are wonderful in an education setting -- where I've assumed most people would use DrRacket.

The equivalent #lang racket program using check-equal? from rackunit does work:

#lang racket
(require 2htdp/image
         rackunit)

(define (double n) 0)
(check-equal? (double 3) 6)
(check-equal? (double 4.2) 8.4)

When that program is racket-run, the racket-repl-mode buffer shows:

--------------------
; FAILURE
; /tmp/issue-316.rkt:6:0
name:       check-equal?
location:   issue-316.rkt:6:0
actual:     0
expected:   6
--------------------
--------------------
; FAILURE
; /tmp/issue-316.rkt:7:0
name:       check-equal?
location:   issue-316.rkt:7:0
actual:     0
expected:   8.4
--------------------
issue-316.rkt> 
samth commented 5 years ago

The teaching language testing framework needs a call to the test function at the end of the file, which is automatically inserted by the language and/or by DrRacket (I forget the mechanism).

cflewis commented 4 years ago

@samth : It must be DrRacket that does it, because it certainly doesn't happen in Racket Mode.

(test) doesn't seem to be defined, and I can't find anything in the docs to indicate what it would be. It would be nice to know what it is as I prefer using Racket Mode over DrRacket (working Vim bindings!)

samth commented 4 years ago

It's just running the test submodule (which is generated by the language).

An easy workaround is adding

(require test-engine/racket-tests)
(test)

at the end of your file.

cflewis commented 4 years ago

This worked, thank you :)

greghendershott commented 4 years ago
  1. I'm not sure how I overlooked @samth 's comment from June 17, 2019. Sorry!

  2. Now that I understand it's simply a (hidden!) test submodule, another simple work-around in Racket Mode is to use the M-x racket-test command (bound by default to C-c C-t).


If I understand correctly, this is similar to #430, where a #lang created a (hidden!) main submodule. In this case, instead it's a hidden test submodule.

I handled #430 with commit 5226366: Now M-x racket-run bound to F5 runs a main submodule if any, else the file module. (The previous, explicit behavior is still available as M-x racket-run-module-at-point C-c C-c.)

Possibly I should extend the new racket-run F5 also to run hidden test submodules, to even more closely emulate the DrRacket Run button behavior?


@cflewis The reason I keep typing "hidden!" is because the "normal" #lang racket way, you'd put main, test, or other submodules in your code, explicitly. And so the "traditional" M-x racket-run-module-at-point is kind of handy. Whatever module point (the cursor) is in, will be run. It's not wrong for some of these #langs to create the submodules for you, it just wasn't something I was accustomed to seeing, and until #430 Racket Mode didn't even attempt to handle those.

samth commented 4 years ago

One thing just to note here is the notion of "hidden" is really racket-mode-specific, since module+ is "hidden" in the sense that it also expands to a submodule, but racket-mode recognizes it specifically (and somewhat heuristically).

cflewis commented 4 years ago

@greghendershott : I would not wish to be so presumptuous as to have a beginner using a kinda-but-not-really supported dialect of Racket inform how a full-fat Racket plugin should operate!

I think the thing that makes this really an issue is that the "failure" is silent. (check-expect) simply does nothing, which is pretty confusing. One would at least think that if (check-expect) wasn't going to do what HTDP says it would do, that it was not doing it because (check-expect) wasn't a known definition, and throw an error that way. Unfortunately, it is a known definition, but it just doesn't do anything without the invisible cajoling.

As you said, this is an easy workaround: I just rebind my racket-run-and-switch-to-repl shortcut to do run tests as well.

For me, I think it would be totally fine for this to be a Google-able GitHub issue for people running into trouble. Perhaps there could also be a racket-test-and-switch-to-repl command to shorten the distance.

greghendershott commented 4 years ago

@samth By "hidden" all I meant is "the language adds a submodule that is not apparent in the user's program source". run-module-at-point doesn't work very well when there is no module within which to put point. :smile:

@cflewis I agree 100% about the UX. Handling the beginner-student languages, at all (much less making a just-works out-of-box experience) just hasn't been a high-priority goal, so far. I don't dogfood them. I'm not even sure, out of the relatively few people who use Racket Mode, how fewer many people use them.

Having said that, I think I could extend the F5 racket-run to handle test as well as main submodules -- regardless of who/what creates them. :wink: That's my new plan for how to handle this issue. Thanks again for commenting here, to revive this!

capfredf commented 4 years ago

FWIW, I used racket mode for teaching languages when I was an AI for a class that uses HTDP as the textbook. I think I used the workaround Sam mentions above to get check-expects running.

jacksonbenete commented 4 years ago

Sorry to be a Necromancer, I just want to say that I literally googled check-expect racket-mode and this open issue saved me from a lot of trouble and further confusion. I agree that the problem is the function failing silently.

I'm using racket-mode since day one I think, DrRacket is a good software but it's very memory hungry and slow on my laptop, also even common shortcuts like C-backspace doesn't work on it, racket-mode provide a lot of QoL for students as well. :)

If you later decide to implement a fix, would not be better to include the hidden (test) on (kbd "C-c C-t") instead of on (kbd "F5")? I'm assuming that loading the (test) module might slow down a bit (even if unnoticeable), someone running (kbd "C-c C-t") is explicitly asking for running tests, and that might as well include loading the hidden (test), so the person is already expecting that racket-mode would do some "extra-work". This would avoid racket-mode to reproduce the behavior of DrRacket of calling it's various hidden functions when you're doing a "standard run". Sorry if it sounds a bad suggestion, I'm just thinking that as those "hidden features" of DrRacket caused confusion, might be better to avoid implement then in the same way or at least document it better for users to know that hidden modules are being called.

greghendershott commented 4 years ago

I had the "engine hood open" working on #492, looking at code in the same neighborhood in the implementations of both Dr Racket and Racket Mode, and decided also to tackle the bigger change of implementing this.

The documentation for the new customization variable racket-submodules-to-run describes the run commands for which this works (or not).

racket-submodules-to-run

Extra submodules to run.

This is a list of submodules. Each submodule is described as a list, to support submodules nested to any depth.

This is used by commands that emulate the DrRacket Run command:

  • racket-run
  • racket-run-and-switch-to-repl F5

It is NOT used by commands that run one specific module, such as:

  • racket-run-module-at-point C-c C-k or C-c C-c
  • racket-test C-F5 or C-c C-t
  • racket-profile

The default value is '((test) (main)), similar to Dr Racket.

greghendershott commented 4 years ago

I should point out that, in addition to racket-submodules-to-run having a global default value, you can override it easily per-file. Just use the general Emacs ability to specify file-local variables.

For example:

#lang racket

(module+ test
  "I am the test submodule")

(module+ main
  "I am the main submodule")

"I am the file module"

Press F5 for racket-run-and-switch-to-repl and you see:

"I am the file module"
"I am the test submodule"
"I am the main submodule"

Back in the file buffer, do M-x and enter add-file-local-variable. Enter racket-submodules-to-run. Enter the value ((test)). Note: Do not add the leading quote '.

Now the edit buffer shows:

#lang racket

(module+ test
  "I am the test submodule")

(module+ main
  "I am the main submodule")

"I am the file module"

;; Local Variables:
;; racket-submodules-to-run: ((test))
;; End:

This will take effect every time you visit the file. But you haven't done so, yet, since making this change. So do e.g. M-x revert-buffer. (Emacs suggested doing this, but you might have missed the message in the echo area.)

Now when you F5 it outputs:

"I am the file module"
"I am the test submodule"

In other words it did not run main anymore, just test as you specified.