gift-parser 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,27 @@
1
+ // true/false
2
+ ::Q1:: 1+1=2 {T}
3
+
4
+ // multiple choice with specified feedback for right and wrong answers
5
+ ::Q2:: What's between orange and green in the spectrum?
6
+ { =yellow # right; good! ~red # wrong, it's yellow ~blue # wrong, it's yellow }
7
+
8
+ // fill-in-the-blank
9
+ ::Q3:: Two plus {=two =2} equals four.
10
+
11
+ // matching
12
+ ::Q4:: Which animal eats which food? { =cat -> cat food =dog -> dog food }
13
+
14
+ // math range question
15
+ ::Q5:: What is a number from 1 to 5? {#3:2}
16
+
17
+ // math range specified with interval end points
18
+ ::Q6:: What is a number from 1 to 5? {#1..5}
19
+
20
+ // multiple numeric answers with partial credit and feedback
21
+ ::Q7:: When was Ulysses S. Grant born? {#
22
+ =1822:0 # Correct! Full credit.
23
+ =%50%1822:2 # He was born in 1822. Half credit for being close.
24
+ }
25
+
26
+ // essay
27
+ ::Q8:: How are you? {}
@@ -0,0 +1,179 @@
1
+ #!/usr/bin/env ruby
2
+ # Tests for GIFT parser
3
+
4
+ # This only tests that the input is parsed.
5
+ # so far does not test output.
6
+
7
+ require 'pp'
8
+ require 'test/unit'
9
+ require 'test/unit/ui/console/testrunner'
10
+
11
+ require File.expand_path('../../lib/gift', __FILE__)
12
+
13
+ class GiftSemanticTest < Test::Unit::TestCase
14
+
15
+ attr_accessor :parser
16
+
17
+ def setup
18
+ @parser = GiftParser.new
19
+ end
20
+
21
+ def assert_can_parse(string)
22
+ string << "\n\n" # Insert a blank line at end of string
23
+ assert !@parser.parse(string).nil?, "Failed on:\n#{string}\n Reason:\n#{@parser.failure_reason.inspect}\n"
24
+ end
25
+
26
+ def assert_cannot_parse(string)
27
+ string << "\n\n" # Insert a blank line at end of string
28
+ p = @parser.parse(string)
29
+ assert p.nil?, "Failed on:\n#{string}\n Reason:\nThis should not be correctly parsed, but was.\nParser Output:\n#{p.inspect}"
30
+ end
31
+
32
+ def test_description_question
33
+ q = @parser.parse("This is a description.\n\n").questions[0]
34
+ assert q.class == Gift::DescriptionQuestion
35
+ assert q.text == "This is a description."
36
+ end
37
+
38
+ def test_essay_question
39
+ q = @parser.parse("Write an essay on the toic of your choice{ }\n\n").questions[0]
40
+ assert q.class == Gift::EssayQuestion
41
+ assert q.text == "Write an essay on the toic of your choice"
42
+ end
43
+
44
+ def test_true_false_question_true
45
+ q = @parser.parse("Is the sky blue?{T}\n\n").questions[0]
46
+ assert q.class == Gift::TrueFalseQuestion
47
+ assert q.text == "Is the sky blue?"
48
+ assert q.answers == [{:value => true, :correct => true, :feedback => nil}]
49
+ end
50
+
51
+ def test_true_false_question_false
52
+ q = @parser.parse("Is the sky green?{F}\n\n").questions[0]
53
+ assert q.text == "Is the sky green?"
54
+ assert q.answers == [{:value => false, :correct => true, :feedback => nil}]
55
+ end
56
+
57
+ def test_tf_with_feedback
58
+ q = @parser.parse("Is the sky green?{F#No It's blue.}\n\n").questions[0]
59
+ assert q.text == "Is the sky green?"
60
+ assert q.answers == [{:value => false, :correct => true, :feedback => "No It's blue."}]
61
+ end
62
+
63
+ def test_multiple_choice_question
64
+ q = @parser.parse("What color is the sky?{=blue ~green ~red}\n\n").questions[0]
65
+ assert q.class == Gift::MultipleChoiceQuestion
66
+ assert q.text == "What color is the sky?"
67
+ assert q.answers == [{:value => "blue", :correct => true, :feedback => nil},
68
+ {:value => "green", :correct => false, :feedback => nil},
69
+ {:value => "red", :correct => false, :feedback => nil}]
70
+
71
+ end
72
+
73
+ def test_multiple_choice_question_with_feedback
74
+ q = @parser.parse("What color is the sky?{=blue#Correct ~green#Wrong! ~red}\n\n").questions[0]
75
+ assert q.answers == [{:value => "blue", :correct => true, :feedback => "Correct"},
76
+ {:value => "green", :correct => false, :feedback => "Wrong!"},
77
+ {:value => "red", :correct => false, :feedback => nil}]
78
+
79
+ end
80
+
81
+ def test_multiple_choice_question_with_weights
82
+ q = @parser.parse("What color is the sky?{=blue#Correct ~%50%green#Wrong! ~red}\n\n").questions[0]
83
+ assert q.answers == [{:value => "blue", :correct => true, :feedback => "Correct"},
84
+ {:value => "green", :correct => false, :feedback => "Wrong!", :weight => 50.0},
85
+ {:value => "red", :correct => false, :feedback => nil}]
86
+ end
87
+
88
+ def test_short_answer_question
89
+ q = @parser.parse("Who's buried in Grant's tomb?{=Grant =Ulysses S. Grant =Ulysses Grant}\n\n").questions[0]
90
+ assert q.class == Gift::ShortAnswerQuestion
91
+ assert q.text == "Who's buried in Grant's tomb?"
92
+ assert q.answers == [{:feedback => nil, :value => "Grant", :correct => true},
93
+ {:feedback => nil, :value => "Ulysses S. Grant", :correct => true},
94
+ {:feedback => nil, :value => "Ulysses Grant" , :correct => true}]
95
+ end
96
+
97
+ def test_short_answer_question_with_weights
98
+ q = @parser.parse("Name three colors of the rainbow?{=%33.3%red =%33.3%orange =%33.3%yellow =%33.3%green =%33.3%blue =%33.3%indigo =%33.3%violet }\n\n").questions[0]
99
+ q.answers.each do |a|
100
+ assert a[:weight] == 33.3
101
+ end
102
+ end
103
+
104
+ def test_numeric_question
105
+ q = @parser.parse("What is 3 + 4?{#7}\n\n").questions[0]
106
+ assert q.class == Gift::NumericQuestion
107
+ assert q.answers == [{:maximum => 7.0, :minimum => 7.0}]
108
+ end
109
+
110
+ def test_numeric_with_tolerance
111
+ q = @parser.parse("What is the value of PI?{#3.1:0.5}\n\n").questions[0]
112
+ assert q.answers == [{:maximum => 3.6, :minimum => 2.6}]
113
+ end
114
+
115
+ def test_numeric_range
116
+ q = @parser.parse("What is a number from 1 to 5? {#1..5}\n\n").questions[0]
117
+ assert q.answers == [{:maximum => 1.0, :minimum => 5.0}]
118
+ end
119
+
120
+ def test_numeric_multiple_answers
121
+ q = @parser.parse("What is the value of PI?{#3.1415 =%50%3.1 =%25%3 }\n\n").questions[0]
122
+ assert q.answers == [{:maximum => 3.1415, :minimum => 3.1415},
123
+ {:maximum => 3.1, :minimum => 3.1},
124
+ {:maximum => 3.0, :minimum => 3.0}]
125
+ end
126
+
127
+ def test_numeric_neagitve_numbers
128
+ q = @parser.parse("Calculate 2 - 6.{#-4.0}\n\n").questions[0]
129
+ assert q.answers == [{:maximum => -4.0, :minimum => -4.0}]
130
+ end
131
+
132
+ def test_matching_question
133
+ q = @parser.parse("Match the names. { =Charlie -> Chaplin =Groucho -> Marx =Buster -> Keaton =Stan -> Laurel }\n\n").questions[0]
134
+ assert q.class == Gift::MatchQuestion
135
+ assert q.text == "Match the names."
136
+ assert q.answers == {'Charlie' => 'Chaplin', 'Groucho' => 'Marx', 'Buster' => 'Keaton', 'Stan' => 'Laurel'}
137
+ end
138
+
139
+ def test_fill_in_question
140
+ q = @parser.parse("There were {=three} little pigs.\n\n").questions[0]
141
+ assert q.class == Gift::FillInQuestion
142
+ assert q.text == "There were %% little pigs."
143
+ assert q.answers == [{:value => 'three', :correct => true, :feedback => nil}]
144
+ end
145
+
146
+ def test_title
147
+ q = @parser.parse("::Essay One:: Write an essay on the toic of your choice{ }\n\n").questions[0]
148
+ assert q.title == "Essay One"
149
+ end
150
+
151
+ def test_title_from_question
152
+ q = @parser.parse("Write an essay on the toic of your choice{ }\n\n").questions[0]
153
+ assert q.title == q.text
154
+ end
155
+
156
+ def test_comment
157
+ q = @parser.parse("//Here's an easy one\nWrite an essay on the toic of your choice{ }\n\n").questions[0]
158
+ assert q.comment == "Here's an easy one"
159
+ end
160
+
161
+ def test_command
162
+ q = @parser.parse("$COMMAND=1\n\nQuestion{}\n\n")
163
+ assert q.commands == ["COMMAND=1"]
164
+ end
165
+
166
+ def test_category_setting
167
+ q = @parser.parse("$CATEGORY: food \n\nIs apple a food?{T}\n\n$CATEGORY: drink \n\nIs water drinkable?{T}\n\n" )
168
+ assert q.questions[0].category == "food"
169
+ assert q.questions[1].category == "drink"
170
+ end
171
+
172
+ def test_markup_type
173
+ q = @parser.parse("[textile] This *essay* is marked up in textile.{}\n\n").questions[0]
174
+ assert q.markup == "textile"
175
+ end
176
+
177
+ end
178
+
179
+ Test::Unit::UI::Console::TestRunner.run(GiftSemanticTest)
@@ -0,0 +1,243 @@
1
+ #!/usr/bin/env ruby
2
+ # Tests for GIFT parser
3
+
4
+ # This only tests that the input is parsed.
5
+ # so far does not test output.
6
+
7
+ require 'pp'
8
+ require 'test/unit'
9
+ require 'test/unit/ui/console/testrunner'
10
+
11
+ require File.expand_path('../../lib/gift', __FILE__)
12
+ require File.expand_path('../GIFT-examples.rb', __FILE__)
13
+
14
+
15
+ class GiftSyntaxTest < Test::Unit::TestCase
16
+
17
+ attr_accessor :parser
18
+
19
+ def setup
20
+ @parser = GiftParser.new
21
+ end
22
+
23
+ def assert_can_parse(string)
24
+ string << "\n\n" # Insert a blank line at end of string
25
+ assert !@parser.parse(string).nil?, "Failed on:\n#{string}\n Reason:\n#{@parser.failure_reason.inspect}\n"
26
+ end
27
+
28
+ def assert_cannot_parse(string)
29
+ string << "\n\n"
30
+ p = @parser.parse(string)
31
+ assert p.nil?, "Failed on:\n#{string}\n Reason:\nThis should not be correctly parsed, but was.\nParser Output:\n#{p.inspect}"
32
+ end
33
+
34
+ def test_essay_question
35
+ assert_can_parse("Write an essay about something.{}")
36
+ end
37
+
38
+ def test_description_question
39
+ assert_can_parse("This is simply a description")
40
+ end
41
+
42
+ def test_true_false_questions
43
+ assert_can_parse("The sky is blue.{T}")
44
+ assert_can_parse("The sky is blue.{TRUE}")
45
+ assert_can_parse("The sky is green.{F}")
46
+ assert_can_parse("The sky is blue.{FALSE}")
47
+ end
48
+
49
+ def test_feedback
50
+ assert_can_parse("Grant is buried in Grant's tomb.{FALSE#No one is buried in Grant's tomb.}")
51
+ end
52
+
53
+ def test_multiple_choice_question
54
+ assert_can_parse("What color is the sky?{ = Blue ~Green ~Red }")
55
+ end
56
+
57
+ def test_multiple_choice_question_with_feedback
58
+ assert_can_parse('What color is the sky?{ = Blue#Right ~Green ~Red#Very wrong}')
59
+ end
60
+
61
+ def test_multiple_choice_on_multiple_lines
62
+ test_text= <<EOS
63
+ What color is the sky?{
64
+ = Blue#Right
65
+ ~Green
66
+ ~Red
67
+ #Very wrong
68
+ }
69
+ EOS
70
+ assert_can_parse(test_text)
71
+ end
72
+
73
+ def test_multiple_choice_with_weights
74
+ assert_can_parse('Which of these are primary colors?{ ~%33%Blue ~%33%Yellow ~Beige ~%33%Red}')
75
+ end
76
+
77
+ def test_numeric_question
78
+ assert_can_parse("How many pounds in a kilogram?{#2.2:0.1}")
79
+ end
80
+
81
+ def test_numeric_question_with_multiple_answer
82
+ assert_can_parse("How many pounds in a kilogram?{# =2.2:0.1 =%50%2 }")
83
+ end
84
+
85
+ def test_numeric_range_question
86
+ assert_can_parse("::Q5:: What is a number from 1 to 5? {#1..5}")
87
+ end
88
+
89
+ def test_numeric_negative_number
90
+ assert_can_parse("What is 6 - 10?{#-4}")
91
+ end
92
+
93
+ def test_short_answer
94
+ assert_can_parse("Who's buried in Grant's tomb?{=Grant =Ulysses S. Grant =Ulysses Grant}")
95
+ end
96
+
97
+ def test_matching_question
98
+ test_text= <<EOS
99
+ Match the following countries with their corresponding capitals. {
100
+ =Canada -> Ottawa
101
+ =Italy -> Rome
102
+ =Japan -> Tokyo
103
+ =India -> New Delhi
104
+ }
105
+ EOS
106
+ assert_can_parse(test_text)
107
+ end
108
+
109
+ def test_fill_in_question
110
+ assert_can_parse("Little {~blue =red ~green } riding hood.\n")
111
+ assert_can_parse("Two plus two equals {=four =4}.\n")
112
+ end
113
+
114
+ def test_question_with_title
115
+ assert_can_parse('::Colors 1:: Which of these are primary colors?{ ~%33%Blue ~%33%Yellow ~Beige ~%33%Red}')
116
+ end
117
+
118
+ def test_question_with_comment
119
+ assert_can_parse("//This is an easy one\n Which of these are primary colors?{ ~%33%Blue ~%33%Yellow ~Beige ~%33%Red}")
120
+ end
121
+
122
+ def test_multiline_comments
123
+ assert_can_parse("//This is an easy one\n//With more than one line of comment\nWhich of these are primary colors?{ ~%33%Blue ~%33%Yellow ~Beige ~%33%Red}")
124
+ end
125
+
126
+ def test_that_questions_must_be_separated
127
+ test_text = <<EOS
128
+ Who's buried in Grant's tomb?{=Grant =Ulysses S. Grant =Ulysses Grant}
129
+ Which of these are primary colors?{ ~%33%Blue ~%33%Yellow ~Beige ~%33%Red}
130
+ EOS
131
+ assert_cannot_parse(test_text)
132
+
133
+ test_text = <<EOS
134
+ Who's buried in Grant's tomb?{=Grant =Ulysses S. Grant =Ulysses Grant}
135
+
136
+ Which of these are primary colors?{ ~%33%Blue ~%33%Yellow ~Beige ~%33%Red}
137
+ EOS
138
+ assert_can_parse(test_text)
139
+ end
140
+
141
+ def test_escape_left_bracket
142
+ assert_can_parse('Can a \{ be escaped?{=Yes ~No}')
143
+ assert_can_parse('Can a \{ be escaped?{=Yes ~No\{ }')
144
+
145
+ end
146
+
147
+ def test_escape_right_bracket
148
+ assert_can_parse('Can a \} be escaped?{=Yes \} can be escaped. ~No}')
149
+
150
+ end
151
+
152
+ def test_escape_tilde
153
+ assert_can_parse('Can a \~ be escaped?{=Yes ~No \~ can\'t}')
154
+ end
155
+
156
+ def test_escape_hash
157
+ assert_can_parse('Can a \# be escaped?{=Yes \# can ~No}')
158
+ end
159
+
160
+ def test_escape_equals
161
+ assert_can_parse('Can a \= be escaped?{=Yes ~No}')
162
+ end
163
+
164
+ def test_escape_colon
165
+ assert_can_parse('Can a \: be escaped?{=Yes \: can be escaped. ~No}')
166
+ end
167
+
168
+ def test_escapes_in_title
169
+ assert_can_parse('::\{This is in\: Brackets\}::Who\'s buried in Grant\'s tomb?{=Grant =Ulysses S. Grant =Ulysses Grant}')
170
+ end
171
+
172
+ def test_escapes_in_comment
173
+ assert_can_parse("//Escapes in comments are redundant \\: since they end in a \n Question?{}")
174
+ end
175
+
176
+ def test_crlf_support
177
+ assert_can_parse("Can we have DOS style line breaks?{\r\n=yes \r\n~no}\r\n \r\n And is it seen as a line_break?{TRUE}")
178
+ end
179
+
180
+ def test_comment_line_break
181
+ assert_can_parse("//Comment\nWho's buried in Grant's tomb?{=Grant =Ulysses S. Grant =Ulysses Grant}")
182
+ assert_can_parse("//Comment\r\nWho's buried in Grant's tomb?{=Grant =Ulysses S. Grant =Ulysses Grant}")
183
+ end
184
+
185
+ def test_dealing_with_blank_lines_at_top_of_file
186
+ assert_can_parse("\n \n//Comment\nWho's buried in Grant's tomb?{=Grant =Ulysses S. Grant =Ulysses Grant}")
187
+ end
188
+
189
+ def test_dealing_with_blank_lines_at_end_of_file
190
+ assert_can_parse("//Comment\nWho's buried in Grant's tomb?{=Grant =Ulysses S. Grant =Ulysses Grant}\n \n \n ")
191
+ end
192
+
193
+ def test_title_line_breaks
194
+ test_text= <<EOS
195
+ ::Title::
196
+ Match the following countries with their corresponding capitals. {
197
+ =Canada -> Ottawa
198
+ =Italy -> Rome
199
+ =Japan -> Tokyo
200
+ =India -> New Delhi
201
+ }
202
+
203
+ EOS
204
+ assert_can_parse(test_text)
205
+ end
206
+
207
+
208
+ def test_single_short_answer
209
+ test_text = <<EOS
210
+ // ===Short Answer===
211
+ What is your favorite color?{=blue}
212
+ EOS
213
+ assert_can_parse(test_text)
214
+ end
215
+
216
+ def test_multiple_short_answer
217
+ test_text = <<EOS
218
+ ::Multiple Short Answer::
219
+ What are your four favorite colors?{
220
+ =%25%Blue
221
+ =%25% Red
222
+ =%25% Green
223
+ =%25% pink
224
+ =%25% azure
225
+ =%25% gold
226
+ }
227
+ EOS
228
+ assert_can_parse(test_text)
229
+ end
230
+
231
+ def test_moodle_examples
232
+ GiftExamples.examples.each do |key, value|
233
+ assert_can_parse value
234
+ end
235
+ end
236
+
237
+ def test_can_have_commands
238
+ assert_can_parse("$COMMAND=1\n\nQuestion text{}")
239
+ end
240
+ end
241
+
242
+ Test::Unit::UI::Console::TestRunner.run(GiftSyntaxTest)
243
+