PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/test/yaml/test_yaml.rb

https://github.com/fizx/ruby
Ruby | 1326 lines | 1173 code | 68 blank | 85 comment | 5 complexity | f2a23eb6e6b68ef22b53bb05480e162a MD5 | raw file
Possible License(s): LGPL-2.1, AGPL-3.0, GPL-2.0, BSD-3-Clause
  1. # -*- mode: ruby; ruby-indent-level: 4; tab-width: 4 -*-
  2. # vim:sw=4:ts=4
  3. # $Id$
  4. #
  5. require 'test/unit'
  6. require 'yaml'
  7. # [ruby-core:01946]
  8. module YAML_Tests
  9. StructTest = Struct::new( :c )
  10. end
  11. class YAML_Unit_Tests < Test::Unit::TestCase
  12. #
  13. # Convert between YAML and the object to verify correct parsing and
  14. # emitting
  15. #
  16. def assert_to_yaml( obj, yaml )
  17. assert_equal( obj, YAML::load( yaml ) )
  18. assert_equal( obj, YAML::parse( yaml ).transform )
  19. assert_equal( obj, YAML::load( obj.to_yaml ) )
  20. assert_equal( obj, YAML::parse( obj.to_yaml ).transform )
  21. assert_equal( obj, YAML::load(
  22. obj.to_yaml( :UseVersion => true, :UseHeader => true, :SortKeys => true )
  23. ) )
  24. end
  25. #
  26. # Test parser only
  27. #
  28. def assert_parse_only( obj, yaml )
  29. assert_equal( obj, YAML::load( yaml ) )
  30. assert_equal( obj, YAML::parse( yaml ).transform )
  31. end
  32. def assert_cycle( obj )
  33. assert_equal( obj, YAML::load( obj.to_yaml ) )
  34. end
  35. def assert_path_segments( path, segments )
  36. YAML::YPath.each_path( path ) { |choice|
  37. assert_equal( choice.segments, segments.shift )
  38. }
  39. assert_equal( segments.length, 0, "Some segments leftover: #{ segments.inspect }" )
  40. end
  41. #
  42. # Make a time with the time zone
  43. #
  44. def mktime( year, mon, day, hour, min, sec, usec, zone = "Z" )
  45. usec = Rational(usec.to_s) * 1000000
  46. val = Time::utc( year.to_i, mon.to_i, day.to_i, hour.to_i, min.to_i, sec.to_i, usec )
  47. if zone != "Z"
  48. hour = zone[0,3].to_i * 3600
  49. min = zone[3,2].to_i * 60
  50. ofs = (hour + min)
  51. val = Time.at( val.tv_sec - ofs, val.tv_nsec / 1000.0 )
  52. end
  53. return val
  54. end
  55. #
  56. # Tests modified from 00basic.t in YAML.pm
  57. #
  58. def test_basic_map
  59. # Simple map
  60. assert_parse_only(
  61. { 'one' => 'foo', 'three' => 'baz', 'two' => 'bar' }, <<EOY
  62. one: foo
  63. two: bar
  64. three: baz
  65. EOY
  66. )
  67. end
  68. def test_basic_strings
  69. # Common string types
  70. assert_cycle("x")
  71. assert_cycle(":x")
  72. assert_cycle(":")
  73. assert_parse_only(
  74. { 1 => 'simple string', 2 => 42, 3 => '1 Single Quoted String',
  75. 4 => 'YAML\'s Double "Quoted" String', 5 => "A block\n with several\n lines.\n",
  76. 6 => "A \"chomped\" block", 7 => "A folded\n string\n", 8 => ": started string" },
  77. <<EOY
  78. 1: simple string
  79. 2: 42
  80. 3: '1 Single Quoted String'
  81. 4: "YAML's Double \\\"Quoted\\\" String"
  82. 5: |
  83. A block
  84. with several
  85. lines.
  86. 6: |-
  87. A "chomped" block
  88. 7: >
  89. A
  90. folded
  91. string
  92. 8: ": started string"
  93. EOY
  94. )
  95. end
  96. #
  97. # Test the specification examples
  98. # - Many examples have been changes because of whitespace problems that
  99. # caused the two to be inequivalent, or keys to be sorted wrong
  100. #
  101. def test_spec_simple_implicit_sequence
  102. # Simple implicit sequence
  103. assert_to_yaml(
  104. [ 'Mark McGwire', 'Sammy Sosa', 'Ken Griffey' ], <<EOY
  105. - Mark McGwire
  106. - Sammy Sosa
  107. - Ken Griffey
  108. EOY
  109. )
  110. end
  111. def test_spec_simple_implicit_map
  112. # Simple implicit map
  113. assert_to_yaml(
  114. { 'hr' => 65, 'avg' => 0.278, 'rbi' => 147 }, <<EOY
  115. avg: 0.278
  116. hr: 65
  117. rbi: 147
  118. EOY
  119. )
  120. end
  121. def test_spec_simple_map_with_nested_sequences
  122. # Simple mapping with nested sequences
  123. assert_to_yaml(
  124. { 'american' =>
  125. [ 'Boston Red Sox', 'Detroit Tigers', 'New York Yankees' ],
  126. 'national' =>
  127. [ 'New York Mets', 'Chicago Cubs', 'Atlanta Braves' ] }, <<EOY
  128. american:
  129. - Boston Red Sox
  130. - Detroit Tigers
  131. - New York Yankees
  132. national:
  133. - New York Mets
  134. - Chicago Cubs
  135. - Atlanta Braves
  136. EOY
  137. )
  138. end
  139. def test_spec_simple_sequence_with_nested_map
  140. # Simple sequence with nested map
  141. assert_to_yaml(
  142. [
  143. {'name' => 'Mark McGwire', 'hr' => 65, 'avg' => 0.278},
  144. {'name' => 'Sammy Sosa', 'hr' => 63, 'avg' => 0.288}
  145. ], <<EOY
  146. -
  147. avg: 0.278
  148. hr: 65
  149. name: Mark McGwire
  150. -
  151. avg: 0.288
  152. hr: 63
  153. name: Sammy Sosa
  154. EOY
  155. )
  156. end
  157. def test_spec_sequence_of_sequences
  158. # Simple sequence with inline sequences
  159. assert_parse_only(
  160. [
  161. [ 'name', 'hr', 'avg' ],
  162. [ 'Mark McGwire', 65, 0.278 ],
  163. [ 'Sammy Sosa', 63, 0.288 ]
  164. ], <<EOY
  165. - [ name , hr , avg ]
  166. - [ Mark McGwire , 65 , 0.278 ]
  167. - [ Sammy Sosa , 63 , 0.288 ]
  168. EOY
  169. )
  170. end
  171. def test_spec_mapping_of_mappings
  172. # Simple map with inline maps
  173. assert_parse_only(
  174. { 'Mark McGwire' =>
  175. { 'hr' => 65, 'avg' => 0.278 },
  176. 'Sammy Sosa' =>
  177. { 'hr' => 63, 'avg' => 0.288 }
  178. }, <<EOY
  179. Mark McGwire: {hr: 65, avg: 0.278}
  180. Sammy Sosa: {hr: 63,
  181. avg: 0.288}
  182. EOY
  183. )
  184. end
  185. def test_ambiguous_comments
  186. # [ruby-talk:88012]
  187. assert_to_yaml( "Call the method #dave", <<EOY )
  188. --- "Call the method #dave"
  189. EOY
  190. end
  191. def test_spec_nested_comments
  192. # Map and sequences with comments
  193. assert_parse_only(
  194. { 'hr' => [ 'Mark McGwire', 'Sammy Sosa' ],
  195. 'rbi' => [ 'Sammy Sosa', 'Ken Griffey' ] }, <<EOY
  196. hr: # 1998 hr ranking
  197. - Mark McGwire
  198. - Sammy Sosa
  199. rbi:
  200. # 1998 rbi ranking
  201. - Sammy Sosa
  202. - Ken Griffey
  203. EOY
  204. )
  205. end
  206. def test_spec_anchors_and_aliases
  207. # Anchors and aliases
  208. assert_parse_only(
  209. { 'hr' =>
  210. [ 'Mark McGwire', 'Sammy Sosa' ],
  211. 'rbi' =>
  212. [ 'Sammy Sosa', 'Ken Griffey' ] }, <<EOY
  213. hr:
  214. - Mark McGwire
  215. # Name "Sammy Sosa" scalar SS
  216. - &SS Sammy Sosa
  217. rbi:
  218. # So it can be referenced later.
  219. - *SS
  220. - Ken Griffey
  221. EOY
  222. )
  223. assert_to_yaml(
  224. [{"arrival"=>"EDI", "departure"=>"LAX", "fareref"=>"DOGMA", "currency"=>"GBP"}, {"arrival"=>"MEL", "departure"=>"SYD", "fareref"=>"MADF", "currency"=>"AUD"}, {"arrival"=>"MCO", "departure"=>"JFK", "fareref"=>"DFSF", "currency"=>"USD"}], <<EOY
  225. -
  226. &F fareref: DOGMA
  227. &C currency: GBP
  228. &D departure: LAX
  229. &A arrival: EDI
  230. - { *F: MADF, *C: AUD, *D: SYD, *A: MEL }
  231. - { *F: DFSF, *C: USD, *D: JFK, *A: MCO }
  232. EOY
  233. )
  234. assert_to_yaml(
  235. {"ALIASES"=>["fareref", "currency", "departure", "arrival"], "FARES"=>[{"arrival"=>"EDI", "departure"=>"LAX", "fareref"=>"DOGMA", "currency"=>"GBP"}, {"arrival"=>"MEL", "departure"=>"SYD", "fareref"=>"MADF", "currency"=>"AUD"}, {"arrival"=>"MCO", "departure"=>"JFK", "fareref"=>"DFSF", "currency"=>"USD"}]}, <<EOY
  236. ---
  237. ALIASES: [&f fareref, &c currency, &d departure, &a arrival]
  238. FARES:
  239. - *f: DOGMA
  240. *c: GBP
  241. *d: LAX
  242. *a: EDI
  243. - *f: MADF
  244. *c: AUD
  245. *d: SYD
  246. *a: MEL
  247. - *f: DFSF
  248. *c: USD
  249. *d: JFK
  250. *a: MCO
  251. EOY
  252. )
  253. end
  254. def test_spec_mapping_between_sequences
  255. # Complex key #1
  256. dj = Date.new( 2001, 7, 23 )
  257. assert_parse_only(
  258. { [ 'Detroit Tigers', 'Chicago Cubs' ] => [ Date.new( 2001, 7, 23 ) ],
  259. [ 'New York Yankees', 'Atlanta Braves' ] => [ Date.new( 2001, 7, 2 ), Date.new( 2001, 8, 12 ), Date.new( 2001, 8, 14 ) ] }, <<EOY
  260. ? # PLAY SCHEDULE
  261. - Detroit Tigers
  262. - Chicago Cubs
  263. :
  264. - 2001-07-23
  265. ? [ New York Yankees,
  266. Atlanta Braves ]
  267. : [ 2001-07-02, 2001-08-12,
  268. 2001-08-14 ]
  269. EOY
  270. )
  271. # Complex key #2
  272. assert_parse_only(
  273. { [ 'New York Yankees', 'Atlanta Braves' ] =>
  274. [ Date.new( 2001, 7, 2 ), Date.new( 2001, 8, 12 ),
  275. Date.new( 2001, 8, 14 ) ],
  276. [ 'Detroit Tigers', 'Chicago Cubs' ] =>
  277. [ Date.new( 2001, 7, 23 ) ]
  278. }, <<EOY
  279. ?
  280. - New York Yankees
  281. - Atlanta Braves
  282. :
  283. - 2001-07-02
  284. - 2001-08-12
  285. - 2001-08-14
  286. ?
  287. - Detroit Tigers
  288. - Chicago Cubs
  289. :
  290. - 2001-07-23
  291. EOY
  292. )
  293. end
  294. def test_spec_sequence_key_shortcut
  295. # Shortcut sequence map
  296. assert_parse_only(
  297. { 'invoice' => 34843, 'date' => Date.new( 2001, 1, 23 ),
  298. 'bill-to' => 'Chris Dumars', 'product' =>
  299. [ { 'item' => 'Super Hoop', 'quantity' => 1 },
  300. { 'item' => 'Basketball', 'quantity' => 4 },
  301. { 'item' => 'Big Shoes', 'quantity' => 1 } ] }, <<EOY
  302. invoice: 34843
  303. date : 2001-01-23
  304. bill-to: Chris Dumars
  305. product:
  306. - item : Super Hoop
  307. quantity: 1
  308. - item : Basketball
  309. quantity: 4
  310. - item : Big Shoes
  311. quantity: 1
  312. EOY
  313. )
  314. end
  315. def test_spec_sequence_in_sequence_shortcut
  316. # Seq-in-seq
  317. assert_parse_only( [ [ [ 'one', 'two', 'three' ] ] ], <<EOY )
  318. - - - one
  319. - two
  320. - three
  321. EOY
  322. end
  323. def test_spec_sequence_shortcuts
  324. # Sequence shortcuts combined
  325. assert_parse_only(
  326. [
  327. [
  328. [ [ 'one' ] ],
  329. [ 'two', 'three' ],
  330. { 'four' => nil },
  331. [ { 'five' => [ 'six' ] } ],
  332. [ 'seven' ]
  333. ],
  334. [ 'eight', 'nine' ]
  335. ], <<EOY )
  336. - - - - one
  337. - - two
  338. - three
  339. - four:
  340. - - five:
  341. - six
  342. - - seven
  343. - - eight
  344. - nine
  345. EOY
  346. end
  347. def test_spec_single_literal
  348. # Literal scalar block
  349. assert_parse_only( [ "\\/|\\/|\n/ | |_\n" ], <<EOY )
  350. - |
  351. \\/|\\/|
  352. / | |_
  353. EOY
  354. end
  355. def test_spec_single_folded
  356. # Folded scalar block
  357. assert_parse_only(
  358. [ "Mark McGwire's year was crippled by a knee injury.\n" ], <<EOY
  359. - >
  360. Mark McGwire\'s
  361. year was crippled
  362. by a knee injury.
  363. EOY
  364. )
  365. end
  366. def test_spec_preserve_indent
  367. # Preserve indented spaces
  368. assert_parse_only(
  369. "Sammy Sosa completed another fine season with great stats.\n\n 63 Home Runs\n 0.288 Batting Average\n\nWhat a year!\n", <<EOY
  370. --- >
  371. Sammy Sosa completed another
  372. fine season with great stats.
  373. 63 Home Runs
  374. 0.288 Batting Average
  375. What a year!
  376. EOY
  377. )
  378. end
  379. def test_spec_indentation_determines_scope
  380. assert_parse_only(
  381. { 'name' => 'Mark McGwire', 'accomplishment' => "Mark set a major league home run record in 1998.\n",
  382. 'stats' => "65 Home Runs\n0.278 Batting Average\n" }, <<EOY
  383. name: Mark McGwire
  384. accomplishment: >
  385. Mark set a major league
  386. home run record in 1998.
  387. stats: |
  388. 65 Home Runs
  389. 0.278 Batting Average
  390. EOY
  391. )
  392. end
  393. def test_spec_multiline_scalars
  394. # Multiline flow scalars
  395. assert_parse_only(
  396. { 'plain' => 'This unquoted scalar spans many lines.',
  397. 'quoted' => "So does this quoted scalar.\n" }, <<EOY
  398. plain: This unquoted
  399. scalar spans
  400. many lines.
  401. quoted: "\\
  402. So does this quoted
  403. scalar.\\n"
  404. EOY
  405. )
  406. end
  407. def test_spec_type_int
  408. assert_parse_only(
  409. { 'canonical' => 12345, 'decimal' => 12345, 'octal' => '014'.oct, 'hexadecimal' => '0xC'.hex }, <<EOY
  410. canonical: 12345
  411. decimal: +12,345
  412. octal: 014
  413. hexadecimal: 0xC
  414. EOY
  415. )
  416. assert_parse_only(
  417. { 'canonical' => 685230, 'decimal' => 685230, 'octal' => 02472256, 'hexadecimal' => 0x0A74AE, 'sexagesimal' => 685230 }, <<EOY)
  418. canonical: 685230
  419. decimal: +685,230
  420. octal: 02472256
  421. hexadecimal: 0x0A,74,AE
  422. sexagesimal: 190:20:30
  423. EOY
  424. end
  425. def test_spec_type_float
  426. assert_parse_only(
  427. { 'canonical' => 1230.15, 'exponential' => 1230.15, 'fixed' => 1230.15,
  428. 'negative infinity' => -1.0/0.0 }, <<EOY)
  429. canonical: 1.23015e+3
  430. exponential: 12.3015e+02
  431. fixed: 1,230.15
  432. negative infinity: -.inf
  433. EOY
  434. nan = YAML::load( <<EOY )
  435. not a number: .NaN
  436. EOY
  437. assert( nan['not a number'].nan? )
  438. end
  439. def test_spec_type_misc
  440. assert_parse_only(
  441. { nil => nil, true => true, false => false, 'string' => '12345' }, <<EOY
  442. null: ~
  443. true: yes
  444. false: no
  445. string: '12345'
  446. EOY
  447. )
  448. end
  449. def test_spec_complex_invoice
  450. # Complex invoice type
  451. id001 = { 'given' => 'Chris', 'family' => 'Dumars', 'address' =>
  452. { 'lines' => "458 Walkman Dr.\nSuite #292\n", 'city' => 'Royal Oak',
  453. 'state' => 'MI', 'postal' => 48046 } }
  454. assert_parse_only(
  455. { 'invoice' => 34843, 'date' => Date.new( 2001, 1, 23 ),
  456. 'bill-to' => id001, 'ship-to' => id001, 'product' =>
  457. [ { 'sku' => 'BL394D', 'quantity' => 4,
  458. 'description' => 'Basketball', 'price' => 450.00 },
  459. { 'sku' => 'BL4438H', 'quantity' => 1,
  460. 'description' => 'Super Hoop', 'price' => 2392.00 } ],
  461. 'tax' => 251.42, 'total' => 4443.52,
  462. 'comments' => "Late afternoon is best. Backup contact is Nancy Billsmer @ 338-4338.\n" }, <<EOY
  463. invoice: 34843
  464. date : 2001-01-23
  465. bill-to: &id001
  466. given : Chris
  467. family : !str Dumars
  468. address:
  469. lines: |
  470. 458 Walkman Dr.
  471. Suite #292
  472. city : Royal Oak
  473. state : MI
  474. postal : 48046
  475. ship-to: *id001
  476. product:
  477. - !map
  478. sku : BL394D
  479. quantity : 4
  480. description : Basketball
  481. price : 450.00
  482. - sku : BL4438H
  483. quantity : 1
  484. description : Super Hoop
  485. price : 2392.00
  486. tax : 251.42
  487. total: 4443.52
  488. comments: >
  489. Late afternoon is best.
  490. Backup contact is Nancy
  491. Billsmer @ 338-4338.
  492. EOY
  493. )
  494. end
  495. def test_spec_log_file
  496. doc_ct = 0
  497. YAML::load_documents( <<EOY
  498. ---
  499. Time: 2001-11-23 15:01:42 -05:00
  500. User: ed
  501. Warning: >
  502. This is an error message
  503. for the log file
  504. ---
  505. Time: 2001-11-23 15:02:31 -05:00
  506. User: ed
  507. Warning: >
  508. A slightly different error
  509. message.
  510. ---
  511. Date: 2001-11-23 15:03:17 -05:00
  512. User: ed
  513. Fatal: >
  514. Unknown variable "bar"
  515. Stack:
  516. - file: TopClass.py
  517. line: 23
  518. code: |
  519. x = MoreObject("345\\n")
  520. - file: MoreClass.py
  521. line: 58
  522. code: |-
  523. foo = bar
  524. EOY
  525. ) { |doc|
  526. case doc_ct
  527. when 0
  528. assert_equal( doc, { 'Time' => mktime( 2001, 11, 23, 15, 01, 42, 00, "-05:00" ),
  529. 'User' => 'ed', 'Warning' => "This is an error message for the log file\n" } )
  530. when 1
  531. assert_equal( doc, { 'Time' => mktime( 2001, 11, 23, 15, 02, 31, 00, "-05:00" ),
  532. 'User' => 'ed', 'Warning' => "A slightly different error message.\n" } )
  533. when 2
  534. assert_equal( doc, { 'Date' => mktime( 2001, 11, 23, 15, 03, 17, 00, "-05:00" ),
  535. 'User' => 'ed', 'Fatal' => "Unknown variable \"bar\"\n",
  536. 'Stack' => [
  537. { 'file' => 'TopClass.py', 'line' => 23, 'code' => "x = MoreObject(\"345\\n\")\n" },
  538. { 'file' => 'MoreClass.py', 'line' => 58, 'code' => "foo = bar" } ] } )
  539. end
  540. doc_ct += 1
  541. }
  542. assert_equal( doc_ct, 3 )
  543. end
  544. def test_spec_root_fold
  545. y = YAML::load( <<EOY
  546. --- >
  547. This YAML stream contains a single text value.
  548. The next stream is a log file - a sequence of
  549. log entries. Adding an entry to the log is a
  550. simple matter of appending it at the end.
  551. EOY
  552. )
  553. assert_equal( y, "This YAML stream contains a single text value. The next stream is a log file - a sequence of log entries. Adding an entry to the log is a simple matter of appending it at the end.\n" )
  554. end
  555. def test_spec_root_mapping
  556. y = YAML::load( <<EOY
  557. # This stream is an example of a top-level mapping.
  558. invoice : 34843
  559. date : 2001-01-23
  560. total : 4443.52
  561. EOY
  562. )
  563. assert_equal( y, { 'invoice' => 34843, 'date' => Date.new( 2001, 1, 23 ), 'total' => 4443.52 } )
  564. end
  565. def test_spec_oneline_docs
  566. doc_ct = 0
  567. YAML::load_documents( <<EOY
  568. # The following is a sequence of three documents.
  569. # The first contains an empty mapping, the second
  570. # an empty sequence, and the last an empty string.
  571. --- {}
  572. --- [ ]
  573. --- ''
  574. EOY
  575. ) { |doc|
  576. case doc_ct
  577. when 0
  578. assert_equal( doc, {} )
  579. when 1
  580. assert_equal( doc, [] )
  581. when 2
  582. assert_equal( doc, '' )
  583. end
  584. doc_ct += 1
  585. }
  586. assert_equal( doc_ct, 3 )
  587. end
  588. def test_spec_domain_prefix
  589. customer_proc = proc { |type, val|
  590. if Hash === val
  591. scheme, domain, type = type.split( ':', 3 )
  592. val['type'] = "domain #{type}"
  593. val
  594. else
  595. raise ArgumentError, "Not a Hash in domain.tld,2002/invoice: " + val.inspect
  596. end
  597. }
  598. YAML.add_domain_type( "domain.tld,2002", 'invoice', &customer_proc )
  599. YAML.add_domain_type( "domain.tld,2002", 'customer', &customer_proc )
  600. assert_parse_only( { "invoice"=> { "customers"=> [ { "given"=>"Chris", "type"=>"domain customer", "family"=>"Dumars" } ], "type"=>"domain invoice" } }, <<EOY
  601. # 'http://domain.tld,2002/invoice' is some type family.
  602. invoice: !domain.tld,2002/^invoice
  603. # 'seq' is shorthand for 'http://yaml.org/seq'.
  604. # This does not effect '^customer' below
  605. # because it is does not specify a prefix.
  606. customers: !seq
  607. # '^customer' is shorthand for the full
  608. # notation 'http://domain.tld,2002/customer'.
  609. - !^customer
  610. given : Chris
  611. family : Dumars
  612. EOY
  613. )
  614. end
  615. def test_spec_throwaway
  616. assert_parse_only(
  617. {"this"=>"contains three lines of text.\nThe third one starts with a\n# character. This isn't a comment.\n"}, <<EOY
  618. ### These are four throwaway comment ###
  619. ### lines (the second line is empty). ###
  620. this: | # Comments may trail lines.
  621. contains three lines of text.
  622. The third one starts with a
  623. # character. This isn't a comment.
  624. # These are three throwaway comment
  625. # lines (the first line is empty).
  626. EOY
  627. )
  628. end
  629. def test_spec_force_implicit
  630. # Force implicit
  631. assert_parse_only(
  632. { 'integer' => 12, 'also int' => 12, 'string' => '12' }, <<EOY
  633. integer: 12
  634. also int: ! "12"
  635. string: !str 12
  636. EOY
  637. )
  638. end
  639. def test_spec_private_types
  640. doc_ct = 0
  641. YAML::parse_documents( <<EOY
  642. # Private types are per-document.
  643. ---
  644. pool: !!ball
  645. number: 8
  646. color: black
  647. ---
  648. bearing: !!ball
  649. material: steel
  650. EOY
  651. ) { |doc|
  652. case doc_ct
  653. when 0
  654. assert_equal( doc['pool'].type_id, 'x-private:ball' )
  655. assert_equal( doc['pool'].transform.value, { 'number' => 8, 'color' => 'black' } )
  656. when 1
  657. assert_equal( doc['bearing'].type_id, 'x-private:ball' )
  658. assert_equal( doc['bearing'].transform.value, { 'material' => 'steel' } )
  659. end
  660. doc_ct += 1
  661. }
  662. assert_equal( doc_ct, 2 )
  663. end
  664. def test_spec_url_escaping
  665. YAML.add_domain_type( "domain.tld,2002", "type0" ) { |type, val|
  666. "ONE: #{val}"
  667. }
  668. YAML.add_domain_type( "domain.tld,2002", "type%30" ) { |type, val|
  669. "TWO: #{val}"
  670. }
  671. assert_parse_only(
  672. { 'same' => [ 'ONE: value', 'ONE: value' ], 'different' => [ 'TWO: value' ] }, <<EOY
  673. same:
  674. - !domain.tld,2002/type\\x30 value
  675. - !domain.tld,2002/type0 value
  676. different: # As far as the YAML parser is concerned
  677. - !domain.tld,2002/type%30 value
  678. EOY
  679. )
  680. end
  681. def test_spec_override_anchor
  682. # Override anchor
  683. a001 = "The alias node below is a repeated use of this value.\n"
  684. assert_parse_only(
  685. { 'anchor' => 'This scalar has an anchor.', 'override' => a001, 'alias' => a001 }, <<EOY
  686. anchor : &A001 This scalar has an anchor.
  687. override : &A001 >
  688. The alias node below is a
  689. repeated use of this value.
  690. alias : *A001
  691. EOY
  692. )
  693. end
  694. def test_spec_explicit_families
  695. YAML.add_domain_type( "somewhere.com,2002", 'type' ) { |type, val|
  696. "SOMEWHERE: #{val}"
  697. }
  698. assert_parse_only(
  699. { 'not-date' => '2002-04-28', 'picture' => "GIF89a\f\000\f\000\204\000\000\377\377\367\365\365\356\351\351\345fff\000\000\000\347\347\347^^^\363\363\355\216\216\216\340\340\340\237\237\237\223\223\223\247\247\247\236\236\236i^\020' \202\n\001\000;", 'hmm' => "SOMEWHERE: family above is short for\nhttp://somewhere.com/type\n" }, <<EOY
  700. not-date: !str 2002-04-28
  701. picture: !binary |
  702. R0lGODlhDAAMAIQAAP//9/X
  703. 17unp5WZmZgAAAOfn515eXv
  704. Pz7Y6OjuDg4J+fn5OTk6enp
  705. 56enmleECcgggoBADs=
  706. hmm: !somewhere.com,2002/type |
  707. family above is short for
  708. http://somewhere.com/type
  709. EOY
  710. )
  711. end
  712. def test_spec_application_family
  713. # Testing the clarkevans.com graphs
  714. YAML.add_domain_type( "clarkevans.com,2002", 'graph/shape' ) { |type, val|
  715. if Array === val
  716. val << "Shape Container"
  717. val
  718. else
  719. raise ArgumentError, "Invalid graph of type #{val.class}: " + val.inspect
  720. end
  721. }
  722. one_shape_proc = Proc.new { |type, val|
  723. if Hash === val
  724. type = type.split( /:/ )
  725. val['TYPE'] = "Shape: #{type[2]}"
  726. val
  727. else
  728. raise ArgumentError, "Invalid graph of type #{val.class}: " + val.inspect
  729. end
  730. }
  731. YAML.add_domain_type( "clarkevans.com,2002", 'graph/circle', &one_shape_proc )
  732. YAML.add_domain_type( "clarkevans.com,2002", 'graph/line', &one_shape_proc )
  733. YAML.add_domain_type( "clarkevans.com,2002", 'graph/text', &one_shape_proc )
  734. assert_parse_only(
  735. [[{"radius"=>7, "center"=>{"x"=>73, "y"=>129}, "TYPE"=>"Shape: graph/circle"}, {"finish"=>{"x"=>89, "y"=>102}, "TYPE"=>"Shape: graph/line", "start"=>{"x"=>73, "y"=>129}}, {"TYPE"=>"Shape: graph/text", "value"=>"Pretty vector drawing.", "start"=>{"x"=>73, "y"=>129}, "color"=>16772795}, "Shape Container"]], <<EOY
  736. - !clarkevans.com,2002/graph/^shape
  737. - !^circle
  738. center: &ORIGIN {x: 73, y: 129}
  739. radius: 7
  740. - !^line # !clarkevans.com,2002/graph/line
  741. start: *ORIGIN
  742. finish: { x: 89, y: 102 }
  743. - !^text
  744. start: *ORIGIN
  745. color: 0xFFEEBB
  746. value: Pretty vector drawing.
  747. EOY
  748. )
  749. end
  750. def test_spec_float_explicit
  751. assert_parse_only(
  752. [ 10.0, 10.0, 10.0, 10.0 ], <<EOY
  753. # All entries in the sequence
  754. # have the same type and value.
  755. - 10.0
  756. - !float 10
  757. - !yaml.org,2002/^float '10'
  758. - !yaml.org,2002/float "\\
  759. 1\\
  760. 0"
  761. EOY
  762. )
  763. end
  764. def test_spec_builtin_seq
  765. # Assortment of sequences
  766. assert_parse_only(
  767. { 'empty' => [], 'in-line' => [ 'one', 'two', 'three', 'four', 'five' ],
  768. 'nested' => [ 'First item in top sequence', [ 'Subordinate sequence entry' ],
  769. "A multi-line sequence entry\n", 'Sixth item in top sequence' ] }, <<EOY
  770. empty: []
  771. in-line: [ one, two, three # May span lines,
  772. , four, # indentation is
  773. five ] # mostly ignored.
  774. nested:
  775. - First item in top sequence
  776. -
  777. - Subordinate sequence entry
  778. - >
  779. A multi-line
  780. sequence entry
  781. - Sixth item in top sequence
  782. EOY
  783. )
  784. end
  785. def test_spec_builtin_map
  786. # Assortment of mappings
  787. assert_parse_only(
  788. { 'empty' => {}, 'in-line' => { 'one' => 1, 'two' => 2 },
  789. 'spanning' => { 'one' => 1, 'two' => 2 },
  790. 'nested' => { 'first' => 'First entry', 'second' =>
  791. { 'key' => 'Subordinate mapping' }, 'third' =>
  792. [ 'Subordinate sequence', {}, 'Previous mapping is empty.',
  793. { 'A key' => 'value pair in a sequence.', 'A second' => 'key:value pair.' },
  794. 'The previous entry is equal to the following one.',
  795. { 'A key' => 'value pair in a sequence.', 'A second' => 'key:value pair.' } ],
  796. 12.0 => 'This key is a float.', "?\n" => 'This key had to be protected.',
  797. "\a" => 'This key had to be escaped.',
  798. "This is a multi-line folded key\n" => "Whose value is also multi-line.\n",
  799. [ 'This key', 'is a sequence' ] => [ 'With a sequence value.' ] } }, <<EOY
  800. empty: {}
  801. in-line: { one: 1, two: 2 }
  802. spanning: { one: 1,
  803. two: 2 }
  804. nested:
  805. first : First entry
  806. second:
  807. key: Subordinate mapping
  808. third:
  809. - Subordinate sequence
  810. - { }
  811. - Previous mapping is empty.
  812. - A key: value pair in a sequence.
  813. A second: key:value pair.
  814. - The previous entry is equal to the following one.
  815. -
  816. A key: value pair in a sequence.
  817. A second: key:value pair.
  818. !float 12 : This key is a float.
  819. ? >
  820. ?
  821. : This key had to be protected.
  822. "\\a" : This key had to be escaped.
  823. ? >
  824. This is a
  825. multi-line
  826. folded key
  827. : >
  828. Whose value is
  829. also multi-line.
  830. ?
  831. - This key
  832. - is a sequence
  833. :
  834. - With a sequence value.
  835. # The following parses correctly,
  836. # but Ruby 1.6.* fails the comparison!
  837. # ?
  838. # This: key
  839. # is a: mapping
  840. # :
  841. # with a: mapping value.
  842. EOY
  843. )
  844. end
  845. def test_spec_builtin_literal_blocks
  846. # Assortment of literal scalar blocks
  847. assert_parse_only(
  848. {"both are equal to"=>" This has no newline.", "is equal to"=>"The \\ ' \" characters may be\nfreely used. Leading white\n space is significant.\n\nLine breaks are significant.\nThus this value contains one\nempty line and ends with a\nsingle line break, but does\nnot start with one.\n", "also written as"=>" This has no newline.", "indented and chomped"=>" This has no newline.", "empty"=>"", "literal"=>"The \\ ' \" characters may be\nfreely used. Leading white\n space is significant.\n\nLine breaks are significant.\nThus this value contains one\nempty line and ends with a\nsingle line break, but does\nnot start with one.\n"}, <<EOY
  849. empty: |
  850. literal: |
  851. The \\ ' " characters may be
  852. freely used. Leading white
  853. space is significant.
  854. Line breaks are significant.
  855. Thus this value contains one
  856. empty line and ends with a
  857. single line break, but does
  858. not start with one.
  859. is equal to: "The \\ ' \\" characters may \\
  860. be\\nfreely used. Leading white\\n space \\
  861. is significant.\\n\\nLine breaks are \\
  862. significant.\\nThus this value contains \\
  863. one\\nempty line and ends with a\\nsingle \\
  864. line break, but does\\nnot start with one.\\n"
  865. # Comments may follow a nested
  866. # scalar value. They must be
  867. # less indented.
  868. # Modifiers may be combined in any order.
  869. indented and chomped: |2-
  870. This has no newline.
  871. also written as: |-2
  872. This has no newline.
  873. both are equal to: " This has no newline."
  874. EOY
  875. )
  876. str1 = "This has one newline.\n"
  877. str2 = "This has no newline."
  878. str3 = "This has two newlines.\n\n"
  879. assert_parse_only(
  880. { 'clipped' => str1, 'same as "clipped" above' => str1,
  881. 'stripped' => str2, 'same as "stripped" above' => str2,
  882. 'kept' => str3, 'same as "kept" above' => str3 }, <<EOY
  883. clipped: |
  884. This has one newline.
  885. same as "clipped" above: "This has one newline.\\n"
  886. stripped: |-
  887. This has no newline.
  888. same as "stripped" above: "This has no newline."
  889. kept: |+
  890. This has two newlines.
  891. same as "kept" above: "This has two newlines.\\n\\n"
  892. EOY
  893. )
  894. end
  895. def test_spec_span_single_quote
  896. assert_parse_only( {"third"=>"a single quote ' must be escaped.", "second"=>"! : \\ etc. can be used freely.", "is same as"=>"this contains six spaces\nand one line break", "empty"=>"", "span"=>"this contains six spaces\nand one line break"}, <<EOY
  897. empty: ''
  898. second: '! : \\ etc. can be used freely.'
  899. third: 'a single quote '' must be escaped.'
  900. span: 'this contains
  901. six spaces
  902. and one
  903. line break'
  904. is same as: "this contains six spaces\\nand one line break"
  905. EOY
  906. )
  907. end
  908. def test_spec_span_double_quote
  909. assert_parse_only( {"is equal to"=>"this contains four spaces", "third"=>"a \" or a \\ must be escaped.", "second"=>"! : etc. can be used freely.", "empty"=>"", "fourth"=>"this value ends with an LF.\n", "span"=>"this contains four spaces"}, <<EOY
  910. empty: ""
  911. second: "! : etc. can be used freely."
  912. third: "a \\\" or a \\\\ must be escaped."
  913. fourth: "this value ends with an LF.\\n"
  914. span: "this contains
  915. four \\
  916. spaces"
  917. is equal to: "this contains four spaces"
  918. EOY
  919. )
  920. end
  921. def test_spec_builtin_time
  922. # Time
  923. assert_parse_only(
  924. { "space separated" => mktime( 2001, 12, 14, 21, 59, 43, ".10", "-05:00" ),
  925. "canonical" => mktime( 2001, 12, 15, 2, 59, 43, ".10" ),
  926. "date (noon UTC)" => Date.new( 2002, 12, 14),
  927. "valid iso8601" => mktime( 2001, 12, 14, 21, 59, 43, ".10", "-05:00" ) }, <<EOY
  928. canonical: 2001-12-15T02:59:43.1Z
  929. valid iso8601: 2001-12-14t21:59:43.10-05:00
  930. space separated: 2001-12-14 21:59:43.10 -05:00
  931. date (noon UTC): 2002-12-14
  932. EOY
  933. )
  934. end
  935. def test_spec_builtin_binary
  936. arrow_gif = "GIF89a\f\000\f\000\204\000\000\377\377\367\365\365\356\351\351\345fff\000\000\000\347\347\347^^^\363\363\355\216\216\216\340\340\340\237\237\237\223\223\223\247\247\247\236\236\236iiiccc\243\243\243\204\204\204\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371!\376\016Made with GIMP\000,\000\000\000\000\f\000\f\000\000\005, \216\2010\236\343@\024\350i\020\304\321\212\010\034\317\200M$z\357\3770\205p\270\2601f\r\e\316\001\303\001\036\020' \202\n\001\000;"
  937. assert_parse_only(
  938. { 'canonical' => arrow_gif, 'base64' => arrow_gif,
  939. 'description' => "The binary value above is a tiny arrow encoded as a gif image.\n" }, <<EOY
  940. canonical: !binary "\\
  941. R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOf\\
  942. n515eXvPz7Y6OjuDg4J+fn5OTk6enp56enmlpaW\\
  943. NjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++\\
  944. f/++f/++f/++f/++f/++f/++f/++SH+Dk1hZGUg\\
  945. d2l0aCBHSU1QACwAAAAADAAMAAAFLCAgjoEwnuN\\
  946. AFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84Bww\\
  947. EeECcgggoBADs="
  948. base64: !binary |
  949. R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOf
  950. n515eXvPz7Y6OjuDg4J+fn5OTk6enp56enmlpaW
  951. NjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++
  952. f/++f/++f/++f/++f/++f/++f/++SH+Dk1hZGUg
  953. d2l0aCBHSU1QACwAAAAADAAMAAAFLCAgjoEwnuN
  954. AFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84Bww
  955. EeECcgggoBADs=
  956. description: >
  957. The binary value above is a tiny arrow
  958. encoded as a gif image.
  959. EOY
  960. )
  961. end
  962. def test_ruby_regexp
  963. # Test Ruby regular expressions
  964. assert_to_yaml(
  965. { 'simple' => /a.b/, 'complex' => %r'\A"((?:[^"]|\")+)"',
  966. 'case-insensitive' => /George McFly/i }, <<EOY
  967. case-insensitive: !ruby/regexp "/George McFly/i"
  968. complex: !ruby/regexp "/\\\\A\\"((?:[^\\"]|\\\\\\")+)\\"/"
  969. simple: !ruby/regexp "/a.b/"
  970. EOY
  971. )
  972. end
  973. #
  974. # Test of Ranges
  975. #
  976. def test_ranges
  977. # Simple numeric
  978. assert_to_yaml( 1..3, <<EOY )
  979. --- !ruby/range 1..3
  980. EOY
  981. # Simple alphabetic
  982. assert_to_yaml( 'a'..'z', <<EOY )
  983. --- !ruby/range a..z
  984. EOY
  985. # Float
  986. assert_to_yaml( 10.5...30.3, <<EOY )
  987. --- !ruby/range 10.5...30.3
  988. EOY
  989. end
  990. def test_ruby_struct
  991. # Ruby structures
  992. book_struct = Struct::new( "BookStruct", :author, :title, :year, :isbn )
  993. assert_to_yaml(
  994. [ book_struct.new( "Yukihiro Matsumoto", "Ruby in a Nutshell", 2002, "0-596-00214-9" ),
  995. book_struct.new( [ 'Dave Thomas', 'Andy Hunt' ], "The Pickaxe", 2002,
  996. book_struct.new( "This should be the ISBN", "but I have another struct here", 2002, "None" )
  997. ) ], <<EOY
  998. - !ruby/struct:BookStruct
  999. author: Yukihiro Matsumoto
  1000. title: Ruby in a Nutshell
  1001. year: 2002
  1002. isbn: 0-596-00214-9
  1003. - !ruby/struct:BookStruct
  1004. author:
  1005. - Dave Thomas
  1006. - Andy Hunt
  1007. title: The Pickaxe
  1008. year: 2002
  1009. isbn: !ruby/struct:BookStruct
  1010. author: This should be the ISBN
  1011. title: but I have another struct here
  1012. year: 2002
  1013. isbn: None
  1014. EOY
  1015. )
  1016. assert_to_yaml( YAML_Tests::StructTest.new( 123 ), <<EOY )
  1017. --- !ruby/struct:YAML_Tests::StructTest
  1018. c: 123
  1019. EOY
  1020. end
  1021. def test_ruby_rational
  1022. assert_to_yaml( Rational(1, 2), <<EOY )
  1023. --- !ruby/object:Rational
  1024. numerator: 1
  1025. denominator: 2
  1026. EOY
  1027. # Read YAML dumped by the ruby 1.8.3.
  1028. assert_to_yaml( Rational(1, 2), "!ruby/object:Rational 1/2\n" )
  1029. assert_raise( ArgumentError ) { YAML.load("!ruby/object:Rational INVALID/RATIONAL\n") }
  1030. end
  1031. def test_ruby_complex
  1032. assert_to_yaml( Complex(3, 4), <<EOY )
  1033. --- !ruby/object:Complex
  1034. image: 4
  1035. real: 3
  1036. EOY
  1037. # Read YAML dumped by the ruby 1.8.3.
  1038. assert_to_yaml( Complex(3, 4), "!ruby/object:Complex 3+4i\n" )
  1039. assert_raise( ArgumentError ) { YAML.load("!ruby/object:Complex INVALID+COMPLEXi\n") }
  1040. end
  1041. def test_emitting_indicators
  1042. assert_to_yaml( "Hi, from Object 1. You passed: please, pretty please", <<EOY
  1043. --- "Hi, from Object 1. You passed: please, pretty please"
  1044. EOY
  1045. )
  1046. end
  1047. #
  1048. # Test the YAML::Stream class -- INACTIVE at the moment
  1049. #
  1050. def test_document
  1051. y = YAML::Stream.new( :Indent => 2, :UseVersion => 0 )
  1052. y.add(
  1053. { 'hi' => 'hello', 'map' =>
  1054. { 'good' => 'two' },
  1055. 'time' => Time.now,
  1056. 'try' => /^po(.*)$/,
  1057. 'bye' => 'goodbye'
  1058. }
  1059. )
  1060. y.add( { 'po' => 'nil', 'oper' => 90 } )
  1061. y.add( { 'hi' => 'wow!', 'bye' => 'wow!' } )
  1062. y.add( { [ 'Red Socks', 'Boston' ] => [ 'One', 'Two', 'Three' ] } )
  1063. y.add( [ true, false, false ] )
  1064. end
  1065. #
  1066. # Test YPath choices parsing
  1067. #
  1068. def test_ypath_parsing
  1069. assert_path_segments( "/*/((one|three)/name|place)|//place",
  1070. [ ["*", "one", "name"],
  1071. ["*", "three", "name"],
  1072. ["*", "place"],
  1073. ["/", "place"] ]
  1074. )
  1075. end
  1076. #
  1077. # Tests from Tanaka Akira on [ruby-core]
  1078. #
  1079. def test_akira
  1080. # Commas in plain scalars [ruby-core:1066]
  1081. assert_to_yaml(
  1082. {"A"=>"A,","B"=>"B"}, <<EOY
  1083. A: "A,"
  1084. B: B
  1085. EOY
  1086. )
  1087. # Double-quoted keys [ruby-core:1069]
  1088. assert_to_yaml(
  1089. {"1"=>2, "2"=>3}, <<EOY
  1090. '1': 2
  1091. "2": 3
  1092. EOY
  1093. )
  1094. # Anchored mapping [ruby-core:1071]
  1095. assert_to_yaml(
  1096. [{"a"=>"b"}] * 2, <<EOY
  1097. - &id001
  1098. a: b
  1099. - *id001
  1100. EOY
  1101. )
  1102. # Stress test [ruby-core:1071]
  1103. # a = []; 1000.times { a << {"a"=>"b", "c"=>"d"} }
  1104. # YAML::load( a.to_yaml )
  1105. end
  1106. #
  1107. # Test Time.now cycle
  1108. #
  1109. def test_time_now_cycle
  1110. #
  1111. # From Minero Aoki [ruby-core:2305]
  1112. #
  1113. require 'yaml'
  1114. t = Time.now
  1115. t = Time.at(t.tv_sec, t.tv_usec)
  1116. 5.times do
  1117. assert_cycle(t)
  1118. end
  1119. end
  1120. #
  1121. # Test Range cycle
  1122. #
  1123. def test_range_cycle
  1124. #
  1125. # From Minero Aoki [ruby-core:02306]
  1126. #
  1127. assert_cycle("a".."z")
  1128. #
  1129. # From Nobu Nakada [ruby-core:02311]
  1130. #
  1131. assert_cycle(0..1)
  1132. assert_cycle(1.0e20 .. 2.0e20)
  1133. assert_cycle("0".."1")
  1134. assert_cycle(".."..."...")
  1135. assert_cycle(".rb"..".pl")
  1136. assert_cycle(".rb"...".pl")
  1137. assert_cycle('"'...".")
  1138. assert_cycle("'"...".")
  1139. end
  1140. #
  1141. # Circular references
  1142. #
  1143. def test_circular_references
  1144. a = []; a[0] = a; a[1] = a
  1145. inspect_str = "[[...], [...]]"
  1146. assert_equal( inspect_str, YAML::load( a.to_yaml ).inspect )
  1147. end
  1148. #
  1149. # Test Symbol cycle
  1150. #
  1151. def test_symbol_cycle
  1152. #
  1153. # From Aaron Schrab [ruby-Bugs:2535]
  1154. #
  1155. assert_cycle(:"^foo")
  1156. end
  1157. #
  1158. # Test Numeric cycle
  1159. #
  1160. class NumericTest < Numeric
  1161. def initialize(value)
  1162. @value = value
  1163. end
  1164. def ==(other)
  1165. @value == other.instance_eval{ @value }
  1166. end
  1167. end
  1168. def test_numeric_cycle
  1169. assert_cycle(1) # Fixnum
  1170. assert_cycle(111111111111111111111111111111111) # Bignum
  1171. assert_cycle(NumericTest.new(3)) # Subclass of Numeric
  1172. end
  1173. #
  1174. # Test empty map/seq in map cycle
  1175. #
  1176. def test_empty_map_key
  1177. #
  1178. # empty seq as key
  1179. #
  1180. o = YAML.load({[]=>""}.to_yaml)
  1181. assert_equal(Hash, o.class)
  1182. assert_equal([[]], o.keys)
  1183. #
  1184. # empty map as key
  1185. #
  1186. o = YAML.load({{}=>""}.to_yaml)
  1187. assert_equal(Hash, o.class)
  1188. assert_equal([{}], o.keys)
  1189. end
  1190. #
  1191. # contributed by riley lynch [ruby-Bugs-8548]
  1192. #
  1193. def test_object_id_collision
  1194. omap = YAML::Omap.new
  1195. 1000.times { |i| omap["key_#{i}"] = { "value" => i } }
  1196. raise "id collision in ordered map" if omap.to_yaml =~ /id\d+/
  1197. end
  1198. def test_date_out_of_range
  1199. assert_nothing_raised{YAML::load('1900-01-01T00:00:00+00:00')}
  1200. end
  1201. def test_normal_exit
  1202. YAML.load("2000-01-01 00:00:00.#{"0"*1000} +00:00\n")
  1203. # '[ruby-core:13735]'
  1204. end
  1205. end
  1206. if $0 == __FILE__
  1207. suite = Test::Unit::TestSuite.new('YAML')
  1208. ObjectSpace.each_object(Class) do |klass|
  1209. suite << klass.suite if (Test::Unit::TestCase > klass)
  1210. end
  1211. require 'test/unit/ui/console/testrunner'
  1212. Test::Unit::UI::Console::TestRunner.run(suite).passed?
  1213. end