graffle 0.1.9 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/History.txt +9 -0
  2. data/Manifest.txt +15 -8
  3. data/Rakefile.hoe +1 -2
  4. data/bin/interpret-intentions.rb +51 -0
  5. data/examples/objects with notes.rb +1 -0
  6. data/examples/old-style-rails-workflow-test.expected +11 -0
  7. data/examples/old-style-rails-workflow-test.rb +43 -0
  8. data/examples/rails-workflow-test.rb +7 -6
  9. data/examples/sheet with key.expected +5 -0
  10. data/examples/sheet with key.graffle +0 -0
  11. data/examples/sheet with key.rb +38 -0
  12. data/graffle.tmproj +242 -75
  13. data/lib/graffle.rb +11 -2
  14. data/lib/graffle/nodoc/hacks.rb +1 -0
  15. data/lib/graffle/point.rb +1 -0
  16. data/lib/graffle/stereotypes.rb +34 -3
  17. data/lib/graffle/version.rb +3 -1
  18. data/lib/graphical_tests_for_rails.rb +11 -9
  19. data/lib/graphical_tests_for_rails/jumbled-string-canonicalizer.rb +117 -0
  20. data/lib/graphical_tests_for_rails/orderings.rb +1 -2
  21. data/lib/graphical_tests_for_rails/picture-applier.rb +257 -0
  22. data/lib/graphical_tests_for_rails/stereotype-extensions.rb +111 -0
  23. data/lib/graphical_tests_for_rails/text-appliers.rb +10 -84
  24. data/lib/graphical_tests_for_rails/user-intention.rb +250 -0
  25. data/test/abstract-graphic-tests.rb +10 -0
  26. data/test/container-tests.rb +29 -0
  27. data/test/graphical_tests_for_rails/jumbled-string-canonicalizer-tests.rb +174 -0
  28. data/test/graphical_tests_for_rails/new-style-picture-applier-tests.rb +286 -0
  29. data/test/graphical_tests_for_rails/old-style-picture-applier-tests.rb +129 -0
  30. data/test/graphical_tests_for_rails/user-intention-tests.rb +389 -0
  31. data/test/graphical_tests_for_rails/util.rb +9 -0
  32. data/test/sheet-tests.rb +21 -0
  33. data/test/text-tests.rb +6 -0
  34. metadata +25 -26
  35. data/design-notes/graphical-tests-for-rails-objects.graffle +0 -644
  36. data/lib/graphical_tests_for_rails/graphic-volunteers.rb +0 -75
  37. data/lib/graphical_tests_for_rails/picture-appliers.rb +0 -225
  38. data/lib/graphical_tests_for_rails/volunteer-pool.rb +0 -115
  39. data/test/graphical_tests_for_rails/deprecated-graphic-interpreter-tests.rb +0 -121
  40. data/test/graphical_tests_for_rails/graphic-volunteer-tests.rb +0 -218
  41. data/test/graphical_tests_for_rails/picture-applier-tests.rb +0 -215
  42. data/test/graphical_tests_for_rails/text-applier-tests.rb +0 -111
@@ -101,4 +101,14 @@ class TestAbstractGraphics < Test::Unit::TestCase
101
101
  assert_true(s.find_by_id(333).note.behaves_like?(Graffle::Note))
102
102
  end
103
103
 
104
+ def test_abstract_graphics_in_collections_can_delete_themselves
105
+ s = sheet {
106
+ with shaped_graphic { graffle_id_is 1}
107
+ }
108
+
109
+ g = s.find_by_id(1)
110
+ g.delete_yourself
111
+ assert_equal(nil, s.find_by_id(1))
112
+ end
113
+
104
114
  end
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Brian Marick on 2007-07-25.
4
+ # Copyright (c) 2007. All rights reserved.
5
+
6
+ # TODO: Some tests from sheets and groups could probably be moved here.
7
+
8
+
9
+ require "set-standalone-test-paths.rb" unless $started_from_rakefile
10
+ require 'test/unit'
11
+ require 's4t-utils'
12
+ include S4tUtils
13
+ require 'test/util'
14
+
15
+ require 'graffle'
16
+
17
+ class TestContainers < Test::Unit::TestCase
18
+ include Graffle
19
+ include Graffle::Builders
20
+
21
+ def test_deletion
22
+ s = sheet {
23
+ with line_graphic { graffle_id_is 1 }
24
+ }
25
+ g = s.find_by_id(1)
26
+ s.delete_graphic(g)
27
+ assert_nil(s.find_by_id(1))
28
+ end
29
+ end
@@ -0,0 +1,174 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Brian Marick on 2007-07-29.
4
+ # Copyright (c) 2007. All rights reserved.
5
+
6
+ require 'test/unit'
7
+ require 's4t-utils'
8
+ include S4tUtils
9
+
10
+ require "../set-standalone-test-paths.rb" unless $started_from_rakefile
11
+
12
+ require 'extensions/string'
13
+ require 'test/graphical_tests_for_rails/util'
14
+ require 'graphical_tests_for_rails/jumbled-string-canonicalizer'
15
+
16
+ class TestJumbledStringCanonicalizer < Test::Unit::TestCase
17
+ include GraphicalTestsForRails
18
+
19
+ def test_simple_canonicalization_selects_one_string
20
+ patterns = {
21
+ [/\b(ones?)\b/] => "one",
22
+ }
23
+
24
+ izer = JumbledStringCanonicalizer.new(patterns)
25
+ canonical = izer.canonicalize(s = "they are the ones.")
26
+ assert_equal("one", canonical.value)
27
+ assert_equal(s, canonical.source)
28
+ assert_equal([['ones']], canonical.raw_reason)
29
+ assert_equal("'ones'", canonical.pretty_reason)
30
+ end
31
+
32
+ def test_canonicalization_can_have_more_than_one_regexp
33
+ patterns = {
34
+ [/\b(ones?\stwos?)\b/] => "wrong",
35
+ [/\b(ones?)\b/, /\b(twos?)\b/] => "one two",
36
+ }
37
+
38
+ izer = JumbledStringCanonicalizer.new(patterns)
39
+ canonical = izer.canonicalize(s = "two is two ones")
40
+ assert_equal("one two", canonical.value)
41
+ assert_equal(s, canonical.source)
42
+ assert_equal([['ones'], ['two']], canonical.raw_reason)
43
+ assert_equal("'ones'; and also 'two'", canonical.pretty_reason)
44
+ end
45
+
46
+ def test_raw_reasons_are_available
47
+ patterns = {
48
+ [/\b(a)\s+(cs?)\b/, /\b(d+).*?(e+).*?(f)\b/] => "success",
49
+ }
50
+
51
+ izer = JumbledStringCanonicalizer.new(patterns)
52
+ canonical = izer.canonicalize(s = "ddd a cs ee not included f")
53
+ assert_equal("success", canonical.value)
54
+
55
+ assert_equal([["a", "cs"], ["ddd", "ee", "f"]], canonical.raw_reason)
56
+ end
57
+
58
+ def test_raw_reasons_do_not_include_nil_alternatives
59
+ patterns = {
60
+ [/(a)|(b)/] => "success",
61
+ }
62
+
63
+ izer = JumbledStringCanonicalizer.new(patterns)
64
+ canonical = izer.canonicalize(s = "a") # $1 = 'a', $2 = nil
65
+ assert_equal([["a"]], canonical.raw_reason)
66
+ end
67
+
68
+ def test_reasons_can_be_produced_prettily
69
+ patterns = {
70
+ [/\b(a)\s+(cs?)\b/, /\b(d+).*?(e+).*?(f)\b/] => "success",
71
+ }
72
+
73
+ izer = JumbledStringCanonicalizer.new(patterns)
74
+ canonical = izer.canonicalize(s = "ddd a cs ee not included f")
75
+ assert_equal("success", canonical.value)
76
+ assert_equal("'a' and 'cs'; and also 'ddd', 'ee', and 'f'", canonical.pretty_reason)
77
+ end
78
+
79
+ def test_normally_no_match_is_an_error
80
+ izer = JumbledStringCanonicalizer.new
81
+ assert_raise_with_matching_message(StandardError,
82
+ /Cannot recognize.*'foo'/) {
83
+ izer.canonicalize("foo")
84
+ }
85
+ end
86
+
87
+ def test_the_no_match_error_message_can_be_customized
88
+ izer = JumbledStringCanonicalizer.new {
89
+ no_match_message {|source| "Dude! What's with '#{source}'?"}
90
+ }
91
+ assert_raise_with_matching_message(StandardError, /Dude! What's with 'foo'\?/) {
92
+ izer.canonicalize("foo")
93
+ }
94
+ end
95
+
96
+ def test_there_can_be_a_default_value_in_case_of_no_match
97
+ patterns = {
98
+ [/\b(ones?\stwos?)\b/] => "wrong",
99
+ [/\b(ones?)\b/, /\b(twos?)\b/] => "one two",
100
+ }
101
+ izer = JumbledStringCanonicalizer.new(patterns) {
102
+ default "default"
103
+ }
104
+ canonical = izer.canonicalize(s = "ddd a cs ee not included f")
105
+ assert_equal("default", canonical.value)
106
+ assert_equal(s, canonical.source)
107
+ assert_equal([], canonical.raw_reason)
108
+ assert_equal("no matching words", canonical.pretty_reason)
109
+ end
110
+
111
+ def test_nil_is_a_fine_default_value
112
+ izer = JumbledStringCanonicalizer.new {
113
+ default nil
114
+ }
115
+ canonical = izer.canonicalize(s = "match me!")
116
+ assert_equal(nil, canonical.value)
117
+ assert_equal(s, canonical.source)
118
+ assert_equal([], canonical.raw_reason)
119
+ assert_equal("no matching words", canonical.pretty_reason)
120
+ end
121
+
122
+ def test_normally_multiple_matches_are_an_error
123
+ patterns = {
124
+ [/(one) (two)/] => 'one',
125
+ [/(two)/] => 'two'
126
+ }
127
+ izer = JumbledStringCanonicalizer.new(patterns)
128
+ assert_raise_with_matching_message(StandardError,
129
+ /Multiple matches in 'one two'.*'one' and 'two'/m) {
130
+ # Can't check both match reasons because order is unknown.
131
+ izer.canonicalize("one two")
132
+ }
133
+ end
134
+
135
+ def test_the_multiple_match_error_can_be_customized_using_pretty_reasons
136
+ patterns = {
137
+ [/(one) (two)/, /(one)/] => 'one',
138
+ [/(one) (two)/] => 'two'
139
+ }
140
+ izer = JumbledStringCanonicalizer.new(patterns) {
141
+ multiple_match_message { | source, pretty_reasons, raw_reasons |
142
+ pretty_reasons = pretty_reasons.sort_by { |r| r.length }
143
+ "Could not match '#{source}' for multiple reasons:\n" +
144
+ "One match is against these words: #{pretty_reasons[0]}\n" +
145
+ pretty_reasons[1..-1].collect { |r| "Another is against these: #{r}" }.join("\n")
146
+ }
147
+ }
148
+ assert_raise_with_matching_message(StandardError,
149
+ /not match 'one two'.*One match.*'one' and 'two'.*Another.*'one' and 'two'.*also 'one'/m) {
150
+ izer.canonicalize("one two")
151
+ }
152
+ end
153
+
154
+ def test_multiple_matches_can_be_disambiguated
155
+ patterns = {
156
+ [/(one) (two)/] => 'one',
157
+ [/(two)|(one)|(three)/] => 'two'
158
+ }
159
+ izer = JumbledStringCanonicalizer.new(patterns) {
160
+ disambiguate { |a, b|
161
+ words = proc { | x | - x.raw_reason.flatten.compact.length }
162
+ words[a] <=> words[b]
163
+ }
164
+ }
165
+ canonical = izer.canonicalize(s = "one two")
166
+ assert_equal("one", canonical.value)
167
+ assert_equal(s, canonical.source)
168
+ assert_equal("'one' and 'two'", canonical.pretty_reason)
169
+ end
170
+
171
+
172
+
173
+
174
+ end
@@ -0,0 +1,286 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by Brian Marick on 2007-07-25.
4
+ # Copyright (c) 2007. All rights reserved.
5
+
6
+ require 'test/unit'
7
+ require 's4t-utils'
8
+ include S4tUtils
9
+
10
+ require "../set-standalone-test-paths.rb" unless $started_from_rakefile
11
+
12
+ require 'extensions/string'
13
+ require 'test/graphical_tests_for_rails/util'
14
+ require 'graffle'
15
+ require 'graphical_tests_for_rails/picture-applier'
16
+
17
+ class NewStylePictureApplierTest < Test::Unit::TestCase
18
+ include Graffle
19
+ include Graffle::Builders
20
+ include GraphicalTestsForRails
21
+
22
+ def setup
23
+ @sheet = sheet {
24
+ with shaped_graphic {
25
+ graffle_id_is 1
26
+ with_content "just a comment"
27
+ with_note 'ignore'
28
+ }
29
+
30
+ with shaped_graphic {
31
+ graffle_id_is 11
32
+ with_content "PAGE"
33
+ }
34
+
35
+ with line_label {
36
+ for_line 22
37
+ with_content "fred tests the label text"
38
+ }
39
+ with line_graphic {
40
+ graffle_id_is 22
41
+ }
42
+ with shaped_graphic {
43
+ with_content "Key"
44
+ with_note %Q{skip graphics with an "ignore" annotation
45
+
46
+ line label contents are user actions with args in quotes
47
+ shape content names a page
48
+ }
49
+ }
50
+
51
+ }
52
+ end
53
+
54
+
55
+ def test_from_sheet_requires_key
56
+ assert_raises_with_matching_message(StandardError, /has no key/) do
57
+ PictureApplier.from_sheet(sheet)
58
+ end
59
+ end
60
+
61
+
62
+ def test_but_defaults_can_be_used_instead_of_key
63
+ applier = PictureApplier.from_sheet(sheet,
64
+ "notes are claims with quoted args",
65
+ "contents are page names")
66
+
67
+ matches = lambda do |graphic|
68
+ ints = applier.intentions_that_apply_to(graphic)
69
+ ints.collect { |i| i.source }
70
+ end
71
+
72
+ benoted = shaped_graphic { with_note "fred" }
73
+ contented = shaped_graphic { with_content "barry" }
74
+
75
+ assert_equal(["notes are claims with quoted args"], matches[benoted])
76
+ assert_equal(["contents are page names"], matches[contented])
77
+ end
78
+
79
+
80
+ def test_from_sheet_key_requires_notes
81
+ assert_raises_with_matching_message(StandardError, /supposed to have an annotation/) do
82
+ s = sheet {
83
+ with shaped_graphic {
84
+ with_content "key"
85
+ }
86
+ }
87
+ PictureApplier.from_sheet(s)
88
+ end
89
+ end
90
+
91
+ def test_from_sheet_key_contents_must_be_recognized
92
+ assert_raises_with_matching_message(StandardError, /does not refer to any graphic/) do
93
+ s = sheet {
94
+ with shaped_graphic {
95
+ with_content "key"
96
+ with_note "missing"
97
+ }
98
+ }
99
+ PictureApplier.from_sheet(s)
100
+ end
101
+ end
102
+
103
+ def test_graphics_are_filtered_by_place
104
+ any_note = "match any note (action would be skip)"
105
+ any_shape_note = "match any shape note (action would be skip)"
106
+ kp = PictureApplier.from_written_intentions(any_note, any_shape_note)
107
+
108
+ matches = lambda do |graphic|
109
+ ints = kp.intentions_that_apply_to(graphic)
110
+ ints.collect { |i| i.source }
111
+ end
112
+
113
+ assert_equal([], matches[shaped_graphic])
114
+ assert_equal([], matches[shaped_graphic {with_content "t"}])
115
+ assert_equal([any_note],
116
+ matches[besheeted(line_graphic { with_note "hi"})])
117
+ assert_equal([any_note, any_shape_note],
118
+ matches[shaped_graphic {with_note "t"}])
119
+ end
120
+
121
+ def test_graphics_can_be_ignored
122
+ skipper = "skip if note says 'ignore'"
123
+ kp = PictureApplier.from_written_intentions(skipper)
124
+
125
+ matches = lambda do |graphic|
126
+ ints = kp.intentions_that_apply_to(graphic)
127
+ ints.collect { |i| i.source }
128
+ end
129
+
130
+ assert_equal([], matches[shaped_graphic])
131
+ assert_equal([], matches[shaped_graphic {with_content "t"}])
132
+ assert_equal([],
133
+ matches[besheeted(line_graphic { with_note "hi"})])
134
+ assert_equal([skipper],
135
+ matches[shaped_graphic {with_note "ignore"}])
136
+ end
137
+
138
+ def test_application_of_intentions
139
+ @applier = PictureApplier.from_sheet(@sheet)
140
+ target = CommandRecorder.new
141
+ @applier.apply(@sheet.graphics_without_labels, target)
142
+ assert_equal(['assert_on_page("page")', 'tests_the_label_text("fred")'],
143
+ target.record)
144
+ end
145
+
146
+ def test_action_must_be_recognized
147
+ assert_raises_with_matching_message(StandardError, /doesn't say what should happen/) do
148
+ s = sheet {
149
+ with shaped_graphic {
150
+ with_content "key"
151
+ with_note "note with friends dines"
152
+ }
153
+ }
154
+ PictureApplier.from_sheet(s)
155
+ end
156
+ end
157
+
158
+ def test_different_lines_can_cause_different_actions
159
+ s = sheet {
160
+ with shaped_graphic {
161
+ graffle_id_is 1
162
+ with_content %Q{page\njohn logs in as "john".}
163
+ }
164
+
165
+ with shaped_graphic {
166
+ with_content "Key"
167
+ with_note %Q{shape content with one word is page name
168
+ otherwise, shape content is user action with args in quotes
169
+ }
170
+ }
171
+ }
172
+ applier = PictureApplier.from_sheet(s)
173
+ target = CommandRecorder.new
174
+ applier.apply(s.graphics_without_labels, target)
175
+ assert_equal(['assert_on_page("page")', 'logs_in_as("john", "john")'],
176
+ target.record)
177
+ end
178
+
179
+ def test_empty_graphics_are_gracefully_ignored
180
+ s = sheet {
181
+ with shaped_graphic
182
+ with line_graphic
183
+ }
184
+
185
+ applier = PictureApplier.from_written_intentions(
186
+ "notes are page names",
187
+ "contents are page names"
188
+ )
189
+
190
+ applier.apply(s.graphics_without_labels, nil)
191
+ end
192
+
193
+ def test_non_empty_graphics_that_do_not_match_are_too
194
+ s = sheet {
195
+ with shaped_graphic { with_content "hi"}
196
+ with line_label {
197
+ for_line 1
198
+ with_content "bye"
199
+ }
200
+ with line_graphic {
201
+ graffle_id_is 1
202
+ }
203
+ }
204
+
205
+ applier = PictureApplier.from_written_intentions(
206
+ "notes are page names"
207
+ )
208
+
209
+ applier.apply(s.graphics_without_labels, nil)
210
+ end
211
+
212
+ def test_lines_that_do_not_match_are_ignored
213
+ s = sheet {
214
+ with shaped_graphic { with_content "only line ignored"}
215
+ with line_label {
216
+ for_line 1
217
+ with_content "multiple lines\n\nare ignored but single are\nnot"
218
+ }
219
+ with line_graphic {
220
+ graffle_id_is 1
221
+ }
222
+ }
223
+
224
+ applier = PictureApplier.from_written_intentions(
225
+ "content lines with one word are page names"
226
+ )
227
+
228
+ target = CommandRecorder.new
229
+ applier.apply(s.graphics_without_labels, target)
230
+ assert_equal(['assert_on_page("not")'],
231
+ target.record)
232
+ end
233
+
234
+
235
+
236
+ def four_counting_shaped_graphics
237
+ sheet {
238
+ with shaped_graphic { with_content "first" }
239
+ with shaped_graphic { with_content "second" }
240
+ with shaped_graphic { with_content "third" }
241
+ with shaped_graphic { with_content "yikes!"}
242
+ }.graphics
243
+ end
244
+
245
+ def four_graphic_contents
246
+ four_counting_shaped_graphics.collect { |g| g.content.as_plain_text }
247
+ end
248
+
249
+
250
+
251
+ class ThriceThenDie
252
+ include Test::Unit::Assertions
253
+
254
+ def initialize
255
+ @count = 0
256
+ end
257
+
258
+ def assert_on_page(value)
259
+ @count += 1
260
+ assert_equal("right", "wrong") if @count >= 4
261
+ end
262
+
263
+ end
264
+
265
+
266
+ def assert_thrice_then_die_log(exc, expected_echoed)
267
+ assert_match(/right.*expected.*was.*wrong/m, exc.message)
268
+ expected = expected_echoed.collect do |w|
269
+ "assert_on_page\\(" + w.inspect + "\\)"
270
+ end.join('.*')
271
+ assert_match(/#{expected}/m, exc.message)
272
+ end
273
+
274
+ def test_logging_prints_list_of_calls_into_target
275
+ @applier = PictureApplier.from_written_intentions(
276
+ "shape content is page name"
277
+ )
278
+ begin
279
+ @applier.apply(four_counting_shaped_graphics, ThriceThenDie.new)
280
+ flunk "never reached"
281
+ rescue Test::Unit::AssertionFailedError => e
282
+ assert_thrice_then_die_log(e, four_graphic_contents)
283
+ end
284
+ end
285
+
286
+ end