require "../spec_helper" require "../../support/number" require "yaml" require "big" require "big/yaml" enum YAMLSpecEnum Zero One Two OneHundred end @[Flags] enum YAMLSpecFlagEnum One Two OneHundred end private record FooPrivate, x : Int32 do def self.new(ctx : YAML::ParseContext, node : YAML::Nodes::Node) new(Int32.new(ctx, node)) end end alias YamlRec = Int32 | Array(YamlRec) | Hash(YamlRec, YamlRec) # libyaml 0.2.1 removed the erroneously written document end marker (`...`) after some scalars in root context (see https://github.com/yaml/libyaml/pull/18). # Earlier libyaml releases still write the document end marker and this is hard to fix on Crystal's side. # So we just ignore it and adopt the specs accordingly to coincide with the used libyaml version. private def assert_yaml_document_end(actual, expected, file = __FILE__, line = __LINE__) actual.rchop("...\n").should eq(expected), file: file, line: line end describe "YAML serialization" do describe "from_yaml" do it "does Nil#from_yaml" do %w(~ null Null NULL).each do |string| Nil.from_yaml(string).should be_nil end Nil.from_yaml("--- \n...\n").should be_nil end it "does Bool#from_yaml" do %w(yes Yes YES true True TRUE on On ON).each do |string| Bool.from_yaml(string).should be_true end %w(no No NO false False FALSE off Off OFF).each do |string| Bool.from_yaml(string).should be_false end end {% for int in BUILTIN_INTEGER_TYPES %} it "does {{ int }}.from_yaml" do {{ int }}.from_yaml("0").should(be_a({{ int }})).should eq(0) {{ int }}.from_yaml("123").should(be_a({{ int }})).should eq(123) {{ int }}.from_yaml({{ int }}::MIN.to_s).should(be_a({{ int }})).should eq({{ int }}::MIN) {{ int }}.from_yaml({{ int }}::MAX.to_s).should(be_a({{ int }})).should eq({{ int }}::MAX) end it "raises if {{ int }}.from_yaml overflows" do expect_raises(YAML::ParseException, "Can't read {{ int }}") do {{ int }}.from_yaml(({{ int }}::MIN.to_big_i - 1).to_s) end expect_raises(YAML::ParseException, "Can't read {{ int }}") do {{ int }}.from_yaml(({{ int }}::MAX.to_big_i + 1).to_s) end end {% end %} it "does Int.from_yaml with prefixes" do Int32.from_yaml("0xabc").should eq(0xabc) Int32.from_yaml("0b10110").should eq(0b10110) Int32.from_yaml("0777").should eq(0o777) end it "does Int.from_yaml with underscores" do Int32.from_yaml("1_2_34").should eq(1_2_34) end it "does String#from_yaml" do String.from_yaml("hello").should eq("hello") end it "does String#from_yaml (empty string)" do String.from_yaml("").should eq("") end it "can parse string that looks like a number" do String.from_yaml(%(1.2)).should eq("1.2") end it "does Path.from_yaml" do Path.from_yaml(%("foo/bar")).should eq(Path.new("foo/bar")) end it "does Float32#from_yaml" do Float32.from_yaml("1.5").should eq(1.5_f32) Float32.from_yaml(".nan").nan?.should be_true Float32.from_yaml(".inf").should eq(Float32::INFINITY) Float32.from_yaml("-.inf").should eq(-Float32::INFINITY) end it "does Float64#from_yaml" do value = Float64.from_yaml("1.5") value.should eq(1.5) value.should be_a(Float64) Float64.from_yaml(".nan").nan?.should be_true Float64.from_yaml(".inf").should eq(Float64::INFINITY) Float64.from_yaml("-.inf").should eq(-Float64::INFINITY) end it "does Array#from_yaml" do Array(Int32).from_yaml("---\n- 1\n- 2\n- 3\n").should eq([1, 2, 3]) end it "does Set#from_yaml" do Set(Int32).from_yaml("---\n- 1\n- 2\n- 2\n").should eq(Set.new([1, 2])) end it "does Array#from_yaml from IO" do io = IO::Memory.new "---\n- 1\n- 2\n- 3\n" Array(Int32).from_yaml(io).should eq([1, 2, 3]) end it "does Array#from_yaml with block" do elements = [] of Int32 Array(Int32).from_yaml("---\n- 1\n- 2\n- 3\n") do |element| elements << element end elements.should eq([1, 2, 3]) end it "does Hash#from_yaml" do Hash(Int32, Bool).from_yaml("---\n1: true\n2: false\n").should eq({1 => true, 2 => false}) end it "does Hash#from_yaml with value type anchors" do Hash(String, Int32).from_yaml(<<-YAML).should eq({"x" => 123, "y" => 123}) --- x: &foo 123 y: *foo YAML end it "does Hash#from_yaml with merge" do yaml = <<-YAML - &foo bar: 1 baz: 2 - <<: *foo YAML array = Array(Hash(String, Int32)).from_yaml(yaml) array[1].should eq(array[0]) end it "does Hash#from_yaml with merge (recursive)" do yaml = <<-YAML - &foo foo: 1 - &bar bar: 2 <<: *foo - <<: *bar YAML array = Array(Hash(String, Int32)).from_yaml(yaml) array[2].should eq({"foo" => 1, "bar" => 2}) end it "does for tuple" do tuple = Tuple(Int32, String, Bool).from_yaml("---\n- 1\n- foo\n- true\n") tuple.should eq({1, "foo", true}) typeof(tuple).should eq(Tuple(Int32, String, Bool)) end it "does for tuple with file-private type" do tuple = Tuple(FooPrivate).from_yaml %([1]) tuple.should eq({FooPrivate.new(1)}) typeof(tuple).should eq(Tuple(FooPrivate)) end it "does for empty tuple" do typeof(Tuple.new).from_yaml("[]").should eq(Tuple.new) end it "does for named tuple" do tuple = NamedTuple(x: Int32, y: String).from_yaml(%({"y": "hello", "x": 1})) tuple.should eq({x: 1, y: "hello"}) typeof(tuple).should eq(NamedTuple(x: Int32, y: String)) end it "does for empty named tuple" do tuple = typeof(NamedTuple.new).from_yaml(%({})) tuple.should eq(NamedTuple.new) tuple.should be_a(typeof(NamedTuple.new)) end it "does for named tuple with nilable fields (#8089)" do tuple = NamedTuple(x: Int32?, y: String).from_yaml(%({"y": "hello"})) tuple.should eq({x: nil, y: "hello"}) typeof(tuple).should eq(NamedTuple(x: Int32?, y: String)) end it "does for named tuple with nilable fields and null (#8089)" do tuple = NamedTuple(x: Int32?, y: String).from_yaml(%({"y": "hello", "x": null})) tuple.should eq({x: nil, y: "hello"}) typeof(tuple).should eq(NamedTuple(x: Int32?, y: String)) end it "does for named tuple with spaces in key (#10918)" do tuple = NamedTuple(a: Int32, "xyz b-23": Int32).from_yaml %{{"a": 1, "xyz b-23": 2}} tuple.should eq({a: 1, "xyz b-23": 2}) typeof(tuple).should eq(NamedTuple(a: Int32, "xyz b-23": Int32)) end it "does for named tuple with spaces in key and quote char (#10918)" do tuple = NamedTuple(a: Int32, "xyz \"foo\" b-23": Int32).from_yaml %{{"a": 1, "xyz \\"foo\\" b-23": 2}} tuple.should eq({a: 1, "xyz \"foo\" b-23": 2}) typeof(tuple).should eq(NamedTuple(a: Int32, "xyz \"foo\" b-23": Int32)) end it "does for named tuple with file-private type" do tuple = NamedTuple(a: FooPrivate).from_yaml %({"a": 1}) tuple.should eq({a: FooPrivate.new(1)}) typeof(tuple).should eq(NamedTuple(a: FooPrivate)) end it "does for BigInt" do big = BigInt.from_yaml("123456789123456789123456789123456789123456789") big.should be_a(BigInt) big.should eq(BigInt.new("123456789123456789123456789123456789123456789")) end it "does for BigFloat" do big = BigFloat.from_yaml("1234.567891011121314") big.should be_a(BigFloat) big.should eq(BigFloat.new("1234.567891011121314")) end it "does for BigDecimal" do big = BigDecimal.from_yaml("1234.567891011121314") big.should be_a(BigDecimal) big.should eq(BigDecimal.new("1234.567891011121314")) end describe "Enum" do it "normal enum" do YAMLSpecEnum.from_yaml(%("one")).should eq(YAMLSpecEnum::One) YAMLSpecEnum.from_yaml(%("One")).should eq(YAMLSpecEnum::One) YAMLSpecEnum.from_yaml(%("two")).should eq(YAMLSpecEnum::Two) YAMLSpecEnum.from_yaml(%("ONE_HUNDRED")).should eq(YAMLSpecEnum::OneHundred) YAMLSpecEnum.from_yaml(%("ONE-HUNDRED")).should eq(YAMLSpecEnum::OneHundred) expect_raises(YAML::ParseException, %(Unknown enum YAMLSpecEnum value: " one ")) do YAMLSpecEnum.from_yaml(%(" one ")) end expect_raises(YAML::ParseException, %(Unknown enum YAMLSpecEnum value: "three")) do YAMLSpecEnum.from_yaml(%("three")) end expect_raises(YAML::ParseException, %(Expected String, not "1")) do YAMLSpecEnum.from_yaml(%(1)) end expect_raises(YAML::ParseException, %(Unknown enum YAMLSpecEnum value: "1")) do YAMLSpecEnum.from_yaml(%("1")) end expect_raises(YAML::ParseException, "Expected scalar, not mapping") do YAMLSpecEnum.from_yaml(%({})) end expect_raises(YAML::ParseException, "Expected scalar, not sequence") do YAMLSpecEnum.from_yaml(%([])) end end it "flag enum" do YAMLSpecFlagEnum.from_yaml(%(["one"])).should eq(YAMLSpecFlagEnum::One) YAMLSpecFlagEnum.from_yaml(%(["One"])).should eq(YAMLSpecFlagEnum::One) YAMLSpecFlagEnum.from_yaml(%([one])).should eq(YAMLSpecFlagEnum::One) YAMLSpecFlagEnum.from_yaml(%(["one", "one"])).should eq(YAMLSpecFlagEnum::One) YAMLSpecFlagEnum.from_yaml(%(["one", "two"])).should eq(YAMLSpecFlagEnum::One | YAMLSpecFlagEnum::Two) YAMLSpecFlagEnum.from_yaml(%([one, two])).should eq(YAMLSpecFlagEnum::One | YAMLSpecFlagEnum::Two) YAMLSpecFlagEnum.from_yaml(%(["one", "two", "one_hundred"])).should eq(YAMLSpecFlagEnum::All) YAMLSpecFlagEnum.from_yaml(%([])).should eq(YAMLSpecFlagEnum::None) expect_raises(YAML::ParseException, "Expected scalar, not sequence") do YAMLSpecFlagEnum.from_yaml(%(["one", ["two"]])) end expect_raises(YAML::ParseException, %(Unknown enum YAMLSpecFlagEnum value: "three")) do YAMLSpecFlagEnum.from_yaml(%(["one", "three"])) end expect_raises(YAML::ParseException, %(Expected String, not "1")) do YAMLSpecFlagEnum.from_yaml(%([1, 2])) end expect_raises(YAML::ParseException, %(Expected String, not "2")) do YAMLSpecFlagEnum.from_yaml(%(["one", 2])) end expect_raises(YAML::ParseException, "Expected sequence, not mapping") do YAMLSpecFlagEnum.from_yaml(%({})) end expect_raises(YAML::ParseException, "Expected sequence, not scalar") do YAMLSpecFlagEnum.from_yaml(%("one")) end end end describe "Enum::ValueConverter.from_yaml" do it "normal enum" do Enum::ValueConverter(YAMLSpecEnum).from_yaml("0").should eq(YAMLSpecEnum::Zero) Enum::ValueConverter(YAMLSpecEnum).from_yaml("1").should eq(YAMLSpecEnum::One) Enum::ValueConverter(YAMLSpecEnum).from_yaml("2").should eq(YAMLSpecEnum::Two) Enum::ValueConverter(YAMLSpecEnum).from_yaml("3").should eq(YAMLSpecEnum::OneHundred) expect_raises(YAML::ParseException, %(Expected Int64, not "3")) do Enum::ValueConverter(YAMLSpecEnum).from_yaml(%("3")) end expect_raises(YAML::ParseException, %(Unknown enum YAMLSpecEnum value: 4)) do Enum::ValueConverter(YAMLSpecEnum).from_yaml("4") end expect_raises(YAML::ParseException, %(Unknown enum YAMLSpecEnum value: -1)) do Enum::ValueConverter(YAMLSpecEnum).from_yaml("-1") end expect_raises(YAML::ParseException, %(Expected Int64, not )) do Enum::ValueConverter(YAMLSpecEnum).from_yaml("") end expect_raises(YAML::ParseException, %(Expected Int64, not "one")) do Enum::ValueConverter(YAMLSpecEnum).from_yaml(%("one")) end expect_raises(YAML::ParseException, "Expected scalar, not mapping") do Enum::ValueConverter(YAMLSpecEnum).from_yaml(%({})) end expect_raises(YAML::ParseException, "Expected scalar, not sequence") do Enum::ValueConverter(YAMLSpecEnum).from_yaml(%([])) end end it "flag enum" do Enum::ValueConverter(YAMLSpecFlagEnum).from_yaml("0").should eq(YAMLSpecFlagEnum::None) Enum::ValueConverter(YAMLSpecFlagEnum).from_yaml("1").should eq(YAMLSpecFlagEnum::One) Enum::ValueConverter(YAMLSpecFlagEnum).from_yaml("2").should eq(YAMLSpecFlagEnum::Two) Enum::ValueConverter(YAMLSpecFlagEnum).from_yaml("4").should eq(YAMLSpecFlagEnum::OneHundred) Enum::ValueConverter(YAMLSpecFlagEnum).from_yaml("5").should eq(YAMLSpecFlagEnum::OneHundred | YAMLSpecFlagEnum::One) Enum::ValueConverter(YAMLSpecFlagEnum).from_yaml("7").should eq(YAMLSpecFlagEnum::All) expect_raises(YAML::ParseException, %(Unknown enum YAMLSpecFlagEnum value: 8)) do Enum::ValueConverter(YAMLSpecFlagEnum).from_yaml("8") end expect_raises(YAML::ParseException, %(Unknown enum YAMLSpecFlagEnum value: -1)) do Enum::ValueConverter(YAMLSpecFlagEnum).from_yaml("-1") end expect_raises(YAML::ParseException, %(Expected Int64, not "")) do Enum::ValueConverter(YAMLSpecFlagEnum).from_yaml("") end expect_raises(YAML::ParseException, %(Expected Int64, not "one")) do Enum::ValueConverter(YAMLSpecFlagEnum).from_yaml(%("one")) end expect_raises(YAML::ParseException, "Expected scalar, not mapping") do Enum::ValueConverter(YAMLSpecFlagEnum).from_yaml(%({})) end expect_raises(YAML::ParseException, "Expected scalar, not sequence") do Enum::ValueConverter(YAMLSpecFlagEnum).from_yaml(%([])) end end end it "does Time::Format#from_yaml" do ctx = YAML::ParseContext.new nodes = YAML::Nodes.parse("--- 2014-01-02\n...\n").nodes.first value = Time::Format.new("%F").from_yaml(ctx, nodes) value.should eq(Time.utc(2014, 1, 2)) end it "deserializes union with nil, string and int (#7936)" do Array(Int32 | String | Nil).from_yaml(%([1, "hello", null])).should eq([1, "hello", nil]) end it "deserializes time" do Time.from_yaml("2010-11-12").should eq(Time.utc(2010, 11, 12)) t = Time.local(2001, 12, 14, 21, 59, 43, nanosecond: 100000000, location: Time::Location.fixed(-18000)) Time.from_yaml("2001-12-14t21:59:43.10-05:00").should eq(t) Time.from_yaml("2001-12-14 21:59:43.10 -5").should eq(t) Time.from_yaml("2001-12-14 21:59:43.10\t\t -5").should eq(t) Time.from_yaml(%(!!timestamp "2001-12-14 21:59:43.10\t\\t -5")).should eq(t) expect_raises(YAML::ParseException) { Time.from_yaml(%(!!timestamp "2001-12-14\\f21:59:43.10 -5")) } expect_raises(YAML::ParseException) { Time.from_yaml(%(!!timestamp "2001-12-14\\n21:59:43.10 -5")) } expect_raises(YAML::ParseException) { Time.from_yaml(%(!!timestamp "2001-12-14\\r21:59:43.10 -5")) } expect_raises(YAML::ParseException) { Time.from_yaml(%(!!timestamp "2001-12-14\\v21:59:43.10 -5")) } end it "deserializes Time::Location" do Time::Location.from_yaml("UTC").should eq(Time::Location.load("UTC")) end it "deserializes bytes" do Bytes.from_yaml("!!binary aGVsbG8=").should eq("hello".to_slice) end describe "Union.from_yaml" do it "String prioritization" do (Int32 | String).from_yaml(%(42)).should eq 42 (Int32 | String).from_yaml(%("42")).should eq "42" (String | UInt32).from_yaml(%(42)).should eq 42 (String | UInt32).from_yaml(%("42")).should eq "42" (Int32 | UInt32).from_yaml(%("42")).should eq 42 end end describe "parse exceptions" do it "has correct location when raises in Nil#from_yaml" do ex = expect_raises(YAML::ParseException) do Array(Nil).from_yaml <<-YAML [ 1 ] YAML end ex.message.should eq(%(Expected Nil, not "1" at line 2, column 3)) ex.location.should eq({2, 3}) end it "has correct location when raises in Int32#from_yaml" do ex = expect_raises(YAML::ParseException) do Array(Int32).from_yaml <<-YAML [ "hello" ] YAML end ex.location.should eq({2, 3}) end it "has correct location when raises in NamedTuple#from_yaml" do ex = expect_raises(YAML::ParseException) do Array({foo: Int32, bar: String}).from_yaml <<-YAML [ {"foo": 1} ] YAML end ex.location.should eq({2, 3}) end it "has correct location when raises in Union#from_yaml" do ex = expect_raises(YAML::ParseException) do Array(Int32 | Bool).from_yaml <<-YAML [ {"foo": "bar"} ] YAML end ex.location.should eq({2, 3}) end end end describe "to_yaml" do it "does for Nil" do Nil.from_yaml(nil.to_yaml).should be_nil end it "does for Nil (empty string)" do Nil.from_yaml("").should be_nil end it "does for Bool" do Bool.from_yaml(true.to_yaml).should be_true Bool.from_yaml(false.to_yaml).should be_false end it "does for Int32" do Int32.from_yaml(1.to_yaml).should eq(1) end it "does for Float32" do Float32.from_yaml(1.5_f32.to_yaml).should eq(1.5_f32) end it "does for Float32 (infinity)" do Float32.from_yaml(Float32::INFINITY.to_yaml).should eq(Float32::INFINITY) end it "does for Float32 (-infinity)" do Float32.from_yaml((-Float32::INFINITY).to_yaml).should eq(-Float32::INFINITY) end it "does for Float32 (nan)" do Float32.from_yaml(Float32::NAN.to_yaml).nan?.should be_true end it "does for Float64" do Float64.from_yaml(1.5.to_yaml).should eq(1.5) end it "does for Float64 (infinity)" do Float64.from_yaml(Float64::INFINITY.to_yaml).should eq(Float64::INFINITY) end it "does for Float64 (-infinity)" do Float64.from_yaml((-Float64::INFINITY).to_yaml).should eq(-Float64::INFINITY) end it "does for Float64 (nan)" do Float64.from_yaml(Float64::NAN.to_yaml).nan?.should be_true end it "does for String" do String.from_yaml("hello".to_yaml).should eq("hello") end it "does for String with stars (#3353)" do String.from_yaml("***".to_yaml).should eq("***") end it "does for String with quote" do String.from_yaml("hel\"lo".to_yaml).should eq("hel\"lo") end it "does for String with slash" do String.from_yaml("hel\\lo".to_yaml).should eq("hel\\lo") end it "does for String with unicode characters (#8131)" do "你好".to_yaml.should contain("你好") end it "quotes string if reserved" do ["1", "1.2", "true", "2010-11-12"].each do |string| string.to_yaml.should eq(%(--- "#{string}"\n)) end end it "does for Path" do Path.from_yaml(Path.new("foo", "bar", "baz").to_yaml).should eq(Path.new("foo", "bar", "baz")) end it "does for Array" do Array(Int32).from_yaml([1, 2, 3].to_yaml).should eq([1, 2, 3]) end it "does for Set" do Array(Int32).from_yaml(Set(Int32).new([1, 1, 2]).to_yaml).should eq([1, 2]) end it "does for Hash" do Hash(String, Int32).from_yaml({"foo" => 1, "bar" => 2}.to_yaml).should eq({"foo" => 1, "bar" => 2}) end it "does for Hash with symbol keys" do Hash(String, Int32).from_yaml({:foo => 1, :bar => 2}.to_yaml).should eq({"foo" => 1, "bar" => 2}) end it "does for Tuple" do Tuple(Int32, String).from_yaml({1, "hello"}.to_yaml).should eq({1, "hello"}) end it "does for NamedTuple" do {x: 1, y: "hello"}.to_yaml.should eq({:x => 1, :y => "hello"}.to_yaml) end it "does for BigInt" do big = BigInt.new("123456789123456789123456789123456789123456789") BigInt.from_yaml(big.to_yaml).should eq(big) end it "does for BigFloat" do big = BigFloat.new("1234.567891011121314") BigFloat.from_yaml(big.to_yaml).should eq(big) end it "does for BigDecimal" do big = BigDecimal.new("1234.567891011121314") BigDecimal.from_yaml(big.to_yaml).should eq(big) end describe "Enum" do it "normal enum" do assert_yaml_document_end(YAMLSpecEnum::One.to_yaml, "--- one\n") YAMLSpecEnum.from_yaml(YAMLSpecEnum::One.to_yaml).should eq(YAMLSpecEnum::One) assert_yaml_document_end(YAMLSpecEnum::OneHundred.to_yaml, "--- one_hundred\n") YAMLSpecEnum.from_yaml(YAMLSpecEnum::OneHundred.to_yaml).should eq(YAMLSpecEnum::OneHundred) # undefined members can't be parsed back because the standard converter only accepts named # members assert_yaml_document_end(YAMLSpecEnum.new(42).to_yaml, %(--- "42"\n)) end it "flag enum" do assert_yaml_document_end(YAMLSpecFlagEnum::One.to_yaml, %(--- [one]\n)) YAMLSpecFlagEnum.from_yaml(YAMLSpecFlagEnum::One.to_yaml).should eq(YAMLSpecFlagEnum::One) assert_yaml_document_end(YAMLSpecFlagEnum::OneHundred.to_yaml, %(--- [one_hundred]\n)) YAMLSpecFlagEnum.from_yaml(YAMLSpecFlagEnum::OneHundred.to_yaml).should eq(YAMLSpecFlagEnum::OneHundred) combined = YAMLSpecFlagEnum::OneHundred | YAMLSpecFlagEnum::One assert_yaml_document_end(combined.to_yaml, %(--- [one, one_hundred]\n)) YAMLSpecFlagEnum.from_yaml(combined.to_yaml).should eq(combined) assert_yaml_document_end(YAMLSpecFlagEnum::None.to_yaml, %(--- []\n)) YAMLSpecFlagEnum.from_yaml(YAMLSpecFlagEnum::None.to_yaml).should eq(YAMLSpecFlagEnum::None) assert_yaml_document_end(YAMLSpecFlagEnum::All.to_yaml, %(--- [one, two, one_hundred]\n)) YAMLSpecFlagEnum.from_yaml(YAMLSpecFlagEnum::All.to_yaml).should eq(YAMLSpecFlagEnum::All) assert_yaml_document_end(YAMLSpecFlagEnum.new(42).to_yaml, "--- [two]\n") end end describe "Enum::ValueConverter" do it "normal enum" do converter = Enum::ValueConverter(YAMLSpecEnum) assert_yaml_document_end(converter.to_yaml(YAMLSpecEnum::One), "--- 1\n") converter.from_yaml(converter.to_yaml(YAMLSpecEnum::One)).should eq(YAMLSpecEnum::One) assert_yaml_document_end(converter.to_yaml(YAMLSpecEnum::OneHundred), "--- 3\n") converter.from_yaml(converter.to_yaml(YAMLSpecEnum::OneHundred)).should eq(YAMLSpecEnum::OneHundred) # undefined members can't be parsed back because the standard converter only accepts named # members assert_yaml_document_end(converter.to_yaml(YAMLSpecEnum.new(42)), %(--- 42\n)) end it "flag enum" do converter = Enum::ValueConverter(YAMLSpecFlagEnum) assert_yaml_document_end(converter.to_yaml(YAMLSpecFlagEnum::One), %(--- 1\n)) converter.from_yaml(converter.to_yaml(YAMLSpecFlagEnum::One)).should eq(YAMLSpecFlagEnum::One) assert_yaml_document_end(converter.to_yaml(YAMLSpecFlagEnum::OneHundred), %(--- 4\n)) converter.from_yaml(converter.to_yaml(YAMLSpecFlagEnum::OneHundred)).should eq(YAMLSpecFlagEnum::OneHundred) combined = YAMLSpecFlagEnum::OneHundred | YAMLSpecFlagEnum::One assert_yaml_document_end(converter.to_yaml(combined), %(--- 5\n)) converter.from_yaml(converter.to_yaml(combined)).should eq(combined) assert_yaml_document_end(converter.to_yaml(YAMLSpecFlagEnum::None), %(--- 0\n)) converter.from_yaml(converter.to_yaml(YAMLSpecFlagEnum::None)).should eq(YAMLSpecFlagEnum::None) assert_yaml_document_end(converter.to_yaml(YAMLSpecFlagEnum::All), %(--- 7\n)) converter.from_yaml(converter.to_yaml(YAMLSpecFlagEnum::All)).should eq(YAMLSpecFlagEnum::All) assert_yaml_document_end(converter.to_yaml(YAMLSpecFlagEnum.new(42)), "--- 42\n") end end it "does for utc time" do time = Time.utc(2010, 11, 12, 1, 2, 3) assert_yaml_document_end(time.to_yaml, "--- 2010-11-12 01:02:03.000000000\n") end it "does for time at date" do time = Time.utc(2010, 11, 12) assert_yaml_document_end(time.to_yaml, "--- 2010-11-12\n") end it "does for utc time with nanoseconds" do time = Time.utc(2010, 11, 12, 1, 2, 3, nanosecond: 456_000_000) assert_yaml_document_end(time.to_yaml, "--- 2010-11-12 01:02:03.456000000\n") end it "does for time location" do location = Time::Location.load("UTC") assert_yaml_document_end(location.to_yaml, "--- UTC\n") end it "does for bytes" do yaml = "hello".to_slice.to_yaml if YAML.libyaml_version < SemanticVersion.new(0, 2, 2) yaml.should eq("--- !!binary 'aGVsbG8=\n\n'\n") else yaml.should eq("--- !!binary 'aGVsbG8=\n\n '\n") end end it "does a full document" do data = { :hello => "World", :integer => 2, :float => 3.5, :hash => { :a => 1, :b => 2, }, :array => [1, 2, 3], :null => nil, } expected = /\A---\nhello: World\ninteger: 2\nfloat: 3.5\nhash:\n a: 1\n b: 2\narray:\n- 1\n- 2\n- 3\nnull: ?\n\z/ data.to_yaml.should match(expected) end it "writes to a stream" do string = String.build do |str| %w(a b c).to_yaml(str) end string.should eq("---\n- a\n- b\n- c\n") end it "serializes recursive data structures" do a = [] of YamlRec a << 1 a << a a.to_yaml.should eq("--- &1\n- 1\n- *1\n") h = {} of YamlRec => YamlRec h[1] = 2 h[h] = h h.to_yaml.should match(/\A--- &1\n1: 2\n\*1 ?: \*1\n\z/) end end end