Providing Libraries and Helper Files

This section should be your go to reference if there are any issues with providing a library or creating helper functions for testing. For the most part, all you need to do is put the file in the provided folder, then make sure it's in full Racket and have a line like (provide test-constant test-function) at the top for the identifiers you want to make available in the student's file and/or test.rkt. Add the name of the file to (modules ...) in options.rkt and you're all set.

One thing to be careful of is to make your provided names unique. If students happen to also call one of their constants "test-constant," you can get troublesome double define errors. Try doing something like (provide test-constant-cs135 test-function-cs135) to prevent this.

If you want to provide your own function definitions, please see the section "How to Provide Our Own Function Definition" in SpecialCases.

Working with Racket languages

You should be aware of how the various language levels and their function libraries interact across files. The test.rkt file is always evaluated in the same language level as the assignment, i.e. what is specified in the options.rkt file. You will not be able to call things like sort and local on Assignment 2, when the class is still in Beginning Student! However, the function (require …) is always available no matter which level of Racket you’re in, which is exactly what using the (modules …) line in options.rkt does. When you require another Racket module, that file is made visible to your working file, but you can only call on the identifiers which have been "revealed" in a (provide …) line.

It is recommended to work in full Racket for any provided modules you write. You can get this by selecting “Determine language from source” in <a href=";nowysiwyg=0" rel="nofollow" title="DrRacket (this topic does not yet exist; you can create it)"> DrRacket </a> and having a “#lang racket” line at the top of your file. You’ll be able to use all the important functions from the teaching languages, plus lots of other useful functions. There won’t be any conflicts stemming from the language set in options.rkt, because a Racket file calls functions from a provided module with their definitions “hidden,” - the language level in your provided file operates independently of the language level where its functions are called. For example, you can have a provided file like this if the language is Beginning Student for the assignment:

(provide my-sort)

(define (my-sort lst pred) (sort lst pred))

Now, we haven’t talked about the disallowed functions from disallowed.rkt, but this actually doesn’t introduce any new issues. A disallowed.rkt file is used like any other module, and included at the top of the student’s submission file. It does not check for the banned functions in your provided files, even if those functions are called from within the student’s solution. However, in general, we try to have the disallowed file be the last module in the (modules …) line, because if it bans require, the testing will break once it tries to include any subsequent modules using require.

There are slight differences in full Racket that the test writer should get to know when making provided files. An important one is that structs are opaque (or private, if you’ve done OOP) by default, and you must add #:transparent if you want to access the fields.

Another one is that full Racket actually recognizes types (even though variables are not typed), so be especially careful working with floats: (= 3.0 3) is true, but (equal? 3.0 3) is false in full Racket! Even (equal? (list 1.0 2.0 3.0) (list 1.0 2.0 3)) is false! In test.rkt, the result and expected are compared using equal? in whatever teaching language is used, so you probably won’t have to worry about this. Incidentally, #i3 in the teaching languages is equivalent to what 3.0 does in full Racket, so you can have it go the other way where (equal? #i3 3.0) is true in full Racket but false in Beginning Student (because the 3.0 gets treated as the exact value, 3).

Providing cs135-trace.rkt

If the course provides cs135-trace.rkt as a library file for students, then we will need to account for that in our testing set up.

When students submit their files, they might include ("require cs135-trace.rkt") and they might have some define/trace inside of their code still. None of these should affect our testing setup, but we have not tested this out thoroughly to know 100%. In order to prevent issues, we try to let students know when they submit to MarkUs that they should at least remove define/trace from their code.

To do this, move the cs135-trace-basic.rkt file located in marking/provided-library to the provided directory in for the assignment that you're setting up. Make sure to include this file in the instructor-script macro in the options.rkt file for the assignment. This should now fail students on the basic tests for including define/trace and give them a warning to remove it before they submit their code.

For correctness tests, we want to include the real cs135-trace.rkt file so that if students do happen to submit their file with define/trace, then we can still run correctness on them without any penalties. To do this, move the cs135-trace-correctness.rkt file located in marking/provided-library to the provided directory in test.0 for the assignment that you're setting up. Once again, make sure to include this file in the instructor-script macro. Also, make sure that you unban requires since students will probably still have (require "cs135-trace.rkt") in their file.

If ISAs are confident that cs135-trace.rkt will not cause any issues during correctness tests, then they can just set up the cs135-trace-correctness.rkt file for both public and private tests. Just be careful, because if there are issues that arise from this in correctness tests, then it with be course personnels' responsibility to deal with the issue.

Testing with Structures (NEW)

To test with structures we use deep-copy (created by Dan Holtby), located in ~/marking/provided-library/deep-copy.rkt

Calling (deep-copy [(structure-name #-of-fields-in-struct)] struct-constant) will take our structure and convert it so that it works in the test.rkt file. An example of how it’s used in a test.rkt:

(result (sort-dept-name (deep-copy [(grocery 4)] cs135-communal-store)))

(expected whatever-the-return-value-should-be)

For context, this is what the provided constants file for that assignment might be:

(provide cs135-communal-store)

(define-struct grocery (dept name cost mass) #:transparent)

(define cs135-communal-store (list (make-grocery “desert” “cake” 8 500)))

Structures defined in terms of other structures

deep-copy also works on structures defined in terms of other structures.

Example from Fall 2020 Assignment 6:

(provide cs135-no-grades cs135-all-grades)

;; -------- A06 structures --------

(define-struct student (id name grade) #:transparent)

;; A Student is a (make-student StudentID Str Grade)

(define-struct rnode (student left right) #:transparent)

;; A Roster Node (RN) is a (make-rnode Student Roster Roster)

;; Requires: all students in the left subtree have an ID < student's ID

;; all students in the right subtree have an ID > student's ID

;; A Roster is one of

;; * empty

;; * RN

;; For A06, since rnode is defined in terms of student, you must specify the number of

;; fields for both rnode and student to deep-copy.

;; Example call:

;; (result ( 100 (class-average (deep-copy [(rnode 3) (student 3)] cs135-sample-roster))))=

;; (expected what-ever-the-return-value-should-be)

;; ---------------------------------------------

(define cs135-no-grades

(make-rnode (make-student 12345678 "Beth" false)

(make-rnode (make-student 08675309 "Jenny" false) empty empty)

(make-rnode (make-student 15519021 "Will" false)

(make-rnode (make-student 14475311 "John" false) empty empty)

(make-rnode (make-student 29070811 "James" false) empty empty))))

(define cs135-all-grades

(make-rnode (make-student 1982 "Bethany" 88)

(make-rnode (make-student 19 "Hundred" 100)

(make-rnode (make-student 15 "Fifty" 50)

(make-rnode (make-student 14 "Left" 68) empty empty)


(make-rnode (make-student 20 "Zero" 0) empty empty))

(make-rnode (make-student 19345679 "Jennifer" 73) empty empty)))

Providing Functions That Use Structures

When providing functions for testing that use structures, deep-copy is needed once again. The only difference being that it needs to be called with one additional field. For example, for groceries it would be:

(define-struct grocery (dept name cost mass _) #:transparent)

(deep-copy [(grocery 5)] cs135-communal-store)

Example use case:

(require "deep-copy.rkt")

(provide valid-shop-cs135?)

(define-struct grocery (dept name cost mass _) #:transparent)

(define (valid-shop-cs135? shop)

(local [(define our-shop (deep-copy ((grocery 5)) shop)) ….*do something with our-shop*] …))

See OldConstantsAndHelpers for more information on the previous constants and helpers setup.

Edit | Attach | Watch | Print version | History: r7 < r6 < r5 < r4 < r3 | Backlinks | Raw View | Raw edit | More topic actions
Topic revision: r7 - 2021-12-23 - XiyuChen
This site is powered by the TWiki collaboration platform Powered by PerlCopyright © 2008-2024 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback