├── .gitignore
├── README.md
├── UserScenarios.md
├── corefns-conditions.txt
├── exceptions
├── 4clojure-prob134-AssertionError.ser
├── 4clojure-prob15-ClassCast.ser
├── 4clojure-prob156-AssertionError.ser
├── 4clojure-prob16-Arity.ser
├── 4clojure-prob17-NullPointer.ser
├── 4clojure-prob18-AssertionError.ser
├── 4clojure-prob20-AssertionError.ser
├── 4clojure-prob21-ArityException.ser
├── 4clojure-prob23-IndexOutOfBounds.ser
├── 4clojure-prob24-pre-plus-rewrite-ClassCast.ser
├── 4clojure-prob27-ArityException.ser
├── 4clojure-prob38-ArityException.ser
├── 4clojure-prob57-ArityException.ser
├── 4clojure-prob64-NullPointer.ser
├── ArityException-1-1.ser
├── ClassCast-1-1.ser
├── ClassCast-1-2.ser
├── DrRacket-Exercise2-ClassCast.ser
├── DrRacket-Exercise3-IndexOutOfBounds.ser
├── DrRacket-Exercise9-ArityException.ser
├── IllegalArgument-1-1.ser
├── IllegalArgument-1-2.ser
├── IndexOutOfBounds-1-1.ser
├── NullPointer-1-1.ser
├── add-five-IllegalArgException.ser
├── classcast1.ser
├── compilation_errors
│ ├── #_must_be_followed_by_symbol.clj
│ ├── eof.clj
│ ├── first_argument_must_be_symbol.clj
│ ├── greater_than_20_parameters.clj
│ ├── greater_than_20_parameters2.clj
│ ├── invalid_number.clj
│ ├── invalid_token_error1.clj
│ ├── invalid_token_error2.clj
│ ├── keyword_wrong_number_of_args.clj
│ ├── keyword_wrong_number_of_args2.clj
│ ├── let-no-matching-pair.clj
│ ├── let-odd-number-bindings.clj
│ ├── loopevenforms.clj
│ ├── non_closing_string.clj
│ ├── only_recur_from_tail.clj
│ ├── parameter_declaration.clj
│ ├── percent_followed_by_letter.clj
│ ├── unable_to_resolve_squareThis.clj
│ ├── unable_to_resolve_symbol_hello.clj
│ ├── unable_to_resolve_world.clj
│ ├── unmatched_delim_re.clj
│ └── unmatched_delimiter.clj
├── end_of_file.ser
├── generate1.clj
└── unmatched_delimiter.ser
├── installation_instructions.md
├── project.clj
├── src
├── corefns
│ ├── assert_handling.clj
│ ├── collection_fns.clj
│ ├── corefns.clj
│ ├── failed_asserts_info.clj
│ └── specs.clj
├── errors
│ ├── dictionaries.clj
│ ├── error_dictionary.clj
│ ├── error_hints.clj
│ ├── errorgui.clj
│ ├── exceptionobj.clj
│ ├── messageobj.clj
│ ├── prettify_exception.clj
│ └── stacktraces.clj
├── intro
│ ├── core.clj
│ └── student.clj
└── utilities
│ └── defn_generator.clj
└── test
├── core_fns_behavior
└── core_fns_behavior.clj
├── corefns
└── corefns_test.clj
├── errors
├── dictionaries_test.clj
├── error_dictionary_test.clj
├── error_hints_test.clj
├── prettify_exception_test.clj
└── testing_tools.clj
├── exception_msgs
└── overwritten_fns
│ ├── higher_order_fns.clj
│ ├── num_fns.clj
│ ├── seq_ops.clj
│ └── str_fns.clj
├── intro
└── student_test.clj
├── location
├── compilation_errors.clj
├── overwritten_fns.clj
└── runtime_errors.clj
├── spec_behavior
└── spec_test.clj
├── stacktrace_testing
└── stacktrace_testing.clj
└── utilities
├── file_IO.clj
├── stacktrace_functions.clj
└── stacktrace_functions_test.clj
/.gitignore:
--------------------------------------------------------------------------------
1 | pom.xml
2 | *jar
3 | /lib/
4 | /classes/
5 | /target/classes/*
6 | /target/stale/*
7 | .lein-deps-sum
8 | *.*~
9 | .*.*.swp
10 | *.nrepl-port
11 | target/
12 | .lein-plugins
13 | .gitignore.swp
14 | *.silly
15 | .lein-repl-history
16 | /src/intro/test.clj
17 | /src/intro/test_standard.clj
18 |
19 | ### Latex ###
20 | *.aux
21 | *.log
22 | *.out
23 | *.pdf
24 | *synctex.gz
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | clojure-intro-class
2 | ===================
3 |
4 | A pilot project to use Clojure for introductory computer science courses at the University of Minnesota - Morris
5 |
6 | This repository contains both error-related tools, versions of core functions with preconditions, and a string library for beginners.
7 |
--------------------------------------------------------------------------------
/UserScenarios.md:
--------------------------------------------------------------------------------
1 | ###User Scenarios
2 | ####Day 1
3 | Suzie starts her first “Hello World” program on the first day of class in a repl from our project. Here are all her code attempts:
4 | * ``(print Hello World)``
5 | * Clojure message:
6 | * ``CompilerException java.lang.RuntimeException: Unable to resolve symbol: Hello in this context``
7 | * Our project message:
8 | * ``ERROR: Compilation error: name Hello is undefined, while compiling``
9 | * Hint/Explanation:
10 | * This breaks because Clojure thinks that Hello is a symbol (identifier) when Suzie wanted it to be just plain text. Our program could respond with a suggestion that if the user wanted the phrase to be plain text to surround it with double quotes. It could also suggest to double-check that the user spelled the symbol correctly.
11 |
12 | * ``(print 'Hello World')``
13 | * Clojure message:
14 | * ``CompilerException java.lang.RuntimeException: Unable to resolve symbol: World' in this context ``
15 | * Our project message:
16 | * ``ERROR: Compilation error: name World' is undefined, while compiling``
17 | * Hint/Explanation:
18 | * Be careful about your usage of single quotation marks, they do not behave the same as double quotes. Make sure your strings are wrapped in double quotes.
19 |
20 | * `` print("Hello World")``
21 | * Clojure message:
22 | * ``ClassCastException java.lang.String cannot be cast to clojure.lang.IFn``
23 | * Our project message:
24 | * ``ERROR: Attempted to use a string, but a function was expected.``
25 | * Hint/Explanation:
26 | * This breaks because Clojure thinks the first thing after an open paren should be a function. Our program could respond with a suggestion to the user to make sure that the item right after the open paren is a function, otherwise check syntax.
27 |
28 | * ``(print "Hello World")``
29 | * Hint/Explanation:
30 | * This works!
31 |
32 |
33 | ####Day 2
34 | For the second day of Clojure, Jaden wants to define a function in a repl from within our project. Here are all his code attempts:
35 | * ``(fn squareThis input*input)``
36 | * Clojure message:
37 | * ``IllegalArgumentException Parameter declaration input*input should be a vector``
38 | * Our project message:
39 | * ``ERROR: Parameters in defn should be a vector, but is input*input`` NOTE: WE ACTUALLY USED FN
40 | * Hint/Explanation:
41 | * In this particular case, fn was expecting a vector of parameters and it didn't find one. In the general case, something was looking for a collection and didn't find one.
42 |
43 | * ``(fn squareThis [x] (* x x))``
44 | ``(squareThis 5)``
45 | * Clojure message:
46 | * ``CompilerException java.lang.RuntimeException: Unable to resolve symbol: squareThis in this context``
47 | * Our project message:
48 | * ``ERROR: Compilation error: name squareThis is undefined, while compiling``
49 | * Hint/Explanation:
50 | * If you want squareThis to be a function that you can reference later, use defn instead of fn.
51 |
52 | * ``(defn squareThis [x] (* x x))``
53 | ``(squareThis 5)``
54 | * Hint/Explanation:
55 | * This works!
56 |
57 | ####Day 3
58 | On the third day of Clojure, Laken is super-excited to start working with lists! She has a list of her favorite bands and she wants to add a new one. Here are her attempts to do this in Clojure:
59 |
60 | * ``(conj "ACDC" ("Daft Punk" "U2" "ZZ Top"))``
61 | * Clojure message:
62 | * ``ClassCastException java.lang.String cannot be cast to clojure.lang.IFn``
63 | * Our project message:
64 | * ``ERROR: Attempted to use a string, but a function was expected.``
65 | * Hint/Explanation:
66 | * Be careful about what comes immediately after an open paren. Clojure might think that it is a function name.
67 | Example: ("add" 5 3) (first argument would be dynamic depending on what type the exception complains about
68 |
69 | * ``(conj "ACDC" '("Daft Punk" "U2" "ZZ Top"))``
70 | * Clojure message:
71 | * ``ClassCastException java.lang.String cannot be cast to clojure.lang.IPersistentCollection``
72 | * Our project message:
73 | * ``ERROR: Attempted to use a string, but a collection was expected.``
74 | * Hint/Explanation:
75 | * Check that the order of arguments for a function are in the correct order.
76 |
77 | * ``(conj '("Daft Punk" "U2" "ZZ Top") "ACDC")``
78 | * Hint/Explanation:
79 | * This works!
80 |
81 | ####Day 4
82 | On the fourth day of Clojure, Keylan is a little bit nervous to work with Maps. He wants to update a value associated with a key in a map he has created. Here are his attempts:
83 |
84 | * ``(assoc :a 3 {:a 5, :b 8, :c 9})``
85 | * Clojure message:
86 | * ``ClassCastException clojure.lang.Keyword cannot be cast to clojure.lang.Associative``
87 | * Our project message:
88 | * ``ERROR: Attempted to use a keyword, but a map or a vector was expected.``
89 | * Hint/Explanation:
90 | * Check that the order of arguments for a function are in the correct order.
91 |
92 | * ``(assoc (:a 3) {:a 5, :b 8, :c 9})``
93 | * Clojure message:
94 | * ``ArityException Wrong number of args (2) passed to: core/assoc``
95 | * Our project message:
96 | * ``ERROR: Wrong number of arguments (2) passed to a function assoc``
97 | * Hint/Explanation:
98 | * Check the documentation of the function to see how many arguments are required.
99 | (possibly adding links to the documentation to the error message)
100 |
101 | * ``(assoc {:a 3} {:a 5, :b 8, :c 9})``
102 | * Clojure message:
103 | * ``ArityException Wrong number of args (2) passed to: core/assoc``
104 | * Our project message:
105 | * ``ERROR: Wrong number of arguments (2) passed to a function assoc``
106 | * Hint/Explanation:
107 | * Check the documentation of the function to see how many arguments are required.
108 |
109 | * ``(assoc {:a 5, :b 8, :c 9} :a 3)``
110 | * Hint/Explanation:
111 | * This works!
112 |
113 | ####Day 5
114 | On the fifth day of Clojure, Addison needs to write a function which takes a sequence and returns the maximum value from the sequence without using max or key-max. Here are Addison's attempts:
115 |
116 | * ``(defn [coll] penultimate (last (drop-last coll)))``
117 | * Clojure message:
118 | * ``IllegalArgumentException First argument to defn must be a symbol``
119 | * Our project message:
120 | * ``ERROR: First argument to defn must be a symbol``
121 | * Hint/Explanation:
122 | *
123 |
124 | * ``(defn penultimate [coll] (last (drop-last coll)))``
125 | ``(penultimate [3 4 5 8 9 0 3])``
126 | * Hint/Explanation:
127 | * This works!
128 |
129 | ####Day 6
130 | On the sixth day of Clojure, Darwin has been given the task to write a print statement that prints out a list of numbers using loop and he needs some help. Here are Darwin's attempts:
131 |
132 | * ``(loop [x 10] (while (> x 1) (println x) (recur (- x 1))))``
133 | * Clojure message:
134 | * ``CompilerException java.lang.UnsupportedOperationException: Can only recur from tail position``
135 | * Our project message:
136 | * ``ERROR: Compilation error: recur can only occur as a tail call, meaning no operations can be done after its return, while compiling``
137 | * Hint/Explanation:
138 | *
139 |
140 | * ``(loop [x 10] (if (= x 0) x (do (println x) (recur (- x 1)))))``
141 | * Clojure message:
142 | * No error message given
143 | * Our project message:
144 | * ``ERROR: An index in a sequence is out of bounds. The index is: 10``
145 | * Hint/Explanation
146 | * This works????? (I think??)
147 |
148 | ####Day 7
149 | On the seventh day of Clojure, Geoff has been given the task to write a factorial function that uses recurion and he needs help. Here are his attempts:
150 |
151 | * ``(defn factorial [input] (* input (factorial (- input 1))))``
152 | ``(factorial 5)``
153 | * Clojure message:
154 | * ``StackOverflowError clojure.lang.Numbers$LongOps.negate (Numbers.java:518)``
155 | * Our project message:
156 | * ``Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.util.regex.Pattern``
157 | * Hint/Explanation:
158 | * You may have forgetten a base case for your recursive/looping function.
159 |
160 | * ``(defn factorial [input] (if (>= input 1) (* input (factorial (- input 1))) 1))``
161 | ``(factorial 5)``
162 | ``-> 120``
163 | * Hint/Explanation
164 | * This works!
165 |
166 | ####Day 8
167 | On the eighth day of Clojure, Symmetra has been given the task to find the square root of the first odd number in a collection. Here are her attempts:
168 |
169 | * ``(defn first-odd-sqrt [my-collection] (Math/sqrt (first (filter odd? my-collection))))``
170 | ``(first-odd-sqrt [2, 4])``
171 | * Clojure message:
172 | * ``NullPointerException clojure.lang.RT.doubleCast (RT.java:1222)``
173 | * Our project message:
174 | * ``ERROR: An attempt to access a non-existing object. (NullPointerException)``
175 | * Hint/Explanation:
176 | * One of the functions you are using might be returning nil for some reason. Try to evaluate the function calls from the inside out to see where this might be occuring.
177 |
178 | * ``(defn first-odd-sqrt [my-collection] (Math/sqrt (filter odd? my-collection)))``
179 | ``(first-odd-sqrt [2, 3, 9, 12, 5])``
180 | * Clojure message:
181 | * ``ClassCastException clojure.lang.LazySeq cannot be cast to java.lang.Number``
182 | * Our project message:
183 | * ``ERROR: Attempted to use a sequence, but a number was expected.``
184 | * Hint/Explanation:
185 | *
186 |
--------------------------------------------------------------------------------
/exceptions/4clojure-prob134-AssertionError.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clojure-Intro-Course/clojure-intro-class/07fa408a825892be65e3ede9ce34e5b518cc324f/exceptions/4clojure-prob134-AssertionError.ser
--------------------------------------------------------------------------------
/exceptions/4clojure-prob15-ClassCast.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clojure-Intro-Course/clojure-intro-class/07fa408a825892be65e3ede9ce34e5b518cc324f/exceptions/4clojure-prob15-ClassCast.ser
--------------------------------------------------------------------------------
/exceptions/4clojure-prob156-AssertionError.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clojure-Intro-Course/clojure-intro-class/07fa408a825892be65e3ede9ce34e5b518cc324f/exceptions/4clojure-prob156-AssertionError.ser
--------------------------------------------------------------------------------
/exceptions/4clojure-prob16-Arity.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clojure-Intro-Course/clojure-intro-class/07fa408a825892be65e3ede9ce34e5b518cc324f/exceptions/4clojure-prob16-Arity.ser
--------------------------------------------------------------------------------
/exceptions/4clojure-prob17-NullPointer.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clojure-Intro-Course/clojure-intro-class/07fa408a825892be65e3ede9ce34e5b518cc324f/exceptions/4clojure-prob17-NullPointer.ser
--------------------------------------------------------------------------------
/exceptions/4clojure-prob18-AssertionError.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clojure-Intro-Course/clojure-intro-class/07fa408a825892be65e3ede9ce34e5b518cc324f/exceptions/4clojure-prob18-AssertionError.ser
--------------------------------------------------------------------------------
/exceptions/4clojure-prob20-AssertionError.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clojure-Intro-Course/clojure-intro-class/07fa408a825892be65e3ede9ce34e5b518cc324f/exceptions/4clojure-prob20-AssertionError.ser
--------------------------------------------------------------------------------
/exceptions/4clojure-prob21-ArityException.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clojure-Intro-Course/clojure-intro-class/07fa408a825892be65e3ede9ce34e5b518cc324f/exceptions/4clojure-prob21-ArityException.ser
--------------------------------------------------------------------------------
/exceptions/4clojure-prob23-IndexOutOfBounds.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clojure-Intro-Course/clojure-intro-class/07fa408a825892be65e3ede9ce34e5b518cc324f/exceptions/4clojure-prob23-IndexOutOfBounds.ser
--------------------------------------------------------------------------------
/exceptions/4clojure-prob24-pre-plus-rewrite-ClassCast.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clojure-Intro-Course/clojure-intro-class/07fa408a825892be65e3ede9ce34e5b518cc324f/exceptions/4clojure-prob24-pre-plus-rewrite-ClassCast.ser
--------------------------------------------------------------------------------
/exceptions/4clojure-prob27-ArityException.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clojure-Intro-Course/clojure-intro-class/07fa408a825892be65e3ede9ce34e5b518cc324f/exceptions/4clojure-prob27-ArityException.ser
--------------------------------------------------------------------------------
/exceptions/4clojure-prob38-ArityException.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clojure-Intro-Course/clojure-intro-class/07fa408a825892be65e3ede9ce34e5b518cc324f/exceptions/4clojure-prob38-ArityException.ser
--------------------------------------------------------------------------------
/exceptions/4clojure-prob57-ArityException.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clojure-Intro-Course/clojure-intro-class/07fa408a825892be65e3ede9ce34e5b518cc324f/exceptions/4clojure-prob57-ArityException.ser
--------------------------------------------------------------------------------
/exceptions/4clojure-prob64-NullPointer.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clojure-Intro-Course/clojure-intro-class/07fa408a825892be65e3ede9ce34e5b518cc324f/exceptions/4clojure-prob64-NullPointer.ser
--------------------------------------------------------------------------------
/exceptions/ArityException-1-1.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clojure-Intro-Course/clojure-intro-class/07fa408a825892be65e3ede9ce34e5b518cc324f/exceptions/ArityException-1-1.ser
--------------------------------------------------------------------------------
/exceptions/ClassCast-1-1.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clojure-Intro-Course/clojure-intro-class/07fa408a825892be65e3ede9ce34e5b518cc324f/exceptions/ClassCast-1-1.ser
--------------------------------------------------------------------------------
/exceptions/ClassCast-1-2.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clojure-Intro-Course/clojure-intro-class/07fa408a825892be65e3ede9ce34e5b518cc324f/exceptions/ClassCast-1-2.ser
--------------------------------------------------------------------------------
/exceptions/DrRacket-Exercise2-ClassCast.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clojure-Intro-Course/clojure-intro-class/07fa408a825892be65e3ede9ce34e5b518cc324f/exceptions/DrRacket-Exercise2-ClassCast.ser
--------------------------------------------------------------------------------
/exceptions/DrRacket-Exercise3-IndexOutOfBounds.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clojure-Intro-Course/clojure-intro-class/07fa408a825892be65e3ede9ce34e5b518cc324f/exceptions/DrRacket-Exercise3-IndexOutOfBounds.ser
--------------------------------------------------------------------------------
/exceptions/DrRacket-Exercise9-ArityException.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clojure-Intro-Course/clojure-intro-class/07fa408a825892be65e3ede9ce34e5b518cc324f/exceptions/DrRacket-Exercise9-ArityException.ser
--------------------------------------------------------------------------------
/exceptions/IllegalArgument-1-1.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clojure-Intro-Course/clojure-intro-class/07fa408a825892be65e3ede9ce34e5b518cc324f/exceptions/IllegalArgument-1-1.ser
--------------------------------------------------------------------------------
/exceptions/IllegalArgument-1-2.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clojure-Intro-Course/clojure-intro-class/07fa408a825892be65e3ede9ce34e5b518cc324f/exceptions/IllegalArgument-1-2.ser
--------------------------------------------------------------------------------
/exceptions/IndexOutOfBounds-1-1.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clojure-Intro-Course/clojure-intro-class/07fa408a825892be65e3ede9ce34e5b518cc324f/exceptions/IndexOutOfBounds-1-1.ser
--------------------------------------------------------------------------------
/exceptions/NullPointer-1-1.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clojure-Intro-Course/clojure-intro-class/07fa408a825892be65e3ede9ce34e5b518cc324f/exceptions/NullPointer-1-1.ser
--------------------------------------------------------------------------------
/exceptions/add-five-IllegalArgException.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clojure-Intro-Course/clojure-intro-class/07fa408a825892be65e3ede9ce34e5b518cc324f/exceptions/add-five-IllegalArgException.ser
--------------------------------------------------------------------------------
/exceptions/classcast1.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clojure-Intro-Course/clojure-intro-class/07fa408a825892be65e3ede9ce34e5b518cc324f/exceptions/classcast1.ser
--------------------------------------------------------------------------------
/exceptions/compilation_errors/#_must_be_followed_by_symbol.clj:
--------------------------------------------------------------------------------
1 | (ns compilation_errors.#_must_be_followed_by_symbol.clj)
2 |
3 | #2
4 |
--------------------------------------------------------------------------------
/exceptions/compilation_errors/eof.clj:
--------------------------------------------------------------------------------
1 | (ns compilation_errors.eof)
2 |
3 | ;(defn eof-function[] (+ 2 3)
4 |
--------------------------------------------------------------------------------
/exceptions/compilation_errors/first_argument_must_be_symbol.clj:
--------------------------------------------------------------------------------
1 | (ns compilation_errors.first_argument_must_be_symbol)
2 |
3 | ;(defn [coll] penultimate (last (drop-last coll)))
4 |
--------------------------------------------------------------------------------
/exceptions/compilation_errors/greater_than_20_parameters.clj:
--------------------------------------------------------------------------------
1 | (ns compilation_errors.greater_than_20_parameters)
2 |
3 | (#(+ %22222 1) 2 3)
4 |
5 | ;(expect #"A function may not take more than more than 20 parameters."
6 | ; (get-text-no-location (run-and-catch-pretty-no-stacktrace (load-file compilation_errors.greater_than_20_parameters))))
7 |
8 | ;(load-file compilation_errors.greater_than_20_parameters)
9 |
--------------------------------------------------------------------------------
/exceptions/compilation_errors/greater_than_20_parameters2.clj:
--------------------------------------------------------------------------------
1 | (ns compilation_errors.greater_than_20_parameters2)
2 |
3 | (defn function_with_too_many_parameters [a b c d e f g h i j k l m n o p q r s t u v w x y z] 1)
4 |
--------------------------------------------------------------------------------
/exceptions/compilation_errors/invalid_number.clj:
--------------------------------------------------------------------------------
1 | (ns compilation_errors.invalie_number)
2 |
3 | (+ 1.2.2 0)
4 |
--------------------------------------------------------------------------------
/exceptions/compilation_errors/invalid_token_error1.clj:
--------------------------------------------------------------------------------
1 | (ns compilation_errors.invalid_token_error1)
2 |
3 | (/string "abcd")
4 |
--------------------------------------------------------------------------------
/exceptions/compilation_errors/invalid_token_error2.clj:
--------------------------------------------------------------------------------
1 | (ns compilation_errors.invalid_token_error2)
2 |
3 | (defn # :)
4 |
--------------------------------------------------------------------------------
/exceptions/compilation_errors/keyword_wrong_number_of_args.clj:
--------------------------------------------------------------------------------
1 | (ns compilation_errors.keyword_wrong_number_of_args)
2 |
3 | (:a)
4 |
--------------------------------------------------------------------------------
/exceptions/compilation_errors/keyword_wrong_number_of_args2.clj:
--------------------------------------------------------------------------------
1 | (ns compilation_errors.keyword_wrong_number_of_args2)
2 |
3 | (let [f (if (= (+ 1 1) 2) (:a) +)]
4 | (f 1 2 3))
5 |
--------------------------------------------------------------------------------
/exceptions/compilation_errors/let-no-matching-pair.clj:
--------------------------------------------------------------------------------
1 | (ns exceptions.compilation_errors.let-no-matching-pair)
2 |
3 | (let [x] (+ x 2))
4 |
--------------------------------------------------------------------------------
/exceptions/compilation_errors/let-odd-number-bindings.clj:
--------------------------------------------------------------------------------
1 | (ns exceptions.compilation_errors.let-odd-number-bindings)
2 |
3 | (let (x 2))
4 |
--------------------------------------------------------------------------------
/exceptions/compilation_errors/loopevenforms.clj:
--------------------------------------------------------------------------------
1 | (ns compilation_errors.loopevenforms)
2 |
3 | ;(defn s [s] (loop [s]))
4 |
--------------------------------------------------------------------------------
/exceptions/compilation_errors/non_closing_string.clj:
--------------------------------------------------------------------------------
1 | (ns compilation_errors.non_closing_string)
2 |
3 | "This string never closes
4 |
5 |
--------------------------------------------------------------------------------
/exceptions/compilation_errors/only_recur_from_tail.clj:
--------------------------------------------------------------------------------
1 | (ns compilation_errors.only_recur_from_tail)
2 |
3 | ;(loop [x 10] (while (> x 1) (println x) (recur (- x 1))))
4 |
--------------------------------------------------------------------------------
/exceptions/compilation_errors/parameter_declaration.clj:
--------------------------------------------------------------------------------
1 | (ns compilation_errors.parameter_declaration)
2 |
3 | (defn square-this (input * input))
4 |
--------------------------------------------------------------------------------
/exceptions/compilation_errors/percent_followed_by_letter.clj:
--------------------------------------------------------------------------------
1 | (ns exceptions.compilation_errors.percent-followed-by-letter)
2 |
3 | (map #(+ %a 1) [1 2 3])
4 |
--------------------------------------------------------------------------------
/exceptions/compilation_errors/unable_to_resolve_squareThis.clj:
--------------------------------------------------------------------------------
1 | (ns compilation_errors.unable_to_resolve_squareThis)
2 |
3 | (fn squareThis [x] (* x x))
4 | (squareThis 5)
5 |
--------------------------------------------------------------------------------
/exceptions/compilation_errors/unable_to_resolve_symbol_hello.clj:
--------------------------------------------------------------------------------
1 | (ns compilation_errors.unable_to_resolve_symbol_hello)
2 |
3 | ;(print Hello World)
4 | print(Hello World)
5 |
--------------------------------------------------------------------------------
/exceptions/compilation_errors/unable_to_resolve_world.clj:
--------------------------------------------------------------------------------
1 | (ns compilation_errors.unable_to_resolve_symbol_world)
2 |
3 | ;(print 'Hello World')
4 |
--------------------------------------------------------------------------------
/exceptions/compilation_errors/unmatched_delim_re.clj:
--------------------------------------------------------------------------------
1 | (ns compilation_errors.unmatched_delim_re)
2 |
3 | (println #)
4 |
--------------------------------------------------------------------------------
/exceptions/compilation_errors/unmatched_delimiter.clj:
--------------------------------------------------------------------------------
1 | (ns compilation_errors.unmatched_delimiter)
2 |
3 | ;(defn oops [] + 2))
4 |
--------------------------------------------------------------------------------
/exceptions/end_of_file.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clojure-Intro-Course/clojure-intro-class/07fa408a825892be65e3ede9ce34e5b518cc324f/exceptions/end_of_file.ser
--------------------------------------------------------------------------------
/exceptions/generate1.clj:
--------------------------------------------------------------------------------
1 | (ns exceptions.generate1)
2 |
3 | ;###################
4 | ;### STATIC FILE ###
5 | ;## DO NOT CHANGE ##
6 | ;### DO NOT RUN ####
7 | ;###################
8 |
9 | ;*** Environment ***
10 | ; Windows 7 v.6.1.7601 , Intel i7 4700MQ @ 2.40ghz 4 cores (8 hyperthreaded), 8gb ram
11 |
12 | ;*** Functions ***
13 | (def path "exceptions/")
14 |
15 | (defn- run-and-catch
16 | "A function that takes quoted code and runs it, attempting to catch any exceptions it may throw. Returns the exeception or nil."
17 | [code] (try
18 | (eval code)
19 | (catch Exception e e)))
20 |
21 | (defn- export-to-file
22 | "Uses Java's Serializable to write a (java) object to a file"
23 | [obj filepath]
24 | (let [file-stream (java.io.FileOutputStream. filepath)
25 | obj-stream (java.io.ObjectOutputStream. file-stream)
26 | ]
27 | (.writeObject obj-stream obj)
28 | (.close obj-stream)
29 | (.close file-stream)
30 | (println (str "data saved in project folder or: " filepath))
31 | ))
32 |
33 | (defn- import-from-file
34 | "Uses Java's Serializable to read a (java) object from a file"
35 | [filepath]
36 | (let [file-stream (java.io.FileInputStream. filepath)
37 | obj-stream (java.io.ObjectInputStream. file-stream)
38 | e (.readObject obj-stream)]
39 | (.close obj-stream)
40 | (.close file-stream)
41 | e))
42 |
43 | (defn- write-objects-local
44 | "writes a java object to a file, creating it if it does not exist, in path (see errors.exceptions)"
45 | [object filename]
46 | (export-to-file object (str path filename)))
47 |
48 | (defn- read-objects-local
49 | "reads a file in path (see errors.exceptions) as a java-object"
50 | [filename]
51 | (import-from-file (str path filename)))
52 |
53 | ;*** file generation ***
54 |
55 | ;ClassCastException
56 | (comment
57 |
58 | (write-objects-local (run-and-catch '(inc "duck.")) "ClassCast-1-1.ser")
59 |
60 | (write-objects-local (run-and-catch '(+ 2 "goose.")) "ClassCast-1-2.ser")
61 |
62 | ;IllegalArgumentException
63 |
64 | (write-objects-local (run-and-catch '(cons 1 2)) "IllegalArgument-1-1.ser")
65 |
66 | (write-objects-local (run-and-catch '(contains? (seq [1 3 6]) 2)) "IllegalArgument-1-2.ser")
67 |
68 | ;IndexOutOfBoundsException
69 |
70 | (write-objects-local (run-and-catch '(nth [1 2 3] 5)) "IndexOutOfBounds-1-1.ser")
71 |
72 | ;ArityException
73 |
74 | (write-objects-local (run-and-catch '(conj 2)) "ArityException-1-1.ser")
75 |
76 | ;NullPointerException
77 |
78 | (write-objects-local (run-and-catch '(+ nil 2)) "NullPointer-1-1.ser")
79 |
80 | )
81 |
--------------------------------------------------------------------------------
/exceptions/unmatched_delimiter.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Clojure-Intro-Course/clojure-intro-class/07fa408a825892be65e3ede9ce34e5b518cc324f/exceptions/unmatched_delimiter.ser
--------------------------------------------------------------------------------
/installation_instructions.md:
--------------------------------------------------------------------------------
1 | Introduction-to-LightTable
2 | ==========================
3 |
4 | A guide to setting up and using the features of LightTable.
5 |
6 | Our cloning url is to the right, or you can just click Clone in Desktop to get a copy of this repository on your personal computer. (If you have git installed, go ahead and clone it in the command line).
7 |
8 | #Set Up of Java
9 |
10 | Firstly, you should have Java, and the Java Developers Kit, already downloaded on your computer. If that isn't installed already, go to http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html and click the JDK option for your operating system. Follow the given directions for completing this download.
11 |
12 | #Set Up of Leiningen
13 |
14 | You can download Leiningen from http://leiningen.org/. Downloading Leiningen can be tricky, so be patient. Start by downloading the lein script. If when trying to download the lein script, the script just opens (instead of downloading), first try right-clicking and saving it as lein to a place such as the desktop. If that doesn't work, then try making a new text file and copy-pasting the whole script into the new file on your computer and saving it as lein to a location such as the desktop. Then cd into the place you've saved the file, such as desktop. Then move it using the terminal by typing
15 |
16 | ```bash
17 | sudo mv lein /usr/bin/lein
18 | ```
19 |
20 | Then, make it executable by typing
21 |
22 | ```bash
23 | chmod a+x usr/bin/lein
24 | ```
25 |
26 | into the command line. After this, then type
27 |
28 | ```bash
29 | lein
30 | ```
31 |
32 | into the command line, and watch it self-install a bunch of things. Then, type lein once more, and when a list of leiningen commands appear, you know that leiningen is fully installed!
33 |
34 | #Set Up of LightTable
35 |
36 | You can download LightTable from http://www.lighttable.com/. Downloading LightTable is pretty easy. Follow the steps on the LightTable download site, move the folder that appears to the location you want on your computer, and you're set to go!
37 |
38 | #Set Up of Autoexpect
39 |
40 | For expectations, there is a little bit of formatting needed to make sure everything works correctly. You can make a new project by cd-ing into the desired directory and then typing:
41 |
42 | ``` bash
43 | lein new your-project-name
44 | ```
45 |
46 | into the command line. We've named our project: test-project. It should say that a new project has been created. Open LightTable and open the Workspace by clicking View -> Workspace. Right click on the workspace area and click Add Folder. Find where you've put your project and click Upload. Open the file project.clj by single-clicking on the name of your project and then single-clicking on project.clj. What's currently in the file should look something like this:
47 |
48 | ```bash
49 | (defproject test-project "1.0.0-SNAPSHOT"
50 | :description "FIXME: write description"
51 | :dependencies [[org.clojure/clojure "1.3.0"]])
52 | ```
53 |
54 | Change your project.clj file so it looks like this:
55 |
56 | ```bash
57 | (defproject test-project "1.0.0-SNAPSHOT"
58 | :description "A project for showing basic uses of LightTable and Autoexpect"
59 | :dependencies [[org.clojure/clojure "1.5.1"]
60 | [expectations "2.0.6"]]
61 | :plugins [[lein-autoexpect "1.0"]])
62 | ```
63 |
64 | making sure that you add the expectations dependency and the lein-autoexpect plugin; the description can be whatever you like. Save the file. Next, open up your test file by clicking test/ and clicking through as many nested folders as needed to get to core_test.clj. Once open, your project should look something like this:
65 |
66 | ```bash
67 | (ns test-project.core-test
68 | (:require [clojure.test :refer :all]
69 | [test-project.core :refer :all]))
70 |
71 | (deftest a-test
72 | (testing "FIXME, I fail."
73 | (is (= 0 1))))
74 | ```
75 |
76 | Replace the phrase clojure.test to the word expectations. It should look something like this:
77 |
78 | ```bash
79 | (ns test-project.core-test
80 | (:require [expectations :refer :all]
81 | [test-project.core :refer :all]))
82 |
83 | (deftest a-test
84 | (testing "FIXME, I fail."
85 | (is (= 0 1))))
86 | ```
87 |
88 | Now, you can erase the call to deftest, replacing it with something along the lines of (expect 5 (+ 2 3)). This is the most basic form of testing using autoexpect. Now, be sure you save the file and then go to the command line, cd into the project directory, and type
89 |
90 | ```bash
91 | lein autoexpect
92 | ```
93 |
94 | and then hit enter. Some extra things might print; you can ignore those. At the end, it should say something like:
95 |
96 | ```bash
97 | Ran 1 tests containing 1 assertions in 14 msecs
98 | 0 failures, 0 errors.
99 | ```
100 |
101 | It might even use pretty colors!
102 |
103 | Now you can happily code in LightTable and every time you save, your tests will run automatically and show the results in the terminal. Note, if you want autoexpect to stop running, hit Ctrl+c (or Ctrl+z on Mac).
104 |
105 | Happy coding!
106 |
107 | For more information on Autoexpect, take a look at this link: http://jayfields.com/expectations/installing.html
108 |
109 | #Uninstalling the Programs Listed Above
110 |
111 | If you ever would like to uninstall any of the software explained above, then feel free to follow these steps.
112 |
113 | Java: If you would like to uninstall Java (although we have no idea why you would feel the need to), you can go to this site:
114 |
115 | ```bash
116 | java.com/remove
117 | ```
118 | and follow the directions to uninstall Java.
119 |
120 | Leiningen: First, you must make sure that you delete both the .lein hidden file. This would probably be somewhere like your home, but it could be other places. If needed, use the command
121 |
122 | ```bash
123 | ls -a
124 | ```
125 |
126 | (on mac and linux) or
127 |
128 | ```bash
129 | dir /a
130 | ```
131 | (on windows). Then, assuming that lein is in your /usr/bin, you can go ahead and do
132 |
133 | ```bash
134 | sudo rm /usr/bin/lein
135 | ```
136 | to delete the lein folder from your /usr/bin. Check that leiningen is fully uninstalled by typing
137 |
138 | ```bash
139 | lein
140 | ```
141 |
142 | into your command line. No leiningen commands should appear.
143 |
144 | LightTable: On windows, you can use the uninstall tool, otherwise, your best bet would be to just drag it to trash and then empty your trash.
145 |
146 | Autoexpect: Autoexpect isn't a thing we really "installed," so therefore, there is no uninstalling. If you would like to stop using Autoexpect, just stop using it. Technically, Expectations and Autoexpect are just part of Leiningen.
147 |
148 | Any comments and/or confusions? We'd love to hear from you! Email saxxx027@morris.umn.edu if you have any concerns!
149 |
--------------------------------------------------------------------------------
/project.clj:
--------------------------------------------------------------------------------
1 | (defproject intro "0.2.3"
2 | :description "A pilot project to use Clojure for introductory computer science courses at the University of Minnesota - Morris"
3 | :dependencies [
4 | [org.clojure/clojure "1.9.0-alpha10"]
5 | [clj-stacktrace "0.2.8"] ;;No updates for over a year
6 | [org.clojure/core.incubator "0.1.2"] ;;No updates for over a year
7 | [seesaw "1.4.5"]
8 | [expectations "2.1.1"]]
9 | :plugins [[lein-expectations "0.0.7"]
10 | [lein-autoexpect "1.0"]
11 | [lein-pprint "1.1.2"]]
12 | :main intro.core)
13 |
--------------------------------------------------------------------------------
/src/corefns/assert_handling.clj:
--------------------------------------------------------------------------------
1 | (ns corefns.assert_handling
2 | ;; seqable? is in core since Clojure veresion 1.9
3 | ;; (:use [clojure.core.incubator])
4 | (:require [corefns.failed_asserts_info :refer :all]))
5 |
6 |
7 | ;;; Functions to check pre-conditions and record the offenders for
8 | ;;; error processing.
9 |
10 |
11 | (defn check-if-seqable?
12 | "returns true if x is seqable and false otherwise, sets data
13 | in seen-failed-asserts. If the second argument is present, it's added
14 | to the seen-failed-asserts as the number of the argument"
15 | [fname x & [n]]
16 | (if (seqable? x) true
17 | (do (add-to-seen {:check "a sequence"
18 | :class (class x)
19 | :value x
20 | :fname fname})
21 | (if n (add-to-seen {:arg-num n}))
22 | false)))
23 | ; (cond
24 | ; (number? n) (add-to-seen {:arg-num n})
25 | ; (string? n) (add-to-seen {:number-of-args (read-string n)}))
26 | ; (if n
27 | ; (if (number? n) (add-to-seen {:arg-num n})
28 | ; (if (string? n) (add-to-seen {:number-of-args (read-string n)})))
29 | ; (if q (add-to-seen {:number-of-args (read-string n)}))
30 |
31 | ;this is currently unused
32 | (defn check-if-sequential?
33 | "returns true if x is sequential and false otherwise, sets data
34 | in seen-failed-asserts. If the second argument is present, it's added
35 | to the seen-failed-asserts as the number of the argument"
36 | [fname x & [n]]
37 | (if (sequential? x) true
38 | (do (add-to-seen {:check "a sequence"
39 | :class (class x)
40 | :value x
41 | :fname fname})
42 | (if n (add-to-seen {:arg-num n}))
43 | false)))
44 |
45 | ;i just added this, needs tests, used in assoc pre-conditions
46 | (defn check-if-map-or-vector? [fname x & [n]]
47 | (if (or (vector? x) (map? x) (nil? x)) true
48 | (do (add-to-seen {:check "a map or vector"
49 | :class (class x)
50 | :value x
51 | :fname fname})
52 | (if n (add-to-seen {:arg-num n}))
53 | false)))
54 |
55 | (defn check-if-map? [fname x & [n]]
56 | (if (or (map? x) (nil? x)) true
57 | (do (add-to-seen {:check "a map"
58 | :class (class x)
59 | :value x
60 | :fname fname})
61 | (if n (add-to-seen {:arg-num n}))
62 | false)))
63 |
64 | (defn check-if-function? [fname x & [n]]
65 | (if (ifn? x) true
66 | (do (add-to-seen {:check "a function"
67 | :class (class x)
68 | :value x
69 | :fname fname})
70 | (if n (add-to-seen {:arg-num n}))
71 | false)))
72 |
73 | (defn check-if-number? [fname x & [n]]
74 | (if (number? x) true
75 | (do (add-to-seen {:check "a number"
76 | :class (class x)
77 | :value x
78 | :fname fname})
79 | (if n (add-to-seen {:arg-num n}))
80 | false)))
81 |
82 | (defn check-if-integer? [fname x & [n]]
83 | (if (integer? x) true
84 | (do (add-to-seen {:check "an integer number"
85 | :class (class x)
86 | :value x
87 | :fname fname})
88 | (if n (add-to-seen {:arg-num n}))
89 | false)))
90 |
91 | (defn check-if-string? [fname x & [n]]
92 | (if (string? x) true
93 | (do (add-to-seen {:check "a string"
94 | :class (class x)
95 | :value x
96 | :fname fname})
97 | (if n (add-to-seen {:arg-num n}))
98 | false)))
99 |
100 | (defn check-if-character? [fname x & [n]]
101 | (if (char? x) true
102 | (do (add-to-seen {:check "a character"
103 | :class (class x)
104 | :value x
105 | :fname fname})
106 | (if n (add-to-seen {:arg-num n}))
107 | false)))
108 |
109 | (defn check-if-var? [fname x & [n]]
110 | (if (var? x) true
111 | (do (add-to-seen {:check "a var"
112 | :class (class x)
113 | :value x
114 | :fname fname})
115 | (if n (add-to-seen (:arg-num n)))
116 | false)))
117 |
118 | (defn check-if-string-or-nil? [fname x & [n]]
119 | (if (or (instance? CharSequence x) (nil? x)) true
120 | (do (add-to-seen {:check "a string or nil"
121 | :class (class x)
122 | :value x
123 | :fname fname})
124 | (if n (add-to-seen {:arg-num n}))
125 | false)))
126 |
127 | (defn check-if-regex? [fname x & [n]]
128 | (if (= (class x) java.util.regex.Pattern) true
129 | (do (add-to-seen {:check "a regular expression"
130 | :class (class x)
131 | :value x
132 | :fname fname})
133 | (if n (add-to-seen {:arg-num n}))
134 | false)))
135 |
136 | (defn check-if-string-or-character? [fname x & [n]] ;for string-contains?
137 | (if (or (string? x) (char? x)) true
138 | (do (add-to-seen {:check "either a string or character"
139 | :class (class x)
140 | :value x
141 | :fname fname})
142 | (if n (add-to-seen {:arg-num n}))
143 | false)))
144 |
145 | ;; should pass the starting arg number: it's different for different functions
146 | (defn check-if-seqables? [fname arguments start]
147 | (loop [args arguments n start]
148 | (if (empty? args)
149 | true
150 | (if (not (check-if-seqable? fname (first args) n))
151 | false
152 | (recur (rest args) (inc n))))))
153 |
154 | ;; should pass the starting arg number: it's different for different functions
155 | (defn check-if-numbers? [fname arguments start]
156 | (loop [args arguments n start]
157 | (if (empty? args)
158 | true
159 | (if (not (check-if-number? fname (first args) n))
160 | false
161 | (recur (rest args) (inc n))))))
162 |
163 | ;; should pass the starting arg number: it's different for different functions
164 | (defn check-if-strings? [fname arguments start]
165 | (loop [args arguments n start]
166 | (if (empty? args)
167 | true
168 | (if (not (check-if-string? fname (first args) n))
169 | false
170 | (recur (rest args) (inc n))))))
171 |
172 | ;; allows for us to not say nth arg when there is only one arg
173 | (defn only-arg [arg] (if arg arg (when-not arg (add-to-seen {:only-arg true}) arg)))
174 |
175 | ;; should pass the starting arg number: it's different for different functions (not sure if true for this one)
176 | (defn check-if-functions? [fname arguments start]
177 | (loop [args arguments n start]
178 | (if (empty? args)
179 | true
180 | (if (not (check-if-function? fname (first args) n))
181 | false
182 | (recur (rest args) (inc n))))))
183 |
184 | ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
185 |
186 | ;(defn all-elems-are-map-or-vector? [coll]
187 | ; (every? #(or (vector? %) (map? %)) coll))
188 | ;
189 | ;(defn all-elems-have-length-two? [coll]
190 | ; (every? #(= (count %) 2) coll)) ;with hashmaps, this function will ALWAYS return true because it breaks up hashmaps into
191 | ; ;vectors of 2, so it will only return false if the hashmap is not a valid hashmap, since
192 | ; ;hashmaps always are built up of pairs of 2
193 | ;
194 | ;(defn all-elems-are-map-or-vector-with-length-2? [fname coll & [n]]
195 | ; (if (and (all-elems-are-map-or-vector? coll)
196 | ; (all-elems-have-length-two? coll))
197 | ; true ;return true
198 | ; (do (add-to-seen {:check "either a hashmap, or a collection of vectors or hashmaps of length 2,"
199 | ; :class (class coll) ;else add-to seen
200 | ; :value coll
201 | ; :fname fname})
202 | ; (if n (add-to-seen {:arg-num n}))
203 | ; false))) ;and return false
204 | ;
205 | ;(defn check-if-can-convert-to-hashmap [fname arg1 arg2 & [n]]
206 | ; (if (map? arg1) ;is arg1 a hashmap?
207 | ; (if (map? arg2) ;if so, is arg2 a hashmap?
208 | ; true ;if both args are hashmaps, return true
209 | ; (all-elems-are-map-or-vector-with-length-2? fname arg2 n)) ;if arg1 is a hashmap, but not arg2, do all-elems-are-map-or-vector-with-length-2?
210 | ; true)) ;if arg1 is NOT a hashamp, return true
211 |
--------------------------------------------------------------------------------
/src/corefns/collection_fns.clj:
--------------------------------------------------------------------------------
1 | (ns corefns.collection_fns
2 | (:require [corefns.assert_handling :refer :all]
3 | [corefns.failed_asserts_info :refer :all]))
4 |
5 |
6 | ;;; Beginner-friendly functions for type-independent sequence handling.
7 |
8 |
9 | (defn add-first [argument1 argument2]
10 | {:pre [(check-if-seqable? "add-first" argument1)]}
11 | (cons argument2 argument1))
12 |
13 | (defn add-last [argument1 argument2]
14 | {:pre [(check-if-seqable? "add-last" argument1)]}
15 | (doall (concat argument1 [argument2])))
16 |
17 | ;; user-friendly versions of confusing functions
18 | (defn contains-value? [coll x]
19 | {:pre [(check-if-seqable? "contains-value?" coll 1)]}
20 | (let [values (if (map? coll) (vals coll) coll)]
21 | (not (every? #(not= x %) values))))
22 |
23 | (defn contains-key? [coll key]
24 | {:pre [(check-if-seqable? "contains-key?" coll 1)]}
25 | (clojure.core/contains? coll key))
26 |
27 | ;; more content tests
28 | (defn any? [pred coll] ;;unsure how to check if somethings a predicate
29 | {:pre [(check-if-function? "any?" pred 1)
30 | (check-if-seqable? "any?" coll 2)]}
31 | (not (not-any? pred coll)))
32 | ; yes, I know :-(
33 | ;(defn some? [pred coll]
34 | ; (not (not-any? pred coll)))
35 |
--------------------------------------------------------------------------------
/src/corefns/failed_asserts_info.clj:
--------------------------------------------------------------------------------
1 | (ns corefns.failed_asserts_info)
2 |
3 |
4 | ;;; a global hashmap of recorded types/messages
5 |
6 |
7 | (def seen-failed-asserts (atom {}))
8 |
9 | (defn add-to-seen
10 | "adds bindings to seen objects"
11 | [binds]
12 | (swap! seen-failed-asserts merge binds)) ; merge overwrites the same fields but adds new ones
13 |
14 | (defn empty-seen
15 | "removes all bindings from seen objects"
16 | []
17 | (swap! seen-failed-asserts {}))
18 |
--------------------------------------------------------------------------------
/src/corefns/specs.clj:
--------------------------------------------------------------------------------
1 | (ns corefns.specs
2 | (require [clojure.spec :as s]
3 | [clojure.spec.test :as stest]))
4 |
5 |
6 | ;;; Spec definitions and instruments for corefns functions
7 |
8 |
9 | ;; Spec for clojure.spec by Tony
10 | ;; =======================================================================================|
11 | ;; * For every function with inlining, we can't use spec without overwriting it. |
12 | ;; |
13 | ;; * put spec before a funtion -> specify conditions for the core function |
14 | ;; vs |
15 | ;; put spec after a function -> specify conditions for the overwritten function |
16 | ;; |
17 | ;; * There are two cases that we need to put spec after we overwrite a function |
18 | ;; 1. When the function has different numbers of :inline-arities like #{2 3} |
19 | ;; 2. When the function has only :inline without :inline-arities |
20 | ;; |
21 | ;; * s/cat gives a key(name) to each element in a sequence and returns a hashmap |
22 | ;; |
23 | ;; * To get the function name, we first need to attach the spec to the core function |
24 | ;; and then overwrite them (overwriting first doesn't work) |
25 | ;; 1. Overwritten map : #object[corefns.corefns$map ... ] |
26 | ;; 2. Not overwritten map: #object[clojure.spec.test$spec_checking_fn$fn__12959 ... ] |
27 | ;; |
28 | ;; * 'NO' means the function doesn't need to be overwritten |
29 | ;; 'O' means the function should be overwritten |
30 | ;; =======================================================================================|
31 |
32 |
33 | ;; #######################################
34 | ;; # helper functions/predicates #
35 | ;; #######################################
36 | (defn length1? [coll] (= (count coll) 1))
37 | (defn length2? [coll] (= (count coll) 2))
38 | (defn length3? [coll] (= (count coll) 3))
39 | (defn length-greater0? [coll] (> (count coll) 0))
40 | (defn length-greater1? [coll] (> (count coll) 1))
41 | (defn length-greater2? [coll] (> (count coll) 2))
42 |
43 | (s/def ::length-one length1?)
44 | (s/def ::length-two length2?)
45 | (s/def ::length-three length3?)
46 | (s/def ::length-greater-zero length-greater0?)
47 | (s/def ::length-greater-one length-greater1?)
48 | (s/def ::length-greater-two length-greater2?)
49 |
50 |
51 | ;; #######################################
52 | ;; # specs #
53 | ;; #######################################
54 |
55 | ; ##### NO #####
56 | (s/fdef clojure.core/empty?
57 | :args (s/and ::length-one
58 | (s/cat :check-seqable seqable?)))
59 | (stest/instrument 'clojure.core/empty?)
60 |
61 | ; ##### NO #####
62 | (s/fdef clojure.core/map
63 | :args (s/and ::length-greater-one
64 | (s/cat :check-function ifn? :check-seqable (s/+ seqable?))))
65 | (stest/instrument 'clojure.core/map)
66 |
67 |
68 | ; ##### NO #####
69 | (s/fdef clojure.core/conj
70 | :args (s/and ::length-greater-one
71 | (s/cat :check-seqable seqable? :dummy (s/+ any?))))
72 | (stest/instrument 'clojure.core/conj)
73 |
74 | ; ##### NO #####
75 | (s/fdef clojure.core/into
76 | :args (s/and ::length-two
77 | (s/cat :check-seqable seqable? :check-seqable seqable?)))
78 | (stest/instrument 'clojure.core/into)
79 |
80 | ; ##### NO #####
81 | (s/fdef clojure.core/cons
82 | :args (s/and ::length-two
83 | (s/cat :dummy any? :check-seqable seqable?)))
84 | (stest/instrument 'clojure.core/cons)
85 |
86 | ; ##### NO #####
87 | ; This doesn't differenciate two and three arity cases - issue #116
88 | ;; (s/fdef reduce
89 | ;; :args (s/cat :check-funtion ifn? :dummy (s/? ::s/any) :check-seqable seqable?))
90 | ; Doesn't work - s/cat returns a hashmap
91 | ;; (s/fdef reduce
92 | ;; :args (s/and (s/or :two (s/cat :dummy ::s/any :dummy ::s/any)
93 | ;; :three (s/cat :dummy ::s/any :dummy ::s/any :dummy ::s/any))
94 | ;; (s/or :three-args (s/cat :check-function ifn? :dummy ::s/any :check-seqable seqable?)
95 | ;; :two-args (s/cat :check-function ifn? :check-seqable seqable?))))
96 | ;; (s/fdef reduce
97 | ;; :args (s/or
98 | ;; :three (s/cat :check-function ifn? :dummy ::s/any :check-seqable seqable?)
99 | ;; :two (s/cat :check-function ifn? :check-seqable seqable?)))
100 |
101 | ;; (s/fdef reduce
102 | ;; :args (s/or :two-case (s/and ::length-two
103 | ;; (s/cat :check-function ifn? :check-seqable seqable?))
104 | ;; :three-case (s/and ::length-three
105 | ;; (s/cat :check-function ifn? :dummy ::s/any :check-seqable seqable?))))
106 | ;; (s/instrument #'reduce)
107 | (s/fdef clojure.core/reduce
108 | :args (s/or :two-case (s/and ::length-two
109 | (s/cat :check-function ifn? :check-seqable seqable?))
110 | :three-case (s/and ::length-three
111 | (s/cat :check-function ifn? :dummy any? :check-seqable seqable?))))
112 | (stest/instrument 'clojure.core/reduce)
113 |
114 | ; ##### O ##### - doesn't work unless the spec is after the overwritten function -> handled
115 | ;; (s/fdef clojure.core/nth
116 | ;; :args (s/or :two-case (s/and ::length-two
117 | ;; (s/cat :check-seqable seqable? :check-number number?))
118 | ;; :three-case (s/and ::length-three
119 | ;; (s/cat :check-seqable seqable? :check-number number? :dummy any?))))
120 | ;; (stest/instrument 'clojure.core/nth)
121 |
122 |
123 | ;; List of functions that have specs and aren't overwritten:
124 | ; ##### NO #####
125 | (s/fdef clojure.core/filter
126 | :args (s/and ::length-two
127 | (s/cat :check-function ifn? :check-seqable seqable?)))
128 | (stest/instrument 'clojure.core/filter)
129 |
130 |
131 | ; ##### NO #####
132 | (s/fdef clojure.core/mapcat
133 | :args (s/and ::length-greater-one
134 | (s/cat :check-function ifn? :check-seqable (s/+ seqable?))))
135 | (stest/instrument 'clojure.core/mapcat)
136 |
137 | ; ##### NO #####
138 | ; We need s/nilable here because map? and vector? return false for nil
139 | (s/fdef clojure.core/assoc
140 | :args (s/and ::length-greater-two
141 | (s/cat :check-map-or-vector (s/or :check-map (s/nilable map?) :check-vector vector?)
142 | :dummy any?
143 | :dummies (s/+ any?))))
144 | (stest/instrument 'clojure.core/assoc)
145 |
146 | ; ##### NO #####
147 | ; We need s/nilable here because map? returns false for nil
148 | (s/fdef clojure.core/dissoc
149 | :args (s/and ::length-greater-zero
150 | (s/cat :check-map (s/nilable map?) :dummies (s/* any?))))
151 | (stest/instrument 'clojure.core/dissoc)
152 |
153 | ; ##### NO #####
154 | (s/fdef clojure.core/odd?
155 | :args (s/and ::length-one
156 | (s/cat :check-integer integer?)))
157 | (stest/instrument 'clojure.core/odd?)
158 |
159 | ; ##### NO #####
160 | (s/fdef clojure.core/even?
161 | :args (s/and ::length-one
162 | (s/cat :check-integer integer?)))
163 | (stest/instrument 'clojure.core/even?)
164 |
165 | ; ##### O #####
166 | (s/fdef clojure.core/<
167 | :args (s/and ::length-greater-zero
168 | (s/cat :check-number (s/+ number?))))
169 | (stest/instrument 'clojure.core/<)
170 |
171 | ; ##### O #####
172 | (s/fdef clojure.core/+
173 | :args (s/cat :check-number (s/* number?)))
174 | (stest/instrument 'clojure.core/+)
175 |
176 | ; ##### O #####
177 | (s/fdef clojure.core/-
178 | :args (s/cat :check-number (s/+ number?)))
179 | (stest/instrument 'clojure.core/-)
180 |
181 | ; ##### O ##### - TODO: doesn't work. the same behavior as nth -> handled
182 | ;; (s/fdef clojure.core/quot
183 | ;; :args (s/and ::length-two
184 | ;; (s/cat :check-number number? :check-number number?)))
185 | ;; (stest/instrument 'clojure.core/quot)
186 |
187 | ; ##### NO #####
188 | (s/fdef clojure.core/comp
189 | :args (s/cat :check-function (s/* ifn?)))
190 | (stest/instrument 'clojure.core/comp)
191 |
192 | ; ##### NO #####
193 | (s/fdef clojure.core/repeatedly
194 | :args (s/or :one-case (s/and ::length-one
195 | (s/cat :check-function ifn?))
196 | :two-case (s/and ::length-two
197 | (s/cat :check-number number? :check-function ifn?))))
198 | (stest/instrument 'clojure.core/repeatedly)
199 |
200 | ; ##### NO #####
201 | (s/fdef clojure.core/repeat
202 | :args (s/or :one-case ::length-one
203 | :two-case (s/and ::length-two
204 | (s/cat :check-number number? :dummy any?))))
205 | (stest/instrument 'clojure.core/repeat)
206 |
207 | ; ##### NO #####
208 | (s/fdef clojure.core/distinct
209 | :args (s/and ::length-one
210 | (s/cat :check-seqable seqable?)))
211 | (stest/instrument 'clojure.core/distinct)
212 |
213 | ;; This hashmap is used to get function names because 'speced' functions are stored differently
214 | (def specs-map
215 | {(str map) "map", (str filter) "filter", (str reduce) "reduce", (str empty?) "empty?",
216 | (str conj) "conj", (str into) "into", (str cons) "cons", (str mapcat) "mapcat",
217 | (str assoc) "assoc", (str dissoc) "dissoc", (str odd?) "odd?", (str even?) "even?",
218 | (str <) "<", (str comp) "comp", (str repeatedly) "repeatedly", (str repeat) "repeat",
219 | (str distinct) "distinct"})
220 |
--------------------------------------------------------------------------------
/src/errors/dictionaries.clj:
--------------------------------------------------------------------------------
1 | (ns errors.dictionaries
2 | (:use [corefns.corefns]
3 | [errors.messageobj]
4 | [corefns.specs :only [specs-map]]
5 | [corefns.failed_asserts_info]
6 | ))
7 | (require '[clojure.string :as cs])
8 |
9 |
10 | ;;; A dictionary of known types and their user-friendly representations.
11 | ;;; Potentially, we can have multiple dictionaries depending on the level.
12 |
13 |
14 | (def type-dictionary {:java.lang.String "a string"
15 | :java.lang.CharSequence "a string"
16 | :java.lang.Number "a number"
17 | :clojure.lang.Keyword "a keyword"
18 | :java.lang.Boolean "a boolean"
19 | ;; I think this is better for new students to lump all numbers together
20 | :java.lang.Long "a number"
21 | :java.lang.Integer "a number"
22 | :java.lang.Double "a number"
23 | :java.lang.Float "a number"
24 | :java.lang.Short "a number"
25 | :clojure.lang.BigInt "a number"
26 | ;; perhaps add big ints and such
27 | :java.lang.Character "a character" ;; switched back from a symbol
28 | ;; to short-cut processing of error messages for
29 | ;; "Don't know how to create a sequence from ..."
30 | :clojure.lang.ISeq "a sequence"
31 | :ISeq "a sequence"
32 | ;; Refs come up in turtle graphics
33 | :clojure.lang.Ref "a mutable object"
34 | ;; regular expressions wouldn't make sense to beginners,
35 | ;; but it's better to recognize their types for easier
36 | ;; help with diagnostics
37 | :java.util.regex.Pattern "a regular expression pattern"
38 | :java.util.regex.Matcher "a regular expression matcher"
39 | ;; also not something beginners would know,
40 | ;; but useful for understanding errors
41 | :clojure.lang.Symbol "a symbol"
42 | :clojure.lang.IPersistentStack "an object that behaves as a stack (such as a vector or a list)"
43 | :clojure.lang.PersistentArrayMap "a map"
44 | ;; assoc works on maps and vectors:
45 | :clojure.lang.Associative "a map or a vector"
46 | :clojure.lang.Reversible "a vector or a sorted-map"
47 | :clojure.lang.Sorted "a collection stored in a sorted manner (such as sorted-map or sorted-set)"
48 | :clojure.lang.Sequential "a sequential collection (such as a vector or a list)"
49 | ;; This is here because of shuffle. It's not ideal, too similar to Sequential
50 | :java.util.Collection " a traversable collection (such as a vector, list, or set)" ; not sure if this makes sense in relation to the previous one
51 | ;; got this in a seesaw error message. Not sure what other types are "Named"
52 | ;; source: https://groups.google.com/forum/?fromgroups#!topic/clojure/rd-MDXvn3q8
53 | :clojure.lang.Named "a keyword or a symbol"
54 | :clojure.lang.nil "nil"})
55 |
56 |
57 | ;; matching type interfaces to beginner-friendly names.
58 | ;; Note: since a type may implement more than one interface,
59 | ;; the order is essential. The lookup is done in order, so
60 | ;; the first match is returned.
61 | ;; That's why it's a vector, not a hashmap.
62 | ;; USE CAUTION WHEN ADDING NEW TYPES!
63 |
64 | (def general-types [[Number "a number"]
65 | [clojure.lang.IPersistentVector "a vector"]
66 | [clojure.lang.IPersistentList "a list"]
67 | [clojure.lang.IPersistentSet "a set"]
68 | [clojure.lang.IPersistentMap "a map"]
69 | [clojure.lang.ISeq "a sequence"]
70 | ;; collections - must go before functions since some collections
71 | ;; implement the IFn interface
72 | [clojure.lang.IPersistentCollection "a collection"]
73 | [clojure.lang.IFn "a function"]])
74 |
75 | ;; The best approximation of a type t not listed in the type-dictionary (as a string)
76 | ;;; best-approximation: type -> string
77 | (defn best-approximation [t]
78 | "returns a string representation of a type t not listed in the type-dictionary for user-friendly error messages"
79 | (let [attempt (resolve (symbol t))
80 | type (if attempt attempt (clojure.lang.RT/loadClassForName (str "clojure.lang." t))) ;; may need to add clojure.lang. for some types.
81 | matched-type (if type (first (filter #(isa? type (first %)) general-types)))]
82 | (if matched-type (second matched-type) (str "unrecognized type " t))))
83 |
84 | ;;; get-type: type -> string
85 | (defn get-type
86 | "returns a user-friendly representation of a type if it exists in the type-dictionary,
87 | or its default representation as an unknown type"
88 | [t]
89 | ((keyword t) type-dictionary (best-approximation t)))
90 |
91 | (defn get-type-with-nil
92 | "Takes a value that can be nil and returns its type in a user-readable form"
93 | [v]
94 | (if (nil? v) "nil" (get-type (.getName (type v)))))
95 |
96 |
97 | ;; hashmap of internal function names and their user-friendly versions
98 | (def predefined-names {:_PLUS_ "+" :_ "-" :_SLASH_ "/" })
99 |
100 | ;;; lookup-funct-name: predefined function name -> string
101 | (defn lookup-funct-name
102 | "looks up pre-defined function names, such as _PLUS_. If not found,
103 | returns the original"
104 | [fname]
105 | (let [lookup ((keyword fname) predefined-names)]
106 | (if lookup lookup (-> fname
107 | (clojure.string/replace #"_QMARK_" "?")
108 | (clojure.string/replace #"_BANG_" "!")
109 | (clojure.string/replace #"_EQ_" "=")
110 | (clojure.string/replace #"_LT_" "<")
111 | (clojure.string/replace #"_GT_" ">")
112 | (clojure.string/replace #"_STAR_" "*")))))
113 |
114 | ;;; check-if-anonymous-function: string -> string
115 | (defn check-if-anonymous-function
116 | "Takes a string as function name and returns a string \"anonymous function\"
117 | if it is an anonymous function, its name otherwise"
118 | [fname]
119 | (if (or (= fname "fn") (re-matches #"fn_(.*)" fname) (re-matches #"fn-(.*)" fname))
120 | "anonymous-function" fname))
121 |
122 | ;;; get-match-name: string -> string
123 | (defn get-match-name
124 | "extract a function name from a qualified name"
125 | [fname]
126 | (let [check-spec ((merge corefns-map specs-map) fname)
127 | m (if check-spec check-spec (nth (re-matches #"(.*)\$(.*)" fname) 2))
128 | matched (if m m (nth (re-matches #"(.*)/(.*)" fname) 2))]
129 | (if matched
130 | (check-if-anonymous-function (lookup-funct-name matched))
131 | fname)))
132 |
133 | ;;; remove-inliner: string -> string
134 | (defn- remove-inliner
135 | "If fname ends with inliner this will return everything before it"
136 | [fname]
137 | (let [match (nth (re-matches #"(.*)--inliner(.*)" fname) 1)]
138 | (if match match fname)))
139 |
140 | ;;; get-function-name: string -> string
141 | (defn get-function-name
142 | [fname]
143 | "Returns a human-readable unqualified name of the function,
144 | or \"anonymous function\" if no name is found."
145 | (remove-inliner (get-match-name fname)))
146 |
147 | ;;; get-macro-name: string -> string
148 | (defn get-macro-name [mname]
149 | "extract a macro name from a qualified name"
150 | (nth (re-matches #"(.*)/(.*)" mname) 2))
151 |
152 |
153 | (defn pretty-print-single-value
154 | "returns a pretty-printed value that is not a collection"
155 | [value]
156 | ;; need to check for nil first because .getName fails otherwise
157 | (if (nil? value) "nil"
158 | (let [fname (.getName (type value))]
159 | (cond (string? value) (str "\"" value "\"") ; strings are printed in double quotes
160 | ; extract a function from the class fname (easier than from value):
161 | (= (get-type fname) "a function") (get-function-name fname)
162 | (coll? value) "(...)"
163 | :else value))))
164 |
165 | (defn delimeters
166 | "takes a collection and returns a vector of its delimeters as a vector of two strings"
167 | [coll]
168 | (cond
169 | (vector? coll) ["[" "]"]
170 | (set? coll) ["#{" "}"]
171 | (map? coll) ["{" "}"]
172 | :else ["(" ")"]))
173 |
174 | (defn add-commas
175 | "takes a sequence and returns a sequence of the same elements with a comma
176 | inserted after every 3rd element in every 4-element group"
177 | [sq]
178 | (loop [result [] s sq n 1]
179 | (if (empty? s) result
180 | (if (= n 4)
181 | (recur (into result ["," (first s)]) (rest s) 1)
182 | (recur (conj result (first s)) (rest s) (inc n))))))
183 |
184 | (defn add-spaces-etc
185 | "takes a sequence s and a limit n and returns the elements of s with spaces in-between
186 | and with ... at the end if s is longer than n"
187 | [s n is-map]
188 | (let [seq-with-spaces (interpose " " (take n s))
189 | seq-done (if is-map (add-commas seq-with-spaces) seq-with-spaces)]
190 | (if (> (count s) n) (concat seq-done '("...")) seq-done)))
191 |
192 | (defn pretty-print-nested-values
193 | "returns a vector of pretty-printed values. If it's a collection, uses the first limit
194 | number as the number of elements it prints, passes the rest of the limit numbers
195 | to the call that prints the nested elements. If no limits passed, returns (...)
196 | for a collection and teh string of a value for a single value"
197 | [value & limits]
198 | (if (or (not limits) (not (coll? value))) (pretty-print-single-value value)
199 | (let [[open close] (delimeters value)
200 | ;; a sequence of a map is a sequence of vectors, turning it into a flat sequence:
201 | flat-seq (if (map? value) (flatten (seq value)) value)]
202 | (conj (into [open] (add-spaces-etc
203 | (take (inc (first limits)) (map #(apply pretty-print-nested-values (into [%] (rest limits))) flat-seq))
204 | (first limits)
205 | (map? value)))
206 | close))))
207 |
208 | (defn preview-arg
209 | "returns a pretty-printed value of a preview of an arbitrary collection or value.
210 | The input consists of a value and a variable number of integers that indicate
211 | how many elements of a collection are displayed at each level. The rest of the
212 | sequence is truncated, ... is included to indicate truncated sequences."
213 | [& params]
214 | (let [pretty-val (apply pretty-print-nested-values params)]
215 | (if (coll? pretty-val) (cs/join (flatten pretty-val)) (str pretty-val))))
216 |
217 |
218 | ;;; arg-str: number -> string
219 | (defn arg-str [n]
220 | (let [abs (fn [m] (if (> 0 m) (- m) m))
221 | n1 (mod (abs n) 100)
222 | n2 (mod (abs n) 10)]
223 | (case n
224 | 1 "first argument"
225 | 2 "second argument"
226 | 3 "third argument"
227 | 4 "fourth argument"
228 | 5 "fifth argument"
229 | (cond
230 | (or (= 11 n1) (= 12 n1) (= 13 n1)) (str n "th argument")
231 | (= 1 n2) (str n "st argument")
232 | (= 2 n2) (str n "nd argument")
233 | (= 3 n2) (str n "rd argument")
234 | :else (str n "th argument"))
235 | )))
236 |
237 | (defn number-word [n]
238 | (case n
239 | "0" "zero"
240 | "1" "one"
241 | "2" "two"
242 | "3" "three"
243 | "4" "four"
244 | "5" "five"
245 | "6" "six"
246 | "7" "seven"
247 | "8" "eight"
248 | "9" "nine"
249 | n))
250 |
251 | ;;; process-asserts-obj: string or nil -> string
252 | (defn process-asserts-obj ; where the global atom (seen-failed-asserts) is used
253 | "Returns a msg-info-obj generated for an assert failure based on the
254 | global seen-failed-asserts hashmap, clears the hashmap"
255 | [n]
256 | ;; and perhaps need manual error handling, in case the seen-object is empty
257 | (let [t (:check @seen-failed-asserts)
258 | cl (:class @seen-failed-asserts)
259 | c-type (if cl (get-type (.getName cl)) "nil")
260 | fname (:fname @seen-failed-asserts)
261 | v (:value @seen-failed-asserts)
262 | v-print (preview-arg v 10 4)
263 | arg-num (if (:only-arg @seen-failed-asserts) "argument" (arg-str (if n (Integer. n) (:arg-num @seen-failed-asserts))))]
264 | (empty-seen) ; empty the seen-failed-asserts hashmap
265 | (if (not (= "nil" v-print))
266 | (make-msg-info-hashes
267 | "In function " fname :arg ", the " arg-num " " v-print :arg
268 | " must be " t :type " but is " c-type :type ".")
269 | (make-msg-info-hashes
270 | "In function " fname :arg ", the " arg-num
271 | " must be " t :type " but is " "nil" :arg "."))))
272 |
273 | (defn process-assert-obj-with-extra-arg
274 | "Returns a msg-info-obj generated for an assert failure based on the
275 | global seen-failed-asserts hashmap, clears the hashmap, adds
276 | an extra argument at the end of the hash map"
277 | [n addition]
278 | (add-to-msg-info (process-asserts-obj n) addition))
279 |
280 | (defn get-compile-error-location
281 | "takes a message of a compiler error and returns
282 | the location part that matches after 'compiling',
283 | as a hashmap. Returns an empty hashmap (no keys)
284 | when there is no match"
285 | [m]
286 | (zipmap [:file :line :char] (rest (rest (re-matches #"(.*), compiling:\((.+):(.+):(.+)\)" m)))))
287 |
288 | ;; do we want to move this to corefns?
289 | (def known-args-number {:map "at least two", :count "one", :conj "at least two", :rand-nth "one",
290 | :into "two", :cons "two", :nth "two or three", :drop "two",
291 | :take "two", :filter "two", :reduce "two or three", :mapcat "at least one",
292 | :reverse "one", :sort "one or two", :sort-by "two or three", :ffirst "one",
293 | :map-indexed "two", :for "two", :pmap "two or more", :reductions "two or three",
294 | :second "one", :last "one", :rest "one", :next "one",
295 | :nfirst "one", :fnext "one", :nnext "one", :nthnext "two",
296 | :some "two", :realized? "one", :index "two", :contains? "two",
297 | :first "one", :empty? "one", :join "one or two", :string? "one",
298 | :- "at least one", :rem "two", :mod "two", :inc "one",
299 | :dec "one", :max "one or more", :min "one or more", :rand "zero or one",
300 | :rand-int "one", :odd? "one", :even? "one", :assoc "at least three",
301 | :dissoc "at least one",
302 | })
303 |
304 | (defn lookup-arity
305 | "returns expected arity (as a string) for a function if we know it, nil otherwise"
306 | [f]
307 | ((keyword f) known-args-number))
308 |
--------------------------------------------------------------------------------
/src/errors/error_hints.clj:
--------------------------------------------------------------------------------
1 | (ns errors.error_hints)
2 |
3 | (def hints
4 | {#{:class-cast-exception} "Attempted to use a , but a was expected.
5 | The error happens when a function's argument is not of the type for which the function is defined.
6 | For instance, (+ 1 \"2\") results in this error because \"2\" is not a number, but a string.
7 | Likewise, (< 1 :two) would cause this error because :two is a keyword, and not a number. "
8 | ;This error may happen if you were using a variable and had a wrong value in the variable
9 | ; or if you have switched the order of arguments in a function.
10 |
11 | ; Error example for (+ 1 :two):\n
12 | ; \"The + function on line 7 was expecting a number but was given a keyword.\"\n
13 | ; Error example for(< 8 \"12\"):\n
14 | ; \"The < function on line 52 was expecting a number but was given a string.\"\n
15 | ; Error example for (num \"ten\"):\n
16 | ; \"The num function on line 42 was expecting a number but was given a string.\""
17 | #{:illegal-argument-cannot-convert-type} "The ___ function on line ___ was expecting ___ but was given ___.
18 | Error example for (cons 1 2):
19 | \"The cons function on line 4 was expecting a sequence but was given a number.\"
20 | Error example for (into {} [1 2 3]):
21 | \"The into function on line 7 was expecting a vector of vectors but was given a vector.\""
22 | #{:index-out-of-bounds-index-not-provided} "Trying to look at a certain location that doesn’t exist in a specific collection.
23 | Error example for (nth [1 2 3] 5):
24 | \"Trying to look at a certain location that doesn’t exist in a specific collection.\""
25 | #{:arity-exception-wrong-number-of-arguments} "Make sure you have the correct number of arguments.
26 | Error example for (first [2 1] [3 6]):
27 | \"Make sure you have the correct number of arguments.\"
28 | Error example for (conj 2):
29 | \"Make sure you have the correct number of arguments.\""
30 | #{:compiler-exception-cannot-resolve-symbol} "____ is undefined; the compiler doesn’t know what it means. See below for possible mistakes.
31 | Cases:
32 | \t -you wanted it to be a string:
33 | \t \t If you want ___ to be a string (words), put quotes around it:
34 | \t \t \"hello\" instead of hello
35 | \t -you wanted it to be a variable
36 | \t \t If you want ___ to be a variable (name for a value), define the value:
37 | \t \t (def hello 8) instead of hello
38 | \t -you wanted it to be a keyword
39 | \t \t If you want ___ to be a keyword, put a colon in front of it:
40 | \t \t :hello instead of hello
41 | \t -you mistyped a higher-order function
42 | \t \t Look back at your code for spelling errors:(first [2 3]) instead of (firST [2 3])"
43 |
44 | ;; We might also need to include a hint for accidentally using a dot or a slash
45 | #{:compiler-exception-no-such-namespace} "1. If you are using functions from other namespaces, make sure you use 'refer' in the beginning of your file to include the namespace.\n
46 | 2. Check the spelling of namespaces you might be using, such as clojure.string\n
47 | 3. Make sure that your namespace uses dots, and the function in the namespaces is separated by a slash: clojure.string/split,
48 | where clojure.string is the namespace, and split is the function."
49 |
50 | #{:class-not-found-exception} "If you are using functions from another file, make sure you use dots for namespaces and slashes for functions, such as clojure.string/split."
51 |
52 | #{:string-index-out-of-bounds} "String positions start at zero, so there is no character at a position equal to the string length.
53 | Example: a string \"hi\" does not have a character at position 2. \n
54 | Also the string may be empty, in this case accessing any position in it gives this error."
55 |
56 | #{:compiler-exception-wrong-number-of-arguments-to-recur} "1. You are passing a wrong number of arguments to recur. Check its function or loop.\n
57 | 2. recur might be outside of the scope of its function or loop."
58 |
59 | #{:null-pointer-non-existing-object-provided} "Put hint here"
60 | }
61 | )
62 |
--------------------------------------------------------------------------------
/src/errors/errorgui.clj:
--------------------------------------------------------------------------------
1 | (ns errors.errorgui
2 | (:use [seesaw.core]
3 | [clojure.string :only [join]]
4 | [errors.messageobj]))
5 |
6 | (def error-prefix "Error: ")
7 | (def comp-error-prefix "Syntax error: ")
8 | (def trace-elems-separator "\t")
9 | (def trace-lines-separator "\n")
10 |
11 | ;; Swing must be initialized to prefer native skinning before any elements are created
12 | (invoke-now (native!))
13 |
14 | ;; Formatting functions
15 |
16 | ;(defn format-stacktrace [e]
17 | ; Force Java to give us the preformatted stacktrace as a string
18 | ; (let [writer (java.io.StringWriter.)]
19 | ; (.printStackTrace e (java.io.PrintWriter. writer))
20 | ; (.toString writer)))
21 |
22 | (defn trace-elem->string [trace-elem trace-elems-separator]
23 | "Takes a stack trace element from a parsed exception
24 | and converts it into a string to be displayed"
25 | ; might need to change to separate handling for
26 | ; java and coljure elements and add
27 | ; handling for anonymous functions
28 | (let [ns (:ns trace-elem)
29 | ns-not-nil (if ns ns (:class trace-elem))
30 | fn (:fn trace-elem)
31 | fn-or-method (if fn fn (:method trace-elem))
32 | file (:file trace-elem)
33 | line (:line trace-elem)]
34 | (str trace-elems-separator ns-not-nil "/" fn-or-method " (" file " line " line ")")))
35 |
36 | (defn trace->string [trace-elems trace-elems-separator trace-lines-separator]
37 | "Takes a stack trace from a parsed exception
38 | and converts it into a string to be displayed"
39 | (join trace-lines-separator (map #(trace-elem->string % trace-elems-separator) trace-elems)))
40 |
41 | (defn- display-msg-object! [display-msg msg-area]
42 | "add text and styles from msg-obj to msg-area"
43 | (doall (map #(style-text! msg-area
44 | (:stylekey %)
45 | (:start %)
46 | (:length %))
47 | display-msg)))
48 |
49 | (defn- make-message-with-filtered-trace [exc-obj]
50 | "returns a display-msg that includes the filtered stacktrace"
51 | (make-display-msg (concat (make-msg-info-hashes (if (:compiler? exc-obj) comp-error-prefix error-prefix) :err)
52 | (:msg-info-obj exc-obj)
53 | (make-msg-info-hashes trace-lines-separator) ; to separate the message from the stacktrace
54 | (make-msg-info-hashes (trace->string (:filtered-stacktrace exc-obj) trace-elems-separator trace-lines-separator)
55 | :stack))))
56 |
57 | ;; Graphics
58 | ;; msg-obj will contain parts and styles and lengths
59 | (defn display-error [exc-obj]
60 | "Takes an exception object and displays it in a swing panel"
61 | ;; extracting elements of exception-object and
62 | ;; forming a message object with the filtered stack trace to be displayed
63 | (let [trace (:stacktrace exc-obj)
64 | msg-filtered-trace (make-message-with-filtered-trace exc-obj)]
65 | (try
66 | (let ;; styles for formatting various portions of a message
67 | [styles [[:arg :font "monospace" :bold true] [:loc :font "monospace" :bold true]
68 | [:call :font "monospace" :bold true] [:reg] [:stack] [:err] [:type] [:causes]]
69 |
70 | ;; forming graphical elements of the swing panel
71 | errormsg (styled-text :wrap-lines? true :text (str (get-all-text msg-filtered-trace))
72 | :styles styles)
73 | stacktrace (text :multi-line? true :editable? false :rows 16 :text
74 | (trace->string trace trace-elems-separator trace-lines-separator))
75 | hints (text :multi-line? true :editable? false :rows 16 :text
76 | (:hints exc-obj))
77 | d (dialog :title "Clojure Error",
78 | :content (tabbed-panel :placement :bottom
79 | :overflow :scroll
80 | :tabs [{:title "Error"
81 | :tip "The simplified error message"
82 | :content (do (display-msg-object! msg-filtered-trace errormsg)
83 | (scrollable errormsg))}
84 | {:title "Stacktrace"
85 | :tip "The full Java stacktrace of the error"
86 | :content (scrollable stacktrace)}
87 | ; {:title "Common sources of error"
88 | ; :tip "Reasons why this error may have happened"
89 | ; :content (scrollable hints)}
90 | ]))]
91 | (invoke-now
92 | (scroll! errormsg :to :top) ;; Scrollboxes default to being scrolled to the bottom - not what we want
93 | (scroll! stacktrace :to :top)
94 | (.setAlwaysOnTop d true) ;; to make errors pop up on top of other programs
95 | ;; a mouse anywhere in the window resets it's always-on-top to false
96 | (listen d :mouse-entered (fn [e] (.setAlwaysOnTop d false)))
97 | (-> d pack! show!)))
98 | (catch java.lang.reflect.InvocationTargetException e
99 | (if (instance? java.awt.HeadlessException (.getCause e))
100 | ; If there is no GUI available on Windows, this throws an InvocationTargetException
101 | ; wrapping a HeadlessException - print the error instead of showing a window.
102 | (println (get-all-text msg-filtered-trace))
103 | ; And if the error does not originate from a HeadlessException, throw it again.
104 | (throw e)))
105 | (catch java.awt.HeadlessException e
106 | ; If there is no GUI available on Linux, it simply throws a HeadlessException - print the erorr.
107 | (println (get-all-text msg-filtered-trace))))))
108 |
--------------------------------------------------------------------------------
/src/errors/exceptionobj.clj:
--------------------------------------------------------------------------------
1 | (ns errors.exceptionobj
2 | (:require [clj-stacktrace.core :as stacktrace])
3 | (:use [errors.messageobj]))
4 |
5 | ;; Not currently used anywhere
6 | (defn exception->exceptionobj
7 | ""
8 | [e]
9 | (let [e-class nil
10 | msg-obj nil
11 | st nil
12 | fst nil
13 | hints nil]
14 | {:exception-class e-class
15 | :msg-info-obj msg-obj ;; without the stack trace
16 | :stacktrace st
17 | :filtered-stacktrace fst
18 | :hints hints}))
19 |
--------------------------------------------------------------------------------
/src/errors/messageobj.clj:
--------------------------------------------------------------------------------
1 | (ns errors.messageobj)
2 | ;(:refer corefn/core :only [add-fisrt add-last]))
3 | ;; Functions related to a message object. msg-info-obj
4 | ;; is a vector of parts of a message (in order). Each
5 | ;; part is a hash-map that contains the message text :msg,
6 | ;; the formatting id (e.g. :reg), the length of the text
7 | ;; :length
8 | ;; A msg-info-obj doesn't have :start
9 |
10 | (defn make-msg-info-hash
11 | "creates a hash-map for a msg-info-obj out of a msg and style, with the form {:msg message :stylekey style :length n}"
12 | ([msg style] (let [m (str msg)]
13 | {:msg m :stylekey style :length (count m)}))
14 | ([msg] (let [m (str msg)]
15 | {:msg m :stylekey :reg :length (count m)})))
16 |
17 | (defn- make-msg-info-hashes-helper [messages result]
18 | (if (empty? messages) result
19 | (let [next (second messages)]
20 | (if (keyword? next) (recur (rest (rest messages))
21 | (conj result (make-msg-info-hash (first messages) next)))
22 | (recur (rest messages)
23 | (conj result (make-msg-info-hash (first messages))))))))
24 |
25 | (defn make-msg-info-hashes [& args]
26 | "creates a vector of hash-maps out of a vector that are strings, possibly followed by optional keywords"
27 | (make-msg-info-hashes-helper args []))
28 |
29 |
30 | (defn add-to-msg-info
31 | "adds an addition (converted to a string) to the end of an existing msg-info-obj, with an optional style keyword"
32 | ([old-msg-info addition style] (conj old-msg-info (make-msg-info-hash addition style)))
33 | ([old-msg-info addition] (conj old-msg-info (make-msg-info-hash addition))))
34 |
35 |
36 | ;(defn make-msg-info-hashes [messages]
37 | ; "creates a vector of hash-maps out of a vector of vectors of msg + optional style"
38 | ; ;; apply is needed since messages contains vectors of 1 or 2 elements
39 | ; (map #(apply make-msg-info-hash %) messages))
40 |
41 | (defn make-display-msg [msg-info-obj] ; msg-info-obj is a vector of hash-maps
42 | "fills in the starting points of objects in the hash maps, in the context of the output from make-msg-info-hashes "
43 | (loop [hashes msg-info-obj start 0 res []]
44 | (if (empty? hashes) res
45 | (recur (rest hashes)
46 | (+ start (:length (first hashes)))
47 | (conj res (assoc (first hashes) :start start))))))
48 |
49 | (defn get-all-text [msg-obj]
50 | "concatenate all text from a message object into a string"
51 | ;(println (str "MESSAGE in get-all-text" msg-obj))
52 | (reduce #(str %1 (:msg %2)) "" msg-obj))
53 |
54 | (defn make-mock-preobj [matches]
55 | "creates a test msg-info-obj. Used for testing so that things don't break"
56 | (make-msg-info-hashes "This is a " "test." :arg))
57 |
--------------------------------------------------------------------------------
/src/errors/prettify_exception.clj:
--------------------------------------------------------------------------------
1 | (ns errors.prettify_exception
2 | (:require [clj-stacktrace.core :as stacktrace]
3 | [expectations :refer :all]
4 | [clojure.string :as str]
5 | [errors.error_dictionary :refer :all]
6 | [errors.error_hints :refer :all])
7 | (:use [errors.dictionaries]
8 | [errors.messageobj]
9 | [errors.errorgui]
10 | [errors.stacktraces]
11 | [seesaw.core]))
12 |
13 |
14 | ;; Main error processing file. Standard errors are processed by `standard` function, and
15 | ;; modified errors are processed by `prettify-exception` function.
16 |
17 |
18 | (defn first-match
19 | [e-class message]
20 | (first (filter #(and (= (:class %) e-class) (re-matches (:match %) message))
21 | error-dictionary)))
22 |
23 | (defn fn-name
24 | "Takes a function object and returns a symbol that corresponds to the result of
25 | the lookup of its name.
26 | If no name is found, a symbol 'anonymous function' (non-conformant)
27 | is returned.
28 | Handles spec-checking functions differently since they are looked up in corefns-map
29 | by full name.
30 | Warning: 'anonymous function' symbol is non-conformant"
31 | [f]
32 | (let [f-str (str f)]
33 | (if (re-matches #"clojure\.spec\.test\$spec_checking_fn(.*)" f-str)
34 | (symbol (get-function-name f-str))
35 | (symbol (get-function-name (.getName (type f)))))))
36 |
37 | (defn is-function?
38 | "Uses our dictionary to check if a value should be printed as a function"
39 | [v]
40 | ;; checking for nil first:
41 | (and v (= (get-type (.getName (type v))) "a function")))
42 |
43 |
44 | (defn single-val-str
45 | "Takes a single (non-collection) value and returns its string represntation.
46 | Returns a string 'nil' for nil, encloses strings into double quotes,
47 | performs a lookup for function names, returns 'anonymous function' for
48 | anonymous functions"
49 | [v]
50 | (cond
51 | (nil? v) "nil"
52 | (string? v) (str "\"" v "\"")
53 | (is-function? v) (fn-name v)
54 | :else (str v)))
55 |
56 | (expect "nil" (single-val-str nil))
57 | (expect "\"hi\"" (single-val-str "hi"))
58 | (expect ":hi" (single-val-str :hi))
59 | ;; testing for function names requires namespaces magic, so not doing it here right now
60 |
61 | (defn lookup-fns
62 | "Recursively replace internal Clojure function names with user-readable ones
63 | in the given value"
64 | [v]
65 | (cond
66 | (not (coll? v)) (if (is-function? v) (fn-name v) v)
67 | (vector? v) (into [] (map lookup-fns v))
68 | (seq? v) (into '() (reverse (map lookup-fns v)))
69 | (set? v) (into #{} (map lookup-fns v))
70 | (map? v) (reduce #(apply assoc %1 %2) {} (map lookup-fns v));; map has key/val pairs
71 | :else v))
72 |
73 | (defn val-str
74 | "If v is a not a collection, returns the result of apply single-val-str to it.
75 | Otherwise returns the same collection, but with functions replaced by their
76 | names, recursively throughout the collection."
77 | [v]
78 | (if-not (coll? v) (single-val-str v) (lookup-fns v)))
79 |
80 | (defn- message-arity
81 | "Gives the arity part of the message when there is an arity error detected by spec"
82 | ;; currently doesn't use the reason, for consistency with the wording of non-spec arity errors
83 | ;; the reasons may be "Insufficient input" and "Extra input" (as strings)
84 | [reason args fname]
85 | (let [arg-args (if (= 1 (count args)) " argument" " arguments")
86 | arity (lookup-arity fname)]
87 | (make-msg-info-hashes "You cannot pass " (number-word (str (count args))) arg-args " to a function "
88 | fname :arg (if arity (str ", need " arity) ""))))
89 |
90 | (defn- function-call-string
91 | "Gives the function call part of the message for spec error messages"
92 | [args fname]
93 | (let [all-args-str (if args (str (val-str args)) "")
94 | call-str (str "(" fname " " (if args (subs all-args-str 1) ")"))]
95 | (make-msg-info-hashes ",\n" "in the function call " call-str :call)))
96 |
97 | (defn- type-from-failed-pred
98 | "Returns a type name from the name of a failed predicate"
99 | [pred-str]
100 | (cond (= pred-str "seqable?") "a sequence"
101 | (= pred-str "ifn?") "a function"
102 | (= pred-str "map?") "a hashmap"
103 | (= pred-str "integer?") "an integer number"
104 | :else (str "a " (subs pred-str 0 (dec (count pred-str))))))
105 |
106 | (defn- or-str
107 | "Takes a vector of predicates, returns a string of their names separated by 'or'"
108 | [pred-strs]
109 | (apply str (interpose " or " (filter #(not (str/starts-with? % "a length")) ;; to weed out the args-length mismatches
110 | (distinct (map type-from-failed-pred pred-strs))))))
111 |
112 | (defn- messages-types
113 | "Gives the part of the message for spec conditions failure"
114 | [problems value arg-num n]
115 | (let [pred-str (if (map? problems) (str (:pred problems)) (map #(str (:pred %)) problems)) ;; it's a map when it's a single predcate
116 | pred-type (if (map? problems) (type-from-failed-pred pred-str) (or-str pred-str))
117 | value-str (val-str value)
118 | value-type (get-type-with-nil value)
119 | arg-num-str (if arg-num (if (= n 1) "argument" (arg-str (inc (first arg-num)))) "")]
120 | (if (nil? value)
121 | (make-msg-info-hashes (str ", the " arg-num-str) " must be " pred-type :type " but is " value-type :arg)
122 | (make-msg-info-hashes (str ", the " arg-num-str " ") value-str :arg " must be " pred-type :type " but is " value-type :type))))
123 |
124 | (defn- get-predicates
125 | "If there is only one non-nil predicate in data, the hash map for that predicate
126 | is returned. If there are several non-nil predicates, a vector of their hash maps is
127 | returned."
128 | [data]
129 | (let [predicates (:clojure.spec/problems data)]
130 | (if (= 1 (count predicates))
131 | (first predicates)
132 | (let [non-nils (filter #(not= "nil?" (str (:pred %))) predicates)]
133 | (if (= (count non-nils) 1) (first non-nils) non-nils)))))
134 |
135 | (defn- arity-error?
136 | "Returns true if all predicates have arity errors and false otherwise.
137 | Assumes that spec predicates for non-matching number of arguments start with 'length'"
138 | [problems]
139 | (every? #(str/starts-with? (str (:pred %)) "length") problems))
140 |
141 | (defn- first-non-length
142 | "Returns the first non-length predicate"
143 | [problems]
144 | (first (filter #(not (str/starts-with? (str (:pred %)) "length")) problems)))
145 |
146 | (defn msg-info-obj-with-data
147 | "Creates a message info object from an exception that contains data"
148 | [entry message data]
149 | (let [entry-info ((:make-msg-info-obj entry) (re-matches (:match entry) message))
150 | fname (:msg (second entry-info))
151 | problems (get-predicates data) ; returns a predicate hashmap or a vector of pred hashmaps
152 | problem (if (map? problems) problems (first-non-length problems)) ; to make it easy to extract fields
153 | args (:clojure.spec/args data)
154 | reason (:reason problem)
155 | arity? (if reason true (arity-error? (if (map? problems) [problems] problems)))
156 | value (:val problem)
157 | arg-num (:in problem)]
158 | ;(println "Data:" data)
159 | ;(println "Problem" problem)
160 | (if arity?
161 | (into (message-arity reason args fname) (function-call-string args fname))
162 | (into entry-info (into (messages-types problems value arg-num (count args)) (function-call-string args fname))))))
163 |
164 | (defn msg-from-matched-entry [entry message data]
165 | "Creates a message info object from an exception and its data, if exists"
166 | (cond
167 | (and data entry) (msg-info-obj-with-data entry message data)
168 | entry ((:make-msg-info-obj entry) (re-matches (:match entry) message))
169 | :else (make-msg-info-hashes message)))
170 |
171 | (defn hints-for-matched-entry
172 | "Returns all hints found for an exception key, as one string."
173 | [entry-key]
174 | (apply str (vals (filter #((first %) entry-key) hints))))
175 |
176 |
177 | (defn get-location-info
178 | "returns the location at the top-most occurrence of student code,
179 | assuming that the stack trace has already been filtered"
180 | [filtered-trace]
181 | (let [top-student-line (first (filter is-student-code? filtered-trace))]
182 | {:file (:file top-student-line), :line (:line top-student-line), :fn (:fn top-student-line)}))
183 |
184 | (expect
185 | {:file "student.clj", :line 154, :fn "my-fn"}
186 | (get-location-info (filter-stacktrace
187 | [{:file "AFn.java", :line 429, :java true, :class "clojure.lang.AFn", :method "throwArity"}
188 | {:file "AFn.java", :line 44, :java true, :class "clojure.lang.AFn", :method "invoke"}
189 | {:file "Compiler.java", :line 6792, :java true, :class "clojure.lang.Compiler", :method "eval"}
190 | {:file "Compiler.java", :line 6755, :java true, :class "clojure.lang.Compiler", :method "eval"}
191 | {:file "core.clj", :line 3079, :clojure true, :ns "clojure.core", :fn "eval", :anon-fn false}
192 | {:file "student.clj", :line 154, :clojure true, :ns "intro.student", :fn "my-fn", :anon-fn false}
193 | {:file "student_test.clj", :line 153, :clojure true, :ns "intro.student-test", :fn "expect1132213279", :anon-fn true}
194 | {:file "student_test.clj", :line 150, :clojure true, :ns "intro.student-test", :fn "expect1132213279", :anon-fn false}
195 | {:file "expectations.clj", :line 229, :clojure true, :ns "expectations", :fn "test-var", :anon-fn true}
196 | {:file "expectations.clj", :line 225, :clojure true, :ns "expectations", :fn "test-var", :anon-fn false}
197 | {:file "expectations.clj", :line 263, :clojure true, :ns "expectations", :fn "test-vars", :anon-fn true}
198 | {:file "expectations.clj", :line 243, :clojure true, :ns "expectations", :fn "create-context", :anon-fn false}
199 | {:file "expectations.clj", :line 263, :clojure true, :ns "expectations", :fn "test-vars", :anon-fn false}
200 | {:file "expectations.clj", :line 308, :clojure true, :ns "expectations", :fn "run-tests", :anon-fn false}
201 | {:file "expectations.clj", :line 314, :clojure true, :ns "expectations", :fn "run-all-tests", :anon-fn false}
202 | {:file "expectations.clj", :line 571, :clojure true, :ns "expectations", :fn "eval2603", :anon-fn true}
203 | {:file nil, :line nil, :java true, :class "expectations.proxy$java.lang.Thread$ff19274a", :method "run"}])))
204 |
205 | (defn get-cause-if-needed
206 | "returns the cause of a compilation exception in cases when we need
207 | to process the cause, not the exception itself"
208 | [e]
209 | (let [cause (.getCause e)]
210 | (if (= (class cause) clojure.lang.Compiler$CompilerException)
211 | ;recursively look for non-compiler exception cause
212 | (get-cause-if-needed cause)
213 | (if (and (= (class e) clojure.lang.Compiler$CompilerException)
214 | cause) ; has a non-nil cause
215 | cause e))))
216 |
217 | (defn compiler-error?
218 | "an ad-hoc method to determine if an exception is really a compilation error:
219 | it's a compilation error with no cause or a generic cause."
220 | [e]
221 | (let [cause (.getCause e)]
222 | (and (= (class e) clojure.lang.Compiler$CompilerException)
223 | (or (nil? cause) (= (class cause) java.lang.RuntimeException)))))
224 |
225 | ;#########################################
226 | ;############ Location format ###########
227 | ;#########################################
228 |
229 | (defn line-number-format
230 | "Takes a line number and a character poistion and returns a string
231 | of how they are reported in an error message"
232 | [line ch]
233 | (str " on, or before, line " line))
234 |
235 | (defn location-info
236 | "Takes the location hashmap (possibly empty) and returns a message info
237 | object to be merged with the rest of the message"
238 | [location]
239 | (if (empty? location) ""
240 | (let [file (:file location)
241 | line (:line location)
242 | character (:char location)
243 | fn-name (:fn location)
244 | ;character-msg (if character (make-msg-info-hashes " at character " character :loc) nil)
245 | ;character-msg nil ; Removed character info since it seems to confuse people
246 | fn-msg (if fn-name (make-msg-info-hashes " in function " fn-name :loc) nil)]
247 | (reduce into [(make-msg-info-hashes "\nFound in file " (clojure.string/replace file #".*\/" "") :loc (line-number-format line character) :loc)
248 | ;character-msg
249 | fn-msg
250 | (make-msg-info-hashes ".")]))))
251 |
252 | (defn location-from-data
253 | "Take exception data from a spec condition failure,
254 | return the location hashmap if not the caller is core.clj and
255 | the val-scope is clojure.core/apply"
256 | [data]
257 | (let [caller-map (:clojure.spec.test/caller data)]
258 | (when-not
259 | (and (= (:file caller-map) "core.clj")
260 | (= (:var-scope caller-map) (symbol "clojure.core/apply")))
261 | (select-keys caller-map [:file :line]))))
262 |
263 | (defn get-exc-message
264 | "returns the message in an exception or an empty string if the message is nil"
265 | [e]
266 | (let [m (.getMessage e)]
267 | (if m m "")))
268 |
269 | (defn- cut-stack-trace
270 | [stacktrace limit]
271 | (if (> (count stacktrace) limit) (conj (vec (take limit stacktrace)) {:file "" :line nil :clojure true :ns "and more..."}) stacktrace))
272 |
273 | ;; All together:
274 | (defn prettify-exception [ex]
275 | (let [compiler? (compiler-error? ex)
276 | e (get-cause-if-needed ex)
277 | ;; replacing clojure.lang.LispReader$ReaderException by RuntimeException
278 | ;; to avoid code duplication in error handling since their errors
279 | ;; overlap.
280 | e-class (if (= (class e) clojure.lang.LispReader$ReaderException) RuntimeException (class e))
281 | message (get-exc-message e) ; converting an empty message from nil to ""
282 | entry (first-match e-class message) ; We use error dictionary here in first-match
283 | data (ex-data e)
284 | exc (stacktrace/parse-exception e)
285 | stacktrace (:trace-elems exc)
286 | filtered-trace (filter-stacktrace stacktrace)
287 | spec-location (when data (location-from-data data))
288 | comp-location (if (empty? spec-location) (get-compile-error-location (get-exc-message ex)) spec-location)
289 | location (if (empty? comp-location) (get-location-info filtered-trace) comp-location)
290 | msg-info-obj (into (msg-from-matched-entry entry message data) (location-info location))
291 | hint-message (hints-for-matched-entry (:key entry))]
292 | ;; (println ex)
293 | ;; (println spec-location)
294 | ;; (println location)
295 | ;; create an exception object
296 | {:exception-class e-class
297 | :compiler? compiler?
298 | :msg-info-obj msg-info-obj
299 | :stacktrace stacktrace
300 | :filtered-stacktrace filtered-trace
301 | :hints hint-message}))
302 |
303 | (defn standard
304 | "returns a non-transformed error message with the non-filtered stack trace
305 | as an exception object"
306 | [ex]
307 | ;; create an exception object
308 | (let [e (get-cause-if-needed ex)
309 | ;; replacing clojure.lang.LispReader$ReaderException by RuntimeException
310 | ;; to avoid code duplication in error handling since their errors
311 | ;; overlap.
312 | e-class (if (= (class e) clojure.lang.LispReader$ReaderException) RuntimeException (class e))
313 | message (.getMessage e)
314 | exc (stacktrace/parse-exception e)
315 | stacktrace (:trace-elems exc)]
316 | {:exception-class e-class
317 | :compiler? false
318 | :msg-info-obj (make-msg-info-hashes (str (.getName e-class)) ": " (if (> (count message) 40) (str "\n\t" message) message))
319 | :stacktrace stacktrace ;(cut-stack-trace stacktrace 45)
320 | :filtered-stacktrace (cut-stack-trace stacktrace 40)
321 | :hints ""}))
322 |
323 | (defn trace-hashmap-to-StackTraceElement
324 | "Converts a clojure stacktrace element (hashmap) to a java StackTraceElement"
325 | [trace-hashmap]
326 | (let [declaringClass (if-let [class-name (:class trace-hashmap)]
327 | class-name
328 | (:ns trace-hashmap))
329 | methodName (if-let [method (:method trace-hashmap)]
330 | method
331 | (:fn trace-hashmap))
332 | fileName (:file trace-hashmap)
333 | lineNumber (:line trace-hashmap)]
334 | (new StackTraceElement declaringClass methodName fileName lineNumber)))
335 |
336 | (defn exception-obj-to-java-Throwable
337 | "Converts an exception-obj hashmap into the correct subtype of Java Throwable"
338 | [exception-obj]
339 | (let [e-class (:exception-class exception-obj)
340 | message (get-all-text (:msg-info-obj exception-obj))
341 | java-Throwable (eval `(new ~e-class ~message))
342 | stack-trace-element-sequence (map trace-hashmap-to-StackTraceElement (:filtered-stacktrace exception-obj)); "for each thing in :filtered-stacktrace, turn it into StackTraceElement and put it in the array"
343 | stack-trace-element-array (into-array stack-trace-element-sequence)
344 | ]
345 | (do (.setStackTrace java-Throwable stack-trace-element-array)
346 | java-Throwable)))
347 |
--------------------------------------------------------------------------------
/src/errors/stacktraces.clj:
--------------------------------------------------------------------------------
1 | (ns errors.stacktraces
2 | (:require [clj-stacktrace.core :as stacktrace]
3 | [expectations :refer :all]
4 | [errors.error_dictionary :refer :all])
5 | (:use [errors.dictionaries]
6 | [errors.messageobj]))
7 |
8 | ;; namespaces to ignore:
9 |
10 | ;; regular expressions for namespaces to be ignored. Any namespace equal to
11 | ;; or contaning these regexps would be ignored
12 | (def ignored-namespaces ["clojure.main" "clojure.lang" "java" "clojure.tools" "clojure.spec" "user" "autoexpect.runner" "expectations" "clojure.core.protocols"])
13 |
14 | (defn- replace-dots [strings]
15 | (map #(clojure.string/replace % #"\." "\\\\.") strings))
16 |
17 |
18 | (def namespaces-to-ignore (replace-dots ignored-namespaces))
19 |
20 | (expect ["clojure\\.main" "clojure\\.lang" "java" "clojure\\.tools"] (replace-dots ["clojure.main" "clojure.lang" "java" "clojure.tools"]))
21 |
22 | (defn- surround-by-parens [strings]
23 | (map #(str "(" % ")") strings))
24 |
25 | (expect ["(aaa)" "(bbb)"] (surround-by-parens ["aaa" "bbb"]))
26 |
27 | (defn- add-postfix [strings postfix]
28 | (map #(str % postfix) strings))
29 |
30 | (expect ["aaa((\\.|/)?(.*))" "bbb((\\.|/)?(.*))"] (add-postfix ["aaa" "bbb"] "((\\.|/)?(.*))"))
31 |
32 | (defn- add-symbol-except-last [strings]
33 | (let [or-sym "|"]
34 | (conj (vec (add-postfix (butlast strings) or-sym)) (last strings))))
35 |
36 | (expect ["aaa|" "bbb|" "ccc"] (add-symbol-except-last ["aaa" "bbb" "ccc"]))
37 |
38 |
39 | (defn- make-pattern-string [to-ignore]
40 | (let [dot-or-slash-or-nothing "((\\.|/)?(.*))"]
41 | (apply str (add-symbol-except-last (surround-by-parens (add-postfix to-ignore dot-or-slash-or-nothing))))))
42 |
43 |
44 | (expect "(aaa((\\.|/)?(.*)))|(bbb((\\.|/)?(.*)))"
45 | (make-pattern-string ["aaa" "bbb"]))
46 |
47 | (def ns-pattern
48 | (re-pattern (make-pattern-string namespaces-to-ignore)))
49 |
50 |
51 | ;; first is needed in tests when multiple matches are returned
52 | (expect "clojure.main" (first (re-matches ns-pattern "clojure.main")))
53 | (expect "clojure.main" (re-matches (re-pattern "clojure\\.main") "clojure.main"))
54 | (expect "clojure.main" (first (re-matches (re-pattern "clojure\\.main((\\.|/)?(.*))") "clojure.main")))
55 |
56 | ;; specify namespaces and function names or patterns
57 | (def ignore-functions {:clojure.core [#"load.*" "require" "alter-var-root" "ex-info"]})
58 |
59 | ;; these functions are probably not needed for beginners, but might be needed at
60 | ;; more advanced levels
61 | (def ignore-utils-functions {:clojure.core ["print-sequential" "pr-on" "pr"] :intro.test [#"eval.*"]})
62 |
63 | (defn- ignore-function? [str-or-regex fname]
64 | (if (string? str-or-regex) (= str-or-regex fname)
65 | (re-matches str-or-regex fname)))
66 |
67 | (expect true (ignore-function? "require" "require"))
68 | (expect false (ignore-function? "require" "require5"))
69 | (expect "load" (ignore-function? #"load" "load"))
70 | (expect "load5" (ignore-function? #"load.*" "load5"))
71 |
72 |
73 | ;; this thing really needs refactoring
74 | (defn- ignored-function? [nspace fname]
75 | (let [key-ns (keyword nspace)
76 | ;; There should be only one match for filter
77 | names (key-ns (merge-with into ignore-functions ignore-utils-functions))]
78 | ;names (key-ns functions-for-namespace)]
79 | (if (nil? names) false (not (empty? (filter #(ignore-function? % fname) names))))))
80 |
81 | (expect true (ignored-function? "clojure.core" "require"))
82 | (expect false (ignored-function? "clojure.lang" "require"))
83 | (expect false (ignored-function? "clojure.core" "require5"))
84 | (expect true (ignored-function? "clojure.core" "load-one"))
85 |
86 |
87 | (defn keep-stack-trace-elem
88 | "returns true if the stack trace element should be kept
89 | and false otherwise"
90 | [st-elem]
91 | (let [nspace (:ns st-elem)
92 | namespace (if nspace nspace "") ;; in case there's no :ns element
93 | fname (:fn st-elem)]
94 | (and (:clojure st-elem) (not (re-matches ns-pattern namespace))
95 | (not (ignored-function? namespace fname)))))
96 |
97 | (defn filter-stacktrace
98 | "takes a stack trace and filters out unneeded elements"
99 | [stacktrace]
100 | ;(println stacktrace)
101 | ;(println (filter keep-stack-trace-elem stacktrace))
102 | (filter keep-stack-trace-elem stacktrace))
103 |
104 | (def non-student-namespaces ["clojure." "corefns"])
105 |
106 | (def ns-pattern-non-student
107 | (re-pattern (make-pattern-string non-student-namespaces)))
108 |
109 | (defn is-student-code?
110 | "determines whether a stack trace element corresponds to student code,
111 | assuming that the element appears in the filtered stacktrace"
112 | [st-elem]
113 | (let [nspace (:ns st-elem)
114 | namespace (if nspace nspace "")]
115 | (not (re-matches ns-pattern-non-student namespace))))
116 |
117 | (expect true (is-student-code? {:file "NO_SOURCE_FILE", :line 154, :clojure true, :ns "intro.student", :fn "eval15883"}))
118 | (expect false (is-student-code? {:file "core.clj", :line 3079, :clojure true, :ns "clojure.core", :fn "eval", :anon-fn false}))
119 |
--------------------------------------------------------------------------------
/src/intro/core.clj:
--------------------------------------------------------------------------------
1 | (ns intro.core
2 | (:use [errors.prettify_exception]
3 | [seesaw.core])
4 | (:require [expectations :refer :all]
5 | [errors.errorgui :refer :all]
6 | [intro.student :refer :all]
7 | [utilities.file_IO :refer :all]
8 | [errors.prettify_exception :refer :all]
9 | [clojure.pprint :refer :all]
10 | ))
11 |
12 | (defn -main [& args]
13 | (try
14 | ; (try
15 | (if (= (first args) "s")
16 | (print (load-file "src/intro/test_standard.clj"))
17 | (print (load-file "src/intro/test.clj")))
18 | ;(catch Throwable e (print (str "itself:" (class e) ", cause: " (class (.getCause e)))) (throw e)))
19 |
20 | (catch Throwable e ;(print (str "itself:" (class e) ", cause: " (class (.getCause e))))
21 | (if (= (first args) "s")
22 | (display-error (standard e))
23 | (display-error (prettify-exception e))))))
24 |
--------------------------------------------------------------------------------
/src/intro/student.clj:
--------------------------------------------------------------------------------
1 | (ns intro.student
2 | (:require [expectations :refer :all]
3 | [corefns.corefns :refer :all]
4 | [corefns.collection_fns :refer :all]))
5 |
6 | (defn fffff [x]
7 | (map 2 x))
8 |
9 | ;##################################
10 | ;### Testing compilation errors ###
11 | ;##################################
12 |
13 | ;(def f [x] (+ x 2))
14 | ;(+ (* 2 3) 7
15 |
16 | ;(+ (* 2 3) 7)]
17 |
18 | ;(defn f[x] (+ x y))
19 |
20 | ;(defn f[x y]
21 | ; (fn [x] (+ x y z)))
22 |
23 | ;(loop [x 1 y 2]
24 | ; (if (= x 0) 1
25 | ; (recur (dec x) 5)))
26 |
27 |
28 | ;#############################################################
29 | ;### 4clojure and beginner problems like students would do ###
30 | ;#############################################################
31 |
32 | ;; 4clojure Problem 44
33 | (declare prob44)
34 |
35 | (defn prob44-helper-positive [n coll]
36 | (let [result (add-last (rest coll) (first coll))]
37 | (prob44 (- n 1) result)))
38 |
39 | (defn prob44-helper-negative [n coll]
40 | (let [result (add-first (butlast coll) (last coll))]
41 | (prob44 (+ n 1) result)))
42 |
43 | (defn prob44 [n coll]
44 | (cond
45 | (= n 0) coll ;n equal to zero
46 | (> n 0) (prob44-helper-positive n coll) ;n is positive
47 | (< n 0) (prob44-helper-negative n coll))) ;n is negative
48 |
49 | ;; DrRacket Excercise 2 - exception in exceptions/DrRacket-Exercise2-ClassCast.ser
50 | ; ERROR: Attempted to use a string, but a collection was expected.
51 | ; possible hint: are you sure conj is the function you should be using?
52 | ; possible hint: conj cannot be used on strings
53 | ; possible tag :conj
54 | (defn exercise2 [str1 str2]
55 | (conj str1 str2))
56 |
57 | ;; DrRacket Exercise 3 - exception in exception/DrRacket-Exercise3-IndexOutOfBounds.ser
58 | ; ERROR: String index out of range: 12
59 | (defn exercise3 [a-string index]
60 | (str (subs a-string 0 index) "_" (subs a-string index (+ 2 (count a-string)))))
61 |
62 | ;; 4clojure Problem 15 - exception in exceptions/4clojure-prob15-ClassCast.ser\
63 | ; ERROR: Attempted to use a number, but a function was expected.
64 | ; possible hint: make sure you're using prefix notation
65 | (defn prob15 [num]
66 | (2 * num))
67 |
68 | ;; 4clojure Problem 16 - exception in exceptions/4clojure-prob16-Arity.ser
69 | ; ERROR: Wrong number of arguments (3) passed to a function cons
70 | ; possible tag :cons
71 | ; possible hint: are you sure cons is the function you should be using?
72 | (defn prob16 [a-string]
73 | (cons "Hello, " a-string "!"))
74 |
75 | ;; 4clojure Problem 17 - exception in exceptions/4clojure-prob17-NullPointer.ser
76 | ; ERROR: An attempt to access a non-existing object.
77 | ; (NullPointerException)
78 | ; possible hint: make sure you have a base (termination) case
79 | ; possible hint: make sure you're not applying an operation to nil
80 | (defn prob17 [coll-of-nums]
81 | (loop [coll coll-of-nums
82 | result '()]
83 | (recur (rest coll)
84 | (conj result (+ 5 (first coll))))))
85 |
86 | ;; 4clojure Problem 18 - exception in exceptions/4clojure-prob18-AssertionError.ser
87 | ; ERROR: in function < first argument nil must be a number but is nil
88 | ; possible hint: make sure that you have a base (termination) case
89 | (defn prob18 [coll-of-nums]
90 | (if (< (first coll-of-nums) 5)
91 | (list (first coll-of-nums)))
92 | (prob18 (rest coll-of-nums)))
93 |
94 | ;; 4clojure Problem 64 - exception in exceptions/4clojure-prob64-NullPointer.ser -- from saved exception, not from recent running
95 | ; ERROR: An attempt to access a non-existing object.
96 | ; (NullPointerException)
97 | ; possible hint: make sure you have a base (termination) case
98 | ; possible hint: make sure you're not applying an operation to nil
99 | (defn prob64 [coll-of-nums]
100 | (loop [coll coll-of-nums
101 | result 0]
102 | (recur (rest coll)
103 | (+ result (first coll)))))
104 |
105 | ;; take-while now is allowed arity 1, as of Clojure 1.7
106 | ;; 4clojure Problem 57 - exception in exceptions/4clojure-prob57-ArityException.ser
107 | ; ERROR: Wrong number of arguments (1) passed to a function take-while
108 | ; possible hint: make sure you're passing the right number of args to your functions
109 | (defn prob57 [num]
110 | (take-while (range num)))
111 |
112 | ;; 4clojure Problem 134 - exception in exceptions/4clojure-prob134-AssertionError.ser
113 | ; ERROR: in function filter first argument :a must be a function but is a keyword
114 | ; possible hint: keywords can't be used as functions in this context
115 | ;; Elena, 6/17/16: keywords are functions and should be used as such. This shouldn't be an error.
116 | ;; If we want to think of restrictions for beginners, we should be careful with how we set it up
117 | (defn prob134 [key map]
118 | (nil? (filter key map)))
119 |
120 | ;(defn prob145 [num]
121 | ; (for [x (range num)]
122 | ; (when (= 1 (rem num 4))
123 | ; x)))
124 |
125 | ;(defn prob145 [num]
126 | ; (filter (fn [] (= 1 (rem 4 %))) (range num)))
127 |
128 | ;; 4clojure Problem 156 - exception in exceptions/4clojure-prob156-AssertionError.ser
129 | ; ERROR: in function map first argument {:a 0} must be a function but is a map
130 | ; possible hint: be sure that your arguments are of the correct types
131 | (defn prob156 [default keys]
132 | (into {} (map (into {}
133 | {(first keys) default})
134 | keys)))
135 |
136 | ;; 4clojure Problem 20 - exception in exceptions/4clojure-prob20-AssertionError.ser
137 | ; ERROR: in function nth first argument 3 must be a sequence but is a number
138 | ; possible hint: be sure that your arguments are in the correct order
139 | (defn prob20 [coll]
140 | (nth (- (count coll) 1) coll))
141 |
142 | ;; take now is allowed arity 1, as of Clojure 1.7
143 | ;; 4clojure Problem 21 - exception in exceptions/4clojure-prob21-ArityException.ser
144 | ; ERROR: Wrong number of arguments (1) passed to a function take
145 | ; possible hint: make sure you're using the right number of arguments.
146 | (defn prob21 [coll n]
147 | (drop (first coll) (take n)))
148 |
149 | ;; Something Emma made, trying to add numbers - exception in exceptions/add-five-IllegalArgException.ser
150 | ; ERROR: Don't know how to create a sequence from a number
151 | ; possible hint: cons is supposed to be used for collections
152 | (defn add-five [n]
153 | (cons n 5))
154 |
155 | ;; 4clojure Problem 24 - exception in exceptions/4clojure-prob24-ClassCast.ser
156 | ; ERROR: Attempted to use a sequence, but a number was expected.
157 | ; possible hint: be sure you are using the correct argument types
158 | (defn prob24 [coll]
159 | (do
160 | (+ (first coll) (rest coll))
161 | (prob24 (rest rest coll))))
162 |
163 | ;; 4clojure Problem 23 - exception in exceptions/4clojure-prob23-IndexOutOfBounds.ser
164 | ; ERROR: An index in a sequence is out of bounds or invalid
165 | ; possible hint: make sure that the sequence you're using has the desired index
166 | (defn prob23 [coll]
167 | (loop [old-coll coll
168 | new-coll []]
169 | (if (empty? old-coll)
170 | new-coll
171 | (recur (rest old-coll) (cons (nth old-coll (count old-coll)) new-coll)))))
172 |
173 | ;; 4clojure Problem 27 - exception in exceptions/4clojure-prob27-ArityException.ser
174 | ; ERROR: Wrong number of arguments (0) passed to a function equal-to?
175 | ; possible hint: make sure that you're passing the correct amount of parameters
176 | (defn equal-to? [thing1 thing2]
177 | (= (first thing1) (first thing2)))
178 |
179 | (defn prob27 [thing]
180 | (loop [thing thing
181 | reverse-thing (reverse thing)]
182 | (if (empty? thing)
183 | true
184 | (if (equal-to?)
185 | (recur (rest thing) (rest reverse-thing))
186 | false))))
187 |
188 | ;; Dr Racket exercise 9 - exception in exceptions/DrRacket-Exercise9-ArityException.ser
189 | ; ERROR: Wrong number of arguments (1) passed to a function fn--6065
190 | ; possible hint: make sure you're arguments to your functions are correct
191 | (defn exercise9 []
192 | (let [b1 true
193 | b2 false]
194 | (or (#(false?) b1)
195 | (#(true?) b2))))
196 |
197 | ;; 4clojure Problem 38 - exception in exceptions/4clojure-prob38-ArityException.ser
198 | ; ERROR: Wrong number of arguments (4) passed to a function prob38
199 | ; possible hint: if you want a function to take an unlimited number of arguments, use an & in the
200 | ; parameter vector like this: [& args]
201 | (defn prob38 [args]
202 | (if (= 1 (count args))
203 | args
204 | (prob38 (if (> (first args) (second args))
205 | (prob38 (cons (first args) (rest (rest args))))
206 | (prob38 (rest args))))))
207 |
208 | (defn error-in-anonymous []
209 | (doall
210 | (map
211 | #(+ % 2)
212 | [2 3 "hi" "bye"])))
213 |
214 | (defn error-in-map-inc []
215 | (doall
216 | (map
217 | inc
218 | [2 3 "hi" "bye"])))
219 | (def not-your-usual-name 5)
220 |
--------------------------------------------------------------------------------
/src/utilities/defn_generator.clj:
--------------------------------------------------------------------------------
1 | (ns utilities.defn_generator
2 | (:require [corefns.assert_handling :refer :all]))
3 |
4 |
5 | ;;the only part of this hash map we still use is :has-type
6 | ;;the other parts used to be useful when I was also doing the re-defining, and may yet be useful
7 | (def type-data {
8 | :arg {:check nil, :has-type "arg", :argument "arg", :arg-vec ["arg"]},
9 | :coll {:check "check-if-seqable?", :has-type "seqable", :argument "coll", :arg-vec ["coll"]},
10 | :n {:check "check-if-number?", :has-type "number", :argument "n", :arg-vec ["n"]},
11 | :colls {:check "check-if-seqables?", :has-type "seqable", :argument "args", :arg-vec ["&" "args"]},
12 | :str {:check "check-if-string?", :has-type "string", :argument "str", :arg-vec ["str"]},
13 | :strs {:check "check-if-strings?", :has-type "string", :argument "strs", :arg-vec ["&" "strs"]},
14 | :f {:check "check-if-function?", :has-type "function", :argument "f", :arg-vec ["f"]},
15 | :fs {:check "check-if-functions?", :has-type "function", :argument "fs", :arg-vec ["&" "fs"]}
16 | :args {:check nil, :has-type "arg", :argument "args", :arg-vec ["&" "args"]}})
17 |
18 | ;;mapping of rich hickey's argument names in doc-strings to a more consistent naming scheme
19 | ;; (def arg-type (merge
20 | ;; (zipmap [:coll :c :c1 :c2 :c3 :c4 :c5] (repeat :coll)),
21 | ;; (zipmap [:n :number :step :start :end :size] (repeat :n)),
22 | ;; (zipmap [:arg :val :argument :x :y] (repeat :arg)),
23 | ;; (zipmap [:f :function :pred] (repeat :f)),
24 | ;; (zipmap [:fs :functions :preds] (repeat :fs)),
25 | ;; (zipmap [:colls :cs] (repeat :colls)),
26 | ;; (zipmap [:string :str :s] (repeat :str)),
27 | ;; (zipmap [:strs :strings :ss] (repeat :strs)),
28 | ;; (zipmap [:more :args :vals :arguments :xs :ys] (repeat :args))))
29 | (def arg-type (merge
30 | (zipmap [:seq] (repeat :seq)), ; added
31 | (zipmap [:map] (repeat :map-or-vector)), ;added
32 | (zipmap [:coll :c :c1 :c2 :c3 :c4 :c5] (repeat :coll)),
33 | (zipmap [:maps] (repeat :maps-or-vectors)), ;added
34 | (zipmap [:n :number :step :start :end :size] (repeat :n)),
35 | (zipmap [:arg :key :val :argument :x :y] (repeat :arg)), ; key is added
36 | (zipmap [:f :function :pred] (repeat :f)),
37 | (zipmap [:fs :functions :preds] (repeat :fs)),
38 | (zipmap [:colls :cs] (repeat :colls)),
39 | (zipmap [:string :str :s] (repeat :str)),
40 | (zipmap [:strs :strings :ss] (repeat :strs)),
41 | (zipmap [:more :args :vals :arguments :xs :ys] (repeat :args))))
42 |
43 | ;; returns the index of the last logically false member of the array
44 | ;; coll -> number
45 | (defn last-false-index [coll]
46 | (loop [remaining coll
47 | i 1
48 | j 0]
49 | (if (empty? remaining) j (recur (rest remaining) (inc i) (if (first remaining) j i)))))
50 |
51 | ;;does the translating from the names rich hickey gave the arguments, to something consistent
52 | ;;list of lists of strings -> list of lists of keys
53 | (defn arglists->argtypes [arglists] (map (fn [x] (map #(arg-type (keyword %)) x)) arglists))
54 |
55 | ;;returns the longest collection in a collection of collections
56 | ;;arglists -> arglist
57 | (defn last-arglist [arglists] (first (reverse (sort-by count arglists))))
58 |
59 | ;;returns trus if an arglists has & ____ as an argument
60 | ;;arglists -> boolean
61 | (defn chompable? [arglists] (or (not-any? #(< 1 (count %)) arglists) (= "&" (name (first (rest (reverse (last-arglist arglists))))))))
62 |
63 | ;;removes the second to last element of the longest coll in a coll of colls
64 | ;;this is the `&` symbol in our case
65 | ;;arglists -> arglists
66 | (defn remove-and [arglists]
67 | (let [sorted-arglists (reverse (sort-by count arglists))
68 | f-arglists (first sorted-arglists)
69 | r-arglists (reverse (rest sorted-arglists))]
70 | (sort-by count (conj r-arglists (into (vec (drop-last 2 f-arglists)) (take-last 1 f-arglists))))))
71 |
72 | ;;returns the last element of the longest coll in a coll of colls
73 | ;;arglists -> keyword
74 | (defn last-arg [arglists] (keyword (str (name (first (reverse (first (reverse (sort-by count arglists)))))))))
75 |
76 | ;;checks the :type-data of each argument, returning true if they are all the same
77 | ;;args -> boolean
78 | (defn same-type? [& args]
79 | (apply = (map #(:has-type (type-data %)) args)))
80 |
81 | ;;helper function for chomp-arglists, appends last-arg to the end of the longest coll in arglists
82 | ;;arglists -> arglists
83 | (defn append-last-arg [arglists last-arg]
84 | (conj (vec (rest (reverse (sort-by count arglists)))) (conj (vec (first (reverse (sort-by count arglists)))) last-arg)))
85 |
86 | ;;removes redundant arguments in arglists
87 | ;;arglists -> arglists
88 | (defn chomp-arglists [arglists]
89 | (let [f-arglists (arglists->argtypes (remove-and arglists))
90 | last-arg (first (reverse (first (reverse (sort-by count f-arglists)))))
91 | ]
92 | (loop [rem-args f-arglists
93 | diffs []]
94 | (cond
95 | (empty? rem-args) (append-last-arg (vec (filter #(<= (count %) (last-false-index diffs)) f-arglists)) last-arg)
96 | :else (recur (drop-while empty? (map rest rem-args)) (into diffs (apply map same-type? (conj rem-args [last-arg]))))))))
97 |
98 | ;;runs chomp-arglist if the & is present, else just translates to our representation
99 | ;;arglists -> arglists
100 | (defn chomp-if-necessary [arglists]
101 | (if (chompable? arglists)
102 | (chomp-arglists arglists)
103 | (arglists->argtypes arglists)))
104 |
105 | ;; outputs a string of generated data for redefining functions with preconditions
106 | ;; function -> string
107 | (defn pre-re-defn [fvar]
108 | ;; (println "pre-re-defn ing: " (:name (meta fvar)))
109 | (let [fmeta (meta fvar)]
110 | (str "(re-defn #'" (:ns fmeta) "/" (:name fmeta) " "
111 | (apply str (vec (interpose " " (map vec (chomp-if-necessary (map #(map name %) (:arglists fmeta))))))) ")")))
112 |
113 |
114 | (defn println-recur [all-vars]
115 | (when
116 | (not (empty? all-vars))
117 | (try
118 | (println (pre-re-defn (first all-vars)))
119 | (catch java.lang.ClassCastException e))
120 | (println-recur (rest all-vars))))
121 | ;; (println-recur (vals (ns-publics 'clojure.core)))
122 |
123 | (defn println-recur-criminals [all-vars]
124 | (when
125 | (not (empty? all-vars))
126 | (try
127 | (pre-re-defn (first all-vars))
128 | (catch java.lang.ClassCastException e
129 | (println (first all-vars))))
130 | (println-recur-criminals (rest all-vars))))
131 | ;; (println-recur-criminals (vals (ns-publics 'clojure.core)))
132 |
133 |
134 | ;;probably depricated since spec, but below is slightly buggy code to automate the re defining of functions
135 | ;;This should be deleted after I am certain it is no longer useful.
136 | ;
137 | ;;;adds argument counts to the hashmap being passed around, this is a helper function
138 | ;;;for the old way of redefining functions
139 | ;;;coll -> coll
140 | ;(defn add-counts [coll]
141 | ; (let [cnt-key (fn [k coll] (count (filter #(= % k) coll)))]
142 | ; (loop [coll-keys (keys coll)
143 | ; coll-vals (vals coll)
144 | ; out {}]
145 | ; (if (empty? coll-keys)
146 | ; out
147 | ; (recur (rest coll-keys) (rest coll-vals)
148 | ; (assoc out (first coll-keys) (assoc (first coll-vals) :cnt-str (if (= 1 (cnt-key (first coll-keys) (keys coll))) "" (cnt-key (first coll-keys) coll-keys)))))))))
149 | ;
150 | ;;; string vector vector -> string
151 | ;(defn gen-checks [unqualified-name data]
152 | ; (reduce #(str %1 (str unqualified-name "(check-if-" (:type %2) "? \"" unqualified-name "\" " (:argument %2) (:cnt-str %2))) "" data))
153 | ;
154 | ;;; string vector -> string
155 | ;(defn single-re-defn [fname fnamespace arg-types only]
156 | ; (let [f (symbol fname)
157 | ; unqualified-name (name f)
158 | ; qualified-name (str fnamespace "/" unqualified-name)
159 | ; arg-data (add-counts (clj->ourtypes arg-types))
160 | ; do-apply (if (not (empty? (first arg-types))) (re-matches #".*s$" (first (reverse arg-types))) nil)
161 | ; arg-str (apply str (interpose " " (map #(str (:arg-str %) (:cnt-str %)) arg-data)))
162 | ; checks (gen-checks fname arg-data)
163 | ; ]
164 | ; ;(println "arg-vec: " arg-vec)
165 | ; (str " " (if only "" "(") "[" arg-str "]"
166 | ; (if (= (count checks) 0) "" (str "\n {:pre [" checks "]}"))
167 | ; "\n" (str " ("(if do-apply "apply " "") qualified-name " [" arg-str "])")
168 | ; (if only "" ")"))))
169 | ;;
170 | ;;; takes a function name, and a vector of vectors of arguments
171 | ;;; note: arguments DO NOT end in a question mark.
172 | ;(defn re-defn [fvar & arglists]
173 | ; (str "(defn " (:name (meta fvar)) "\n \"" (:doc (meta fvar)) "\""
174 | ; (reduce #(str %1 "\n" (single-re-defn (:name (meta fvar)) (:ns (meta fvar)) %2 (= 1 (count arglists)))) "" arglists) ")"))
175 |
176 |
177 |
--------------------------------------------------------------------------------
/test/corefns/corefns_test.clj:
--------------------------------------------------------------------------------
1 | (ns corefns.corefns_test
2 | (:require [expectations :refer :all]
3 | [corefns.corefns :refer :all]
4 | [errors.messageobj :refer :all]
5 | [errors.testing_tools :refer :all]
6 | [corefns.collection_fns :refer :all]
7 | [intro.core :refer :all]))
8 |
9 | ;#############################################
10 | ;### Testing the functionality of corefns ###
11 | ;### These tests check that we didn't mess ###
12 | ;### up core functionality by overwriting ###
13 | ;#############################################
14 |
15 |
16 |
17 |
18 |
19 | ;;; Elena, 6/22/16: Tests below this line should be moved from here
20 |
21 | ;#################################################
22 | ;### Testing if the corefns preconditions work ###
23 | ;#################################################
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/test/errors/dictionaries_test.clj:
--------------------------------------------------------------------------------
1 | (ns errors.dictionaries_test
2 | (:require [errors.dictionaries :refer :all]
3 | [expectations :refer :all]))
4 |
5 | ;#########################################
6 | ;### Tests for supplementary functions ###
7 | ;### in errors.dictionaries ###
8 | ;#########################################
9 |
10 | ;#####################################
11 | ;### Testing for get-function-name ###
12 | ;#####################################
13 |
14 | (expect "days" (get-function-name "happy$days"))
15 | (expect "days" (get-function-name "happy/days"))
16 | (expect "blahBlahBlahBlahNuthin'" (get-function-name "blahBlahBlahBlahNuthin'"))
17 |
18 | ;###############################################
19 | ;### Testing for check-if-anonymous-function ###
20 | ;###############################################
21 |
22 | (expect "anonymous-function" (check-if-anonymous-function "fn"))
23 | (expect "anonymous-function" (check-if-anonymous-function "fn_test"))
24 | (expect "anonymous-function" (check-if-anonymous-function "fn_"))
25 | (expect "random_function" (check-if-anonymous-function "random_function"))
26 |
27 | ;######################################
28 | ;### Testing for location functions ###
29 | ;######################################
30 |
31 | (expect {} (get-compile-error-location ""))
32 |
33 | (expect ["[" "]"] (delimeters [6 7]))
34 | (expect ["{" "}"] (delimeters (assoc {} :a 1)))
35 | (expect ["#{" "}"] (delimeters (set [2 3 2 3])))
36 | (expect ["(" ")"] (delimeters (range)))
37 |
38 | ;;
39 | (expect "2" (preview-arg 2 10 3))
40 | (expect "\"hi\"" (preview-arg "hi" 10 3))
41 | (expect "[2 3]" (preview-arg [2 3] 10 3))
42 | (expect "(2 3)" (preview-arg '(2 3) 10 3))
43 |
44 | (expect '(2 " " 3 "," " " 4 " " 5) (add-commas '(2 " " 3 " " 4 " " 5)))
45 |
46 | (expect '(1 2 3 "," 4 5 6 7 "," 8 9 10 11) (add-commas '(1 2 3 4 5 6 7 8 9 10 11)))
47 |
48 | (expect '(1 2 3 "," 4 5 6 7 "," 8 9 10 11 "," 12 13 14) (add-commas '(1 2 3 4 5 6 7 8 9 10 11 12 13 14)))
49 |
--------------------------------------------------------------------------------
/test/errors/error_dictionary_test.clj:
--------------------------------------------------------------------------------
1 | (ns errors.error_dictionary_test
2 | (:use [errors.prettify_exception :only [line-number-format]])
3 | (:require [expectations :refer :all]
4 | [errors.messageobj :refer :all]
5 | [errors.testing_tools :refer :all]
6 | [errors.prettify_exception :refer :all]
7 | [utilities.file_IO :refer :all]
8 | ))
9 |
10 |
11 |
12 |
13 | ;##################################
14 | ;### Testing for Java Exceptions###
15 | ;##################################
16 |
17 | ;; Elena: this is a compilation error in clojure 1.7, so we can't test it like this
18 | ;; testing for :java.lang.Exception-improper-identifier
19 | ;(expect "You cannot use 7 as a variable."
20 | ; (get-all-text (run-and-catch-pretty-no-stacktrace 'intro.core '(let [x :two 7 :seven]))))
21 |
22 |
23 |
24 |
25 |
26 | ;; :compiler-exception-must-recur-to-function-or-loop
27 |
--------------------------------------------------------------------------------
/test/errors/error_hints_test.clj:
--------------------------------------------------------------------------------
1 | (ns errors.error_hints_test
2 | (:require [expectations :refer :all]
3 | [corefns.corefns :refer :all]
4 | [errors.prettify_exception :refer :all]
5 | [errors.testing_tools :refer :all]
6 | [errors.error_hints :refer :all]))
7 |
8 |
9 | (expect #"(.*)Check the spelling of namespaces you might be using, such as clojure.string\n(.*)"
10 | (:hints (run-and-catch-pretty-with-stacktrace 'intro.core '(clojure/string/splt "pattern" #"/"))))
11 |
12 | (expect "If you are using functions from another file, make sure you use dots for namespaces and slashes for functions, such as clojure.string/split."
13 | (:hints (run-and-catch-pretty-with-stacktrace 'intro.core '(clojure.string.splt "pattern" #"/"))))
14 |
15 | (expect #"(.*)Make sure you have the correct number of arguments(.*)"
16 | (:hints (run-and-catch-pretty-with-stacktrace 'intro.student '(#(+ % 1) 2 3))))
17 |
18 | ;; Currently there are no hints for this, using it as a test for empty hints.
19 | (expect ""
20 | (:hints (run-and-catch-pretty-with-stacktrace 'intro.student '(map 2 3))))
21 |
22 |
--------------------------------------------------------------------------------
/test/errors/prettify_exception_test.clj:
--------------------------------------------------------------------------------
1 | (ns errors.prettify_exception_test
2 | (:require [expectations :refer :all]
3 | [errors.messageobj :refer :all]
4 | [errors.testing_tools :refer :all]
5 | [errors.errorgui :refer :all]
6 | [clj-stacktrace.core :as stacktrace]
7 | [errors.dictionaries :refer :all]
8 | [utilities.file_IO :refer :all]
9 | [errors.prettify_exception :refer :all]))
10 |
11 | ;;; INDEX ;;;
12 |
13 | ;;1. Functions
14 | ;;2. Prebuilt Exceptions
15 | ;;3. errors.messageobj
16 | ;;4. errors.exceptionobj
17 | ;;5. errors.core, errorgui tests
18 | ;;6. Testing basics of dictionaries.clj
19 |
20 | ;####################
21 | ;### 1. Functions ###
22 | ;####################
23 |
24 | (defn ignore-stacktrace [trace1 trace2] true)
25 |
26 | ;###############################
27 | ;### 2. Prebuilt Exceptions ####
28 | ;###############################
29 |
30 | (def function-call-on-value (run-and-catch-raw '(4 + "pie")))
31 |
32 | (def class-cast-exception (run-and-catch-raw '(+ 4 "pie")))
33 |
34 | ;##################################
35 | ;### 3. errors.messageobj tests ###
36 | ;##################################
37 |
38 | ;***Testing make-msg-info-hashes***
39 |
40 | (expect (make-msg-info-hashes) [])
41 |
42 | ;***Testing make-msg-info-hash***
43 |
44 | (expect (make-msg-info-hash "Hi there")
45 | {:msg "Hi there" :stylekey :reg :length 8})
46 |
47 | (expect (make-msg-info-hash "Hi there" :arg)
48 | {:msg "Hi there" :stylekey :arg :length 8})
49 |
50 | (expect (make-msg-info-hash "Blue Jeans")
51 | {:msg "Blue Jeans", :stylekey :reg, :length 10})
52 |
53 | (expect (make-msg-info-hash "Bootcut" :hippy)
54 | {:msg "Bootcut", :stylekey :hippy, :length 7})
55 |
56 | ;***Testing make-msg-info-hashes***
57 |
58 | (expect (make-msg-info-hashes "Hi there")
59 | [{:msg "Hi there" :stylekey :reg :length 8}])
60 |
61 | (expect (make-msg-info-hashes "Hi there" :arg)
62 | [{:msg "Hi there" :stylekey :arg :length 8}])
63 |
64 | (expect (make-msg-info-hashes "Hi there" "Hello")
65 | [{:msg "Hi there" :stylekey :reg :length 8}
66 | {:msg "Hello" :stylekey :reg :length 5}])
67 |
68 | (expect (make-msg-info-hashes "Hi there" :arg "Hello" :blah)
69 | [{:msg "Hi there" :stylekey :arg :length 8}
70 | {:msg "Hello" :stylekey :blah :length 5}])
71 |
72 | (expect (make-msg-info-hashes "Blue Jeans"
73 | "Khakis" :business-casual
74 | "Bootcut" :hippy
75 | "Jeggings" :casual)
76 |
77 | [{:msg "Blue Jeans", :stylekey :reg, :length 10}
78 | {:msg "Khakis", :stylekey :business-casual, :length 6}
79 | {:msg "Bootcut", :stylekey :hippy, :length 7}
80 | {:msg "Jeggings", :stylekey :casual, :length 8}])
81 |
82 | ;***Testing make-display-msg***
83 |
84 | (expect (make-display-msg (make-msg-info-hashes "Hi there" :arg "Hello"))
85 | [{:msg "Hi there" :stylekey :arg :length 8 :start 0}
86 | {:msg "Hello" :stylekey :reg :length 5 :start 8}])
87 |
88 | (expect (make-display-msg [{:msg "Blue Jeans", :stylekey :reg, :length 10}
89 | {:msg "Khakis", :stylekey :business-casual, :length 6}
90 | {:msg "Bootcut", :stylekey :hippy, :length 7}
91 | {:msg "Jeggings", :stylekey :casual, :length 8}])
92 |
93 | [{:start 0, :msg "Blue Jeans", :stylekey :reg, :length 10}
94 | {:start 10, :msg "Khakis", :stylekey :business-casual, :length 6}
95 | {:start 16, :msg "Bootcut", :stylekey :hippy, :length 7}
96 | {:start 23, :msg "Jeggings", :stylekey :casual, :length 8}])
97 |
98 | ;***Testing get-all-text***
99 |
100 | (expect (get-all-text (make-display-msg (make-msg-info-hashes "Hi there! " :arg "Hello")))
101 | "Hi there! Hello")
102 |
103 | ;***Testing make-mock-preobj***
104 |
105 | (expect (make-mock-preobj ["anything" "will be ignored"])
106 | [{:msg "This is a " :stylekey :reg :length 10}
107 | {:msg "test." :stylekey :arg :length 5}])
108 |
109 |
110 | ;######################################
111 | ;### 5. errors.core, errorgui tests ###
112 | ;######################################
113 |
114 | ;; get a pre-stored exception
115 | (def classcast-exc (import-from-file "exceptions/classcast1.ser"))
116 | (def classcast-exc-parsed (stacktrace/parse-exception classcast-exc))
117 |
118 | (expect java.lang.ClassCastException (class classcast-exc))
119 |
120 | (expect java.lang.ClassCastException (:class classcast-exc-parsed))
121 |
122 | (expect "java.lang.String cannot be cast to java.lang.Number" (:message classcast-exc-parsed))
123 |
124 | (expect 61 (count (:trace-elems classcast-exc-parsed)))
125 |
126 | (expect {:method "add", :class "clojure.lang.Numbers", :java true, :file "Numbers.java", :line 126}
127 | (first (:trace-elems classcast-exc-parsed)))
128 |
129 | (def the-trace
130 | (list {:method "add", :class "clojure.lang.Numbers", :java true, :file "Numbers.java", :line 126}
131 | {:method "add", :class "clojure.lang.Numbers", :java true, :file "Numbers.java", :line 3523}
132 | {:anon-fn false, :fn "eval9481", :ns "experimental.core-test", :clojure true,
133 | :file "core_test.clj", :line 57}
134 | {:method "eval", :class "clojure.lang.Compiler"
135 | , :java true, :file "Compiler.java", :line 6619}
136 | {:method "eval", :class "clojure.lang.Compiler", :java true, :file "Compiler.java", :line 6582}
137 | {:anon-fn false, :fn "eval", :ns "clojure.core", :clojure true, :file "core.clj", :line 2852}
138 | {:anon-fn false, :fn "run-and-catch", :ns "experimental.core-test", :clojure true, :file "core_test.clj", :line 38}
139 | {:method "applyToHelper", :class "clojure.lang.AFn", :java true, :file "AFn.java", :line 161}
140 | {:method "applyTo", :class "clojure.lang.AFn", :java true, :file "AFn.java", :line 151}
141 | {:method "eval", :class
142 | "clojure.lang.Compiler$InvokeExpr", :java true, :file "Compiler.java", :line 3458}
143 | {:method "eval", :class "clojure.lang.Compiler$DefExpr", :java true, :file "Compiler.java", :line 408}
144 | {:method "eval", :class "clojure.lang.Compiler", :java true, :file "Compiler.java", :line 6624}
145 | {:method "load", :class "clojure.lang.Compiler", :java true, :file "Compiler.java", :line 7064}
146 | {:method "loadResourceScript", :class "clojure.lang.RT", :java true, :file "RT.java", :line 370}
147 | {:method "loadResourceScript", :class "clojure.lang.RT", :java true, :file "RT.java", :line 361}
148 | {:method "load", :class "clojure.lang.RT", :java true, :file "RT.java", :line 440}
149 | {:method "load", :class "clojure.lang.RT", :java true, :file "RT.java", :line 411}
150 | {:anon-fn true, :fn "load", :ns "clojure.core", :clojure true, :file "core.clj", :line 5530}
151 | {:anon-fn false, :fn "load", :ns "clojure.core",
152 | :clojure true, :file "core.clj", :line 5529}
153 | {:method "invoke", :class "clojure.lang.RestFn", :java true, :file "RestFn.java", :line 408}
154 | {:anon-fn false, :fn "load-one", :ns "clojure.core", :clojure true, :file "core.clj", :line 5336}
155 | {:anon-fn true, :fn "load-lib", :ns "clojure.core", :clojure true, :file "core.clj"
156 | , :line 5375}
157 | {:anon-fn false, :fn "load-lib", :ns "clojure.core", :clojure true, :file "core.clj", :line 5374}
158 | {:method "applyTo", :class "clojure.lang.RestFn"
159 | , :java true, :file "RestFn.java", :line 142}
160 | {:anon-fn false, :fn "apply", :ns "clojure.core", :clojure true, :file "core.clj", :line 619}
161 | {:anon-fn false, :fn "load-libs", :ns "clojure.core", :clojure true, :file "core.clj", :line 5413}
162 | {:method "applyTo", :class "clojure.lang.RestFn", :java true, :file "RestFn.java"
163 | , :line 137}
164 | {:anon-fn false, :fn "apply", :ns "clojure.core", :clojure true, :file "core.clj", :line 619}
165 | {:anon-fn false, :fn "require", :ns "clojure.core", :clojure true, :file "core.clj", :line 5496}
166 | {:method "invoke", :class "clojure.lang.RestFn", :java true, :file "RestFn.java", :line 421}
167 | {:anon-fn false, :fn "track-reload-one", :ns "clojure.tools.namespace.reload", :clojure true, :file "reload.clj", :line 35}
168 | {:anon-fn false, :fn "track-reload", :ns "clojure.tools.namespace.reload", :clojure true, :file "reload.clj", :line 52}
169 | {:method "applyToHelper", :class "clojure.lang.AFn", :java true, :file "AFn.java", :line 161}
170 | {:method "applyTo", :class "clojure.lang.AFn", :java true, :file "AFn.java", :line 151}
171 | {:method "alterRoot", :class "clojure.lang.Var", :java true, :file "Var.java"
172 | , :line 336}
173 | {:anon-fn false, :fn "alter-var-root", :ns "clojure.core", :clojure
174 | true, :file "core.clj", :line 4946}
175 | {:method "invoke", :class "clojure.lang.RestFn", :java true, :file "RestFn.java", :line 425}
176 | {:anon-fn false, :fn "do-refresh", :ns "clojure.tools.namespace.repl", :clojure true, :file "repl.clj", :line 94}
177 | {:anon-fn false, :fn "refresh", :ns "clojure.tools.namespace.repl", :clojure
178 | true, :file "repl.clj", :line 142}
179 | {:method "invoke", :class "clojure.lang.RestFn", :java true, :file "RestFn.java", :line 397}
180 | {:anon-fn false, :fn "refresh-environment", :ns "autoexpect.runner", :clojure true, :file "runner.clj", :line 23}
181 | {:anon-fn true, :fn "run-tests", :ns "autoexpect.runner", :clojure true, :file "runner.clj", :line 50}
182 | {:anon-fn false, :fn "run-tests", :ns "autoexpect.runner", :clojure true, :file "runner.clj", :line 50}
183 | {:anon-fn true, :fn "monitor-project", :ns "autoexpect.runner", :clojure true, :file "runner.clj", :line 69}
184 | {:anon-fn true, :fn "monitor-project", :ns "autoexpect.runner", :clojure true, :file "runner.clj", :line 68}
185 | {:anon-fn false, :fn "monitor-project", :ns "autoexpect.runner", :clojure true, :file "runner.clj", :line 66}
186 | {:anon-fn false, :fn "eval1187", :ns "user", :clojure true, :file "form-init6834699387848419871.clj",
187 | :line 1}
188 | {:method "eval", :class "clojure.lang.Compiler", :java true, :file "Compiler.java", :line 6619}
189 | {:method "eval", :class "clojure.lang.Compiler", :java true, :file "Compiler.java", :line 6609}
190 | {:method "load", :class "clojure.lang.Compiler", :java true, :file "Compiler.java", :line 7064}
191 | {:method "loadFile", :class "clojure.lang.Compiler", :java true, :file "Compiler.java", :line 7020}
192 | {:anon-fn false, :fn "load-script", :ns "clojure.main", :clojure true, :file "main.clj", :line 294}
193 | {:anon-fn false, :fn "init-opt", :ns "clojure.main", :clojure true, :file "main.clj", :line 299}
194 | {:anon-fn false, :fn "initialize", :ns "clojure.main", :clojure true, :file "main.clj", :line 327}
195 | {:anon-fn false, :fn "null-opt", :ns "clojure.main", :clojure true, :file "main.clj", :line 362}
196 | {:anon-fn false, :fn "main", :ns "clojure.main", :clojure true, :file "main.clj", :line 440}
197 | {:method "invoke", :class "clojure.lang.RestFn", :java true, :file "RestFn.java", :line 421}
198 | {:method "invoke", :class "clojure.lang.Var", :java true, :file "Var.java", :line 419}
199 | {:method "applyToHelper", :class "clojure.lang.AFn", :java true, :file "AFn.java", :line 163}
200 | {:method "applyTo", :class "clojure.lang.Var", :java true, :file "Var.java", :line 532}
201 | {:method "main", :class "clojure.main", :java true, :file "main.java", :line 37}))
202 |
203 | (expect the-trace (:trace-elems classcast-exc-parsed))
204 |
205 | ;*** Testing trace->string ****
206 |
207 | ;; copied the matching string for easier testing:
208 | ;(#(str "\t" (:ns %) "/" (:fn %) " (" (:file %) " line " (:line %) ")")
209 | (expect "\tautoexpect.runner/run-tests (runner.clj line 50)"
210 | (trace-elem->string {:anon-fn true, :fn "run-tests", :ns "autoexpect.runner", :clojure true, :file "runner.clj", :line 50} "\t"))
211 |
212 | ;#############################################
213 | ;### 6. Testing in dictionaries.clj ###
214 | ;#############################################
215 |
216 | ;; type-dictionary tests
217 | (expect "a number" (type-dictionary :java.lang.Float))
218 | (expect "a number" (:java.lang.Float type-dictionary))
219 | (expect "a regular expression matcher" (type-dictionary :java.util.regex.Matcher))
220 |
221 | ;; really simple testing for general-types
222 | (expect "a vector" (second (general-types 1)))
223 |
224 | ;; testing for best-approximation
225 | (expect "unrecognized type atom" (best-approximation 'atom))
226 | (expect "a map" (best-approximation 'clojure.lang.IPersistentMap))
227 | (expect "a collection" (best-approximation 'IPersistentStack)) \
228 | ; this checks that the function best-approximation can add in
229 | ; "clojure.lang." by itself
230 |
231 | ;; testing for get-type
232 | (expect "a boolean" (get-type 'java.lang.Boolean))
233 | (expect "unrecognized type bogus_thingy" (get-type 'bogus_thingy))
234 |
235 | ;; testing for predefined-names
236 | (expect "+" (predefined-names :_PLUS_))
237 | (expect "/" (:_SLASH_ predefined-names))
238 | (expect "-" (predefined-names :_))
239 | (expect nil (:_UNKNOWN_ predefined-names))
240 |
241 | ;; testing for lookup-funct-name
242 | (expect "/" (lookup-funct-name '_SLASH_))
243 | (expect "*" (lookup-funct-name "_STAR_"))
244 | (expect "testingRocks" (lookup-funct-name 'testingRocks))
245 |
246 | ;; testing for get-function-name
247 | (expect "*" (get-function-name "clojure.core$_STAR_"))
248 | (expect "even?" (get-function-name "clojure.core$even_QMARK_"))
249 | (expect "anonymous-function" (get-function-name "intro.core$eval6393$fn__6394"))
250 | (expect "-" (get-function-name "clojure.core$_"))
251 | (expect ">=" (get-function-name "corefns.corefns$_GT__EQ_"))
252 | (expect "swap!" (get-function-name "clojure.core$swap_BANG_"))
253 | (expect "anonymous-function" (get-function-name "intro.core$eval6397$fn__6398"))
254 | (expect "/" (get-function-name "clojure.core$_SLASH_"))
255 |
256 | ;; testing for get-macro-name
257 | (expect "regex" (get-macro-name "clojure.string/regex"))
258 | (expect "->>" (get-macro-name "clojure.core/->>"))
259 | (expect "lazy-cat" (get-macro-name "clojure.core/lazy-cat"))
260 | (expect "cond" (get-macro-name "clojure.core/cond"))
261 | (expect "and" (get-macro-name "clojure.core/and"))
262 |
263 | ;; testing for arg-str
264 | (expect "first argument" (arg-str 1))
265 | (expect "21st argument" (arg-str 21))
266 | (expect "210528th argument" (arg-str 210528))
267 |
268 | ;; testing for lookup-arity
269 | (expect "at least two" (lookup-arity "map"))
270 | (expect "one" (lookup-arity "count"))
271 | (expect nil (lookup-arity "concat"))
272 | (expect "two or three" (lookup-arity "reduce"))
273 | (expect nil (lookup-arity "anonymous function"))
274 | (expect nil (lookup-arity "not a function"))
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
--------------------------------------------------------------------------------
/test/errors/testing_tools.clj:
--------------------------------------------------------------------------------
1 | (ns errors.testing_tools
2 | (:require [expectations :refer :all]
3 | [clj-stacktrace.core :as stacktrace]
4 | [errors.prettify_exception :refer :all]
5 | [errors.messageobj :refer :all]
6 | [utilities.stacktrace_functions :refer :all]
7 | [corefns.collection_fns :refer :all]
8 | [utilities.file_IO :refer :all])
9 | (:import [java.io.FileInputStream]
10 | [java.io.ObjectInputStream]
11 | [java.io.FileOutputStream]
12 | [java.io.ObjectOutputStream]
13 | [java.util.ArrayList]))
14 |
15 |
16 | ; Add files to this set when you make them.
17 | (def saved-exceptions
18 | #{"4clojure-prob134-AssertionError.ser"
19 | "4clojure-prob15-ClassCast.ser"
20 | "4clojure-prob156-AssertionError.ser"
21 | "4clojure-prob16-Arity.ser"
22 | "4clojure-prob17-NullPointer.ser"
23 | "4clojure-prob18-AssertionError.ser"
24 | "4clojure-prob20-AssertionError.ser"
25 | "4clojure-prob21-ArityException.ser"
26 | "4clojure-prob24-pre-plus-rewrite-ClassCast.ser"
27 | "4clojure-prob57-ArityException.ser"
28 | "4clojure-prob64-NullPointer.ser"
29 | "add-five-IllegalArgException.ser"
30 | "DrRacket-Exercise2-ClassCast.ser"
31 | "4clojure-prob23-IndexOutOfBounds.ser"
32 | "4clojure-prob27-ArityException.ser"
33 | "DrRacket-Exercise9-ArityException.ser"
34 | "4clojure-prob38-ArityException.ser"
35 | "DrRacket-Exercise3-IndexOutOfBounds.ser"})
36 |
37 | ;;; INDEX ;;;
38 |
39 | ;1 Generating exceptions
40 | ;|-1.1 functions
41 | ;|-1.2 tests
42 | ;2. Comparing Stacktraces
43 | ;|-2.1 functions
44 | ;|-2.2 tests
45 | ;3. Testing for hints
46 | ;4. More Real-Life Exceptions
47 | ;5. Comparing top elements of stacktraces
48 | ;|-5.1 functions
49 | ;|-5.2 prints
50 | ;|-5.3 tests
51 |
52 | ;##############################
53 | ;## 1. Generating Exceptions ##
54 | ;##############################
55 |
56 | ; 1.1 functions
57 |
58 | (defn run-and-catch-raw
59 | "A function that takes quoted code and runs it, attempting to catch any
60 | exceptions it may throw. Returns the exeception or nil. If a quoted namespace is
61 | given, it runs the code in that namespace.Attempted to use a symbol, but unrecognized type clojure.lang.Namespace was expected."
62 | ([code]
63 | (try
64 | (eval code)
65 | (catch Throwable e e)))
66 | ([name-space code]
67 | (try
68 | (let [our-ns (find-ns name-space)]
69 | ;(print (filter (fn [x] (re-matches #".*intro.*" (str x))) (all-ns)))
70 | ;(print (str "printing namespace: " (nil? our-ns)))
71 | (binding [*ns* our-ns] (eval code)))
72 | (catch Throwable e e))))
73 |
74 | (defn run-and-catch-pretty-no-stacktrace
75 | "A function that takes quoted code and runs it, attempting to catch any
76 | exceptions it may throw. Returns the prettified exception without a stacktrace. If a quoted
77 | namespace is given, it runs the code in that namespace."
78 | ([code]
79 | ;(println (str "MSG: " (count (:msg-info-obj (prettify-exception (run-and-catch-raw code))))))
80 | (:msg-info-obj (prettify-exception (run-and-catch-raw code))))
81 | ([name-space code]
82 | ;(println (str "MSG: " (count (:msg-info-obj (prettify-exception (run-and-catch-raw name-space code)))))
83 | (:msg-info-obj (prettify-exception (run-and-catch-raw name-space code)))))
84 |
85 | ;; when giving a namespace, pass in a quoted namespace
86 | (defn run-and-catch-pretty-with-stacktrace
87 | "A function that takes quoted code and runs it, attempting to catch any
88 | exceptions it may throw. Returns the prettified exception with a stacktrace. If a quoted namespace is given,
89 | it runs the code in that namespace."
90 | ([code]
91 | (prettify-exception (run-and-catch-raw code)))
92 | ([name-space code]
93 | (prettify-exception (run-and-catch-raw name-space code))))
94 |
95 | (defn exception->string
96 | "Converts exceptions to strings, returning a string or the original e if it
97 | is not an exception"
98 | [e] (if (instance? Throwable e) (.getMessage e) e))
99 |
100 | (defn get-text-no-location [m]
101 | ;(println (str "MESSAGE" (get-all-text m)))
102 | (let [text-no-newln (nth (re-matches #"(.*)\nFound(.*)" (get-all-text m)) 1)
103 | text (if text-no-newln
104 | text-no-newln
105 | (let [matches (re-matches #"(.*)\n(.*)\nFound(.*)" (get-all-text m))]
106 | (str (nth matches 1) "\n" (nth matches 2))))]
107 | text))
108 |
109 |
110 | ; 1.2 tests
111 |
112 | (expect "java.lang.Long cannot be cast to clojure.lang.IFn"
113 | (exception->string (run-and-catch-raw '(1 3))))
114 |
115 | ;; Hmmm??? - there's no exception here, is there?
116 | (expect 3
117 | (exception->string (run-and-catch-raw '(+ 1 2))))
118 |
119 | ;##############################
120 | ;## 2. Comparing Stacktraces ##
121 | ;##############################
122 |
123 | ; 2.1 functions
124 |
125 | (defn get-keyword-in-stacktrace
126 | "Gets all of the keywords mentioned in a parsed stacktrace"
127 | [a-keyword trace]
128 | (filter
129 | (fn [ele]
130 | (not (nil? ele)))
131 | (map a-keyword (:trace-elems trace))))
132 |
133 | (defn- get-keyword-but-not-x-in-stacktrace
134 | "Don't actually use this - wrap it in a helper function. Gets all the values of a keyword mentioned in a
135 | parsed stacktrace, except those that the predicate returns true for"
136 | [a-keyword pred trace]
137 | (filter
138 | (fn [ele]
139 | (not (or (nil? ele)
140 | (pred ele))))
141 | (map a-keyword (:trace-elems trace))))
142 |
143 | ;; an example of what get-keyword-but-not-x-in-stacktrace
144 | (defn get-fns-in-stacktrace
145 | "Gets all of the functions mentioned in a parsed stacktrace"
146 | [trace]
147 | (get-keyword-but-not-x-in-stacktrace
148 | :fn
149 | (fn [ele] (not (clojure.string/blank? (re-matches #"eval\d.*" ele))))
150 | trace))
151 |
152 |
153 | (defn get-eval-nums
154 | "Gets all evaulation numbers - a random number (confirm?) that is attached to all evals in the stacktrace. Used
155 | to confirm that two stacktraces came from the same exception."
156 | [trace]
157 | (map (fn [ele] ele)
158 | (get-keyword-in-stacktrace :fn trace)))
159 |
160 | ; 2.2 tests
161 |
162 | (def ex1 (run-and-catch-raw '(+ 2 "pie")))
163 |
164 | (expect "eval" (first (get-fns-in-stacktrace (stacktrace/parse-exception ex1))))
165 |
166 | ;##################################
167 | ;## 3. Testing for hints ##
168 | ;##################################
169 |
170 | (expect :class-cast-exception (:key (first-match ClassCastException
171 | "java.lang.String cannot be cast to clojure.core.Number")))
172 |
173 | ;; because this is now an assertion error, no hint is actually written yet
174 | ;(expect #"The error happens when a function's argument is not of the type for which the function is defined."
175 | ; (:hints (prettify-exception (run-and-catch-raw '(+ 2 "string")))))
176 |
177 | ;; Elena commented out testing for hints since we currently don't have hints
178 |
179 | ;(expect #"Make sure you have the correct number of arguments"
180 | ; (:hints (prettify-exception (run-and-catch-raw '(assoc {1 2} 3)))))
181 |
182 | (expect :assertion-error-with-argument (:key (first-match AssertionError "Assert failed: (check-if-sequable? \"filter\" argument2)")))
183 |
184 | ;(expect "" (:hints (prettify-exception (run-and-catch-raw 'intro.core '(filter odd? 5)))))
185 |
186 |
--------------------------------------------------------------------------------
/test/exception_msgs/overwritten_fns/higher_order_fns.clj:
--------------------------------------------------------------------------------
1 | (ns exception_msgs.overwritten_fns.higher_order_fns
2 | (:use [errors.prettify_exception :only [line-number-format prettify-exception]])
3 | (:require [expectations :refer :all]
4 | [errors.messageobj :refer :all]
5 | [errors.testing_tools :refer :all]
6 | [utilities.file_IO :refer :all]
7 | ))
8 |
9 | ;; testing for :class-cast-exception-cannot-cast-to-map-entry
10 | (expect "Attempted to create a map using a keyword, but a sequence of vectors of length 2 or a sequence of maps is needed."
11 | (get-text-no-location
12 | (run-and-catch-pretty-no-stacktrace 'intro.core
13 | '(into {} [#{:x :m} #{:q :b}]))))
14 |
15 |
16 | ;; Note: the order of elements in a hashmap is not guaranteed, so the actual elements amy be different
17 | (expect "In function +, the argument {:a 6, :k \"a\", :b 4, :c +, :l 6, :m 7} must be a number but is a map,\nin the function call (+ {:a 6, :k \"a\", :b 4, :c +, :l 6, :m 7})"
18 | (get-text-no-location
19 | (run-and-catch-pretty-no-stacktrace 'intro.student '(+ {:a 6 :k "a" :b 4 :c + :l 6 :m 7}))))
20 |
21 | ;; testing for :illegal-argument-no-val-supplied-for-key
22 | (expect "No value found for key d. Every key for a hash-map must be followed by a value."
23 | (get-text-no-location
24 | (run-and-catch-pretty-no-stacktrace 'intro.student '(hash-map "c" :d "d"))))
25 |
26 | ;; testing for :illegal-argument-vector-arg-to-map-conj
27 | (expect "Vectors added to a map must consist of two elements: a key and a value."
28 | (get-text-no-location
29 | (run-and-catch-pretty-no-stacktrace 'intro.student '(into {} [[1 2] [3]]))))
30 |
31 | ;; testing for invalid token error
32 | (expect #"You cannot use : in this position."
33 | (get-text-no-location (:msg-info-obj (try (load-file "exceptions/compilation_errors/invalid_token_error2.clj")
34 | (catch Throwable e (prettify-exception e))))))
35 |
36 | (expect "Vectors added to a map must consist of two elements: a key and a value."
37 | (get-text-no-location
38 | (run-and-catch-pretty-no-stacktrace 'intro.student '(merge {:a 1 :b 2} [1 2 3] [1 2] [1 5]))))
39 |
40 | (expect "Vectors added to a map must consist of two elements: a key and a value."
41 | (get-text-no-location
42 | (run-and-catch-pretty-no-stacktrace 'intro.student '(conj {:a 1 :b 2} [1 2 3 4]))))
43 |
44 | ;; testing for :illegal-argument-even-number-of-forms
45 | (expect #"Parameters for let must come in pairs, but one of them does not have a match; (.*)"
46 | (get-text-no-location (:msg-info-obj (try (load-file "exceptions/compilation_errors/let-no-matching-pair.clj")
47 | (catch Throwable e (prettify-exception e))))))
48 |
49 | (expect #"When declaring a let, you need to pass it a vector of arguments.(.*)"
50 | (get-text-no-location (:msg-info-obj (try (load-file "exceptions/compilation_errors/let-odd-number-bindings.clj")
51 | (catch Throwable e (prettify-exception e))))))
52 |
53 | ;; testing for # must be followed by a symbol error
54 | (expect #"# must be followed by a symbol."
55 | (get-text-no-location (:msg-info-obj (try (load-file "exceptions/compilation_errors/#_must_be_followed_by_symbol.clj")
56 | (catch Throwable e (prettify-exception e))))))
57 |
58 | ;; testing for wrong number of args to a keyword
59 | (expect #"A keyword: :a can only take one or two arguments."
60 | (get-text-no-location (:msg-info-obj (try (load-file "exceptions/compilation_errors/keyword_wrong_number_of_args.clj")
61 | (catch Throwable e (prettify-exception e))))))
62 |
63 | (expect #"A keyword: :a can only take one or two arguments."
64 | (get-text-no-location (:msg-info-obj (try (load-file "exceptions/compilation_errors/keyword_wrong_number_of_args2.clj")
65 | (catch Throwable e (prettify-exception e))))))
66 |
67 | ;; testing for :illegal-argument-parameters-must-be-in-vector
68 | (expect #"Parameters for defn must be a vector, but my-argument was found instead\.(.*)"
69 | (get-all-text
70 | (run-and-catch-pretty-no-stacktrace 'intro.student '(defn my-function my-argument))))
71 |
72 | (expect #"Parameters for defn must be a vector, but 5 was found instead\.(.*)"
73 | (get-all-text
74 | (run-and-catch-pretty-no-stacktrace 'intro.student '(defn my-function 5))))
75 |
76 | (expect #"Parameters for defn must be a vector, but \+ was found instead\.(.*)"
77 | (get-all-text
78 | (run-and-catch-pretty-no-stacktrace 'intro.student '(defn my-function (+ x y)))))
79 |
80 | (expect #"Parameters for defn must be a vector, but \+ was found instead\.(.*)"
81 | (get-all-text
82 | (run-and-catch-pretty-no-stacktrace 'intro.student '(defn my-function + x y))))
83 |
84 | ;; testing for :illegal-argument-exactly-2-forms
85 | (expect #"The function when-let requires exactly 2 forms in binding vector. Line (.*) in the file intro.core"
86 | (get-all-text (run-and-catch-pretty-no-stacktrace 'intro.core '(when-let [num1 1 num2 2] "hello"))))
87 |
88 | (expect "In function dissoc, the argument :s must be a hashmap but is a keyword,\nin the function call (dissoc :s)"
89 | (get-text-no-location
90 | (run-and-catch-pretty-no-stacktrace 'intro.student '(dissoc :s))))
91 |
92 | (expect "% must be either on its own or followed by a number or &."
93 | (get-text-no-location (:msg-info-obj (try (load-file "exceptions/compilation_errors/percent_followed_by_letter.clj")
94 | (catch Throwable e (prettify-exception e))))))
95 |
96 | ;; testing for :index-out-of-bounds-index-not-provided
97 | (expect "An index in a sequence is out of bounds or invalid."
98 | (get-text-no-location
99 | (run-and-catch-pretty-no-stacktrace 'intro.core '(nth [0 1 2 3 4 5] 10))))
100 |
101 | ;; testing for :arity-exception-wrong-number-of-arguments
102 | (expect "You cannot pass three arguments to a function even?, need one,\nin the function call (even? 3 6 1)"
103 | (get-text-no-location (run-and-catch-pretty-no-stacktrace 'intro.core '(even? 3 6 1))))
104 |
105 | (expect "You cannot pass zero arguments to a function even?, need one,\nin the function call (even? )"
106 | (get-text-no-location (run-and-catch-pretty-no-stacktrace 'intro.core '(even?))))
107 |
108 | (expect "You cannot pass one argument to this anonymous function."
109 | (get-text-no-location (run-and-catch-pretty-no-stacktrace 'intro.core '(#(+ %1 %2) 1))))
110 |
111 | (expect "You cannot pass four arguments to a function user-def-fcn."
112 | (get-text-no-location (run-and-catch-pretty-no-stacktrace 'intro.core '((defn user-def-fcn [x] (+ x 1))
113 | (user-def-fcn 1 2 3 4)))))
114 |
115 | (expect "You cannot use the same key in a hash map twice, but you have duplicated the key :b."
116 | ;; note: let is needed in the test since the error must happen only at run time
117 | (get-text-no-location (run-and-catch-pretty-no-stacktrace 'intro.core '(let [a :b b :b] {a 1 b 2}))))
118 |
119 | ;; testing for :unsupported-operation-wrong-type-of-argument
120 | (expect "Function nth does not allow a map as an argument."
121 | (get-text-no-location (run-and-catch-pretty-no-stacktrace 'intro.core '(nth {:a 10 :z 4} 20))))
122 |
123 | ;;;; Elena: we might want to revisit this one
124 | (expect #"Parameters for loop must come in pairs, but one of them does not have a match; on line (\d+) in the file intro\.core"
125 | (get-text-no-location (run-and-catch-pretty-no-stacktrace 'intro.core '(defn s [s] (loop [s])))))
126 |
127 | (expect "This recur is supposed to take zero arguments, but you are passing one." ; this is giving NO_SOURCE_PATH
128 | (get-text-no-location (run-and-catch-pretty-no-stacktrace 'intro.core '(recur (inc 1)))))
129 |
130 | (expect "This recur is supposed to take one argument, but you are passing two." ;
131 | (get-text-no-location (run-and-catch-pretty-no-stacktrace 'intro.core '(loop [x 1] (recur (inc x) (dec x))))))
132 |
133 | (expect "Parameters for cond must come in pairs, but one of them does not have a match." ; this is giving NO_SOURCE_PATH
134 | (get-text-no-location
135 | (run-and-catch-pretty-no-stacktrace 'intro.core '(defn my-num [x] (cond (= 1 x))))))
136 |
137 | (expect #"loop is a macro, cannot be passed to a function." ; this is giving NO_SOURCE_PATH
138 | (get-text-no-location (run-and-catch-pretty-no-stacktrace 'intro.core '(defn my-happy [x] loop [x x]))))
139 |
140 | (expect #"Name banana is undefined." ; this is giving NO_SOURCE_PATH
141 | (get-text-no-location (run-and-catch-pretty-no-stacktrace 'intro.core '(banana 5 6))))
142 |
143 | (expect (str "There is an unmatched delimiter ).\nFound in file unmatched_delimiter.clj" (line-number-format 3 20) ".")
144 | (get-all-text (:msg-info-obj (prettify-exception (import-from-file "exceptions/unmatched_delimiter.ser")))))
145 |
146 | (expect "Too many arguments to def." ; this is giving NO_SOURCE_PATH
147 | (get-text-no-location (run-and-catch-pretty-no-stacktrace 'intro.core '(def my-var 5 6))))
148 |
149 | (expect "Too few arguments to def." ; this is giving NO_SOURCE_PATH
150 | (get-text-no-location (run-and-catch-pretty-no-stacktrace 'intro.core '(def))))
151 |
--------------------------------------------------------------------------------
/test/exception_msgs/overwritten_fns/num_fns.clj:
--------------------------------------------------------------------------------
1 | (ns exception_msgs.overwritten_fns.num_fns
2 | (:use [errors.prettify_exception :only [prettify-exception]])
3 | (:require [expectations :refer :all]
4 | [errors.messageobj :refer :all]
5 | [errors.testing_tools :refer :all]
6 | [utilities.file_IO :refer :all]
7 | ))
8 |
9 |
10 | ;; Since spec evaluates the arguments, the tests below are no longer passing
11 |
12 | ;; testing for :pretty-print-value
13 | ;(expect "In function +, the second argument ((0 1 2 3...) (0 1 2 3...) (0 1 2 3...) (0 1 2 3...) (0 1 2 3...) (0 1 2 3...) (0 1 2 3...) (0 1 2 3...) (0 1 2 3...) (0 1 2 3...)...) must be a number but is a sequence."
14 | ; (get-text-no-location
15 | ; (run-and-catch-pretty-no-stacktrace 'intro.student '(+ 1 (repeat (range))))))
16 |
17 | ;(expect "In function +, the second argument (((...) (...) (...) (...)...) ((...) (...) (...) (...)...) ((...) (...) (...) (...)...) ((...) (...) (...) (...)...) ((...) (...) (...) (...)...) ((...) (...) (...) (...)...) ((...) (...) (...) (...)...) ((...) (...) (...) (...)...) ((...) (...) (...) (...)...) ((...) (...) (...) (...)...)...) must be a number but is a sequence."
18 | ; (get-text-no-location
19 | ; (run-and-catch-pretty-no-stacktrace 'intro.student '(+ 1 (repeat (repeat (range)))))))
20 | ;
21 | ;(expect "In function inc, the argument ((0 1 2 3...) [1 (...) (...) 4...] (1 2 1 2...) (0 1 2 3...) [1 (...) (...) 4...] (1 2 1 2...) (0 1 2 3...) [1 (...) (...) 4...] (1 2 1 2...) (0 1 2 3...)...) must be a number but is a sequence."
22 | ; (get-text-no-location
23 | ; (run-and-catch-pretty-no-stacktrace 'intro.student '(inc (cycle [(range) [1 (repeat 1) (range) 4 5 6 7 8 9 245 4235 5423] (cycle [1 2])])))))
24 |
25 | ;(expect "In function +, the second argument [1 2 3 4 5 6 76 (0 1 2 3...) 756 354...] must be a number but is a vector."
26 | ; (get-text-no-location
27 | ; (run-and-catch-pretty-no-stacktrace 'intro.student '(+ 1 [1 2 3 4 5 6 76 (range) 756 354 6 645]))))
28 |
29 | ;; testing for pretty-print-value for functions within sequences and for vectors
30 |
31 | (expect "In function +, the argument [1 2] must be a number but is a vector,\nin the function call (+ [1 2])"
32 | (get-text-no-location
33 | (run-and-catch-pretty-no-stacktrace 'intro.student '(+ [1 2]))))
34 |
35 | (expect "In function +, the argument [+] must be a number but is a vector,\nin the function call (+ [+])"
36 | (get-text-no-location
37 | (run-and-catch-pretty-no-stacktrace 'intro.student '(+ [+]))))
38 |
39 | (expect "In function +, the argument [map] must be a number but is a vector,\nin the function call (+ [map])"
40 | (get-text-no-location
41 | (run-and-catch-pretty-no-stacktrace 'intro.student '(+ [map]))))
42 |
43 | (expect "In function +, the argument (\"a\" \"b\") must be a number but is a list,\nin the function call (+ (\"a\" \"b\"))"
44 | (get-text-no-location
45 | (run-and-catch-pretty-no-stacktrace 'intro.student '(+ '("a" "b")))))
46 |
47 | (expect "In function +, the argument #{anonymous-function} must be a number but is a set,\nin the function call (+ #{anonymous-function})"
48 | (get-text-no-location
49 | (run-and-catch-pretty-no-stacktrace 'intro.student '(+ #{#(+ % 2)}))))
50 |
51 | (expect "In function +, the argument #{+} must be a number but is a set,\nin the function call (+ #{+})"
52 | (get-text-no-location
53 | (run-and-catch-pretty-no-stacktrace 'intro.student '(+ #{+}))))
54 |
55 | (expect "In function +, the argument {:a +} must be a number but is a map,\nin the function call (+ {:a +})"
56 | (get-text-no-location
57 | (run-and-catch-pretty-no-stacktrace 'intro.student '(+ {:a +}))))
58 |
59 | (expect "In function +, the argument {:a {6 \"a\", 7 true}, :b 4, :c +} must be a number but is a map,\nin the function call (+ {:a {6 \"a\", 7 true}, :b 4, :c +})"
60 | (get-text-no-location
61 | (run-and-catch-pretty-no-stacktrace 'intro.student '(+ {:a {6 "a" 7 true} :b 4 :c +}))))
62 |
63 | ;; Note: the order of elements in a hashmap is not guaranteed, so the actual elements amy be different
64 | (expect "In function +, the argument {:a 6, :k \"a\", :b 4, :c +, :l 6, :m 7} must be a number but is a map,\nin the function call (+ {:a 6, :k \"a\", :b 4, :c +, :l 6, :m 7})"
65 | (get-text-no-location
66 | (run-and-catch-pretty-no-stacktrace 'intro.student '(+ {:a 6 :k "a" :b 4 :c + :l 6 :m 7}))))
67 |
68 | ;; testing for invalid number exception
69 | (expect #"Invalid number: 1.2.2."
70 | (get-text-no-location (:msg-info-obj (try (load-file "exceptions/compilation_errors/invalid_number.clj")
71 | (catch Throwable e (prettify-exception e))))))
72 |
73 | ;; testing for :illegal-argument-type-not-supported
74 | (expect "Function contains? does not allow a sequence as an argument."
75 | (get-text-no-location
76 | (run-and-catch-pretty-no-stacktrace 'intro.student '(contains? (seq [1 3 6]) 2))))
77 |
78 | ;; testing for odd? and even?
79 | (expect "In function even?, the argument must be an integer number but is nil,\nin the function call (even? nil)"
80 | (get-text-no-location
81 | (run-and-catch-pretty-no-stacktrace 'intro.student '(even? nil))))
82 |
83 | (expect "In function odd?, the argument must be an integer number but is nil,\nin the function call (odd? nil)"
84 | (get-text-no-location
85 | (run-and-catch-pretty-no-stacktrace 'intro.student '(odd? nil))))
86 |
87 | ;; (expect "In function even?, the argument [(0 1 2 3...) (0 1 2 3...)] must be an integer number but is a vector."
88 | ;; (get-text-no-location
89 | ;; (run-and-catch-pretty-no-stacktrace 'intro.student '(even? [(range) (range)]))))
90 |
91 | (expect "In function dec, the argument :r must be a number but is a keyword."
92 | (get-text-no-location
93 | (run-and-catch-pretty-no-stacktrace 'intro.student '(dec :r))))
94 |
95 | ;moved from core_fns_behavior
96 |
97 | (expect Exception (-))
98 | (expect Exception (/))
99 | (expect Exception (max))
100 | (expect Exception (min))
101 |
--------------------------------------------------------------------------------
/test/exception_msgs/overwritten_fns/seq_ops.clj:
--------------------------------------------------------------------------------
1 | (ns exception_msgs.overwritten_fns.seq_ops
2 | (:use [errors.prettify_exception :only [line-number-format prettify-exception]])
3 | (:require [expectations :refer :all]
4 | [errors.messageobj :refer :all]
5 | [errors.testing_tools :refer :all]
6 | [utilities.file_IO :refer :all]
7 | ))
8 |
9 | ;; testing for :illegal-argument-no-val-supplied-for-key
10 | (expect "No value found for key d. Every key for a hash-map must be followed by a value."
11 | (get-text-no-location
12 | (run-and-catch-pretty-no-stacktrace 'intro.student '(hash-map "c" :d "d"))))
13 |
14 | ;; testing for :illegal-argument-vector-arg-to-map-conj
15 | (expect "Vectors added to a map must consist of two elements: a key and a value."
16 | (get-text-no-location
17 | (run-and-catch-pretty-no-stacktrace 'intro.student '(into {} [[1 2] [3]]))))
18 |
19 | (expect "Vectors added to a map must consist of two elements: a key and a value."
20 | (get-text-no-location
21 | (run-and-catch-pretty-no-stacktrace 'intro.student '(merge {:a 1 :b 2} [1 2 3] [1 2] [1 5]))))
22 |
23 | (expect "Vectors added to a map must consist of two elements: a key and a value."
24 | (get-text-no-location
25 | (run-and-catch-pretty-no-stacktrace 'intro.student '(conj {:a 1 :b 2} [1 2 3 4]))))
26 |
27 | ;; testing for :illegal-argument-cannot-convert-type
28 | (expect "In function cons, the second argument 2 must be a sequence but is a number,\nin the function call (cons 1 2)"
29 | (get-text-no-location
30 | (run-and-catch-pretty-no-stacktrace 'intro.student '(cons 1 2))))
31 |
32 | (expect #"You cannot use / in this position."
33 | (get-text-no-location (:msg-info-obj (try (load-file "exceptions/compilation_errors/invalid_token_error1.clj")
34 | (catch Throwable e (prettify-exception e))))))
35 |
36 | ;; testing for wrong number of args to a keyword
37 | (expect #"A keyword: :a can only take one or two arguments."
38 | (get-text-no-location (:msg-info-obj (try (load-file "exceptions/compilation_errors/keyword_wrong_number_of_args.clj")
39 | (catch Throwable e (prettify-exception e))))))
40 |
41 | ;; testing for greater than 20 arguments
42 | (expect #"A function may not take more than 20 parameters."
43 | (get-text-no-location (:msg-info-obj (try (load-file "exceptions/compilation_errors/greater_than_20_parameters.clj")
44 | (catch Throwable e (prettify-exception e))))))
45 |
46 | (expect #"A function may not take more than 20 parameters."
47 | (get-text-no-location (:msg-info-obj (try (load-file "exceptions/compilation_errors/greater_than_20_parameters2.clj")
48 | (catch Throwable e (prettify-exception e))))))
49 |
50 | (expect "Cannot call nil as a function."
51 | (get-text-no-location
52 | (run-and-catch-pretty-no-stacktrace 'intro.student '(nil 5))))
53 |
54 | (expect "In function empty?, the argument :s must be a sequence but is a keyword,\nin the function call (empty? :s)"
55 | (get-text-no-location
56 | (run-and-catch-pretty-no-stacktrace 'intro.student '(empty? :s))))
57 |
58 | (expect "In function first, the argument :e must be a sequence but is a keyword."
59 | (get-text-no-location
60 | (run-and-catch-pretty-no-stacktrace 'intro.student '(first :e))))
61 |
62 | (expect "In function rest, the argument :a must be a sequence but is a keyword."
63 | (get-text-no-location
64 | (run-and-catch-pretty-no-stacktrace 'intro.student '(rest :a))))
65 |
66 | (expect "In function next, the argument :n must be a sequence but is a keyword."
67 | (get-text-no-location
68 | (run-and-catch-pretty-no-stacktrace 'intro.student '(next :n))))
69 |
70 | (expect "In function seq, the argument :w must be a sequence but is a keyword."
71 | (get-text-no-location
72 | (run-and-catch-pretty-no-stacktrace 'intro.student '(seq :w))))
73 |
74 | (expect "In function count, the argument :a must be a sequence but is a keyword."
75 | (get-text-no-location
76 | (run-and-catch-pretty-no-stacktrace 'intro.student '(count :a))))
77 |
78 | ;; testing for :index-out-of-bounds-index-provided
79 | (expect "An index in a sequence is out of bounds. The index is: 10."
80 | (get-text-no-location
81 | (run-and-catch-pretty-no-stacktrace 'intro.core '(throw (new IndexOutOfBoundsException "10")))))
82 |
83 | ;; testing for :null-pointer-non-existing-object-provided
84 | (expect "An attempt to access a non-existing object: some message (NullPointerException)."
85 | (get-text-no-location (run-and-catch-pretty-no-stacktrace 'intro.core '(throw (new NullPointerException "some message")))))
86 |
87 | ;; testing for :null-pointer-non-existing-object-not-provided
88 | (expect "An attempt to access a non-existing object (NullPointerException)."
89 | (get-text-no-location (run-and-catch-pretty-no-stacktrace 'intro.core '(int nil))))
90 |
91 | (expect #"The arguments following the map or vector in assoc must come in pairs, but one of them does not have a match."
92 | (get-text-no-location (run-and-catch-pretty-no-stacktrace 'intro.core '(assoc {:a 3 :b 5} :key1 "val1" :key2))))
93 |
94 | ;; the internal representation of zero? is zero?--inliner--4238 (in this particular test), i.e. it has
95 | ;; an inliner part
96 |
97 | (expect "You cannot pass zero arguments to a function zero?."
98 | (get-text-no-location (run-and-catch-pretty-no-stacktrace 'intro.core '(zero?))))
99 |
100 | (expect "Recur can only occur as a tail call: no operations can be done after its return." ; this is giving NO_SOURCE_PATH
101 | (get-text-no-location (run-and-catch-pretty-no-stacktrace 'intro.core '(defn inc-nums [x] ((recur (inc x)) (loop [x x]))))))
102 |
103 | (expect #"def must be followed by a name." ; this is giving NO_SOURCE_PATH
104 | (get-text-no-location (run-and-catch-pretty-no-stacktrace 'intro.core '(def 4 (+ 2 2)))))
105 |
106 | (expect (str "End of file, starting at line 3.\nProbably a non-closing parenthesis or bracket.\nFound in file eof.clj" (line-number-format 4 1) ".")
107 | (get-all-text (:msg-info-obj (prettify-exception (import-from-file "exceptions/end_of_file.ser")))))
108 |
109 | (expect #"Name splt does not exist in the namespace clojure\.string\.\nFound(.+)"
110 | (get-all-text (run-and-catch-pretty-no-stacktrace 'intro.core '(clojure.string/splt "pattern" #"/"))))
111 |
112 | (expect #"The namespace clojure does not exist or is not accessible in your program\.\n(.+)"
113 | (get-all-text (run-and-catch-pretty-no-stacktrace 'intro.core '(clojure/string/splt "pattern" #"/"))))
114 |
115 | (expect "There is an unmatched delimiter )."
116 | (get-text-no-location (:msg-info-obj (try (load-file "exceptions/compilation_errors/unmatched_delim_re.clj")
117 | (catch Throwable e (prettify-exception e))))))
118 |
119 | (expect "An opened \" does not have a matching closing one."
120 | (get-text-no-location (:msg-info-obj (try (load-file "exceptions/compilation_errors/non_closing_string.clj")
121 | (catch Throwable e (prettify-exception e))))))
122 |
123 |
124 |
--------------------------------------------------------------------------------
/test/exception_msgs/overwritten_fns/str_fns.clj:
--------------------------------------------------------------------------------
1 | (ns exception_msgs.overwritten_fns.str_fns
2 | (:use [errors.prettify_exception :only [line-number-format]])
3 | (:require [expectations :refer :all]
4 | [errors.messageobj :refer :all]
5 | [errors.testing_tools :refer :all]
6 | [utilities.file_IO :refer :all]
7 | ))
8 |
9 | ;; testing for :class-cast-exception
10 | (expect "Attempted to use a string, but a character was expected."
11 | (get-text-no-location
12 | (run-and-catch-pretty-no-stacktrace 'intro.core
13 | '(int "banana"))))
14 |
15 | ;; We use the same message as for identfier undefined, but have a hint that is
16 | ;; more specific to dynamic class loading
17 | (expect (more-> "Name clojure.string.split is undefined." get-text-no-location
18 | (re-pattern (str "(.*)Found in (.*)" (line-number-format "(\\d+)" "(\\d+)") "\\.")) get-all-text)
19 | (run-and-catch-pretty-no-stacktrace 'intro.core '(clojure.string.split "a b c" " ")))
20 |
--------------------------------------------------------------------------------
/test/intro/student_test.clj:
--------------------------------------------------------------------------------
1 | (ns intro.student_test
2 | (:use [errors.prettify_exception :only [line-number-format]])
3 | (:require [expectations :refer :all]
4 | [errors.messageobj :refer :all]
5 | [errors.testing_tools :refer :all]
6 | [utilities.stacktrace_functions :refer :all]
7 | [intro.student :refer :all]))
8 |
9 | (def location-regex (re-pattern (str "(.*)\\nFound in file student.clj" (line-number-format "(\\d+)" "(\\d+)") "(.*)")))
10 | (def location-regex-core (re-pattern (str "(.*)\\nFound in file core.clj" (line-number-format "(\\d+)" "(\\d+)") "(.*)")))
11 | (defn location-regex-exact
12 | "takes a line number and returns a regex pattern with the exact line number"
13 | [line-n]
14 | (re-pattern (str "(.*)\\nFound in file student.clj" (line-number-format line-n nil) "(.*)")))
15 |
16 | ;; showing the resulting regex:
17 | (expect #"(.*)\nFound in file student.clj on, or before, line (\d+)(.*)" location-regex)
18 |
19 | ;; testing for exercise3
20 | (expect (more-of x
21 | java.lang.StringIndexOutOfBoundsException (:exception-class x)
22 | #"Position 12 is outside of the string\.(.*)" (get-all-text (:msg-info-obj x))
23 | (location-regex-exact 60) (get-all-text (:msg-info-obj x)) ; use location-regex for generic line number testing
24 | (trace-has-all-pairs? {:fn "subs" :ns "clojure.core"}) (:stacktrace x)
25 | (trace-doesnt-have-pair? :ns "expectations") (:filtered-stacktrace x))
26 | (run-and-catch-pretty-with-stacktrace 'intro.student
27 | '(exercise3 "helloworld" 5)))
28 |
29 | ;; testing for prob15
30 | (expect (more-of x
31 | java.lang.ClassCastException (:exception-class x)
32 | #"Attempted to use a number, but a function was expected\.(.*)" (get-all-text (:msg-info-obj x))
33 | (location-regex-exact 66) (get-all-text (:msg-info-obj x))
34 | (trace-has-all-pairs? {:fn "prob15" :ns "intro.student"}) (:stacktrace x))
35 | (run-and-catch-pretty-with-stacktrace 'intro.student
36 | '(prob15 5)))
37 |
38 | ;; testing for prob16
39 | (expect (more-of x
40 | clojure.lang.ExceptionInfo (:exception-class x)
41 | #"You cannot pass three arguments to a function cons, need two\,\nin the function call \(cons \"Hello, \" \"Dave\" \"!\"\)" (get-all-text (:msg-info-obj x))
42 | (location-regex-exact 73) (get-all-text (:msg-info-obj x))
43 | (trace-has-all-pairs? {:fn "prob16" :ns "intro.student"}) (:stacktrace x))
44 | (run-and-catch-pretty-with-stacktrace 'intro.student
45 | '(prob16 "Dave")))
46 |
47 | ;; testing for prob17############
48 | (expect (more-of x
49 | clojure.lang.ExceptionInfo (:exception-class x)
50 | #"In function \+, the second argument must be a number but is nil,\nin the function call \(\+ 5 nil\)\n(.*)" (get-all-text (:msg-info-obj x))
51 | (location-regex-exact 84) (get-all-text (:msg-info-obj x))
52 | (trace-has-all-pairs? {:fn "prob17" :ns "intro.student"}) (:stacktrace x))
53 | (run-and-catch-pretty-with-stacktrace 'intro.student
54 | '(prob17 '(1 2 3))))
55 |
56 | ;; testing for prob18#########
57 | (expect (more-of x
58 | clojure.lang.ExceptionInfo (:exception-class x)
59 | #"In function <, the first argument must be a number but is nil,\nin the function call \(< nil 5\)\n(.*)" (get-all-text (:msg-info-obj x))
60 | (location-regex-exact 90) (get-all-text (:msg-info-obj x))
61 | (trace-has-all-pairs? {:fn "prob18" :ns "intro.student"}) (:stacktrace x))
62 | (run-and-catch-pretty-with-stacktrace 'intro.student
63 | '(prob18 '(3 4 5 6 7))))
64 |
65 | ;; testing for prob64##########
66 | (expect (more-of x
67 | clojure.lang.ExceptionInfo (:exception-class x)
68 | #"In function \+, the second argument must be a number but is nil,\nin the function call \(\+ 15 nil\)\n(.*)" (get-all-text (:msg-info-obj x))
69 | (location-regex-exact 103) (get-all-text (:msg-info-obj x))
70 | (trace-has-all-pairs? {:fn "prob64" :ns "intro.student"}) (:stacktrace x))
71 | (run-and-catch-pretty-with-stacktrace 'intro.student
72 | '(prob64 [1 2 3 4 5])))
73 |
74 | ;; take-while now is allowed arity 1, as of Clojure 1.7
75 | ;; testing for prob57
76 | ;(expect (more-of x
77 | ; clojure.lang.ArityException (:exception-class x)
78 | ; "Wrong number of arguments (1) passed to a function take-while" (get-all-text (:msg-info-obj x))
79 | ; (trace-has-all-pairs? {:fn "prob57" :ns "intro.student"}) (:stacktrace x))
80 | ; (run-and-catch-pretty-with-stacktrace 'intro.student
81 | ; '(prob57 5)))
82 |
83 | ;; Elena, 6/17/16: this is actually not an error since a keyword is a function
84 | ;; testing for prob134
85 | ;(expect (more-of x
86 | ; java.lang.AssertionError (:exception-class x)
87 | ; #"In function filter, the first argument :a must be a function but is a keyword\.(.*)" (get-all-text (:msg-info-obj x))
88 | ; location-regex (get-all-text (:msg-info-obj x))
89 | ; (trace-has-all-pairs? {:fn "prob134" :ns "intro.student"}) (:filtered-stacktrace x))
90 | ; (run-and-catch-pretty-with-stacktrace 'intro.student
91 | ; '(prob134 :a {:a nil :b 2})))
92 |
93 | ;; Elena, 6/17/16: this is actually not an error since a map is a function
94 | ;; testing for prob156
95 | ;(expect (more-of x
96 | ; java.lang.AssertionError (:exception-class x)
97 | ; #"In function map, the first argument \{:a 0\} must be a function but is a map\.(.*)" (get-all-text (:msg-info-obj x))
98 | ; location-regex (get-all-text (:msg-info-obj x))
99 | ; (trace-has-all-pairs? {:fn "prob156" :ns "intro.student"}) (:filtered-stacktrace x))
100 | ; (run-and-catch-pretty-with-stacktrace 'intro.student
101 | ; '(prob156 0 [:a :b :c])))
102 |
103 | ;; testing for prob20
104 | (expect (more-of x
105 | clojure.lang.ExceptionInfo (:exception-class x)
106 | #"In function nth, the first argument 3 must be a sequence but is a number,\nin the function call \(nth 3 \[:a :b :c :d\]\)\n(.*)" (get-all-text (:msg-info-obj x))
107 | (location-regex-exact 140) (get-all-text (:msg-info-obj x))
108 | (trace-has-all-pairs? {:fn "prob20" :ns "intro.student"}) (:filtered-stacktrace x))
109 | (run-and-catch-pretty-with-stacktrace 'intro.student
110 | '(prob20 [:a :b :c :d])))
111 |
112 | ;; take now is allowed arity 1, as of Clojure 1.7
113 | ;; testing for prob21
114 | ;(expect (more-of x
115 | ; clojure.lang.ArityException (:exception-class x)
116 | ; "Wrong number of arguments (1) passed to a function take" (get-all-text (:msg-info-obj x))
117 | ; (trace-has-all-pairs? {:fn "prob21" :ns "intro.student"}) (:filtered-stacktrace x))
118 | ; (run-and-catch-pretty-with-stacktrace 'intro.student
119 | ; '(prob21 '(4 5 6 7) 2)))
120 |
121 | ;; testing for add-five
122 | (expect (more-of x
123 | clojure.lang.ExceptionInfo (:exception-class x)
124 | #"In function cons, the second argument 5 must be a sequence but is a number,\nin the function call \(cons 5 5\)\n(.*)" (get-all-text (:msg-info-obj x))
125 | (location-regex-exact 153) (get-all-text (:msg-info-obj x))
126 | (trace-has-all-pairs? {:fn "add-five" :ns "intro.student"}) (:filtered-stacktrace x))
127 | (run-and-catch-pretty-with-stacktrace 'intro.student
128 | '(add-five 5)))
129 |
130 | ;; testing for prob24
131 | (expect (more-of x
132 | clojure.lang.ExceptionInfo (:exception-class x)
133 | #"In function \+, the second argument \(2 3\) must be a number but is a sequence,\nin the function call \(\+ 1 \(2 3\)\)\n(.*)" (get-all-text (:msg-info-obj x))
134 | (location-regex-exact 160) (get-all-text (:msg-info-obj x))
135 | (trace-has-all-pairs? {:fn "prob24" :ns "intro.student"}) (:filtered-stacktrace x))
136 | (run-and-catch-pretty-with-stacktrace 'intro.student
137 | '(prob24 [1 2 3])))
138 |
139 | ;; testing for prob23
140 | (expect (more-of x
141 | java.lang.IndexOutOfBoundsException (:exception-class x)
142 | #"An index in a sequence is out of bounds or invalid\.(.*)" (get-all-text (:msg-info-obj x))
143 | (location-regex-exact 171) (get-all-text (:msg-info-obj x))
144 | (trace-has-all-pairs? {:fn "prob23" :ns "intro.student"}) (:filtered-stacktrace x))
145 | (run-and-catch-pretty-with-stacktrace 'intro.student
146 | '(prob23 [5 4 3 2 1])))
147 |
148 | ;; testing for prob27
149 | (expect (more-of x
150 | clojure.lang.ArityException (:exception-class x)
151 | #"You cannot pass zero arguments to a function equal-to\?\.(.*)" (get-all-text (:msg-info-obj x))
152 | (location-regex-exact 184) (get-all-text (:msg-info-obj x))
153 | (trace-has-all-pairs? {:fn "prob27" :ns "intro.student"}) (:filtered-stacktrace x))
154 | (run-and-catch-pretty-with-stacktrace 'intro.student
155 | '(prob27 '(\r \a \c \e \c \a \r))))
156 |
157 |
158 | ;; testing for exercise9
159 | (expect (more-of x
160 | clojure.lang.ArityException (:exception-class x)
161 | #"You cannot pass one argument to this anonymous function\.(.*)" (get-all-text (:msg-info-obj x))
162 | (location-regex-exact 194) (get-all-text (:msg-info-obj x))
163 | (trace-has-all-pairs? {:fn "exercise9" :ns "intro.student"}) (:filtered-stacktrace x))
164 | (run-and-catch-pretty-with-stacktrace 'intro.student
165 | '(exercise9)))
166 |
167 | ;; testing for prob38
168 | (expect (more-of x
169 | clojure.lang.ArityException (:exception-class x)
170 | ;;; This is a legitimate location error, need to look into it -> The line number seems to indicate this file line 174
171 | ;; location-regex (get-all-text (:msg-info-obj x))
172 | #"You cannot pass four arguments to a function prob38\.(.*)" (get-all-text (:msg-info-obj x)))
173 | (run-and-catch-pretty-with-stacktrace 'intro.student
174 | '(prob38 1 8 3 4)))
175 |
176 | ;; testing for error-in-anonymous
177 | (expect (more-of x
178 | clojure.lang.ExceptionInfo (:exception-class x)
179 | #"In function \+, the first argument \"hi\" must be a number but is a string,\nin the function call \(\+ \"hi\" 2\)\n(.*)" (get-all-text (:msg-info-obj x))
180 | ;;#"In function \+, the first argument \"hi\" must be a number but is a string,\nin the function call(.*)\n(.*)" (get-all-text (:msg-info-obj x))
181 | location-regex (get-all-text (:msg-info-obj x))
182 | (trace-has-all-pairs? {:fn "error-in-anonymous" :anon-fn true :ns "intro.student"}) (:filtered-stacktrace x)
183 | (trace-has-all-pairs? {:fn "error-in-anonymous" :anon-fn false :ns "intro.student"}) (:filtered-stacktrace x))
184 | (run-and-catch-pretty-with-stacktrace 'intro.student
185 | '(error-in-anonymous)))
186 |
187 | ;; testing for error-in-map-inc
188 | (expect (more-of x
189 | java.lang.AssertionError (:exception-class x)
190 | #"In function inc, the argument \"hi\" must be a number but is a string\.(.*)" (get-all-text (:msg-info-obj x))
191 | location-regex (get-all-text (:msg-info-obj x))
192 | (trace-has-all-pairs? {:fn "error-in-map-inc" :anon-fn false :ns "intro.student"}) (:filtered-stacktrace x))
193 | (run-and-catch-pretty-with-stacktrace 'intro.student
194 | '(error-in-map-inc)))
195 |
--------------------------------------------------------------------------------
/test/location/compilation_errors.clj:
--------------------------------------------------------------------------------
1 | (ns location.compilation_errors
2 | (:use [errors.prettify_exception :only [line-number-format prettify-exception]])
3 | (:require [expectations :refer :all]
4 | [errors.messageobj :refer :all]
5 | [errors.testing_tools :refer :all]
6 | [utilities.file_IO :refer :all]
7 | ))
8 |
9 | ;######################################
10 | ;### Testing for compilation errors ###
11 | ;######################################
12 |
13 | (expect (str "End of file, starting at line 3.\nProbably a non-closing parenthesis or bracket.\nFound in file eof.clj" (line-number-format 4 1) ".")
14 | (get-all-text (:msg-info-obj (prettify-exception (import-from-file "exceptions/end_of_file.ser")))))
15 |
16 | (expect (str "There is an unmatched delimiter ).\nFound in file unmatched_delimiter.clj" (line-number-format 3 20) ".")
17 | (get-all-text (:msg-info-obj (prettify-exception (import-from-file "exceptions/unmatched_delimiter.ser")))))
18 |
19 | ;###########################################
20 | ;### Testing for ClassNotFoundException ####
21 | ;###########################################
22 | (expect (more-> "Name clojure.string.split is undefined." get-text-no-location
23 | (re-pattern (str "(.*)Found in (.*)" (line-number-format "(\\d+)" "(\\d+)") "\\.")) get-all-text)
24 | (run-and-catch-pretty-no-stacktrace 'intro.core '(clojure.string.split "a b c" " ")))
25 |
26 |
27 | ;; testing for :illegal-argument-exactly-2-forms
28 | (expect #"The function when-let requires exactly 2 forms in binding vector. Line (.*) in the file intro.core"
29 | (get-all-text (run-and-catch-pretty-no-stacktrace 'intro.core '(when-let [num1 1 num2 2] "hello"))))
30 |
--------------------------------------------------------------------------------
/test/location/overwritten_fns.clj:
--------------------------------------------------------------------------------
1 | (ns location.overwritten_fns
2 | (:use [errors.prettify_exception :only [line-number-format]])
3 | (:require [expectations :refer :all]
4 | [errors.messageobj :refer :all]
5 | [errors.testing_tools :refer :all]
6 | [errors.prettify_exception :refer :all]
7 | [utilities.file_IO :refer :all]
8 | ))
9 |
--------------------------------------------------------------------------------
/test/location/runtime_errors.clj:
--------------------------------------------------------------------------------
1 | (ns location.runtime_errors
2 | (:use [errors.prettify_exception :only [line-number-format]])
3 | (:require [expectations :refer :all]
4 | [errors.messageobj :refer :all]
5 | [errors.testing_tools :refer :all]
6 | [errors.prettify_exception :refer :all]
7 | [utilities.file_IO :refer :all]
8 | ))
9 |
--------------------------------------------------------------------------------
/test/spec_behavior/spec_test.clj:
--------------------------------------------------------------------------------
1 | (ns spec_behavior.spec_test
2 | (:require [expectations :refer :all]
3 | [corefns.corefns :refer :all]))
4 |
5 | ;############################################
6 | ;### Tests for spec data in ExceptionInfo ###
7 | ;### ###
8 | ;############################################
9 |
10 | (defn hashmap-gen
11 | "takes a quoted function and an optional path to the acutal error call,
12 | and returns an auto-generated spec hash-map when an error happens.
13 | It gets the first hashmap in the resulting vector without the n-th argument.
14 | Use init-hash-gen function to see how an initial hashmap looks like"
15 | ([f]
16 | (try (eval f)
17 | (catch clojure.lang.ExceptionInfo e
18 | (clojure.core/nth (:clojure.spec/problems (.getData e)) 0))))
19 | ([f n-th]
20 | (try (eval f)
21 | (catch clojure.lang.ExceptionInfo e
22 | (clojure.core/nth (:clojure.spec/problems (.getData e)) n-th)))))
23 |
24 | (defn init-hashmap-gen
25 | "takes a quoted function call and returns the error data as a hashmap
26 | when an error happens"
27 | [f]
28 | (try (eval f)
29 | (catch clojure.lang.ExceptionInfo e
30 | (.getData e))))
31 |
32 | ; 0 failures 0 errors
33 | ; An example error data:
34 | ;{:clojure.spec/problems [{:path [:args], :pred length1?, :val ([] []), :via [:spec-alpha.specs/length-one], :in []}], ...}
35 |
36 | ;; ########## tests for empty? ##########
37 | (expect false (empty? [1 2]))
38 | (expect true (empty? nil))
39 | ; We need ' (quote) for the :pred value
40 | (expect {:path [:args :check-seqable], :pred 'seqable?, :val 3, :via [], :in [0]}
41 | (hashmap-gen '(empty? 3)))
42 | ; The value of key :pred is a lazy sequence
43 | ;; (expect (more-> 'cat first
44 | ;; :check-seqable second
45 | ;; 'seqable? last)
46 | ;; (:pred (hashmap-gen '(empty? [] []))))
47 | (expect {:path [:args], :pred 'length1?, :val '([] []), :via [:corefns.specs/length-one], :in []}
48 | (hashmap-gen '(empty? [] [])))
49 | (expect {:path [:args], :pred 'length1?, :val 'nil, :via [:corefns.specs/length-one], :in []}
50 | (hashmap-gen '(empty?)))
51 | (expect {:path [:args :check-seqable], :pred 'seqable?, :val true, :via [], :in [0]}
52 | (hashmap-gen '(empty? true)))
53 |
54 |
55 | ;; ########## tests for map ##########
56 | (expect [2 3 4] (map inc [1 2 3]))
57 | (expect {:path [:args], :pred 'length-greater1?, :val '(10), :via [:corefns.specs/length-greater-one], :in []}
58 | (hashmap-gen '(map 10)))
59 | (expect {:path [:args :check-function], :pred 'ifn?, :val 1, :via [], :in [0]}
60 | (hashmap-gen '(map 1 [1 3 4] [1 2 3] [1 3 4])))
61 | (expect {:path [:args :check-seqable] :pred 'seqable?, :val 3, :via [], :in [4]}
62 | (hashmap-gen '(map empty? [1 2 3] [2 3 4] [3 4 5] 3)))
63 |
64 |
65 | ;; ########## tests for conj ##########
66 | (expect [1 2 3 3 [2]] (conj [1 2] 3 3 [2]))
67 | (expect {:path [:args], :pred 'length-greater1?, :val '([1]), :via [:corefns.specs/length-greater-one], :in []}
68 | (hashmap-gen '(conj [1])))
69 | (expect {:path [:args :check-seqable], :pred 'seqable?, :val 1, :via [], :in [0]}
70 | (hashmap-gen '(conj 1 3)))
71 | (expect {:path [:args :check-seqable], :pred 'seqable?, :val true, :via [], :in [0]}
72 | (hashmap-gen '(conj true 3)))
73 |
74 |
75 | ;; ########## tests for reduce ##########
76 | (expect 6 (reduce + [1 2 3]))
77 | (expect 16 (reduce + 10 [1 2 3]))
78 | (expect {:path [:args :two-case :check-function], :pred 'ifn?, :val 1, :via [], :in [0]}
79 | (hashmap-gen '(reduce 1 [1 2 3]) 0))
80 | (expect {:path [:args :three-case], :pred 'length3?, :val '(1 [1 2 3]), :via [:corefns.specs/length-three], :in []}
81 | (hashmap-gen '(reduce 1 [1 2 3]) 1))
82 | (expect {:path [:args :two-case], :pred 'length2?, :val '([1 2] 0 [] 1), :via [:corefns.specs/length-two], :in []}
83 | (hashmap-gen '(reduce [1 2] 0 [] 1) 0))
84 | (expect {:path [:args :three-case], :pred 'length3?, :val '([1 2] 0 [] 1), :via [:corefns.specs/length-three], :in []}
85 | (hashmap-gen '(reduce [1 2] 0 [] 1) 1))
86 | (expect {:path [:args :two-case], :pred 'length2?, :val '(1), :via [:corefns.specs/length-two], :in []}
87 | (hashmap-gen '(reduce 1) 0))
88 | (expect {:path [:args :three-case], :pred 'length3?, :val '(1), :via [:corefns.specs/length-three], :in []}
89 | (hashmap-gen '(reduce 1) 1))
90 | (expect {:path [:args :two-case :check-seqable], :pred 'seqable?, :val 3, :via [], :in [1]}
91 | (hashmap-gen '(reduce + 3) 0))
92 | (expect {:path [:args :three-case :check-seqable], :pred 'seqable?, :val 2, :via [], :in [2]}
93 | (hashmap-gen '(reduce + 2 2) 1))
94 |
95 |
96 | ;; ########## tests for nth ############
97 | ; using a helper function and late eval doesn't work for some reason.
98 | ; need to use try-catch directly
99 | (expect "lazy" (nth ["lazy" "person" "you"] 0))
100 | (expect "working" (nth ["lazy" "person" "you"] 4 "working"))
101 | (expect {:path [:args :two-case :check-seqable] :pred 'seqable?, :val 1, :via [], :in [0]}
102 | (try (nth 1 "")
103 | (catch clojure.lang.ExceptionInfo e
104 | (clojure.core/nth (:clojure.spec/problems (.getData e)) 0))))
105 | (expect {:path [:args :three-case] :pred 'length3?, :val '(1 ""), :via [:corefns.specs/length-three], :in []}
106 | (try (nth 1 "")
107 | (catch clojure.lang.ExceptionInfo e
108 | (clojure.core/nth (:clojure.spec/problems (.getData e)) 1))))
109 | (expect {:path [:args :two-case] :pred 'length2?, :val '([1 2 3] [1] "Boo"), :via [:corefns.specs/length-two], :in []}
110 | (try (nth [1 2 3] [1] "Boo")
111 | (catch clojure.lang.ExceptionInfo e
112 | (clojure.core/nth (:clojure.spec/problems (.getData e)) 0))))
113 | (expect {:path [:args :three-case :check-number] :pred 'number?, :val [1], :via [], :in [1]}
114 | (try (nth [1 2 3] [1] "Boo")
115 | (catch clojure.lang.ExceptionInfo e
116 | (clojure.core/nth (:clojure.spec/problems (.getData e)) 1))))
117 |
118 |
119 | ;; ########## tests for assoc ##########
120 | (expect {:key1 "value", :key2 "another value"} (assoc {} :key1 "value" :key2 "another value"))
121 | (expect [1 nil] (assoc [1 2] 1 nil))
122 | (expect {nil nil} (assoc {} nil nil))
123 | (expect {:path [:args], :pred 'length-greater2?, :val '([1 2]), :via [:corefns.specs/length-greater-two], :in []}
124 | (hashmap-gen '(assoc [1 2])))
125 | (expect {:path [:args], :pred 'length-greater2?, :val '({} :random), :via [:corefns.specs/length-greater-two], :in []}
126 | (hashmap-gen '(assoc {} :random)))
127 | (expect {:path [:args :check-map-or-vector :check-map :clojure.spec/nil], :pred 'nil?, :val '(), :via [], :in [0]}
128 | (hashmap-gen '(assoc '() nil nil)))
129 | (expect {:path [:args :check-map-or-vector :check-map :clojure.spec/pred], :pred 'map?, :val '(), :via [], :in [0]}
130 | (hashmap-gen '(assoc '() nil nil) 1))
131 | (expect {:path [:args :check-map-or-vector :check-vector], :pred 'vector?, :val '(), :via [], :in [0]}
132 | (hashmap-gen '(assoc '() nil nil) 2))
133 |
134 |
135 | ;; ########## tests for dissoc ##########
136 | (expect {:c 3} (dissoc {:a 1 :b 2 :c 3} :a :b))
137 | (expect {:path [:args], :pred 'length-greater0?, :val nil, :via [:corefns.specs/length-greater-zero], :in []}
138 | (hashmap-gen '(dissoc)))
139 | (expect {:path [:args :check-map :clojure.spec/nil], :pred 'nil?, :val [0], :via [], :in [0]}
140 | (hashmap-gen '(dissoc [0])))
141 | (expect {:path [:args :check-map :clojure.spec/pred], :pred 'map?, :val [0], :via [], :in [0]}
142 | (hashmap-gen '(dissoc [0]) 1))
143 |
144 |
145 | ;; ########## tests for quot ###########
146 | ; using a helper function and late eval doesn't work for some reason.
147 | ; need to use try-catch directly
148 | (expect 3 (quot 12 4))
149 | (expect 3 (quot 9 3))
150 | (expect {:path [:args :check-number], :pred 'number?, :val false, :via [], :in [0]}
151 | (try (quot false 10)
152 | (catch clojure.lang.ExceptionInfo e
153 | (clojure.core/nth (:clojure.spec/problems (.getData e)) 0))))
154 | (expect {:path [:args], :pred 'length2?, :val '(30 "" true), :via [::corefns.specs/length-two], :in []}
155 | (try (quot 30 "" true)
156 | (catch clojure.lang.ExceptionInfo e
157 | (clojure.core/nth (:clojure.spec/problems (.getData e)) 0))))
158 |
159 |
160 | ;; ########## tests for repeat ###########
161 | (expect '("x" "x" "x" "x" "x") (take 5 (repeat "x")))
162 | (expect '("x" "x" "x" "x" "x") (repeat 5 "x"))
163 | (expect {:path [:args :one-case], :pred 'length1?, :val '("" 5), :via [:corefns.specs/length-one], :in []}
164 | (hashmap-gen '(repeat "" 5)))
165 | (expect {:path [:args :two-case :check-number], :pred 'number?, :val "", :via [], :in [0]}
166 | (hashmap-gen '(repeat "" 5) 1))
167 | (expect {:path [:args :one-case], :pred 'length1?, :val '(5 :some :any 100.0), :via [:corefns.specs/length-one], :in []}
168 | (hashmap-gen '(repeat 5 :some :any 100.0)))
169 | (expect {:path [:args :two-case], :pred 'length2?, :val '(5 :some :any 100.0), :via [:corefns.specs/length-two], :in []}
170 | (hashmap-gen '(repeat 5 :some :any 100.0) 1))
171 |
--------------------------------------------------------------------------------
/test/stacktrace_testing/stacktrace_testing.clj:
--------------------------------------------------------------------------------
1 | (ns stacktrace_testing.stacktrace_testing
2 | (:require [expectations :refer :all]
3 | [errors.messageobj :refer :all]
4 | [clj-stacktrace.core :as stacktrace]
5 | [utilities.stacktrace_functions :refer :all]
6 | [errors.testing_tools :refer :all]
7 | [errors.prettify_exception :refer :all]
8 | [corefns.collection_fns :refer :all]
9 | [errors.testing_tools :refer :all]
10 | [utilities.file_IO :refer :all]
11 | )
12 | (:import [java.io.FileInputStream]
13 | [java.io.ObjectInputStream]
14 | [java.io.FileOutputStream]
15 | [java.io.ObjectOutputStream]
16 | [java.util.ArrayList]))
17 |
18 |
19 |
20 | ;##################################
21 | ;## 4. More real-life exceptions ##
22 | ;##################################
23 |
24 |
25 | ;;; We actually might want to change this exception by adding or changing a pre-condition for conj
26 |
27 | (def dr-racket-exercise-class-cast (import-from-file "exceptions/DrRacket-Exercise2-ClassCast.ser"))
28 |
29 | (def prettified-class-cast (prettify-exception dr-racket-exercise-class-cast))
30 |
31 | (expect ClassCastException (:exception-class prettified-class-cast))
32 |
33 | (expect (trace-has-pair? :fn "exercise2") (:stacktrace prettified-class-cast))
34 |
35 | (expect (trace-has-pair? :fn "exercise2") (:filtered-stacktrace prettified-class-cast))
36 |
37 | (expect (trace-has-all-pairs? {:fn "exercise2" :ns "intro.student" :file "student.clj" :line 50})
38 | (:filtered-stacktrace prettified-class-cast))
39 |
40 | (expect (trace-has-all-pairs? {:fn "-main" :ns "intro.core" :file "core.clj"})
41 | (:filtered-stacktrace prettified-class-cast))
42 |
43 | (expect (trace-doesnt-have-all-pairs? {:ns "core.main"}) prettified-class-cast)
44 |
45 | (expect (check-stack-count? 6) (:filtered-stacktrace prettified-class-cast))
46 |
47 | (expect (trace-has-all-pairs? {:fn "apply" :ns "clojure.core"}) (:filtered-stacktrace prettified-class-cast))
48 |
49 | (expect (top-elem-has-all-pairs? {:fn "conj" :ns "clojure.core"}) (:filtered-stacktrace prettified-class-cast))
50 |
51 | (expect (trace-has-all-pairs? {:fn "conj" :ns "corefns.corefns"}) (:filtered-stacktrace prettified-class-cast))
52 |
53 | ;##############################################
54 | ;## 5. Comparing top elements of stacktraces ##
55 | ;##############################################
56 |
57 | ;#####################
58 | ;### 5.1 functions ###
59 | ;#####################
60 |
61 | (defn important-part?
62 | "Takes a vector of a key-value pair and then returns true if the key matches any key in the vector of keys"
63 | [pair]
64 | (any? true? (map #(= (first pair) %) [:method :class :java :fn :ns :clojure])))
65 |
66 | (defn get-important-in-stacktrace
67 | "Takes a stacktrace (a vector of hashmaps), and then only keeps what returns true from important-part?"
68 | [stacktrace]
69 | (vec (map #(into {} (filter important-part? %)) stacktrace)))
70 |
71 | (defn make-trace-comparison
72 | "Takes an exception and a piece of quoted code or a string filename, makes an exception, and then returns a hashmap consisting
73 | of the filename/quoted code, a boolean stating whether the top elements of both the filtered and unfiltered stacktraces match,
74 | and then the unfiltered stacktrace up until the first match of the top element of the filtered stacktrace to the unfiltered,
75 | excluding unimportant parts, and finally the top element of the filtered, excluding unimportant parts."
76 | [exception source]
77 | (let [prettified-exception (prettify-exception exception)
78 | stacktrace (:stacktrace prettified-exception)
79 | top-of-filtered-stacktrace (first (:filtered-stacktrace prettified-exception))
80 | beginning-of-stacktrace (take (inc (.indexOf stacktrace top-of-filtered-stacktrace)) stacktrace)]
81 | {:source source
82 | :top-elements-match? (= (first beginning-of-stacktrace) top-of-filtered-stacktrace)
83 | :beginning-of-unfiltered-trace (get-important-in-stacktrace beginning-of-stacktrace)
84 | :top-element-of-filtered-trace (into {} (filter important-part? top-of-filtered-stacktrace))}))
85 |
86 | (defn format-and-print-comparison
87 | "Takes a result of make-trace-comparison and will print everything out prettily in the terminal, with new lines and tabs"
88 | [trace-comparison]
89 | (do
90 | (println "source: "(:source trace-comparison) "\n"
91 | "top elements match?: "(:top-elements-match? trace-comparison) "\n"
92 | "beginning of unfiltered trace:" (interpose "\n \t \t \t \t" (:beginning-of-unfiltered-trace trace-comparison)) "\n"
93 | "top element of filtered trace: " (:top-element-of-filtered-trace trace-comparison) "\n")
94 | true))
95 |
96 | ;; To print comparisons, uncomment the doall line.
97 | (defn format-and-print-comparisons
98 | "Takes in a list of hashmaps (results of make-trace-comparison), and calls a function to print everything and returns true.
99 | Uncomment the third line here if you want to see a trace-comparison of stacktrace tops."
100 | [trace-comparisons]
101 | (do
102 | ;(doall (map format-and-print-comparison trace-comparisons))
103 | true))
104 |
105 | (defn make-and-print-comparisons
106 | "Takes in a collection of exceptions and a collection of either strings or quoted code, and then generates a trace-comparison
107 | for them, and then prints them nicely in the terminal."
108 | [coll-of-exceptions coll-of-sources]
109 | (format-and-print-comparisons (map make-trace-comparison coll-of-exceptions coll-of-sources)))
110 |
111 | (defn compare-traces-of-quoted-code
112 | "Takes a namespace, and an unlimited number of pieces of quoted code, and then turns the quoted code into an exceptions,
113 | and then calls make-and-print-comparisons to print trace-comparisons."
114 | [name-space & quoted-code]
115 | (make-and-print-comparisons (map #(run-and-catch-raw name-space %) quoted-code) quoted-code))
116 |
117 | (defn compare-traces-of-saved-exceptions
118 | "Takes an unlimited number of filenames, and then calls make-and-print-comparisons on the saved exceptions to print the trace-comparisons."
119 | [& filenames]
120 | (make-and-print-comparisons (map #(import-from-file (str "exceptions/" %)) filenames) filenames))
121 |
122 | (defn print-n-elements-of-stacktrace
123 | "Takes a number n and a stacktrace (filtered or unfiltered), and then prints that many elements of the stacktrace"
124 | [n trace]
125 | (do
126 | (println "\n First" n "elements of the stacktrace: \n"(interpose "\n" (take n trace)))
127 | true))
128 |
129 | ;##################
130 | ;### 5.2 prints ###
131 | ;##################
132 |
133 | ;; to actually print the result in the terminal, uncomment the line in format-and-print-comparisons
134 |
135 | ;; saved-exceptions is actually at the top of this file
136 |
137 | (expect true (apply compare-traces-of-saved-exceptions saved-exceptions))
138 |
139 | (expect true (compare-traces-of-quoted-code 'intro.student
140 | '(+ 2 "string")
141 | '(cons 1 2)
142 | '(inc "apple")
143 | '(prob16 "Dave")
144 | '(exercise2 "hello " "world")
145 | '(exercise9)
146 | '(error-in-anonymous)))
147 |
148 | ;(expect true (print-n-elements-of-stacktrace 100 (:stacktrace (run-and-catch-pretty-with-stacktrace
149 | ; 'intro.student
150 | ; '(error-in-map-inc)))))
151 |
152 | ;(expect true (print-n-elements-of-stacktrace 100 (:stacktrace (run-and-catch-pretty-with-stacktrace
153 | ; 'intro.student
154 | ; '(error-in-anonymous)))))
155 |
156 | (expect true (compare-traces-of-quoted-code 'intro.student
157 | '(cons 16 79)))
158 |
159 | (expect true (compare-traces-of-saved-exceptions "4clojure-prob156-AssertionError.ser"))
160 |
161 | ;#################
162 | ;### 5.3 tests ###
163 | ;#################
164 |
165 | ;; testing for make-trace-comparison
166 | ;; Elena: this used to be a test for non-matching top of teh stack, but now that we
167 | ;; have overwritten cons this is no longer valid. Commenting out.
168 | ;(expect {:source '(cons 16 79)
169 | ; :top-elements-match? false
170 | ; :beginning-of-unfiltered-trace '({:method "seqFrom", :class "clojure.lang.RT", :java true}
171 | ; {:method "seq", :class "clojure.lang.RT", :java true}
172 | ; {:method "cons", :class "clojure.lang.RT", :java true}
173 | ; {:fn "cons", :ns "clojure.core", :clojure true})
174 | ; :top-element-of-filtered-trace {:fn "cons", :ns "clojure.core", :clojure true}}
175 | ; (make-trace-comparison (run-and-catch-raw 'intro.student
176 | ; '(cons 16 79)) '(cons 16 79)))
177 |
178 | (expect {:source "4clojure-prob156-AssertionError.ser"
179 | :top-elements-match? true
180 | :beginning-of-unfiltered-trace '({:fn "map", :ns "corefns.corefns", :clojure true})
181 | :top-element-of-filtered-trace {:fn "map", :ns "corefns.corefns", :clojure true}}
182 | (make-trace-comparison (import-from-file "exceptions/4clojure-prob156-AssertionError.ser") "4clojure-prob156-AssertionError.ser"))
183 |
--------------------------------------------------------------------------------
/test/utilities/file_IO.clj:
--------------------------------------------------------------------------------
1 | (ns utilities.file_IO
2 | (:require [expectations :refer :all]
3 | ;[clj-stacktrace.core :as stacktrace]
4 | ;[errors.prettify_exception :refer :all]
5 | ;[errors.messageobj :refer :all]
6 | ;[utilities.stacktrace_functions :refer :all]
7 | ;[corefns.collection_fns :refer :all]
8 | )
9 | (:import [java.io.FileInputStream]
10 | [java.io.ObjectInputStream]
11 | [java.io.FileOutputStream]
12 | [java.io.ObjectOutputStream]
13 | [java.util.ArrayList]))
14 |
15 | ;################################
16 | ;## Writing/Reading to file ##
17 | ;################################
18 |
19 | ;## global vars ##
20 | ;(def path "exceptions/")
21 |
22 | ; 1.1 functions
23 |
24 | ;## NOTE ##
25 | ;;The second part of let is a series of expressions that evaluate in /order/, returning the last expression.
26 | ;;This is Important:
27 | ;;The following two functions are dependent on events occurring in chronological order.
28 |
29 | (defn export-to-file
30 | "Uses Java's Serializable to write a (java) object to a file"
31 | [obj filepath]
32 | (let [file-stream (java.io.FileOutputStream. filepath)
33 | obj-stream (java.io.ObjectOutputStream. file-stream)
34 | ]
35 | (.writeObject obj-stream obj)
36 | (.close obj-stream)
37 | (.close file-stream)
38 | ;(println (str "data saved in project folder or: " filepath))
39 | ))
40 |
41 | (defn import-from-file
42 | "Uses Java's Serializable to read a (java) object from a file"
43 | [filepath]
44 | (let [file-stream (java.io.FileInputStream. filepath)
45 | obj-stream (java.io.ObjectInputStream. file-stream)
46 | e (.readObject obj-stream)]
47 | (.close obj-stream)
48 | (.close file-stream)
49 | e))
50 |
51 | ; 1.2 testing reading/writing to file
52 |
53 | (def java-arraylist (new java.util.ArrayList 5))
54 |
55 | (expect (.equals java-arraylist
56 | (let [filename "testfile.silly"
57 | object java-arraylist]
58 | (export-to-file object (str "exceptions/" filename))
59 | (import-from-file (str "exceptions/" filename)))))
60 |
--------------------------------------------------------------------------------
/test/utilities/stacktrace_functions.clj:
--------------------------------------------------------------------------------
1 | (ns utilities.stacktrace_functions
2 | (:require [expectations :refer :all]
3 | [corefns.collection_fns :refer :all]))
4 |
5 | ;; a helper function to test for either nil or false
6 | (defn not-true? [x] (not (true? x)))
7 |
8 | ;###############################
9 | ;### Checks a single element ###
10 | ;###############################
11 |
12 | ;### Item exists in trace elem ###
13 |
14 | (defn helper-trace-elem-has-function? [fun trace-elem]
15 | (= fun (:fn trace-elem)))
16 |
17 | (defn trace-elem-has-function? [fun]
18 | (partial helper-trace-elem-has-function? fun))
19 |
20 | ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
21 |
22 | (defn helper-trace-elem-has-namespace? [name-space trace-elem]
23 | (= name-space (:ns trace-elem)))
24 |
25 | (defn trace-elem-has-namespace? [name-space]
26 | (partial helper-trace-elem-has-namespace? name-space))
27 |
28 | ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
29 |
30 | (defn helper-trace-elem-has-pair? [k v trace-elem]
31 | (= v (k trace-elem)))
32 |
33 | (defn trace-elem-has-pair? [k v]
34 | (partial helper-trace-elem-has-pair? k v))
35 |
36 | ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
37 |
38 | (defn helper-trace-elem-has-all-pairs? [kv-pairs trace-elem]
39 | "checks that every binding in kv-pairs also appears in trace-elem"
40 | (every? true? (map #(helper-trace-elem-has-pair? (first %) (second %) trace-elem) kv-pairs)))
41 |
42 | (defn trace-elem-has-all-pairs? [kv-pairs]
43 | (partial helper-trace-elem-has-all-pairs? kv-pairs))
44 |
45 | ;### Item doesn't exists in trace elem ###
46 |
47 | (defn helper-trace-elem-doesnt-have-function? [fun trace-elem]
48 | (not (helper-trace-elem-has-function? fun trace-elem)))
49 |
50 | (defn trace-elem-doesnt-have-function? [fun]
51 | (partial helper-trace-elem-doesnt-have-function? fun))
52 |
53 | ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
54 |
55 | (defn helper-trace-elem-doesnt-have-namespace? [name-space trace-elem]
56 | (not (helper-trace-elem-has-namespace? name-space trace-elem)))
57 |
58 | (defn trace-elem-doesnt-have-namespace? [name-space]
59 | (partial helper-trace-elem-doesnt-have-namespace? name-space))
60 |
61 | ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
62 |
63 | (defn helper-trace-elem-doesnt-have-pair? [k v trace-elem]
64 | (not (helper-trace-elem-has-pair? k v trace-elem)))
65 |
66 | (defn trace-elem-doesnt-have-pair? [k v]
67 | (partial helper-trace-elem-doesnt-have-pair? k v))
68 |
69 | ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
70 | (defn helper-trace-elem-doesnt-have-all-pairs? [kv-pairs trace-elem]
71 | "checks that every binding in kv-pairs also appears in trace-elem"
72 | (not (helper-trace-elem-has-all-pairs? kv-pairs trace-elem)))
73 |
74 | (defn trace-elem-doesnt-have-all-pairs? [kv-pairs]
75 | (partial helper-trace-elem-doesnt-have-all-pairs? kv-pairs))
76 |
77 | ;#################################
78 | ;### Checks a whole stacktrace ###
79 | ;#################################
80 |
81 | ;; a helper function to test the size of a stacktrace
82 | (defn helper-stack-count? [n stacktrace]
83 | (= n (count stacktrace)))
84 |
85 | (defn check-stack-count? [n]
86 | (partial helper-stack-count? n))
87 |
88 | ;### Item exists in stacktrace ###
89 |
90 | (defn helper-trace-has-function? [fun trace]
91 | (any? (trace-elem-has-function? fun) trace))
92 |
93 | (defn trace-has-function? [fun]
94 | (partial helper-trace-has-function? fun))
95 |
96 | ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
97 |
98 | (defn helper-trace-has-namespace? [name-space trace]
99 | (any? (trace-elem-has-namespace? name-space) trace))
100 |
101 | (defn trace-has-namespace? [name-space]
102 | (partial helper-trace-has-namespace? name-space))
103 |
104 | ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
105 |
106 | (defn helper-trace-has-pair? [k v trace]
107 | (any? (trace-elem-has-pair? k v) trace))
108 |
109 | (defn trace-has-pair? [k v]
110 | (partial helper-trace-has-pair? k v))
111 |
112 | ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
113 |
114 | (defn helper-trace-has-all-pairs? [kv-pairs trace]
115 | (any? (trace-elem-has-all-pairs? kv-pairs) trace))
116 |
117 | (defn trace-has-all-pairs? [kv-pairs]
118 | (partial helper-trace-has-all-pairs? kv-pairs))
119 |
120 | ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
121 |
122 | (defn helper-top-elem-has-pair? [k v trace]
123 | (helper-trace-elem-has-pair? k v (first trace)))
124 |
125 | (defn top-elem-has-pair? [k v]
126 | (partial helper-top-elem-has-pair? k v))
127 |
128 | ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
129 |
130 | (defn helper-top-elem-has-all-pairs? [kv-pairs trace]
131 | (helper-trace-elem-has-all-pairs? kv-pairs (first trace)))
132 |
133 | (defn top-elem-has-all-pairs? [kv-pairs]
134 | (partial helper-top-elem-has-all-pairs? kv-pairs))
135 |
136 | ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
137 |
138 | (defn helper-nth-elem-has-pair? [n k v trace]
139 | (helper-trace-elem-has-pair? k v (nth trace n)))
140 |
141 | (defn nth-elem-has-pair? [n k v]
142 | (partial helper-nth-elem-has-pair? n k v))
143 |
144 | ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
145 |
146 | (defn helper-nth-elem-has-all-pairs? [n kv-pairs trace]
147 | (helper-trace-elem-has-all-pairs? kv-pairs (nth trace n)))
148 |
149 | (defn nth-elem-has-all-pairs? [n kv-pairs]
150 | (partial helper-nth-elem-has-all-pairs? n kv-pairs))
151 |
152 | ;### Item doesn't exists in stacktrace ###
153 |
154 | (defn helper-trace-doesnt-have-function? [fun trace]
155 | (not (helper-trace-has-function? fun trace)))
156 |
157 | (defn trace-doesnt-have-function? [fun]
158 | (partial helper-trace-doesnt-have-function? fun))
159 |
160 | ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
161 |
162 | (defn helper-trace-doesnt-have-namespace? [name-space trace]
163 | (not (helper-trace-has-namespace? name-space trace)))
164 |
165 | (defn trace-doesnt-have-namespace? [name-space]
166 | (partial helper-trace-doesnt-have-namespace? name-space))
167 |
168 | ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
169 |
170 | (defn helper-trace-doesnt-have-pair? [k v trace]
171 | (not (helper-trace-has-pair? k v trace)))
172 |
173 | (defn trace-doesnt-have-pair? [k v]
174 | (partial helper-trace-doesnt-have-pair? k v))
175 |
176 | ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
177 |
178 | (defn helper-trace-doesnt-have-all-pairs? [kv-pairs trace]
179 | (not (helper-trace-has-all-pairs? kv-pairs trace)))
180 |
181 | (defn trace-doesnt-have-all-pairs? [kv-pairs]
182 | (partial helper-trace-doesnt-have-all-pairs? kv-pairs))
183 |
184 | ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
185 |
--------------------------------------------------------------------------------