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