gift-parser 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+