Restoring, for now.

svn: r5221
This commit is contained in:
Eli Barzilay 2007-01-04 18:00:31 +00:00
parent 301e6e9ecb
commit 5cc51c18dd
25 changed files with 1350 additions and 0 deletions

View File

@ -0,0 +1,14 @@
(module extension mzscheme
(provide add-test-suite-extension
test-suite-extensions)
(define (add-test-suite-extension button icon callback)
(test-suite-extensions (append
(test-suite-extensions)
(list (list button icon callback)))))
(define test-suite-extensions (make-parameter null)))

View File

@ -0,0 +1,6 @@
(module info (lib "infotab.ss" "setup")
(define name "Test Suite")
(define tools '(("tool.ss")))
(define tool-names (list "The Test Suite Tool"))
(define tool-icons (list '("question-mark.png" "test-suite" "private" "icons")))
)

View File

@ -0,0 +1,38 @@
#| This module provides a mixin that gives a snip a method called convert-to-string.
This method finds the editor that contains the snip and if it's a text it replaces
the snip with a string in that editor.
|#
(module convert-to-string mzscheme
(require
(lib "class.ss")
(lib "mred.ss" "mred")
(lib "framework.ss" "framework")
(lib "embedded-gui.ss" "embedded-gui")
(lib "contract.ss"))
(provide/contract
(convert-to-string-mixin mixin-contract)
(convert-to-string<%> interface?))
(define convert-to-string<%>
(interface ()
convert-to-string))
(define convert-to-string-mixin
(mixin ((class->interface snip%)) (convert-to-string<%>)
(inherit get-admin get-text)
(define/public (convert-to-string str)
(let ([to-ed (snip-parent this)])
(when (is-a? to-ed text%)
(let ([this-pos (send to-ed get-snip-position this)])
(when this-pos
(send to-ed begin-edit-sequence)
(send to-ed delete this-pos (add1 this-pos))
(send to-ed insert (string-length str) str this-pos)
(send to-ed end-edit-sequence))))))
(super-new)))
)

View File

@ -0,0 +1,38 @@
2003/12/18
* Fixed the bug where fixed width lables didn't fit in their extents.
* Made the test-box take the caret when inserted.
* Made error reporting for out of context test-cases highlight the box.
* Made tabbing work.
2003/12/16
* Made the aligned-pasteboards work
2003/11/17
* Made event handling of the pasteboards work differently
2003/11/13
* Added tabbing.
* Corrected the handling of multiple values in test case boxes.
* Made the empty test case error hightlight the full test case. It'd be
better to have it highlight just the part of the test case that is empty
but this is not possible with the current error highlighting system.
2003/10/??
* Changed the test-suite tool to make test-case-boxes.
2003/04/23
* Added code highlighting to the "Too many expressions in a test box
error message
2003/04/16
* Added a ChangeLog
* Added a seperator to the language menu
* Removed model and window directories from private
* Added an open button which opens the program being tested in drscheme. If
the file is already open in a drscheme window, it uses the existing one.
* Fixed tabbing so that snips that are off screen are scrolled to when
they are tabbed to.
* Made the save button appear when the program to test box gets modified.
* Made the save button not appear when the test-suite is just loaded.
* Documented aligned-snip-mixin and click-forwarding-mixin in the doc.txt
of the mrlib collection.

View File

@ -0,0 +1,59 @@
;;;;;;;;;;
;; Todo ;;
;;;;;;;;;;
multiview:
It'd be cool to allow multiple views of the test suite
errorboxes: future
Need to have boxes that handle errors.
output:
need to handle output (side effect) of test calls. Maybe not.
stepper: future
allow easy addition of break points and stepping of the testcases.
language-pref: future
Make a language level preference to execute or not execute the test cases.
Maybe just do it in debug mode? Actually, probablly just have a preference
to remember wether or not they have enabled or disabled test cases by
default.
disable: future
add a button to disable a test case.
error-no-stop:
when a test cases raises exception don't stop at all even it is not expected
just make it fail or even flag it as raising an error. This could be a
preference really.
project:
want to be able to run a lot of test-suites from the command line. Also sould
have a digest of number of tests failed or succeed at the end of a run.
scheme-unit:
integrate with scheme unit
;;;;;;;;;;
;; Bugs ;;
;;;;;;;;;;
namespace: robby
Adding the test-case macro to the top-level require makes test-cases in
modules not see the macro and fail to execute. Test-cases don't work in
beginner level because require makes no sence.
mouse-cursor: important
If you mouse down on a text box and mouse up in another, the cursor
appears in the second text box.
mouse-off: important, robby
If you mouse down on a button snip and move the mouse off, the button
stays depressed.
This may be the same as what you describe below, but when I type this:
(let loop () (loop)) into a test case window and then type more close
parens after that, things vibrate in a strange way.
test-suite entry boxes are not locked when the program is executing

View File

@ -0,0 +1,160 @@
######################################################################
Immediate TODO list:
######################################################################
To highlight test-cases that are currently selected, use the same method
as in test-text. Only color the snip though, not the editor. This will
fill in the places in the snip that don't have children snips on it.
--------------
To enable and disable the delete snip button use the on-focus or on
not-focus of the test-cases. do something clever to avoid the blinking of
the delete button between off-focus and on-focus.
--------------
make GUI-monkey test-suite for test-suite
--------------
make a better test-suite window using editor-mixin
and derive a controller from it, not the way I
am currently doing it. (this will give undo/redo)
--------------
figure out the problem with resized overloading
and a clean way to fix it.
--------------
override on-default-event of the pasteboard if it's not needed
so that all the funny selection and movement doesn't happen.
-------------------
On Mon, Nov 18, 2002 at 03:03:49PM -0600, Robert Bruce Findler wrote:
> Make deletion actually set the focus to another one of the snips, so I
> can keep deleting.
-------------------
> Also, it may look better to remove the outer box entirely and just have
> lines between the test case items (or even not, if you can figure out
> the background colors)
-------------------
* There's a problem with handling mouse-button releases. They seem to
be handled like clicks. For example, drag a selection in "Expected",
but release the mouse in "Call". The selection will move to "Call".
Matthew
-------------------
- after xcuting and receiving checks for all tests, edit an expected value
and see whether you get a save button
------------------
######################################################################
Nonimmediate TODO:
######################################################################
disable the open button when there is no program to test in the text box
-------------------
make undo/redo work
-------------------
have the 'special' menu in the test-suite tool so that
one might add xml boxes to test-suites for instance.
-------------------
Disable the open button when there is nothing in the program to test box
-------------------
Refactor languaged frame unit so that it does not depend on the model but
instread stores the language and teachpacks in it's own fields and may be
used by DrScheme as a general mixin to add language support to windows.
-------------------
add support for the test definitions boxes.
-------------------
One thing I noticed about your tester: it doesn't seem to use Robby's
string-constants system. So all the menus and buttons and stuff you
have will always be in English, which will look weird to people who
use DrScheme with another language.
Philippe
-------------------
Give better error message on bad file loading.(message box?)
disallow newlines in program to file box
-------------------
It'd also be really nice to have a Stepper associated with individual
test cases -- this is currently a pain with the stepper (I have to
step through old runs to get to new ones, or putz around with
commenting code). With the testing facility you have just the right
interface to make the stepper highly usable!
----------------
Programs aren't associated with the buffer that is open (like the REPL) They
are associated with the saved file. This is confusing when you change a
program and expect it's test to behave differently in the test window.
Currently you must save it. Should there either be an "out of syn" warning
or a way to read straight from the buffer?
-----------------
I think that there is too much whitespace around the boxes, too. It'd
be nicer to use color to separate the lines and get back some screen
"real estate".
Robby
-----------------
make the test-suite not halt on a failure?
-----------------
- we need to be able to associate a test suite with more than one
definitions window (say we reimplement a module to improve its
performance. it should pass the same functionality tests)
------------------
Pls add a checkbox so that you can turn off individual tests
or skip tests that fail. -- Matthias
------------------
######################################################################
Unknown:
######################################################################
I've done a first cut of io in the test suite. It doesn't do everything
-- here are some TODOs for you:
- hide last newline (like in value printer)
- remove IO boxes when executing
- fix the `(lambda () (send this ...)' hack
The last one needs some re-organization, I believe. I'll let you think
about it.
Also, I noticed that execute doesn't shutdown the old custodian, so
frames and things created in previous tests are still around in the new
test. (This also means that there is a memory leak!)
That's already on your list, right?
Robby
-----------------
For your TODO list, please. :)
It would be nice if there was a little turn down triangle in the test
cases that would collapse the entire case to just the triangle. If
adjacent test cases were collapsed, they would sit next to each other
horizontally (ie, leave more vertical room). When executing, if the
test case passes and it was closed, it stays closed, but if it fails
and it was closed, it pops open.
What do you think?
-----------------
When I create a new case, the tester installs a huge big question
mark. It's cute [so are the cross and check -- these will probably be
a big hit with kids -- whoever designed them did a great job!], but
(a) I think they're too large; and (b) I think the ? is misleading.
Something about ?'s shape, size and location makes me think it must be
a button. (Location, especially -- it's exactly where I would expect
the "Submit" button on a Web form.) I clicked on it a few times, but
nothing happened. Then I remembered that there'd been an Execute
button at the top, clicked on it, and saw the outcome. In other
words, I expected ? to play the role of Execute.
-sk
-------------------
The box under Actual surprised me multiple times. After entering a
value for Call and for Expected, I was surprised to find tabbing and
typing do nothing. The color change just tells me "this is special"
(eg, where you enter your SSN on a govt form); the box still tells me
"this is a place you enter text". Can you remove the box entirely
from below Actual? In fact, if Actual and the output never appeared
until you executed the program, that may be even better.
-sk
----------------
BUG: I wanted to make the second test case buggy (but not lose the
subsequent ones). So I clicked on the box around the first one, which
showed me little highlight points around the box. I tried to move it,
but nothing happened. Then I went to the scroll bar and tried to
scroll somewhere in that region, but got a MrEd toolbox method error,
and now the screen won't redraw. I'll try to reproduce it. ... Okay,
did so successfully, I'll send in a bug report. Anyway, it'd be nice
to reorder cases. It'd be especially neat the reorder by
success/failure.
-------------------

View File

@ -0,0 +1,38 @@
;; This code is duplicated from the servelt-builder.ss file
(module find-scheme-menu mzscheme
(provide find-scheme-menu)
(require
(lib "class.ss")
(lib "mred.ss" "mred")
(lib "string-constant.ss" "string-constants"))
; : menu% -> (U menu% #f)
; to crawl up and down the menu hierarcy to find the scheme menu
; This attempts to work even if
; a) the menus and menu items are in a different langauge
; b) the menus are in Philippe's language where they are all blank (and hence the same)
; It starts by selecting the menu by position to avoid problem b).
; Just to be paranoid, it looks in other positions, too.
; The scheme menu must have "Create Executable..." in some language as a menu item.
(define (find-scheme-menu special-menu)
(let* ([bar (send special-menu get-parent)]
[menus (send bar get-items)]
[ordered-menus (if (< (length menus) 5)
menus
(cons (car (cddddr menus)) menus))])
(ormap (lambda (m)
(and (string=? (label->plain-label (string-constant scheme-menu-name))
(send m get-plain-label))
(ormap is-create-executable-item? (send m get-items))
m))
ordered-menus)))
; menu-item% -> bool
(define (is-create-executable-item? item)
(and (is-a? item labelled-menu-item<%>)
(string=? (string-constant create-executable-menu-item-label)
(send item get-label))))
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 718 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 856 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 846 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 813 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 737 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 433 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

View File

@ -0,0 +1,2 @@
(module info (lib "infotab.ss" "setup")
(define name "Test Suite private"))

View File

@ -0,0 +1,36 @@
(module make-snipclass mzscheme
(require
(lib "etc.ss")
(lib "mred.ss" "mred")
(lib "class.ss")
(lib "contract.ss"))
(define read-proc? (class? (is-a?/c editor-stream-in%) . -> . object?))
(provide/contract
(make-snipclass ((class? string?) (read-proc?) . opt-> . (is-a?/c snip-class%)))
(send-read-from-file read-proc?))
;; Creats a snipclass and registers it with the snip class list
(define make-snipclass
(opt-lambda (class% classname (read-proc send-read-from-file))
(let* ([abstract-snip-class%
(class snip-class%
#;((is-a?/c editor-stream-in%) . -> . (is-a?/c interactions-box%))
;; Produces an interaction box from the given file stream
(define/override (read f)
(read-proc class% f))
(super-new))]
[sc (new abstract-snip-class%)])
(send sc set-classname classname)
(send sc set-version 2)
(send (get-the-snip-class-list) add sc)
sc)))
;; Returns an object of class after reading its contents from the given stream
(define (send-read-from-file class% f)
(let ([object (new class%)])
(send object read-from-file f)
object))
)

View File

@ -0,0 +1,62 @@
(module print-to-text mzscheme
(require
(lib "list.ss")
(lib "etc.ss")
(lib "class.ss")
(lib "contract.ss")
(lib "unit.ss")
(lib "mred.ss" "mred")
(lib "framework.ss" "framework")
(lib "tool.ss" "drscheme"))
(provide print-to-text^
print-to-text@)
(define-signature print-to-text^
(print-to-text))
(define-unit print-to-text@
(import drscheme:tool^)
(export print-to-text^)
;; Using the current languages print operations, print the list of values to the text
(define (print-to-text atext vals)
(unless (empty? vals)
(send* atext
(begin-edit-sequence)
(erase))
(let ([port
(make-output-port
'set-actuals
always-evt
(lambda (s start end block? enable-breaks?)
(send atext insert
(list->string
(map integer->char
(bytes->list (subbytes s start end)))))
(- end start))
void
(lambda (v block? enable-breaks?)
(if (v . is-a? . snip%)
(send atext insert v)
(send atext insert (format "~s" v)))
#t))])
(define (print-one v)
(let* ([language-settings
(preferences:get
(drscheme:language-configuration:get-settings-preferences-symbol))]
[language
(drscheme:language-configuration:language-settings-language
language-settings)]
[settings
(drscheme:language-configuration:language-settings-settings
language-settings)])
(send language render-value v settings port)))
(print-one (first vals))
(for-each
(lambda (val)
(newline port)
(print-one val))
(rest vals)))
(send atext end-edit-sequence)))))

View File

@ -0,0 +1,586 @@
#| Note: Test cases have a lot of state like the to-test, predicate, etc. I need to find a way
to not have to maintain this list of state in many places. It's not as simple as a global
list, however, because they need to be instantiation variables, etc.
|#
(module test-case-box mzscheme
(provide test-case-box^ test-case-box@)
(require
(lib "class.ss")
(lib "list.ss")
(lib "mred.ss" "mred")
(lib "unit.ss")
(lib "tool.ss" "drscheme")
(lib "etc.ss")
(lib "match.ss")
(lib "framework.ss" "framework")
(lib "readerr.ss" "syntax")
(lib "string-constant.ss" "string-constants")
(lib "embedded-gui.ss" "embedded-gui")
(lib "shared.ss" "stepper" "private")
"make-snipclass.ss"
"convert-to-string.ss"
"text-syntax-object.ss"
"print-to-text.ss"
"test-case.ss"
(only (lib "teachprims.ss" "lang" "private") beginner-equal?))
(define-signature test-case-box^ extends drscheme:tool-exports^ (test-case-box%))
(define-unit test-case-box@
(import drscheme:tool^ text->syntax-object^ print-to-text^)
(export test-case-box^)
(define test-case:program-editor% false)
(define (phase1) (void))
(define (phase2)
(set! test-case:program-editor%
(init-text-mixin
(tabbable-text-mixin
((drscheme:unit:get-program-editor-mixin)
scheme:text%)))))
;; The test case box that is inserted into the drscheme frame
(define test-case-box%
(class* (on-show-editor-snip-mixin
(convert-to-string-mixin
(decorated-editor-snip-mixin editor-snip%))) (readable-snip<%>)
(inherit get-admin convert-to-string)
;; A text that will uncollapse the test case when it is highlighted for an error
(define error-alert-text%
(class test-case:program-editor%
(define/override highlight-range
(opt-lambda (start end color (bitmap false) (caret-space false) (priority 'low))
(when collapsed? (collapse false))
(super highlight-range start end color bitmap caret-space priority)))
(super-new)))
(init-field
[enabled? true]
[actual-show? false]
[collapsed? false]
[show-right-pane? false]
[error-box? false]
[to-test (new error-alert-text%)]
[expected (new error-alert-text%)]
[predicate (new error-alert-text% (text ""))]
[should-raise (new error-alert-text% (text ""))]
[error-message (new error-alert-text%)])
#;(any? (union integer? false?) (union integer? false?) (union integer? false?) . -> . any?)
;; Called by the execution to get a syntax object that represents this box.
(define/public read-special
(opt-lambda (source (line false) (column false) (position false))
#;((is-a?/c text%) . -> . syntax-object?)
;; Creates a single syntax object out of the text or raises read-error
(define (text->syntax-object text default-content)
(match (text->syntax-objects text default-content)
[() (raise-read-error (string-constant test-case-empty-error)
source line false position 1)]
[(stx) stx]
[(stx next rest-stx ...)
(raise-read-error (string-constant test-case-too-many-expressions-error)
text
(syntax-line next)
(syntax-column next)
(syntax-position next)
(syntax-span next))]))
(syntax-property
(if enabled?
(with-syntax ([to-test-stx (stepper-syntax-property (text->syntax-object to-test #f)
'stepper-test-suite-hint
true)]
[update-stx (lambda (x) (update x))] ; eta public method
[set-actuals-stx set-actuals]
[w printf])
(if error-box?
(with-syntax ([exn-pred-stx (text->syntax-object should-raise #'exn:fail?)]
[exn-handler-stx
(if (empty-text? error-message)
#'(lambda (v) true)
#`(lambda (v)
(equal? (exn-message v)
#,(text->syntax-object
error-message
#f))))])
(syntax/loc (datum->syntax-object
false 'ignored (list source line column position 1))
(test-error-case to-test-stx
exn-pred-stx
exn-handler-stx
update-stx
set-actuals-stx)))
(with-syntax ([exp-stx (text->syntax-object expected #f)]
[pred-stx (text->syntax-object predicate beginner-equal?)])
(syntax/loc (datum->syntax-object
false 'ignored (list source line column position 1))
(test-case pred-stx
to-test-stx
exp-stx
update-stx
set-actuals-stx)))))
(stepper-syntax-property #'(define-values () (values))
'stepper-skip-completely
true))
'test-case-box #t)))
#;(boolean? . -> . void?)
;; sets the test case to the proper result bassed on if it was correct
(define/public (update pass?)
(send result update (if pass? 'pass 'fail)))
#;(-> void?)
;; resets the state of the test case
(define/public (reset)
(send pb lock-alignment true)
(send* actual
(lock false)
(erase)
(lock true))
(when enabled?
(send result update 'unknown))
(send pb lock-alignment false))
#;(boolean? . -> . void?)
;; enables or disables the test case
(define/public (enable enable?)
(unless (boolean=? enabled? enable?)
(if enable?
(begin (set! enabled? true)
(send result update 'unknown))
(begin (set! enabled? false)
(reset)
(send result update 'disabled)))))
#;(-> void)
;; tells the test-box to take the caret
(define/public (take-caret)
(send pb set-caret-owner
(send (send to-test get-admin) get-snip)
'display))
#;(-> string)
;; The textual representation of this test-case
;; STATUS: Begginner language doesn't have with-handlers
;; STATUS: Pretty printing not here yet.
(define (get-string)
(if error-box?
"Not yet implemented. What to do in beginner?"
(format "(~a ~a ~a)"
(send predicate get-text)
(send to-test get-text)
(send expected get-text))))
#;((is-a?/c expand-program%) (listof any?) . -> . void?)
;; set the text in the actual field to the value given
;; STATUS: Ensure the edit-sequence is needed.
(define (set-actuals vals)
(send (send (get-admin) get-editor) begin-edit-sequence)
(send actual lock false)
(print-to-text actual vals)
(send actual lock true)
(send (send (get-admin) get-editor) end-edit-sequence))
;;;;;;;;;;
;; Saving and Copying
#;(-> (is-a?/c test-case-box%))
;; Called by drscheme to copy and paste this test-case
(define/override (copy)
(let ([new-to-test (new error-alert-text%)]
[new-expected (new error-alert-text%)]
[new-predicate (new error-alert-text%)]
[new-should-raise (new error-alert-text%)]
[new-error-message (new error-alert-text%)])
(send to-test copy-self-to new-to-test)
(send expected copy-self-to new-expected)
(send predicate copy-self-to new-predicate)
(send should-raise copy-self-to new-should-raise)
(send error-message copy-self-to new-error-message)
(new test-case-box%
(enabled? enabled?)
(actual-show? actual-show?)
(collapsed? collapsed?)
(show-right-pane? show-right-pane?)
(error-box? error-box?)
(to-test new-to-test)
(expected new-expected)
(predicate predicate)
(should-raise should-raise)
(error-message error-message))))
#;((is-a?/c editor-stream-out%) . -> . void?)
;; Writes this test case box to the given file.
(define/override (write f)
(send to-test write-to-file f)
(send expected write-to-file f)
(send predicate write-to-file f)
(send should-raise write-to-file f)
(send error-message write-to-file f)
(send f put (if enabled? 1 0))
(send f put (if collapsed? 1 0))
(send f put (if error-box? 1 0)))
#;((is-a?/c editor-stream-in%) . -> . void?)
;; Reads the state of the box in from the given stream
(define/public (read-from-file f)
(let ([enabled?-box (box 0)]
[collapsed?-box (box 0)]
[error-box?-box (box 0)])
(let ([vers (send tcb-sc reading-version f)])
(case vers
[(1)
;; Discard comment:
(send (new text%) read-from-file f)
(send* to-test (erase) (read-from-file f))
(send* expected (erase) (read-from-file f))
;; Nothing else is in the stream in version 1,
;; so leave the defaults
]
[(2)
(send* to-test (erase) (read-from-file f))
(send* expected (erase) (read-from-file f))
(send* predicate (erase) (read-from-file f))
(send* should-raise (erase) (read-from-file f))
(send* error-message (erase) (read-from-file f))
(send f get enabled?-box)
(send f get collapsed?-box)
(send f get error-box?-box)
(enable (= (unbox enabled?-box) 1))
;; Presently this is poking a bug in the embedded-gui.
;; I'll leaving it commented til I reduce the bug.
#;(collapse (= (unbox collapsed?-box) 1))
(toggle-error-box (= (unbox error-box?-box) 1))]))))
;;;;;;;;;;
;; Layout
#;(-> (is-a?/c bitmap%))
;; The bitmap to use for the top corner of the box.
(define/override (get-corner-bitmap)
(if error-box?
(make-object bitmap% (icon "scheme-box.jpg"))
(make-object bitmap% (icon "scheme-box.jpg"))))
#;(-> (symbols 'top-right 'top-left 'bottom-left 'bottom-right))
;; The location of the corner bitmap
(define/override (get-position) 'top-right)
#;(-> (union string? (is-a?/c color%)))
;; The color of the border of this box
(define/override (get-color) (if error-box? "red" "purple"))
#;(-> (is-a?/c popup-menu%))
;; The popup menu used for the top corner of this box
(define/override (get-menu)
(let ([the-menu (new popup-menu% (title (string-constant test-case-menu-title)))])
(define (make-toggle label f init)
(letrec ([item (new checkable-menu-item%
(parent the-menu)
(label label)
(checked init)
(callback (lambda (m e)
(f (send item is-checked?)))))])
item))
(new menu-item%
(label (if error-box?
(string-constant test-case-switch-to-nonerror-box)
(string-constant test-case-switch-to-error-box)))
(parent the-menu)
(callback (lambda (m e)
(toggle-error-box (not error-box?)))))
(make-toggle
(string-constant test-case-collapse)
collapse collapsed?)
(make-toggle
(string-constant test-case-show-actual)
show-actual actual-show?)
(make-toggle
(string-constant test-case-enable)
(lambda (b) (enable b)) enabled?) ; eta public method
(make-toggle
(if error-box?
(string-constant test-case-show-error-message)
(string-constant test-case-show-predicate))
show-right-pane show-right-pane?)
(new menu-item%
(label (string-constant test-case-convert-to-text))
(parent the-menu)
(callback
(lambda (m e)
(convert-to-string (get-string)))))
the-menu))
#;(-> void)
;; Hide and show the boxes that differ between error and now and
;; poke the snip-parent to display the new boarder color
(define (toggle-error-box bool)
(set! error-box? bool)
(send pb lock-alignment true)
(send should-be-pane show (not error-box?))
(send should-raise-pane show error-box?)
(send predicate-pane show (not error-box?))
(send error-message-pane show error-box?)
(send pb lock-alignment false)
(if error-box?
(set-tabbing to-test should-raise)
(set-tabbing to-test expected))
(>>= (snip-parent this)
(lambda (admin)
(send admin resized this true))))
#;(boolean? . -> . void)
;; Shows or hides the actual box
(define (show-actual show?)
(set! actual-show? show?)
(send pb lock-alignment true)
(send show-actual-button set-state
(boolean->show-actual-btn-state show?))
(send to-test-pane stretchable-height show?)
(send actual-pane show show?)
(send pb lock-alignment false))
#;(boolean? . -> . void)
;; Toggles the test-case between a collapsed minimal state and one with entry boxes.
(define (collapse bool)
(set! collapsed? bool)
(send collapse-button set-state
(boolean->collapse-btn-state bool))
(send body show (not bool)))
#;(booean? . -> . void)
;; Shows or hides the predicate box
(define (show-right-pane show?)
(set! show-right-pane? show?)
(send right-pane show show-right-pane?))
;;;;;;;;;;
;; Box layout
(field
[pb (new aligned-pasteboard%)]
[main (new horizontal-alignment% (parent pb))])
;;;;;;;;;;
;; The button bar w/ result check mark box
(field
[button-pane (new vertical-alignment% (parent main))]
[result (new result-snip% (status (if enabled? 'unknown 'disabled)))])
(snip-wrap button-pane result)
(field
[collapse-button
(new turn-button%
(parent button-pane)
(state (boolean->collapse-btn-state collapsed?))
(turn-off (lambda (b e) (collapse true)))
(turn-on (lambda (b e) (collapse false))))]
[show-actual-button
(new turn-button%
(parent button-pane)
(state (boolean->show-actual-btn-state actual-show?))
(turn-off (lambda (b e) (show-actual false)))
(turn-on (lambda (b e) (show-actual true))))])
;;;;;;;;;;
;; The text boxes
(field
[body (new horizontal-alignment% (parent main) (show? (not collapsed?)))]
[to-test-pane
(new labeled-text-field%
(parent body)
(label (string-constant test-case-to-test))
(text to-test))]
[result-pane (new vertical-alignment% (parent body))]
[should-be-pane
(new labeled-text-field%
(parent result-pane)
(show? (not error-box?))
(label (string-constant test-case-expected))
(text expected))]
[should-raise-pane
(new labeled-text-field%
(parent result-pane)
(show? error-box?)
(label (string-constant test-case-should-raise))
(text should-raise))]
[actual (new actual-text%)]
[actual-pane
(new labeled-text-field%
(parent result-pane)
(label (string-constant test-case-actual))
(show? actual-show?)
(snip-class (grey-editor-snip-mixin stretchable-editor-snip%))
(text actual))]
[right-pane (new vertical-alignment% (parent body) (show? show-right-pane?))]
[predicate-pane
(new labeled-text-field%
(parent right-pane)
(show? (not error-box?))
(label (string-constant test-case-predicate))
(text predicate))]
[error-message-pane
(new labeled-text-field%
(parent right-pane)
(show? error-box?)
(label (string-constant test-case-error-message))
(text error-message))])
(super-new (editor pb))
(set-tabbing to-test expected predicate)
(set-tabbing should-raise error-message)
;;;;;;;;;;
;; Snip class
(inherit set-snipclass)
(set-snipclass tcb-sc)))
;;;;;;;;;;
;; Snip class
;; A snip-class for the test case box
(define tcb-sc
(make-snipclass
test-case-box%
"test-case-box%"
#;
(lambda (class% f)
(let ([enabled?-box (box 0)]
[collapsed?-box (box 0)]
[error-box?-box (box 0)]
[to-test (new test-case:program-editor%)]
[expected (new test-case:program-editor%)]
[predicate (new test-case:program-editor%)]
[should-raise (new test-case:program-editor%)]
[error-message (new test-case:program-editor%)])
(send to-test read-from-file f)
(send expected read-from-file f)
(send predicate read-from-file f)
(send should-raise read-from-file f)
(send error-message read-from-file f)
(send f get enabled?-box)
(send f get collapsed?-box)
(send f get error-box?-box)
(new class%
(enabled? (= (unbox enabled?-box) 1))
(collapsed? (= (unbox collapsed?-box) 1))
(error-box? (= (unbox error-box?-box) 1))
(to-test to-test)
(expected expected)
(predicate predicate)
(should-raise should-raise)
(error-message error-message))))))
)
#;((-> void?) (-> void?) (symbols 'up 'down) . -> . snip%)
;; a snip which acts as a toggle button for rolling a window up and down
;; STATUS : Change this to derive embedded-toggle-button%
(define turn-button%
(class embedded-toggle-button%
(super-new
(images-off (cons (icon "turn-down.png") (icon "turn-down-click.png")))
(images-on (cons (icon "turn-up.png") (icon "turn-up-click.png"))))))
;; a snip which will display a pass/fail result
(define result-snip%
(class image-snip%
(inherit set-bitmap)
(init-field [status 'unknown])
;; ((symbols 'pass 'fail 'unknown 'disabled) . -> . void?)
;; updates the image with the icon representing one of three results
(define/public (update value)
(set-bitmap
(memoize value
(lambda ()
(make-object bitmap%
(test-icon
(case value
[(pass) "small-check-mark.jpeg"]
[(fail) "small-cross.jpeg"]
[(unknown) "small-empty.gif"]
[(disabled) "small-no.gif"])))))))
(super-new)
(update status)))
(define memory (make-hash-table 'equal))
(define (memoize k thunk)
(hash-table-get memory k (lambda ()
(let ([v (thunk)])
(hash-table-put! memory k v)
v))))
#;(string? . -> . string?)
;; A path to the icon given a file name
(define (icon str)
(build-path (collection-path "icons") str))
#;(string? . -> . string?)
;; A path to the icon in the test-suite given a file name
(define (test-icon str)
(build-path (collection-path "test-suite") "private" "icons" str))
;; a locked text hightlighted to show that it is inactive
(define actual-text%
(class (grey-editor-mixin
(text:hide-caret/selection-mixin scheme:text%))
(inherit hide-caret lock)
(super-new)
(hide-caret true)
(lock true)))
;; a text mixin that gives the text an init arg of an initial contents
(define init-text-mixin
(mixin ((class->interface text%)) ()
(inherit insert)
(init [text ""])
(super-new)
(insert text)))
#;(boolean? . -> . (symbols 'on 'off))
;; converts a boolean to the value expected by the collapse button
(define (boolean->collapse-btn-state bool)
(if bool 'on 'off))
#;(boolean? . -> . (symbols 'on 'off))
;; converts a boolean to the value expected by the show actual button
(define (boolean->show-actual-btn-state bool)
(if bool 'off 'on))
#;((is-a?/c text%) . -> . boolean?)
;; True if the given text is empty
(define (empty-text? t)
(let ([str (send t get-text)])
(string=? str "")))
;;;;;;;;;;
;; Eaiser syntax for embedded-gui
(define (snip-wrap p snip)
(new snip-wrapper% (parent p) (snip snip)))
;; Inserts a label and a text field into the given alignment
(define labeled-text-field%
(class vertical-alignment%
(init label text (snip-class stretchable-editor-snip%))
(super-new (stretchable-height false))
(new embedded-message% (parent this) (label label))
(new snip-wrapper%
(parent this)
(snip (new snip-class
(editor text)
(min-width 80))))))
#;((union any? false?) (any? . -> . any?) . -> . (union any? false?))
;; Send the value to the function unless it 'fails' by returning false. Like haskell's bind operator.
(define (>>= value function)
(cond
[value => function]
[else false]))
)

View File

@ -0,0 +1,77 @@
#|
This module provides a test-case macro for the test-case-box to expand into.
The test-case box does not immediatly expand into the body of the macro itself
because the macro is able to check the (syntax-local-context) of the invocation
to give better error messages when the test-case is not at the top level.
|#
(module test-case mzscheme
(require-for-syntax (lib "shared.ss" "stepper" "private"))
(provide test-case test-error-case)
;; STATUS : Abstract these two syntaxes and use string constant for the error
(define-syntax (test-case stx)
(syntax-case stx ()
[(_ test to-test-stx exp-stx record set-actuals)
(case (syntax-local-context)
[(module top-level)
(stepper-syntax-property
#`(define-values ()
(let ([to-test-values (call-with-values
(lambda () #,(stepper-syntax-property #`to-test-stx
'stepper-test-suite-hint
#t))
list)]
[exp-values (call-with-values (lambda () exp-stx) list)])
(record (and (= (length to-test-values) (length exp-values))
(andmap test to-test-values exp-values)))
(set-actuals to-test-values)
(values)))
'stepper-skipto
(append
;; define-values->body
skipto/third
;; rhs of first binding of let-values:
skipto/second
skipto/first
skipto/second
;; 2nd arg of call-with-values application:
skipto/cdr
skipto/second
;; first (only) body of lambda:
skipto/cddr
skipto/first))]
[else (raise-syntax-error #f
"test case not at toplevel"
(syntax/loc stx (test-case to-test-stx exp-stx)))])]))
(define-syntax (test-error-case stx)
(syntax-case stx ()
[(_ to-test-stx exn-pred exn-handler record set-actuals)
(case (syntax-local-context)
[(module top-level)
(stepper-syntax-property
#'(define-values ()
(with-handlers ([exn-pred
(lambda (v)
(set-actuals (list v))
(record (exn-handler v))
(values))]
[void
(lambda (v)
(set-actuals v)
(record #f)
(values))])
to-test-stx
(record #f)
(values)))
'stepper-skipto
`(,@skipto/third
;; with-handlers:
,@skipto/fourth
))]
[else (raise-syntax-error #f
"test case not at toplevel"
(syntax/loc stx (test-case to-test-stx exp-stx)))])]))
)

View File

@ -0,0 +1,59 @@
(module text-syntax-object mzscheme
(require
(lib "unit.ss")
(lib "class.ss")
(lib "list.ss")
(lib "tool.ss" "drscheme")
(lib "framework.ss" "framework")
(lib "mred.ss" "mred"))
(provide text->syntax-object@
text->syntax-object^)
(define top-id #'here)
(define-signature text->syntax-object^ (text->syntax-objects))
(define-unit text->syntax-object@
(import drscheme:tool^)
(export text->syntax-object^)
#;((is-a?/c text%) . -> . (listof syntax-object?))
;; a syntax object representing the text with the color of the given object
(define (text->syntax-objects text default-v)
(let ([port (open-input-text-editor text)])
#;(-> (listof syntax-object?))
;; Reads all the syntax objects for the text%
(define (read-all-syntax)
(let* ([language-settings
(preferences:get
(drscheme:language-configuration:get-settings-preferences-symbol))]
[language
(drscheme:language-configuration:language-settings-language
language-settings)]
[settings
(drscheme:language-configuration:language-settings-settings
language-settings)])
(if (drscheme:language-configuration:language-settings? language-settings)
(let ([thunk (if (and default-v
(zero? (send text last-position)))
(let ([got? #f])
(lambda ()
(begin0
(if got?
eof
default-v)
(set! got? #t))))
(send language front-end/interaction
(open-input-text-editor text)
settings
(drscheme:teachpack:new-teachpack-cache '())))])
(let loop ()
(let ([expr (thunk)])
(cond [(eof-object? expr) empty]
[else (cons expr (loop))]))))
(error 'text->syntax-object "Invalid language settings"))))
(read-all-syntax)))
))

175
collects/test-suite/tool.ss Normal file
View File

@ -0,0 +1,175 @@
(module tool mzscheme
(provide tool@)
(require
(lib "etc.ss")
(lib "class.ss")
(lib "mred.ss" "mred")
(lib "unit.ss")
(lib "tool.ss" "drscheme")
(lib "framework.ss" "framework")
(lib "string-constant.ss" "string-constants")
(lib "snip-lib.ss" "mrlib" "private" "aligned-pasteboard")
"private/test-case-box.ss"
"private/find-scheme-menu.ss"
"private/text-syntax-object.ss"
"private/print-to-text.ss")
(define-signature menu-extentions^ ())
(define-unit menu-extentions@
(import drscheme:tool^ test-case-box^)
(export menu-extentions^;drscheme:tool-exports^
)
;; This delay is set up because reset-highlighting is called immediately
;; after execution where I don't want the test-cases to be cleared.
;; STATUS: It appears that the problem this flag was created to fix has been
;; fixed and is now delaying unecessarily. I have commented out the place where
;; it is set to true, effectively turning off the feature. I'll remove the code
;; if no bugs creap in after sufficient usage.
(define delay? false)
;; This flag ensures that the test case boxes are only reset when the need
;; to be, which is only once after each execution of the program.
(define needs-reset? false)
;; Adds the test suite tool menu to the Dr. Scheme frame
;; Updates the needs-reset? when the the program is executed
(define test-case-mixin
(mixin (drscheme:unit:frame<%> top-level-window<%> (class->interface frame%)) ()
(inherit get-definitions-text get-edit-target-object get-menu-bar
get-special-menu)
#;(-> void)
;; Called when the program is executed
;; Overriden to rest the test-cases.
(define/override (execute-callback)
(send (get-definitions-text) for-each-test-case
(lambda (case) (send case reset)))
(super execute-callback)
;(set! delay? true)
(set! needs-reset? true))
#;(boolean . -> . void)
;; Enable or disable all of the test-cases
(define (enable enable?)
(send (get-definitions-text) for-each-test-case
(lambda (case) (send case enable enable?))))
#;((is-a?/c menu-item%) . -> . void)
;; NOTE: This function is COPIED from the drscheme/private/unit.ss file
(define (has-editor-on-demand menu-item)
(let ([edit (get-edit-target-object)])
(send menu-item enable (and edit (is-a? edit editor<%>)))))
(super-new)
;; Create the new menu items.
(field
[test-cases-enabled? true]
[insert-menu-item
(new menu-item%
(label (string-constant test-case-insert))
(parent (get-special-menu))
(callback
(lambda (menu event)
(let ([test-box (new test-case-box% (enabled? test-cases-enabled?))]
[text (get-edit-target-object)])
(when text
(send text begin-edit-sequence)
(send text insert test-box)
(send test-box take-caret)
(send text end-edit-sequence)))))
(demand-callback has-editor-on-demand))])
(let ([parent (find-scheme-menu (get-special-menu))])
(and parent
(new menu-item%
(parent parent)
(label (string-constant test-case-disable-all))
(callback
(lambda (menu event)
(set! test-cases-enabled? (not test-cases-enabled?))
(if test-cases-enabled?
(send menu set-label (string-constant test-case-disable-all))
(send menu set-label (string-constant test-case-enable-all)))
(send (get-definitions-text) for-each-test-case
(lambda (tc) (send tc enable test-cases-enabled?))))))))))
(drscheme:get/extend:extend-unit-frame test-case-mixin)
;; Adds a hook in the reset-highlighting to clear all of the test-case results when
;; the appropriate
;; STATUS: It's better to override reset-highlighting but this after-insert/delete works
;; for now.
(define clear-results-mixin
(mixin (editor<%>) ()
(inherit find-first-snip)
;#;(case-> (-> boolean?) (boolean? . -> . void))
;;; Get or set the delay-reset field
;(define/public delay-reset
; (case-lambda
; [() delay?]
; [(v) (set! delay? v)]))
#;(-> void)
;; set all of the test-case-boxes in the definitions text to an unevaluated state
(define/public (reset-test-case-boxes)
(when needs-reset?
(set! needs-reset? false)
(for-each-test-case (lambda (snip) (send snip reset)))))
#;(((is-a?/c test-case-box%) . -> . void) . -> . void)
;; executes the given function on each test-case-box
(define/public (for-each-test-case f)
(for-each-snip
(lambda (snip)
(when (is-a? snip test-case-box%)
(f snip)))
(find-first-snip)))
(super-new)))
(drscheme:get/extend:extend-definitions-text clear-results-mixin)
;; Require the test-case macro into every new namespace when a program is run.
(define require-macro-mixin
(mixin ((class->interface drscheme:rep:text%)) ()
(inherit get-user-namespace get-definitions-text)
#;((is-a?/c area<%>) . -> . (is-a?/c frame%))
;; The frame containing the given area
(define (find-frame area)
(let ([parent (send area get-parent)])
(if parent
(find-frame parent)
area)))
#;(-> void)
;; Called to indicate that the program annotations should be cleared.
;; Overriden to reset test case boxes
(define/override (reset-highlighting)
(super reset-highlighting)
(let ([defs-text (get-definitions-text)])
;(if (send text delay-reset)
; (send text delay-reset false)
; (send text reset-test-case-boxes))))
(send defs-text reset-test-case-boxes)))
#;(-> void)
;; Called when the program is execute to reset the rep:text
;; Overriden to require the test case macro into any program that is executed.
(define/override (reset-console)
(super reset-console)
(parameterize ([current-namespace (get-user-namespace)])
(namespace-require '(lib "test-case.ss" "test-suite" "private"))))
(super-new)))
(drscheme:get/extend:extend-interactions-text require-macro-mixin))
(define tool@
(compound-unit/infer
(import drscheme:tool^)
(export drscheme:tool-exports^)
(link menu-extentions@ test-case-box@ text->syntax-object@ print-to-text@)))
)