wax 0.9.4

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,108 @@
1
+ # This module provides utility methods for working with XML.
2
+ #
3
+ # Copyright 2008 R. Mark Volkmann
4
+ #
5
+ # This file is part of WAX.
6
+ #
7
+ # WAX is free software: you can redistribute it and/or modify it
8
+ # under the terms of the GNU Lesser General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License,
10
+ # or (at your option) any later version.
11
+ #
12
+ # Foobar is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty
14
+ # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15
+ # See the GNU Lesser General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU Lesser General Public License
18
+ # along with WAX. If not, see http://www.gnu.org/licenses.
19
+ #
20
+ # R. Mark Volkmann, Object Computing, Inc.
21
+ module XMLUtil
22
+
23
+ # The default encoding used in XML declarations.
24
+ DEFAULT_ENCODING = "UTF-8"
25
+
26
+ NMTOKEN_PATTERN = /^[A-Za-z][A-Za-z0-9\-_\.]*$/
27
+ XMLSCHEMA_INSTANCE_NS = "http://www.w3.org/1999/XMLSchema-instance"
28
+
29
+ # Escapes special characters in XML text.
30
+ def self.escape(text)
31
+ return text unless text.kind_of?(String)
32
+
33
+ result = ""
34
+ text.each_byte do |c|
35
+ # TODO: Is using [0] the best way to do these comparisons?
36
+ if c == '<'[0]
37
+ result << "&lt;"
38
+ elsif c == '>'[0]
39
+ result << "&gt;"
40
+ elsif c == "'"[0]
41
+ result << "&apos;"
42
+ elsif c == '"'[0]
43
+ result << "&quot;"
44
+ elsif c == '&'[0]
45
+ result << "&amp;"
46
+ else
47
+ result << c
48
+ end
49
+ end
50
+
51
+ result
52
+ end
53
+
54
+ # Determines whether given text is a valid comment.
55
+ def self.is_comment(text)
56
+ /--/ !~ text
57
+ end
58
+
59
+ # Determines whether given text is a name token.
60
+ def self.is_nmtoken(text)
61
+ return false if text == nil
62
+ (NMTOKEN_PATTERN =~ text) != nil
63
+ end
64
+
65
+ # Determines whether given text is a URI.
66
+ def self.is_uri(text)
67
+ # TODO: Finish this
68
+ true
69
+ end
70
+
71
+ # Determines whether given text is a valid XML version.
72
+ def self.is_version(text)
73
+ ["1.0", "1.1", "1.2"].include?(text)
74
+ end
75
+
76
+ # Verifies that the given text is a valid comment
77
+ # and raises an ArgumentError if it isn't.
78
+ def self.verify_comment(text)
79
+ unless is_comment(text)
80
+ raise ArgumentError, "\"#{text}\" is an invalid comment"
81
+ end
82
+ end
83
+
84
+ # Verifies that the given text is a valid name token
85
+ # and raises an ArgumentError if it isn't.
86
+ def self.verify_nmtoken(text)
87
+ unless is_nmtoken(text)
88
+ raise ArgumentError, "\"#{text}\" is an invalid NMTOKEN"
89
+ end
90
+ end
91
+
92
+ # Verifies that the given text is a valid URI
93
+ # and raises an ArgumentError if it isn't.
94
+ def self.verify_uri(text)
95
+ unless is_uri(text)
96
+ raise ArgumentError, "\"#{text}\" is an invalid URI"
97
+ end
98
+ end
99
+
100
+ # Verifies that the given text is a valid XML version
101
+ # and raises an ArgumentError if it isn't.
102
+ def self.verify_version(text)
103
+ unless is_version(text)
104
+ raise ArgumentError, "\"#{text}\" is an invalid XML version"
105
+ end
106
+ end
107
+
108
+ end
@@ -0,0 +1,569 @@
1
+ require 'stringio'
2
+ require 'test/unit'
3
+ require 'wax'
4
+
5
+ # This class provides unit test methods for WAX.
6
+ #
7
+ # Copyright 2008 R. Mark Volkmann
8
+ #
9
+ # This file is part of WAX.
10
+ #
11
+ # WAX is free software: you can redistribute it and/or modify it
12
+ # under the terms of the GNU Lesser General Public License as published
13
+ # by the Free Software Foundation, either version 3 of the License,
14
+ # or (at your option) any later version.
15
+ #
16
+ # Foobar is distributed in the hope that it will be useful,
17
+ # but WITHOUT ANY WARRANTY; without even the implied warranty
18
+ # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19
+ # See the GNU Lesser General Public License for more details.
20
+ #
21
+ # You should have received a copy of the GNU Lesser General Public License
22
+ # along with WAX. If not, see http://www.gnu.org/licenses.
23
+ #
24
+ # author R. Mark Volkmann, Object Computing, Inc.
25
+ class WAXTest < Test::Unit::TestCase
26
+
27
+ def get_file_first_line(file_path)
28
+ file = File.open(file_path)
29
+ line = file.readline
30
+ file.close
31
+ line
32
+ end
33
+
34
+ def setup
35
+ @sio = StringIO.new
36
+ @old_stdout, $stdout = $stdout, @sio
37
+ end
38
+
39
+ def teardown
40
+ $stdout = @old_stdout
41
+ end
42
+
43
+ def test_attributes
44
+ WAX.write do
45
+ set_indent nil
46
+ start 'root'
47
+ attr 'a1', 'v1'
48
+ attr 'a2', 2
49
+ end
50
+
51
+ assert_equal '<root a1="v1" a2="2"/>', @sio.string
52
+ end
53
+
54
+ def test_bad_attribute_name
55
+ assert_raise(ArgumentError) do
56
+ WAX.write do
57
+ start 'root'
58
+ attr '1a', 'value'
59
+ end
60
+ end
61
+ end
62
+
63
+ def test_bad_cdata
64
+ assert_raise(RuntimeError) do
65
+ # Can't call cdata while in prologue section.
66
+ WAX.write { cdata "text" }
67
+ end
68
+ end
69
+
70
+ def test_bad_child
71
+ assert_raise(RuntimeError) do
72
+ WAX.write do
73
+ start "root"
74
+ end_element
75
+ child "child", "text" # can't call child after root is closed
76
+ end
77
+ end
78
+ end
79
+
80
+ def test_bad_close_already_closed
81
+ assert_raise(RuntimeError) do
82
+ WAX.write do
83
+ start "root"
84
+ close
85
+ close # already closed
86
+ end
87
+ end
88
+ end
89
+
90
+ def test_bad_close_without_root
91
+ assert_raise(RuntimeError) do
92
+ WAX.write { close } # didn't write anything yet
93
+ end
94
+ end
95
+
96
+ def test_bad_close_then_write
97
+ assert_raise(RuntimeError) do
98
+ WAX.write do
99
+ start "root"
100
+ close
101
+ start "more" # already closed
102
+ end
103
+ end
104
+ end
105
+
106
+ def test_bad_comment
107
+ assert_raise(ArgumentError) do
108
+ WAX.write do
109
+ start "root"
110
+ comment "foo--bar"
111
+ end
112
+ end
113
+ end
114
+
115
+ def test_bad_dtd_after_root
116
+ assert_raise(RuntimeError) do
117
+ WAX.write do
118
+ start "root"
119
+ dtd "root.dtd" # can't specify DTD after root element
120
+ end
121
+ end
122
+ end
123
+
124
+ def test_bad_element_name
125
+ assert_raise(ArgumentError) do
126
+ WAX.write { start "1root" }
127
+ end
128
+ end
129
+
130
+ def test_bad_end
131
+ assert_raise(RuntimeError) do
132
+ WAX.write { end_element } # haven't called start yet
133
+ end
134
+ end
135
+
136
+ def test_bad_indent_bad_chars
137
+ assert_raise(ArgumentError) do
138
+ WAX.write do
139
+ set_indent "abc" # must be nil, "", spaces or a single tab
140
+ end
141
+ end
142
+ end
143
+
144
+ def test_bad_indent_negative
145
+ assert_raise(ArgumentError) do
146
+ WAX.write { set_indent(-1) } # must be >= 0
147
+ end
148
+ end
149
+
150
+ def test_bad_indent_multiple_tabs
151
+ assert_raise(ArgumentError) do
152
+ WAX.write { set_indent "\t\t" } # more than one tab
153
+ end
154
+ end
155
+
156
+ def test_bad_indent_too_large
157
+ assert_raise(ArgumentError) do
158
+ WAX.write { set_indent 5 } # must be <= 4
159
+ end
160
+ end
161
+
162
+ def test_bad_namespace_duplicate_prefix
163
+ assert_raise(ArgumentError) do
164
+ # Can't define same namespace prefix more than once in the same scope.
165
+ WAX.write do
166
+ start "root"
167
+ namespace "tns", "http://www.ociweb.com/tns"
168
+ namespace "tns", "http://www.ociweb.com/tns"
169
+ end
170
+ end
171
+ end
172
+
173
+ def test_bad_namespace_multiple_default
174
+ assert_raise(ArgumentError) do
175
+ # Can't define default namespace more than once in the same scope.
176
+ WAX.write do
177
+ start "root"
178
+ namespace "http://www.ociweb.com/tns1"
179
+ namespace "http://www.ociweb.com/tns2"
180
+ end
181
+ end
182
+ end
183
+
184
+ def test_bad_prefix
185
+ assert_raise(ArgumentError) do
186
+ WAX.write do
187
+ start "root"
188
+ start "parent"
189
+ namespace "foo", "http://www.ociweb.com/foo"
190
+ child "foo", "child1", "one"
191
+ end_element
192
+ child "foo", "child2", "two"
193
+ end
194
+ end
195
+ end
196
+
197
+ def test_bad_text
198
+ assert_raise(RuntimeError) do
199
+ WAX.write { text "text" } # haven't called start yet
200
+ end
201
+ end
202
+
203
+ def test_bad_write
204
+ assert_raise(IOError) do
205
+ file_path = "temp.xml"
206
+ WAX.write(file_path) do
207
+ start "root"
208
+ @writer.close # closed the Writer instead of allowing WAX to do it
209
+ File.delete(file_path)
210
+ close # attempting to write more after the Writer was closed
211
+ end
212
+ end
213
+ end
214
+
215
+ def test_bad_write_file
216
+ assert_raise(Errno::EISDIR) do
217
+ file_path = "." # the current directory, not a file
218
+ WAX.write(file_path) {}
219
+ end
220
+ end
221
+
222
+ def test_bad_xslt_after_root
223
+ assert_raise(RuntimeError) do
224
+ WAX.write do
225
+ start "root"
226
+ xslt "root.xslt" # can't write pi after root element
227
+ end
228
+ end
229
+ end
230
+
231
+ def test_big
232
+ WAX.write do
233
+ start "root"
234
+ nl_text "text #1"
235
+ child "child1", "text"
236
+ nl_text "text #2"
237
+ start "child2"
238
+ attr "a1", "v1"
239
+ end_element
240
+ nl_text "text #3"
241
+ end
242
+
243
+ xml = "<root>\n" +
244
+ " text #1\n" +
245
+ " <child1>text</child1>\n" +
246
+ " text #2\n" +
247
+ " <child2 a1=\"v1\"/>\n" +
248
+ " text #3\n" +
249
+ "</root>"
250
+
251
+ assert_equal xml, @sio.string
252
+ end
253
+
254
+ def test_blank_line
255
+ WAX.write do
256
+ start "root"
257
+ blank_line
258
+ end
259
+
260
+ xml =
261
+ "<root>\n" +
262
+ "\n" +
263
+ "</root>"
264
+ assert_equal xml, @sio.string
265
+ end
266
+
267
+ def test_cdata
268
+ WAX.write do
269
+ start "root"
270
+ cdata "1<2>3&4'5\"6"
271
+ end
272
+
273
+ xml =
274
+ "<root>\n" +
275
+ " <![CDATA[1<2>3&4'5\"6]]>\n" +
276
+ "</root>"
277
+ assert_equal xml, @sio.string
278
+ end
279
+
280
+ def test_comment
281
+ WAX.write do
282
+ comment "comment #1"
283
+ comment "comment #2"
284
+ start "root"
285
+ comment "comment #3"
286
+ end
287
+
288
+ xml =
289
+ "<!-- comment #1 -->\n" +
290
+ "<!-- comment #2 -->\n" +
291
+ "<root>\n" +
292
+ " <!-- comment #3 -->\n" +
293
+ "</root>"
294
+
295
+ assert_equal xml, @sio.string
296
+ end
297
+
298
+ def test_dtd
299
+ WAX.write do
300
+ dtd "http://www.ociweb.com/xml/root.dtd"
301
+ start "root"
302
+ end
303
+
304
+ xml =
305
+ "<!DOCTYPE root SYSTEM \"http://www.ociweb.com/xml/root.dtd\">\n" +
306
+ "<root/>"
307
+
308
+ assert_equal xml, @sio.string
309
+ end
310
+
311
+ def test_empty
312
+ WAX.write do
313
+ set_indent nil
314
+ start "root"
315
+ end
316
+
317
+ assert_equal "<root/>", @sio.string
318
+ end
319
+
320
+ def test_entity_def
321
+ WAX.write do
322
+ set_indent nil
323
+ entity_def "name", "value"
324
+ start "root"
325
+ end
326
+
327
+ xml = "<!DOCTYPE root [<!ENTITY name \"value\">]><root/>"
328
+ assert_equal xml, @sio.string
329
+ end
330
+
331
+ def test_external_entity_def
332
+ WAX.write do
333
+ set_indent nil
334
+ external_entity_def "name", "value"
335
+ start "root"
336
+ end
337
+
338
+ xml = "<!DOCTYPE root [<!ENTITY name SYSTEM \"value\">]><root/>"
339
+ assert_equal xml, @sio.string
340
+ end
341
+
342
+ def test_escape
343
+ WAX.write do
344
+ set_indent nil
345
+ start "root"
346
+ text "abc<def>ghi'jkl\"mno&pqr"
347
+ end
348
+
349
+ xml = "<root>abc&lt;def&gt;ghi&apos;jkl&quot;mno&amp;pqr</root>"
350
+ assert_equal xml, @sio.string
351
+ end
352
+
353
+ def test_extra_end
354
+ assert_raise(RuntimeError) do
355
+ WAX.write do
356
+ set_indent nil
357
+ start "root"
358
+ end_element
359
+ end_element
360
+ end
361
+ end
362
+ end
363
+
364
+ def test_get_indent
365
+ wax = WAX.new
366
+ assert_equal " ", wax.get_indent
367
+
368
+ wax.set_indent " "
369
+ assert_equal " ", wax.get_indent
370
+
371
+ wax.set_indent "\t"
372
+ assert_equal "\t", wax.get_indent
373
+
374
+ wax.set_indent nil
375
+ assert_equal nil, wax.get_indent
376
+
377
+ wax.set_indent ""
378
+ assert_equal "", wax.get_indent
379
+ end
380
+
381
+ def test_indent_by_num
382
+ WAX.write do
383
+ set_indent 2
384
+ start "root"
385
+ child "child", "text"
386
+ end
387
+
388
+ xml = "<root>\n" + " <child>text</child>\n" + "</root>"
389
+ assert_equal xml, @sio.string
390
+ end
391
+
392
+ def test_indent_by_string
393
+ WAX.write do
394
+ set_indent " "
395
+ start "root"
396
+ child "child", "text"
397
+ end
398
+
399
+ xml = "<root>\n" + " <child>text</child>\n" + "</root>"
400
+ assert_equal xml, @sio.string
401
+ end
402
+
403
+ def test_namespace
404
+ WAX.write do
405
+ set_indent nil
406
+ start "root"
407
+ namespace "http://www.ociweb.com/tns1"
408
+ namespace "tns2", "http://www.ociweb.com/tns2"
409
+ namespace "tns3", "http://www.ociweb.com/tns3"
410
+ end
411
+
412
+ xml = "<root" +
413
+ " xmlns=\"http://www.ociweb.com/tns1\"" +
414
+ " xmlns:tns2=\"http://www.ociweb.com/tns2\"" +
415
+ " xmlns:tns3=\"http://www.ociweb.com/tns3\"/>"
416
+ assert_equal xml, @sio.string
417
+ end
418
+
419
+ def test_no_indent
420
+ WAX.write do
421
+ set_indent nil
422
+ start "root"
423
+ child "child", "text"
424
+ end
425
+
426
+ xml = "<root><child>text</child></root>"
427
+ assert_equal xml, @sio.string
428
+ end
429
+
430
+ def test_no_root
431
+ assert_raise(RuntimeError) do
432
+ WAX.write {}
433
+ end
434
+ end
435
+
436
+ def test_processing_instruction_in_prologue
437
+ WAX.write do
438
+ processing_instruction "xml-stylesheet",
439
+ "type=\"text/xsl\" href=\"http://www.ociweb.com/foo.xslt\""
440
+ start "root"
441
+ end
442
+
443
+ xml =
444
+ "<?xml-stylesheet type=\"text/xsl\" href=\"http://www.ociweb.com/foo.xslt\"?>\n" +
445
+ "<root/>"
446
+ assert_equal xml, @sio.string
447
+ end
448
+
449
+ def test_processing_instruction_after_prologue
450
+ WAX.write do
451
+ set_indent nil
452
+ start "root"
453
+ processing_instruction "target", "data"
454
+ end
455
+
456
+ xml = "<root><?target data?></root>"
457
+ assert_equal xml, @sio.string
458
+ end
459
+
460
+ def test_prefix
461
+ WAX.write do
462
+ set_indent nil
463
+ start "foo", "root"
464
+ attr "bar", "baz"
465
+ # Note that the namespace is defined after it is used,
466
+ # but on the same element, which should be allowed.
467
+ namespace "foo", "http://www.ociweb.com/foo"
468
+ end
469
+
470
+ xml = "<foo:root bar=\"baz\" " +
471
+ "xmlns:foo=\"http://www.ociweb.com/foo\"/>";
472
+ assert_equal xml, @sio.string
473
+ end
474
+
475
+ def test_schemas_with_indent
476
+ WAX.write do
477
+ start "root"
478
+ namespace nil, "http://www.ociweb.com/tns1", "tns1.xsd"
479
+ namespace "tns2", "http://www.ociweb.com/tns2", "tns2.xsd"
480
+ end
481
+
482
+ xml = "<root\n" +
483
+ " xmlns=\"http://www.ociweb.com/tns1\"\n" +
484
+ " xmlns:tns2=\"http://www.ociweb.com/tns2\"\n" +
485
+ " xmlns:xsi=\"http://www.w3.org/1999/XMLSchema-instance\"\n" +
486
+ " xsi:schemaLocation=\"" +
487
+ "http://www.ociweb.com/tns1 tns1.xsd\n" +
488
+ " http://www.ociweb.com/tns2 tns2.xsd" +
489
+ "\"/>"
490
+ assert_equal xml, @sio.string
491
+ end
492
+
493
+ def test_schemas_without_indent
494
+ WAX.write do
495
+ set_indent nil
496
+ start "root"
497
+ namespace nil, "http://www.ociweb.com/tns1", "tns1.xsd"
498
+ namespace "tns2", "http://www.ociweb.com/tns2", "tns2.xsd"
499
+ end
500
+
501
+ xml = "<root" +
502
+ " xmlns=\"http://www.ociweb.com/tns1\"" +
503
+ " xmlns:tns2=\"http://www.ociweb.com/tns2\"" +
504
+ " xmlns:xsi=\"http://www.w3.org/1999/XMLSchema-instance\"" +
505
+ " xsi:schemaLocation=\"" +
506
+ "http://www.ociweb.com/tns1 tns1.xsd " +
507
+ "http://www.ociweb.com/tns2 tns2.xsd" +
508
+ "\"/>"
509
+ assert_equal xml, @sio.string
510
+ end
511
+
512
+ def test_text
513
+ WAX.write do
514
+ set_indent nil
515
+ start "root"
516
+ text "text"
517
+ end
518
+
519
+ assert_equal "<root>text</root>", @sio.string
520
+ end
521
+
522
+ def test_trust_me_false
523
+ assert_raise(ArgumentError) do
524
+ WAX.write do
525
+ set_trust_me false
526
+ # Since error checking is turned on,
527
+ # element names must be valid and text is escaped.
528
+ start "123"
529
+ text "<>&'\""
530
+ end
531
+ end
532
+ end
533
+
534
+ def test_trust_me_true
535
+ WAX.write do
536
+ set_trust_me true
537
+ set_indent nil
538
+ # Since error checking is turned off,
539
+ # invalid element names and unescaped text are allowed.
540
+ start "123"
541
+ text "<>&'\""
542
+ end
543
+
544
+ assert_equal "<123><>&'\"</123>", @sio.string
545
+ end
546
+
547
+ def test_write_file
548
+ file_path = "temp.xml"
549
+ WAX.write(file_path) do
550
+ set_indent nil
551
+ start "root"
552
+ text "text"
553
+ end
554
+
555
+ assert_equal "<root>text</root>", get_file_first_line(file_path)
556
+ File.delete(file_path)
557
+ end
558
+
559
+ def test_xml_declaration
560
+ WAX.write($stdout, "1.0") do
561
+ set_indent nil
562
+ start "root"
563
+ end
564
+
565
+ xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<root/>"
566
+ assert_equal xml, @sio.string
567
+ end
568
+
569
+ end