#ifndef RYML_SINGLE_HEADER #include "c4/yml/std/std.hpp" #include "c4/yml/parse.hpp" #include "c4/yml/emit.hpp" #include #include #include #endif #include #include "./test_lib/test_case.hpp" #include namespace c4 { namespace yml { template std::string emit2file(Emit &&fn) { SCOPED_TRACE("emit2file"); C4_SUPPRESS_WARNING_MSVC_WITH_PUSH(4996) // fopen unsafe std::string filename = fs::tmpnam(); FILE *f = fopen(filename.c_str(), "wb"); C4_CHECK(f != nullptr); fn(f); fflush(f); fclose(f); std::string result = fs::file_get_contents(filename.c_str()); fs::rmfile(filename.c_str()); _c4dbgpf("emit result: [{}]~~~{}~~~", result.size(), to_csubstr(result)); return result; C4_SUPPRESS_WARNING_MSVC_POP } template std::string emit2stream(Emit &&fn) { SCOPED_TRACE("emit2stream"); std::ostringstream ss; fn(ss); std::string result = ss.str(); _c4dbgpf("emit result: [{}]~~~{}~~~", result.size(), to_csubstr(result)); return result; } template std::string emit2buf(Emit &&fn) { SCOPED_TRACE("emit2buf"); std::string buf; buf.resize(2048); substr out = fn(to_substr(buf)); if(out.len > buf.size()) { buf.resize(out.len); out = fn(to_substr(buf)); } buf.resize(out.len); _c4dbgpf("emit result: [{}]~~~{}~~~", buf.size(), to_csubstr(buf)); return buf; } template std::string emitrs_append(csubstr first_part, Emit &&fn) { SCOPED_TRACE("emitrs_append"); std::string buf(first_part.begin(), first_part.end()); fn(&buf); _c4dbgpf("emit result: [{}]~~~{}~~~", buf.size(), to_csubstr(buf)); return buf; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- TEST(as_yaml, basic) { Tree et; { as_yaml j(et); EXPECT_EQ(j.tree, &et); } Tree t = parse_in_arena("[foo, bar]"); { as_yaml j(t); EXPECT_EQ(j.tree, &t); EXPECT_EQ(j.node, t.root_id()); EXPECT_EQ(j.options, EmitOptions{}); } { EmitOptions opts = EmitOptions{}.max_depth(10); as_yaml j(t, opts); EXPECT_EQ(j.tree, &t); EXPECT_EQ(j.node, t.root_id()); EXPECT_EQ(j.options, opts); } { as_yaml j(t, 2u); EXPECT_EQ(j.tree, &t); EXPECT_EQ(j.node, 2u); EXPECT_EQ(j.options, EmitOptions{}); } { EmitOptions opts = EmitOptions{}.max_depth(10); as_yaml j(t, 2u, opts); EXPECT_EQ(j.tree, &t); EXPECT_EQ(j.node, 2u); EXPECT_EQ(j.options, opts); } { as_yaml j(t[0]); EXPECT_EQ(j.tree, &t); EXPECT_EQ(j.node, 1u); EXPECT_EQ(j.options, EmitOptions{}); } { EmitOptions opts = EmitOptions{}.max_depth(10); as_yaml j(t[0], opts); EXPECT_EQ(j.tree, &t); EXPECT_EQ(j.node, 1u); EXPECT_EQ(j.options, opts); } } TEST(as_json, basic) { Tree et; { as_json j(et); EXPECT_EQ(j.tree, &et); } Tree t = parse_in_arena("[foo, bar]"); { as_json j(t); EXPECT_EQ(j.tree, &t); EXPECT_EQ(j.node, t.root_id()); EXPECT_EQ(j.options, EmitOptions{}); } { EmitOptions opts = EmitOptions{}.max_depth(10); as_json j(t, opts); EXPECT_EQ(j.tree, &t); EXPECT_EQ(j.node, t.root_id()); EXPECT_EQ(j.options, opts); } { as_json j(t, 2u); EXPECT_EQ(j.tree, &t); EXPECT_EQ(j.node, 2u); EXPECT_EQ(j.options, EmitOptions{}); } { EmitOptions opts = EmitOptions{}.max_depth(10); as_json j(t, 2u, opts); EXPECT_EQ(j.tree, &t); EXPECT_EQ(j.node, 2u); EXPECT_EQ(j.options, opts); } { as_json j(t[0]); EXPECT_EQ(j.tree, &t); EXPECT_EQ(j.node, 1u); EXPECT_EQ(j.options, EmitOptions{}); } { EmitOptions opts = EmitOptions{}.max_depth(10); as_json j(t[0], opts); EXPECT_EQ(j.tree, &t); EXPECT_EQ(j.node, 1u); EXPECT_EQ(j.options, opts); } } TEST(emit_nested, basic) { const Tree tree = parse_in_arena(R"(- a - b - x0: 1 x1: 2 - champagne: Dom Perignon coffee: Arabica more: vinho verde: Soalheiro vinho tinto: Redoma 2017 beer: - Rochefort 10 - Busch - Leffe Rituel - - and so - many other - wonderful beers - more - seq - members - here )"); EmitOptions without_dash = {}; EmitOptions with_dash = EmitOptions{}.emit_nonroot_dash(true); EmitOptions with_key = {}; EmitOptions without_key = EmitOptions{}.emit_nonroot_key(false); ASSERT_FALSE(without_dash.emit_nonroot_dash()); ASSERT_TRUE(with_dash.emit_nonroot_dash()); ASSERT_TRUE(with_key.emit_nonroot_key()); ASSERT_FALSE(without_key.emit_nonroot_key()); EXPECT_EQ(emitrs_yaml(tree[3]["beer"][0], without_dash), "Rochefort 10"); EXPECT_EQ(emitrs_yaml(tree[3]["beer"][0], with_dash), "- Rochefort 10\n"); EXPECT_EQ(emitrs_yaml(tree[3]["beer"][3], without_dash), R"(- and so - many other - wonderful beers )"); EXPECT_EQ(emitrs_yaml(tree[3]["beer"][3], with_dash), R"(- - and so - many other - wonderful beers )"); EXPECT_EQ(emitrs_yaml(tree[3]["beer"], with_key), R"(beer: - Rochefort 10 - Busch - Leffe Rituel - - and so - many other - wonderful beers )"); EXPECT_EQ(emitrs_yaml(tree[3]["beer"], without_key), R"(- Rochefort 10 - Busch - Leffe Rituel - - and so - many other - wonderful beers )"); } TEST(emit_block_seq, ambiguous_plain_emitted_as_squo) { EXPECT_EQ(scalar_style_query_plain(": odd"), false); EXPECT_EQ(scalar_style_query_plain(":\todd"), false); EXPECT_EQ(scalar_style_choose(": odd"), SCALAR_SQUO); EXPECT_EQ(scalar_style_choose(":\todd"), SCALAR_SQUO); { Tree t; NodeRef r = t.rootref(); r |= SEQ|BLOCK; r[0] = ": odd"; r[0] |= VAL_PLAIN; r[1] = ":\todd"; r[1] |= VAL_PLAIN; EXPECT_EQ(emitrs_yaml(t), "- : odd\n- :\todd\n"); } { Tree t; NodeRef r = t.rootref(); r |= SEQ|BLOCK; r[0] = ": odd"; r[1] = ":\todd"; EXPECT_FALSE(r[0].is_val_plain()); EXPECT_FALSE(r[1].is_val_plain()); EXPECT_EQ(emitrs_yaml(t), "- ': odd'\n- ':\todd'\n"); } } TEST(emit_block_map, ambiguous_plain_emitted_as_squo) { { Tree t; NodeRef r = t.rootref(); r |= MAP|BLOCK; r[0].set_key(": odd"); r[0] = ": odd"; r[1].set_key(":\todd"); r[1] = ":\todd"; EXPECT_EQ(emitrs_yaml(t), "': odd': ': odd'\n':\todd': ':\todd'\n"); } { Tree t; NodeRef r = t.rootref(); r |= MAP|BLOCK; r[0].set_key(": odd"); r[0] = ": odd"; r[0] |= KEY_PLAIN|VAL_PLAIN; r[1].set_key(":\todd"); r[1] = ":\todd"; r[1] |= KEY_PLAIN|VAL_PLAIN; EXPECT_EQ(emitrs_yaml(t), ": odd: : odd\n:\todd: :\todd\n"); } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- struct TmpContainerStyle { Tree &t; id_type id; NodeType prev; TmpContainerStyle(NodeRef n, NodeType tmp) : TmpContainerStyle(*n.tree(), n.id(), tmp) {} TmpContainerStyle(Tree &t_, id_type id_, NodeType tmp) : t(t_), id(id_) { prev = t.type(id); t._p(id)->m_type &= ~CONTAINER_STYLE; t._p(id)->m_type |= tmp; } ~TmpContainerStyle() { t._p(id)->m_type = prev; } }; void test_emits(Tree const& t, id_type id, std::string const& expected, std::string const& expected_json, EmitOptions const& opts={}) { RYML_TRACE_FMT("id={}", id); std::string append_prefix = "#before\n"; if(opts == EmitOptions{}) { EXPECT_EQ(emit2buf([&](substr buf){ return emit_yaml(t, id, buf); }), expected); EXPECT_EQ(emit2buf([&](substr buf){ return emit_json(t, id, buf); }), expected_json); EXPECT_EQ(emit2file([&](FILE *f){ return emit_yaml(t, id, f); }), expected); EXPECT_EQ(emit2file([&](FILE *f){ return emit_json(t, id, f); }), expected_json); EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ oss << as_yaml(t, id); }), expected); EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ oss << as_json(t, id); }), expected_json); EXPECT_EQ(emit2buf([&](substr buf){ EmitterBuf em(buf); return em.emit_as(EMIT_YAML, t, id, /*error_on_excess*/true); }), expected); EXPECT_EQ(emit2buf([&](substr buf){ EmitterBuf em(buf); return em.emit_as(EMIT_JSON, t, id, /*error_on_excess*/true); }), expected_json); EXPECT_EQ(emitrs_yaml(t, id), expected); EXPECT_EQ(emitrs_json(t, id), expected_json); EXPECT_EQ(emitrs_append(to_csubstr(append_prefix), [&](std::string *s) { emitrs_yaml(t, id, s, /*append*/true); } ), append_prefix + expected); EXPECT_EQ(emitrs_append(to_csubstr(append_prefix), [&](std::string *s) { emitrs_json(t, id, s, /*append*/true); } ), append_prefix + expected_json); } EXPECT_EQ(emit2buf([&](substr buf){ return emit_yaml(t, id, opts, buf); }), expected); EXPECT_EQ(emit2buf([&](substr buf){ return emit_json(t, id, opts, buf); }), expected_json); EXPECT_EQ(emit2buf([&](substr buf){ EmitterBuf em(opts, buf); return em.emit_as(EMIT_YAML, t, id, /*error_on_excess*/true); }), expected); EXPECT_EQ(emit2buf([&](substr buf){ EmitterBuf em(opts, buf); return em.emit_as(EMIT_JSON, t, id, /*error_on_excess*/true); }), expected_json); EXPECT_EQ(emit2file([&](FILE *f){ return emit_yaml(t, id, opts, f); }), expected); EXPECT_EQ(emit2file([&](FILE *f){ return emit_json(t, id, opts, f); }), expected_json); EXPECT_EQ(emit2file([&](FILE *f){ EmitterFile em(opts, f); return em.emit_as(EMIT_YAML, t, id, /*error_on_excess*/true).len; }), expected); EXPECT_EQ(emit2file([&](FILE *f){ EmitterFile em(opts, f); return em.emit_as(EMIT_JSON, t, id, /*error_on_excess*/true).len; }), expected_json); EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ oss << as_yaml(t, id, opts); }), expected); EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ oss << as_json(t, id, opts); }), expected_json); EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ EmitterOStream em(opts, oss); em.emit_as(EMIT_YAML, t, id, /*error_on_excess*/true); }), expected); EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ EmitterOStream em(opts, oss); em.emit_as(EMIT_JSON, t, id, /*error_on_excess*/true); }), expected_json); EXPECT_EQ(emitrs_yaml(t, id, opts), expected); EXPECT_EQ(emitrs_json(t, id, opts), expected_json); EXPECT_EQ(emitrs_append(to_csubstr(append_prefix), [&](std::string *s) { emitrs_yaml(t, id, opts, s, /*append*/true); } ), append_prefix + expected); EXPECT_EQ(emitrs_append(to_csubstr(append_prefix), [&](std::string *s) { emitrs_json(t, id, opts, s, /*append*/true); } ), append_prefix + expected_json); // error on max depth if(id == NONE) return; id_type max_depth = t.depth_desc(id); if(max_depth > 1) { EmitOptions optsd = opts; optsd = optsd.max_depth(0); ExpectError::check_error_basic(&t, [&]{ return emit2buf([&](substr buf){ return emit_yaml(t, id, optsd, buf); }); }); ExpectError::check_error_basic(&t, [&]{ return emit2buf([&](substr buf){ return emit_json(t, id, optsd, buf); }); }); ExpectError::check_error_basic(&t, [&]{ return emit2file([&](FILE *f){ return emit_yaml(t, id, optsd, f); }); }); ExpectError::check_error_basic(&t, [&]{ return emit2file([&](FILE *f){ return emit_json(t, id, optsd, f); }); }); ExpectError::check_error_basic(&t, [&]{ return emit2stream([&](std::ostringstream &oss){ oss << as_yaml(t, id, optsd); }); }); ExpectError::check_error_basic(&t, [&]{ return emit2stream([&](std::ostringstream &oss){ oss << as_json(t, id, optsd); }); }); ExpectError::check_error_basic(&t, [&]{ return emitrs_yaml(t, id, optsd); }); ExpectError::check_error_basic(&t, [&]{ return emitrs_json(t, id, optsd); }); } } void test_emits(Tree const& t, std::string const& expected, std::string const& expected_json, EmitOptions const& opts={}) { std::string append_prefix = "#before\n"; if(opts == EmitOptions{}) { EXPECT_EQ(emit2buf([&](substr buf){ return emit_yaml(t, buf); }), expected); EXPECT_EQ(emit2buf([&](substr buf){ return emit_json(t, buf); }), expected_json); EXPECT_EQ(emit2file([&](FILE *f){ return emit_yaml(t, f); }), expected); EXPECT_EQ(emit2file([&](FILE *f){ return emit_json(t, f); }), expected_json); EXPECT_EQ(emit2buf([&](substr buf){ EmitterBuf em(buf); return em.emit_as(EMIT_YAML, t, /*error_on_excess*/true); }), expected); EXPECT_EQ(emit2buf([&](substr buf){ EmitterBuf em(buf); return em.emit_as(EMIT_JSON, t, /*error_on_excess*/true); }), expected_json); EXPECT_EQ(emit2file([&](FILE *f){ EmitterFile em(f); return em.emit_as(EMIT_YAML, t, /*error_on_excess*/true).len; }), expected); EXPECT_EQ(emit2file([&](FILE *f){ EmitterFile em(f); return em.emit_as(EMIT_JSON, t, /*error_on_excess*/true).len; }), expected_json); if(!t.empty()) { EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ oss << as_yaml(t); }), expected); EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ oss << as_json(t); }), expected_json); EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ EmitterOStream em(oss); em.emit_as(EMIT_YAML, t, /*error_on_excess*/true); }), expected); EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ EmitterOStream em(oss); em.emit_as(EMIT_JSON, t, /*error_on_excess*/true); }), expected_json); } EXPECT_EQ(emitrs_yaml(t), expected); EXPECT_EQ(emitrs_json(t), expected_json); EXPECT_EQ(emitrs_append(to_csubstr(append_prefix), [&](std::string *s) { emitrs_yaml(t, s, /*append*/true); } ), append_prefix + expected); EXPECT_EQ(emitrs_append(to_csubstr(append_prefix), [&](std::string *s) { emitrs_json(t, s, /*append*/true); } ), append_prefix + expected_json); } EXPECT_EQ(emit2buf([&](substr buf){ return emit_yaml(t, opts, buf); }), expected); EXPECT_EQ(emit2buf([&](substr buf){ return emit_json(t, opts, buf); }), expected_json); EXPECT_EQ(emit2buf([&](substr buf){ EmitterBuf em(opts, buf); return em.emit_as(EMIT_YAML, t, /*error_on_excess*/true); }), expected); EXPECT_EQ(emit2buf([&](substr buf){ EmitterBuf em(opts, buf); return em.emit_as(EMIT_JSON, t, /*error_on_excess*/true); }), expected_json); EXPECT_EQ(emit2file([&](FILE *f){ return emit_yaml(t, opts, f); }), expected); EXPECT_EQ(emit2file([&](FILE *f){ return emit_json(t, opts, f); }), expected_json); EXPECT_EQ(emit2file([&](FILE *f){ EmitterFile em(opts, f); return em.emit_as(EMIT_YAML, t, /*error_on_excess*/true).len; }), expected); EXPECT_EQ(emit2file([&](FILE *f){ EmitterFile em(opts, f); return em.emit_as(EMIT_JSON, t, /*error_on_excess*/true).len; }), expected_json); EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ oss << as_yaml(t, opts); }), expected); EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ oss << as_json(t, opts); }), expected_json); EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ EmitterOStream em(opts, oss); em.emit_as(EMIT_YAML, t, /*error_on_excess*/true); }), expected); EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ EmitterOStream em(opts, oss); em.emit_as(EMIT_JSON, t, /*error_on_excess*/true); }), expected_json); EXPECT_EQ(emitrs_yaml(t, opts), expected); EXPECT_EQ(emitrs_json(t, opts), expected_json); EXPECT_EQ(emitrs_append(to_csubstr(append_prefix), [&](std::string *s) { emitrs_yaml(t, opts, s, /*append*/true); } ), append_prefix + expected); EXPECT_EQ(emitrs_append(to_csubstr(append_prefix), [&](std::string *s) { emitrs_json(t, opts, s, /*append*/true); } ), append_prefix + expected_json); // error on max depth id_type max_depth = t.empty() ? 0 : t.depth_desc(t.root_id()); if(max_depth > 1) { EmitOptions optsd = opts; optsd = optsd.max_depth(0); ExpectError::check_error_basic(&t, [&]{ return emit2buf([&](substr buf){ return emit_yaml(t, optsd, buf); }); }); ExpectError::check_error_basic(&t, [&]{ return emit2buf([&](substr buf){ return emit_json(t, optsd, buf); }); }); ExpectError::check_error_basic(&t, [&]{ return emit2file([&](FILE *f){ return emit_yaml(t, optsd, f); }); }); ExpectError::check_error_basic(&t, [&]{ return emit2file([&](FILE *f){ return emit_json(t, optsd, f); }); }); ExpectError::check_error_basic(&t, [&]{ return emit2stream([&](std::ostringstream &oss){ oss << as_yaml(ConstNodeRef(&t), optsd); }); }); ExpectError::check_error_basic(&t, [&]{ return emit2stream([&](std::ostringstream &oss){ oss << as_json(ConstNodeRef(&t), optsd); }); }); ExpectError::check_error_basic(&t, [&]{ return emitrs_yaml(t, optsd); }); ExpectError::check_error_basic(&t, [&]{ return emitrs_json(t, optsd); }); } } void test_emits(ConstNodeRef n, std::string const& expected, std::string const& expected_json, EmitOptions const& opts={}) { std::string append_prefix = "#before\n"; if(opts == EmitOptions{}) { EXPECT_EQ(emit2buf([&](substr buf){ return emit_yaml(n, buf); }), expected); EXPECT_EQ(emit2buf([&](substr buf){ return emit_json(n, buf); }), expected_json); EXPECT_EQ(emit2file([&](FILE *f){ return emit_yaml(n, f); }), expected); EXPECT_EQ(emit2file([&](FILE *f){ return emit_json(n, f); }), expected_json); EXPECT_EQ(emit2buf([&](substr buf){ EmitterBuf em(buf); return em.emit_as(EMIT_YAML, n, /*error_on_excess*/true); }), expected); EXPECT_EQ(emit2buf([&](substr buf){ EmitterBuf em(buf); return em.emit_as(EMIT_JSON, n, /*error_on_excess*/true); }), expected_json); EXPECT_EQ(emit2file([&](FILE *f){ EmitterFile em(f); return em.emit_as(EMIT_YAML, n, /*error_on_excess*/true).len; }), expected); EXPECT_EQ(emit2file([&](FILE *f){ EmitterFile em(f); return em.emit_as(EMIT_JSON, n, /*error_on_excess*/true).len; }), expected_json); EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ oss << n; }), expected); EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ oss << as_yaml(n); }), expected); EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ oss << as_json(n); }), expected_json); EXPECT_EQ(emitrs_yaml(n), expected); EXPECT_EQ(emitrs_json(n), expected_json); EXPECT_EQ(emitrs_append(to_csubstr(append_prefix), [&](std::string *s) { emitrs_yaml(n, s, /*append*/true); } ), append_prefix + expected); EXPECT_EQ(emitrs_append(to_csubstr(append_prefix), [&](std::string *s) { emitrs_json(n, s, /*append*/true); } ), append_prefix + expected_json); } EXPECT_EQ(emit2buf([&](substr buf){ return emit_yaml(n, opts, buf); }), expected); EXPECT_EQ(emit2buf([&](substr buf){ return emit_json(n, opts, buf); }), expected_json); EXPECT_EQ(emit2file([&](FILE *f){ return emit_yaml(n, opts, f); }), expected); EXPECT_EQ(emit2file([&](FILE *f){ return emit_json(n, opts, f); }), expected_json); EXPECT_EQ(emit2buf([&](substr buf){ EmitterBuf em(opts, buf); return em.emit_as(EMIT_YAML, n, /*error_on_excess*/true); }), expected); EXPECT_EQ(emit2buf([&](substr buf){ EmitterBuf em(opts, buf); return em.emit_as(EMIT_JSON, n, /*error_on_excess*/true); }), expected_json); EXPECT_EQ(emit2file([&](FILE *f){ EmitterFile em(opts, f); return em.emit_as(EMIT_YAML, n, /*error_on_excess*/true).len; }), expected); EXPECT_EQ(emit2file([&](FILE *f){ EmitterFile em(opts, f); return em.emit_as(EMIT_JSON, n, /*error_on_excess*/true).len; }), expected_json); EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ oss << as_yaml(n, opts); }), expected); EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ oss << as_json(n, opts); }), expected_json); EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ EmitterOStream em(opts, oss); em.emit_as(EMIT_YAML, n, /*error_on_excess*/true); }), expected); EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ EmitterOStream em(opts, oss); em.emit_as(EMIT_JSON, n, /*error_on_excess*/true); }), expected_json); EXPECT_EQ(emitrs_yaml(n, opts), expected); EXPECT_EQ(emitrs_json(n, opts), expected_json); EXPECT_EQ(emitrs_append(to_csubstr(append_prefix), [&](std::string *s) { emitrs_yaml(n, opts, s, /*append*/true); } ), append_prefix + expected); EXPECT_EQ(emitrs_append(to_csubstr(append_prefix), [&](std::string *s) { emitrs_json(n, opts, s, /*append*/true); } ), append_prefix + expected_json); // error on max depth if(n.tree() && n.id() != NONE) { id_type max_depth = n.depth_desc(); if(max_depth > 1) { EmitOptions optsd = opts; optsd = optsd.max_depth(0); ExpectError::check_error_basic(n.tree(), [&]{ return emit2buf([&](substr buf){ return emit_yaml(n, optsd, buf); }); }); ExpectError::check_error_basic(n.tree(), [&]{ return emit2buf([&](substr buf){ return emit_json(n, optsd, buf); }); }); ExpectError::check_error_basic(n.tree(), [&]{ return emit2file([&](FILE *f){ return emit_yaml(n, optsd, f); }); }); ExpectError::check_error_basic(n.tree(), [&]{ return emit2file([&](FILE *f){ return emit_json(n, optsd, f); }); }); ExpectError::check_error_basic(n.tree(), [&]{ return emit2stream([&](std::ostringstream &oss){ oss << as_yaml(n, optsd); }); }); ExpectError::check_error_basic(n.tree(), [&]{ return emit2stream([&](std::ostringstream &oss){ oss << as_json(n, optsd); }); }); ExpectError::check_error_basic(n.tree(), [&]{ return emitrs_yaml(n, optsd); }); ExpectError::check_error_basic(n.tree(), [&]{ return emitrs_json(n, optsd); }); } } } void test_all_emits(ConstNodeRef n, std::string const &expected, std::string const& expected_json, EmitOptions const& opts={}) { SCOPED_TRACE("test_all_emits"); { SCOPED_TRACE("rootref"); test_emits(n, expected, expected_json, opts); } if(n.is_root()) { SCOPED_TRACE("t"); test_emits(*n.tree(), expected, expected_json, opts); } { SCOPED_TRACE("t, id"); test_emits(*n.tree(), n.id(), expected, expected_json, opts); } } TEST(emit, empty_tree) { const Tree t; // must be const! std::string expected = R"()"; test_emits(t, expected, expected); } TEST(emit, empty_node) { std::string expected = R"()"; test_emits(NodeRef{}, expected, expected); test_emits(ConstNodeRef{}, expected, expected); } TEST(emit, existing_tree) { const Tree t = parse_in_arena("[foo, bar]"); std::string expected = "[foo,bar]"; std::string expected_json = R"(["foo","bar"])"; test_emits(t, expected, expected_json); } TEST(emit, no_node) { const Tree t = parse_in_arena("[foo, bar]"); std::string expected = "[foo,bar]"; std::string expected_json = R"(["foo","bar"])"; test_emits(t, NONE, expected, expected_json); } TEST(emit, empty_key_squo) { { SCOPED_TRACE("one only"); const Tree t = parse_in_arena(R"( ? '' : literal )"); { SCOPED_TRACE("indent"); std::string expected = "'': literal\n"; std::string expected_json = "{\n \"\": \"literal\"\n}\n"; test_emits(t, t.root_id(), expected, expected_json); } { SCOPED_TRACE("no indent"); std::string expected = "'': literal\n"; std::string expected_json = "{\n\"\": \"literal\"\n}\n"; test_emits(t, t.root_id(), expected, expected_json, EmitOptions{}.indent_flow_ml(false)); } } { SCOPED_TRACE("nested"); const Tree t = parse_in_arena(R"( level1: ? '' : literal )"); { SCOPED_TRACE("indent"); std::string expected = "level1:\n '': literal\n"; std::string expected_json = "{\n \"level1\": {\n \"\": \"literal\"\n }\n}\n"; test_emits(t, t.root_id(), expected, expected_json); } { SCOPED_TRACE("no indent"); std::string expected = "level1:\n '': literal\n"; std::string expected_json = "{\n\"level1\": {\n\"\": \"literal\"\n}\n}\n"; test_emits(t, t.root_id(), expected, expected_json, EmitOptions{}.indent_flow_ml(false)); } } const Tree t = parse_in_arena(R"( ? '' : literal level1: ? '' : literal level2: ? '' : literal level3: ? '' : literal )"); { SCOPED_TRACE("level3"); { SCOPED_TRACE("indent"); std::string expected = "level3:\n '': literal\n"; std::string expected_json = "\"level3\": {\n \"\": \"literal\"\n}\n"; test_emits(t, t["level1"]["level2"]["level3"].id(), expected, expected_json); } { SCOPED_TRACE("no indent"); std::string expected = "level3:\n '': literal\n"; std::string expected_json = "\"level3\": {\n\"\": \"literal\"\n}\n"; test_emits(t, t["level1"]["level2"]["level3"].id(), expected, expected_json, EmitOptions{}.indent_flow_ml(false)); } } { SCOPED_TRACE("level2"); { SCOPED_TRACE("indent"); std::string expected = "level2:\n '': literal\n level3:\n '': literal\n"; std::string expected_json = "\"level2\": {\n \"\": \"literal\",\n \"level3\": {\n \"\": \"literal\"\n }\n}\n"; test_emits(t, t["level1"]["level2"].id(), expected, expected_json); } { SCOPED_TRACE("no indent"); std::string expected = "level2:\n '': literal\n level3:\n '': literal\n"; std::string expected_json = "\"level2\": {\n\"\": \"literal\",\n\"level3\": {\n\"\": \"literal\"\n}\n}\n"; test_emits(t, t["level1"]["level2"].id(), expected, expected_json, EmitOptions{}.indent_flow_ml(false)); } } { SCOPED_TRACE("level1"); { SCOPED_TRACE("indent"); std::string expected = "level1:\n '': literal\n level2:\n '': literal\n level3:\n '': literal\n"; std::string expected_json = "\"level1\": {\n \"\": \"literal\",\n \"level2\": {\n \"\": \"literal\",\n \"level3\": {\n \"\": \"literal\"\n }\n }\n}\n"; test_emits(t, t["level1"].id(), expected, expected_json); } { SCOPED_TRACE("no indent"); std::string expected = "level1:\n '': literal\n level2:\n '': literal\n level3:\n '': literal\n"; std::string expected_json = "\"level1\": {\n\"\": \"literal\",\n\"level2\": {\n\"\": \"literal\",\n\"level3\": {\n\"\": \"literal\"\n}\n}\n}\n"; test_emits(t, t["level1"].id(), expected, expected_json, EmitOptions{}.indent_flow_ml(false)); } } { SCOPED_TRACE("level0"); { SCOPED_TRACE("indent"); std::string expected = "'': literal\nlevel1:\n '': literal\n level2:\n '': literal\n level3:\n '': literal\n"; std::string expected_json = "{\n \"\": \"literal\",\n \"level1\": {\n \"\": \"literal\",\n \"level2\": {\n \"\": \"literal\",\n \"level3\": {\n \"\": \"literal\"\n }\n }\n }\n}\n"; test_emits(t, NONE, expected, expected_json); } { SCOPED_TRACE("no indent"); std::string expected = "'': literal\nlevel1:\n '': literal\n level2:\n '': literal\n level3:\n '': literal\n"; std::string expected_json = "{\n\"\": \"literal\",\n\"level1\": {\n\"\": \"literal\",\n\"level2\": {\n\"\": \"literal\",\n\"level3\": {\n\"\": \"literal\"\n}\n}\n}\n}\n"; test_emits(t, NONE, expected, expected_json, EmitOptions{}.indent_flow_ml(false)); } } } TEST(emit, empty_key_dquo) { { SCOPED_TRACE("one only"); const Tree t = parse_in_arena(R"( ? "" : literal )"); std::string expected = "\"\": literal\n"; std::string expected_json = "{\n \"\": \"literal\"\n}\n"; test_emits(t, t.root_id(), expected, expected_json); } { SCOPED_TRACE("nested"); const Tree t = parse_in_arena(R"( level1: ? "" : literal )"); std::string expected = "level1:\n \"\": literal\n"; std::string expected_json = "{\n \"level1\": {\n \"\": \"literal\"\n }\n}\n"; test_emits(t, t.root_id(), expected, expected_json); } const Tree t = parse_in_arena(R"( ? "" : literal level1: ? "" : literal level2: ? "" : literal level3: ? "" : literal )"); { SCOPED_TRACE("level3"); std::string expected = "level3:\n \"\": literal\n"; std::string expected_json = "\"level3\": {\n \"\": \"literal\"\n}\n"; test_emits(t, t["level1"]["level2"]["level3"].id(), expected, expected_json); } { SCOPED_TRACE("level2"); std::string expected = "level2:\n \"\": literal\n level3:\n \"\": literal\n"; std::string expected_json = "\"level2\": {\n \"\": \"literal\",\n \"level3\": {\n \"\": \"literal\"\n }\n}\n"; test_emits(t, t["level1"]["level2"].id(), expected, expected_json); } { SCOPED_TRACE("level1"); std::string expected = "level1:\n \"\": literal\n level2:\n \"\": literal\n level3:\n \"\": literal\n"; std::string expected_json = "\"level1\": {\n \"\": \"literal\",\n \"level2\": {\n \"\": \"literal\",\n \"level3\": {\n \"\": \"literal\"\n }\n }\n}\n"; test_emits(t, t["level1"].id(), expected, expected_json); } { SCOPED_TRACE("level0"); std::string expected = "\"\": literal\nlevel1:\n \"\": literal\n level2:\n \"\": literal\n level3:\n \"\": literal\n"; std::string expected_json = "{\n \"\": \"literal\",\n \"level1\": {\n \"\": \"literal\",\n \"level2\": {\n \"\": \"literal\",\n \"level3\": {\n \"\": \"literal\"\n }\n }\n }\n}\n"; test_emits(t, NONE, expected, expected_json); } } TEST(emit, empty_key_plain) { { SCOPED_TRACE("one only"); const Tree t = parse_in_arena(R"( ? : literal )"); std::string expected = ": literal\n"; std::string expected_json = "{\n \"\": \"literal\"\n}\n"; test_emits(t, t.root_id(), expected, expected_json); } { SCOPED_TRACE("nested"); const Tree t = parse_in_arena(R"( level1: ? : literal )"); std::string expected = "level1:\n : literal\n"; std::string expected_json = "{\n \"level1\": {\n \"\": \"literal\"\n }\n}\n"; test_emits(t, t.root_id(), expected, expected_json); } const Tree t = parse_in_arena(R"( ? : literal level1: ? : literal level2: ? : literal level3: ? : literal )"); { SCOPED_TRACE("level3"); std::string expected = "level3:\n : literal\n"; std::string expected_json = "\"level3\": {\n \"\": \"literal\"\n}\n"; test_emits(t, t["level1"]["level2"]["level3"].id(), expected, expected_json); } { SCOPED_TRACE("level2"); std::string expected = "level2:\n : literal\n level3:\n : literal\n"; std::string expected_json = "\"level2\": {\n \"\": \"literal\",\n \"level3\": {\n \"\": \"literal\"\n }\n}\n"; test_emits(t, t["level1"]["level2"].id(), expected, expected_json); } { SCOPED_TRACE("level1"); std::string expected = "level1:\n : literal\n level2:\n : literal\n level3:\n : literal\n"; std::string expected_json = "\"level1\": {\n \"\": \"literal\",\n \"level2\": {\n \"\": \"literal\",\n \"level3\": {\n \"\": \"literal\"\n }\n }\n}\n"; test_emits(t, t["level1"].id(), expected, expected_json); } { SCOPED_TRACE("level0"); std::string expected = ": literal\nlevel1:\n : literal\n level2:\n : literal\n level3:\n : literal\n"; std::string expected_json = "{\n \"\": \"literal\",\n \"level1\": {\n \"\": \"literal\",\n \"level2\": {\n \"\": \"literal\",\n \"level3\": {\n \"\": \"literal\"\n }\n }\n }\n}\n"; test_emits(t, NONE, expected, expected_json); } } TEST(emit, empty_key_literal) { { SCOPED_TRACE("one only"); const Tree t = parse_in_arena(R"( ? |- : literal )"); std::string expected = "? |-\n: literal\n"; std::string expected_json = "{\n \"\": \"literal\"\n}\n"; test_emits(t, t.root_id(), expected, expected_json); } { SCOPED_TRACE("nested1"); const Tree t = parse_in_arena("level1:\n ? |-\n : literal\n"); std::string expected = "level1:\n ? |-\n : literal\n"; std::string expected_json = "{\n \"level1\": {\n \"\": \"literal\"\n }\n}\n"; test_emits(t, t.root_id(), expected, expected_json); } const Tree t = parse_in_arena(R"( ? |- : literal level1: ? |- : literal level2: ? |- : literal level3: ? |- : literal )"); { SCOPED_TRACE("level3"); std::string expected = "level3:\n ? |-\n : literal\n"; std::string expected_json = "\"level3\": {\n \"\": \"literal\"\n}\n"; test_emits(t, t["level1"]["level2"]["level3"].id(), expected, expected_json); } { SCOPED_TRACE("level2"); std::string expected = "level2:\n ? |-\n : literal\n level3:\n ? |-\n : literal\n"; std::string expected_json = "\"level2\": {\n \"\": \"literal\",\n \"level3\": {\n \"\": \"literal\"\n }\n}\n"; test_emits(t, t["level1"]["level2"].id(), expected, expected_json); } { SCOPED_TRACE("level1"); std::string expected = "level1:\n ? |-\n : literal\n level2:\n ? |-\n : literal\n level3:\n ? |-\n : literal\n"; std::string expected_json = "\"level1\": {\n \"\": \"literal\",\n \"level2\": {\n \"\": \"literal\",\n \"level3\": {\n \"\": \"literal\"\n }\n }\n}\n"; test_emits(t, t["level1"].id(), expected, expected_json); } { SCOPED_TRACE("level0"); std::string expected = "? |-\n: literal\nlevel1:\n ? |-\n : literal\n level2:\n ? |-\n : literal\n level3:\n ? |-\n : literal\n"; std::string expected_json = "{\n \"\": \"literal\",\n \"level1\": {\n \"\": \"literal\",\n \"level2\": {\n \"\": \"literal\",\n \"level3\": {\n \"\": \"literal\"\n }\n }\n }\n}\n"; test_emits(t, NONE, expected, expected_json); } } TEST(emit, empty_key_folded) { { SCOPED_TRACE("one only"); const Tree t = parse_in_arena(R"( ? >- : folded )"); std::string expected = "? >-\n: folded\n"; std::string expected_json = "{\n \"\": \"folded\"\n}\n"; test_emits(t, t.root_id(), expected, expected_json); } { SCOPED_TRACE("nested"); const Tree t = parse_in_arena(R"( level1: ? >- : folded )"); std::string expected = "level1:\n ? >-\n : folded\n"; std::string expected_json = "{\n \"level1\": {\n \"\": \"folded\"\n }\n}\n"; test_emits(t, t.root_id(), expected, expected_json); } const Tree t = parse_in_arena(R"( ? >- : folded level1: ? >- : folded level2: ? >- : folded level3: ? >- : folded )"); { SCOPED_TRACE("level3"); std::string expected = "level3:\n ? >-\n : folded\n"; std::string expected_json = "\"level3\": {\n \"\": \"folded\"\n}\n"; test_emits(t, t["level1"]["level2"]["level3"].id(), expected, expected_json); } { SCOPED_TRACE("level2"); std::string expected = "level2:\n ? >-\n : folded\n level3:\n ? >-\n : folded\n"; std::string expected_json = "\"level2\": {\n \"\": \"folded\",\n \"level3\": {\n \"\": \"folded\"\n }\n}\n"; test_emits(t, t["level1"]["level2"].id(), expected, expected_json); } { SCOPED_TRACE("level1"); std::string expected = "level1:\n ? >-\n : folded\n level2:\n ? >-\n : folded\n level3:\n ? >-\n : folded\n"; std::string expected_json = "\"level1\": {\n \"\": \"folded\",\n \"level2\": {\n \"\": \"folded\",\n \"level3\": {\n \"\": \"folded\"\n }\n }\n}\n"; test_emits(t, t["level1"].id(), expected, expected_json); } { SCOPED_TRACE("level0"); std::string expected = "? >-\n: folded\nlevel1:\n ? >-\n : folded\n level2:\n ? >-\n : folded\n level3:\n ? >-\n : folded\n"; std::string expected_json = "{\n \"\": \"folded\",\n \"level1\": {\n \"\": \"folded\",\n \"level2\": {\n \"\": \"folded\",\n \"level3\": {\n \"\": \"folded\"\n }\n }\n }\n}\n"; test_emits(t, NONE, expected, expected_json); } } TEST(emit, existing_seq_node_flow) { Tree nct = parse_in_arena("[foo, bar, [nested, seq], {nested: map}]"); Tree const& t = nct; { SCOPED_TRACE("full"); std::string expected = "[foo,bar,[nested,seq],{nested: map}]"; std::string expected_ml = "[\n foo,\n bar,\n [nested,seq],\n {nested: map}\n]\n"; std::string expected_ml_all = "[\n foo,\n bar,\n [\n nested,\n seq\n ],\n {\n nested: map\n }\n]\n"; std::string expected_json = "[\"foo\",\"bar\",[\"nested\",\"seq\"],{\"nested\": \"map\"}]"; std::string expected_json_ml = "[\n \"foo\",\n \"bar\",\n [\"nested\",\"seq\"],\n {\"nested\": \"map\"}\n]\n"; std::string expected_json_ml_all = "[\n \"foo\",\n \"bar\",\n [\n \"nested\",\n \"seq\"\n ],\n {\n \"nested\": \"map\"\n }\n]\n"; { SCOPED_TRACE("here"); test_all_emits(t.crootref(), expected, expected_json); } { SCOPED_TRACE("ml"); TmpContainerStyle tmp(nct.rootref(), FLOW_ML); test_all_emits(t.crootref(), expected_ml, expected_json_ml); } { SCOPED_TRACE("rootref-ml-all"); TmpContainerStyle tmp1(nct.rootref(), FLOW_ML); TmpContainerStyle tmp2(nct[2], FLOW_ML); TmpContainerStyle tmp3(nct[3], FLOW_ML); test_all_emits(t.crootref(), expected_ml_all, expected_json_ml_all); } } { SCOPED_TRACE("t[0]"); ConstNodeRef n = t[0]; std::string expected = "foo"; std::string expected_json = "\"foo\""; test_all_emits(n, expected, expected_json); nct._add_flags(n.id(), FLOW_SL); expected = "foo"; expected_json = "\"foo\""; test_all_emits(n, expected, expected_json); } { SCOPED_TRACE("t[1]"); ConstNodeRef n = t[1]; std::string expected = "bar"; std::string expected_json = "\"bar\""; test_all_emits(n, expected, expected_json); nct._add_flags(n.id(), FLOW_SL); expected = "bar"; expected_json = "\"bar\""; test_all_emits(n, expected, expected_json); } { SCOPED_TRACE("t[2]"); NodeRef n = nct[2]; { SCOPED_TRACE("FLOW_SL"); TmpContainerStyle tmp(n, FLOW_SL); std::string expected = "[nested,seq]"; std::string expected_json = "[\"nested\",\"seq\"]"; test_all_emits(n, expected, expected_json); } { SCOPED_TRACE("FLOW_ML"); TmpContainerStyle tmp(n, FLOW_ML); std::string expected = "[\n nested,\n seq\n]\n"; std::string expected_json = "[\n \"nested\",\n \"seq\"\n]\n"; test_all_emits(n, expected, expected_json); } { SCOPED_TRACE("BLOCK"); TmpContainerStyle tmp(n, BLOCK); std::string expected = "- nested\n- seq\n"; std::string expected_json = "[\n \"nested\",\n \"seq\"\n]\n"; test_all_emits(n, expected, expected_json); } } { SCOPED_TRACE("t[3]"); NodeRef n = nct[3]; { SCOPED_TRACE("FLOW_SL"); TmpContainerStyle tmp(n, FLOW_SL); std::string expected = "{nested: map}"; std::string expected_json = "{\"nested\": \"map\"}"; test_all_emits(n, expected, expected_json); } { SCOPED_TRACE("FLOW_ML"); TmpContainerStyle tmp(n, FLOW_ML); std::string expected = "{\n nested: map\n}\n"; std::string expected_json = "{\n \"nested\": \"map\"\n}\n"; test_all_emits(n, expected, expected_json); } { SCOPED_TRACE("BLOCK"); TmpContainerStyle tmp(n, BLOCK); std::string expected = "nested: map\n"; std::string expected_json = "{\n \"nested\": \"map\"\n}\n"; test_all_emits(n, expected, expected_json); } } } TEST(emit, existing_seq_node_block) { Tree nct = parse_in_arena("- foo\n- bar\n- - nested\n - seq\n- nested: map\n"); Tree const& t = nct; { SCOPED_TRACE("full-top"); { SCOPED_TRACE("block"); TmpContainerStyle tmp(nct.rootref(), BLOCK); std::string expected = "- foo\n- bar\n- - nested\n - seq\n- nested: map\n"; std::string expected_json = "[\n \"foo\",\n \"bar\",\n [\n \"nested\",\n \"seq\"\n ],\n {\n \"nested\": \"map\"\n }\n]\n"; test_all_emits(t.crootref(), expected, expected_json); } { SCOPED_TRACE("block-flow-ml"); { SCOPED_TRACE("indent"); TmpContainerStyle tmp(nct.rootref(), BLOCK); TmpContainerStyle tmp2(nct[2], FLOW_ML); TmpContainerStyle tmp3(nct[3], FLOW_ML); std::string expected = "- foo\n- bar\n- [\n nested,\n seq\n ]\n- {\n nested: map\n }\n"; std::string expected_json = "[\n \"foo\",\n \"bar\",\n [\n \"nested\",\n \"seq\"\n ],\n {\n \"nested\": \"map\"\n }\n]\n"; test_all_emits(t.crootref(), expected, expected_json); } { SCOPED_TRACE("no indent"); TmpContainerStyle tmp(nct.rootref(), BLOCK); TmpContainerStyle tmp2(nct[2], FLOW_ML); TmpContainerStyle tmp3(nct[3], FLOW_ML); std::string expected = "- foo\n- bar\n- [\n nested,\n seq\n ]\n- {\n nested: map\n }\n"; std::string expected_json = "[\n\"foo\",\n\"bar\",\n[\n\"nested\",\n\"seq\"\n],\n{\n\"nested\": \"map\"\n}\n]\n"; test_all_emits(t.crootref(), expected, expected_json, EmitOptions{}.indent_flow_ml(false)); } } { SCOPED_TRACE("flow_ml"); { SCOPED_TRACE("indent"); TmpContainerStyle tmp(nct.rootref(), FLOW_ML); std::string expected = "[\n foo,\n bar,\n [nested,seq],\n {nested: map}\n]\n"; std::string expected_json = "[\n \"foo\",\n \"bar\",\n [\n \"nested\",\n \"seq\"\n ],\n {\n \"nested\": \"map\"\n }\n]\n"; test_all_emits(t.crootref(), expected, expected_json); } { SCOPED_TRACE("no indent"); TmpContainerStyle tmp(nct.rootref(), FLOW_ML); std::string expected = "[\nfoo,\nbar,\n[nested,seq],\n{nested: map}\n]\n"; std::string expected_json = "[\n\"foo\",\n\"bar\",\n[\n\"nested\",\n\"seq\"\n],\n{\n\"nested\": \"map\"\n}\n]\n"; test_all_emits(t.crootref(), expected, expected_json, EmitOptions{}.indent_flow_ml(false)); } } { SCOPED_TRACE("flow_sl"); TmpContainerStyle tmp(nct.rootref(), FLOW_SL); std::string expected = "[foo,bar,[nested,seq],{nested: map}]"; std::string expected_json = "[\"foo\",\"bar\",[\"nested\",\"seq\"],{\"nested\": \"map\"}]"; test_all_emits(t.crootref(), expected, expected_json); } } { SCOPED_TRACE("full-all"); { SCOPED_TRACE("block"); TmpContainerStyle tmp0(nct.rootref(), BLOCK); TmpContainerStyle tmp2(nct[2], BLOCK); TmpContainerStyle tmp3(nct[3], BLOCK); std::string expected = "- foo\n- bar\n- - nested\n - seq\n- nested: map\n"; std::string expected_json = "[\n \"foo\",\n \"bar\",\n [\n \"nested\",\n \"seq\"\n ],\n {\n \"nested\": \"map\"\n }\n]\n"; test_all_emits(t.crootref(), expected, expected_json); } { SCOPED_TRACE("flow_ml"); { SCOPED_TRACE("indent"); TmpContainerStyle tmp(nct.rootref(), FLOW_ML); TmpContainerStyle tmp2(nct[2], FLOW_ML); TmpContainerStyle tmp3(nct[3], FLOW_ML); std::string expected = "[\n foo,\n bar,\n [\n nested,\n seq\n ],\n {\n nested: map\n }\n]\n"; std::string expected_json = "[\n \"foo\",\n \"bar\",\n [\n \"nested\",\n \"seq\"\n ],\n {\n \"nested\": \"map\"\n }\n]\n"; test_all_emits(t.crootref(), expected, expected_json); } { SCOPED_TRACE("no indent ml"); TmpContainerStyle tmp(nct.rootref(), FLOW_ML); TmpContainerStyle tmp2(nct[2], FLOW_ML); TmpContainerStyle tmp3(nct[3], FLOW_ML); std::string expected = "[\nfoo,\nbar,\n[\nnested,\nseq\n],\n{\nnested: map\n}\n]\n"; std::string expected_json = "[\n\"foo\",\n\"bar\",\n[\n\"nested\",\n\"seq\"\n],\n{\n\"nested\": \"map\"\n}\n]\n"; test_all_emits(t.crootref(), expected, expected_json, EmitOptions{}.indent_flow_ml(false)); } } { SCOPED_TRACE("flow_sl"); TmpContainerStyle tmp(nct.rootref(), FLOW_SL); TmpContainerStyle tmp2(nct[2], FLOW_SL); TmpContainerStyle tmp3(nct[3], FLOW_SL); std::string expected = "[foo,bar,[nested,seq],{nested: map}]"; std::string expected_json = "[\"foo\",\"bar\",[\"nested\",\"seq\"],{\"nested\": \"map\"}]"; test_all_emits(t.crootref(), expected, expected_json); } } { SCOPED_TRACE("t[0]"); NodeRef n = nct[0]; { SCOPED_TRACE("block"); TmpContainerStyle tmp(n, BLOCK); std::string expected = "foo"; std::string expected_json = "\"foo\"\n"; test_all_emits(n, expected, expected_json); } { SCOPED_TRACE("flow_ml"); TmpContainerStyle tmp(n, FLOW_ML); std::string expected = "foo\n"; std::string expected_json = "\"foo\"\n"; test_all_emits(n, expected, expected_json); } { SCOPED_TRACE("flow_sl"); TmpContainerStyle tmp(n, FLOW_SL); std::string expected = "foo"; std::string expected_json = "\"foo\""; test_all_emits(n, expected, expected_json); } } { SCOPED_TRACE("t[1]"); NodeRef n = nct[1]; { SCOPED_TRACE("block"); TmpContainerStyle tmp(n, BLOCK); std::string expected = "bar"; std::string expected_json = "\"bar\"\n"; test_all_emits(n, expected, expected_json); } { SCOPED_TRACE("flow_ml"); TmpContainerStyle tmp(n, FLOW_ML); std::string expected = "bar\n"; std::string expected_json = "\"bar\"\n"; test_all_emits(n, expected, expected_json); } { SCOPED_TRACE("flow_sl"); TmpContainerStyle tmp(n, FLOW_SL); std::string expected = "bar"; std::string expected_json = "\"bar\""; test_all_emits(n, expected, expected_json); } } { SCOPED_TRACE("t[2]"); NodeRef n = nct[2]; { SCOPED_TRACE("block"); TmpContainerStyle tmp(n, BLOCK); std::string expected = "- nested\n- seq\n"; std::string expected_json = "[\n \"nested\",\n \"seq\"\n]\n"; test_all_emits(n, expected, expected_json); } { SCOPED_TRACE("flow_ml"); TmpContainerStyle tmp(n, FLOW_ML); std::string expected = "[\n nested,\n seq\n]\n"; std::string expected_json = "[\n \"nested\",\n \"seq\"\n]\n"; test_all_emits(n, expected, expected_json); } { SCOPED_TRACE("flow_sl"); TmpContainerStyle tmp(n, FLOW_SL); std::string expected = "[nested,seq]"; std::string expected_json = "[\"nested\",\"seq\"]"; test_all_emits(n, expected, expected_json); } } { SCOPED_TRACE("t[3]"); NodeRef n = nct[3]; { SCOPED_TRACE("block"); TmpContainerStyle tmp(n, BLOCK); std::string expected = "nested: map\n"; std::string expected_json = "{\n \"nested\": \"map\"\n}\n"; test_all_emits(n, expected, expected_json); } { SCOPED_TRACE("flow_ml"); TmpContainerStyle tmp(n, FLOW_ML); std::string expected = "{\n nested: map\n}\n"; std::string expected_json = "{\n \"nested\": \"map\"\n}\n"; test_all_emits(n, expected, expected_json); } { SCOPED_TRACE("flow_sl"); TmpContainerStyle tmp(n, FLOW_SL); std::string expected = "{nested: map}"; std::string expected_json = "{\"nested\": \"map\"}"; test_all_emits(n, expected, expected_json); } } } TEST(emit, existing_map_node_flow) { Tree nct = parse_in_arena("{0: foo, 1: bar, 2: [nested, seq], 3: {nested: map}}"); Tree const& t = nct; { SCOPED_TRACE("full-top"); { SCOPED_TRACE("block"); TmpContainerStyle tmp(nct.rootref(), BLOCK); std::string expected = "0: foo\n1: bar\n2: [nested,seq]\n3: {nested: map}\n"; std::string expected_json = "{\n \"0\": \"foo\",\n \"1\": \"bar\",\n \"2\": [\"nested\",\"seq\"],\n \"3\": {\"nested\": \"map\"}\n}\n"; test_all_emits(t.crootref(), expected, expected_json); } { SCOPED_TRACE("flow_ml"); { SCOPED_TRACE("indent"); TmpContainerStyle tmp(nct.rootref(), FLOW_ML); std::string expected = "{\n 0: foo,\n 1: bar,\n 2: [nested,seq],\n 3: {nested: map}\n}\n"; std::string expected_json = "{\n \"0\": \"foo\",\n \"1\": \"bar\",\n \"2\": [\"nested\",\"seq\"],\n \"3\": {\"nested\": \"map\"}\n}\n"; test_all_emits(t.crootref(), expected, expected_json); } { SCOPED_TRACE("no indent"); TmpContainerStyle tmp(nct.rootref(), FLOW_ML); std::string expected = "{\n0: foo,\n1: bar,\n2: [nested,seq],\n3: {nested: map}\n}\n"; std::string expected_json = "{\n\"0\": \"foo\",\n\"1\": \"bar\",\n\"2\": [\"nested\",\"seq\"],\n\"3\": {\"nested\": \"map\"}\n}\n"; test_all_emits(t.crootref(), expected, expected_json, EmitOptions{}.indent_flow_ml(false)); } } { SCOPED_TRACE("flow_sl"); TmpContainerStyle tmp(nct.rootref(), FLOW_SL); std::string expected = "{0: foo,1: bar,2: [nested,seq],3: {nested: map}}"; std::string expected_json = "{\"0\": \"foo\",\"1\": \"bar\",\"2\": [\"nested\",\"seq\"],\"3\": {\"nested\": \"map\"}}"; test_all_emits(t.crootref(), expected, expected_json); } } { SCOPED_TRACE("full-all"); { SCOPED_TRACE("block"); TmpContainerStyle tmp0(nct.rootref(), BLOCK); TmpContainerStyle tmp2(nct[2], BLOCK); TmpContainerStyle tmp3(nct[3], BLOCK); std::string expected = "0: foo\n1: bar\n2:\n - nested\n - seq\n3:\n nested: map\n"; std::string expected_json = "{\n \"0\": \"foo\",\n \"1\": \"bar\",\n \"2\": [\n \"nested\",\n \"seq\"\n ],\n \"3\": {\n \"nested\": \"map\"\n }\n}\n"; test_all_emits(t.crootref(), expected, expected_json); } { SCOPED_TRACE("flow_ml"); { SCOPED_TRACE("indent"); TmpContainerStyle tmp(nct.rootref(), FLOW_ML); TmpContainerStyle tmp2(nct[2], FLOW_ML); TmpContainerStyle tmp3(nct[3], FLOW_ML); std::string expected = "{\n 0: foo,\n 1: bar,\n 2: [\n nested,\n seq\n ],\n 3: {\n nested: map\n }\n}\n"; std::string expected_json = "{\n \"0\": \"foo\",\n \"1\": \"bar\",\n \"2\": [\n \"nested\",\n \"seq\"\n ],\n \"3\": {\n \"nested\": \"map\"\n }\n}\n"; test_all_emits(t.crootref(), expected, expected_json); } { SCOPED_TRACE("no indent"); TmpContainerStyle tmp(nct.rootref(), FLOW_ML); TmpContainerStyle tmp2(nct[2], FLOW_ML); TmpContainerStyle tmp3(nct[3], FLOW_ML); std::string expected = "{\n0: foo,\n1: bar,\n2: [\nnested,\nseq\n],\n3: {\nnested: map\n}\n}\n"; std::string expected_json = "{\n\"0\": \"foo\",\n\"1\": \"bar\",\n\"2\": [\n\"nested\",\n\"seq\"\n],\n\"3\": {\n\"nested\": \"map\"\n}\n}\n"; test_all_emits(t.crootref(), expected, expected_json, EmitOptions{}.indent_flow_ml(false)); } } { SCOPED_TRACE("flow_sl"); TmpContainerStyle tmp(nct.rootref(), FLOW_SL); TmpContainerStyle tmp2(nct[2], FLOW_SL); TmpContainerStyle tmp3(nct[3], FLOW_SL); std::string expected = "{0: foo,1: bar,2: [nested,seq],3: {nested: map}}"; std::string expected_json = "{\"0\": \"foo\",\"1\": \"bar\",\"2\": [\"nested\",\"seq\"],\"3\": {\"nested\": \"map\"}}"; test_all_emits(t.crootref(), expected, expected_json); } } { SCOPED_TRACE("t[0]"); NodeRef n = nct[0]; { SCOPED_TRACE("block"); TmpContainerStyle tmp(n, BLOCK); std::string expected = "0: foo\n"; std::string expected_json = "\"0\": \"foo\"\n"; test_all_emits(n, expected, expected_json); } { SCOPED_TRACE("flow_ml"); TmpContainerStyle tmp(n, FLOW_ML); std::string expected = "0: foo\n"; std::string expected_json = "\"0\": \"foo\"\n"; test_all_emits(n, expected, expected_json); } { SCOPED_TRACE("flow_sl"); TmpContainerStyle tmp(n, FLOW_SL); std::string expected = "0: foo\n"; std::string expected_json = "\"0\": \"foo\""; // FIXME should have trailing newline test_all_emits(n, expected, expected_json); } } { SCOPED_TRACE("t[1]"); NodeRef n = nct[1]; { SCOPED_TRACE("block"); TmpContainerStyle tmp(n, BLOCK); std::string expected = "1: bar\n"; std::string expected_json = "\"1\": \"bar\"\n"; test_all_emits(n, expected, expected_json); } { SCOPED_TRACE("flow_ml"); TmpContainerStyle tmp(n, FLOW_ML); std::string expected = "1: bar\n"; std::string expected_json = "\"1\": \"bar\"\n"; test_all_emits(n, expected, expected_json); } { SCOPED_TRACE("flow_sl"); TmpContainerStyle tmp(n, FLOW_SL); std::string expected = "1: bar\n"; std::string expected_json = "\"1\": \"bar\""; // FIXME should have trailing newline test_all_emits(n, expected, expected_json); } } { SCOPED_TRACE("t[2]"); NodeRef n = nct[2]; { SCOPED_TRACE("block"); TmpContainerStyle tmp(n, BLOCK); std::string expected = "2:\n - nested\n - seq\n"; std::string expected_json = "\"2\": [\n \"nested\",\n \"seq\"\n]\n"; test_all_emits(n, expected, expected_json); } { SCOPED_TRACE("flow_ml"); TmpContainerStyle tmp(n, FLOW_ML); std::string expected = "2: [\n nested,\n seq\n ]\n"; // FIXME should be indented one level, not two std::string expected_json = "\"2\": [\n \"nested\",\n \"seq\"\n]\n"; test_all_emits(n, expected, expected_json); } { SCOPED_TRACE("flow_sl"); TmpContainerStyle tmp(n, FLOW_SL); std::string expected = "2: [nested,seq]\n"; std::string expected_json = "\"2\": [\"nested\",\"seq\"]"; // FIXME should have trailing newline test_all_emits(n, expected, expected_json); } } { SCOPED_TRACE("t[3]"); NodeRef n = nct[3]; { SCOPED_TRACE("block"); TmpContainerStyle tmp(n, BLOCK); std::string expected = "3:\n nested: map\n"; std::string expected_json = "\"3\": {\n \"nested\": \"map\"\n}\n"; test_all_emits(n, expected, expected_json); } { SCOPED_TRACE("flow_ml"); TmpContainerStyle tmp(n, FLOW_ML); std::string expected = "3: {\n nested: map\n }\n"; // FIXME should be indented one level, not two std::string expected_json = "\"3\": {\n \"nested\": \"map\"\n}\n"; test_all_emits(n, expected, expected_json); } { SCOPED_TRACE("flow_sl"); TmpContainerStyle tmp(n, FLOW_SL); std::string expected = "3: {nested: map}\n"; std::string expected_json = "\"3\": {\"nested\": \"map\"}"; // FIXME should have trailing newline test_all_emits(n, expected, expected_json); } } } TEST(emit, existing_map_node_block) { Tree nct = parse_in_arena("0: foo\n1: bar\n2:\n - nested\n - seq\n3:\n nested: map\n"); Tree const& t = nct; { SCOPED_TRACE("full-top"); { SCOPED_TRACE("block"); TmpContainerStyle tmp(nct.rootref(), BLOCK); std::string expected = "0: foo\n1: bar\n2:\n - nested\n - seq\n3:\n nested: map\n"; std::string expected_json = "{\n \"0\": \"foo\",\n \"1\": \"bar\",\n \"2\": [\n \"nested\",\n \"seq\"\n ],\n \"3\": {\n \"nested\": \"map\"\n }\n}\n"; test_all_emits(t.crootref(), expected, expected_json); } { SCOPED_TRACE("block-flow-ml"); { SCOPED_TRACE("indent"); TmpContainerStyle tmp(nct.rootref(), BLOCK); TmpContainerStyle tmp2(nct[2], FLOW_ML); TmpContainerStyle tmp3(nct[3], FLOW_ML); std::string expected = "0: foo\n1: bar\n2: [\n nested,\n seq\n ]\n3: {\n nested: map\n }\n"; std::string expected_json = "{\n \"0\": \"foo\",\n \"1\": \"bar\",\n \"2\": [\n \"nested\",\n \"seq\"\n ],\n \"3\": {\n \"nested\": \"map\"\n }\n}\n"; test_all_emits(t.crootref(), expected, expected_json); } { SCOPED_TRACE("no indent"); TmpContainerStyle tmp(nct.rootref(), BLOCK); TmpContainerStyle tmp2(nct[2], FLOW_ML); TmpContainerStyle tmp3(nct[3], FLOW_ML); std::string expected = "0: foo\n1: bar\n2: [\n nested,\n seq\n ]\n3: {\n nested: map\n }\n"; std::string expected_json = "{\n\"0\": \"foo\",\n\"1\": \"bar\",\n\"2\": [\n\"nested\",\n\"seq\"\n],\n\"3\": {\n\"nested\": \"map\"\n}\n}\n"; test_all_emits(t.crootref(), expected, expected_json, EmitOptions{}.indent_flow_ml(false)); } } { SCOPED_TRACE("flow_ml"); TmpContainerStyle tmp(nct.rootref(), FLOW_ML); { SCOPED_TRACE("indent"); std::string expected = "{\n 0: foo,\n 1: bar,\n 2: [nested,seq],\n 3: {nested: map}\n}\n"; std::string expected_json = "{\n \"0\": \"foo\",\n \"1\": \"bar\",\n \"2\": [\n \"nested\",\n \"seq\"\n ],\n \"3\": {\n \"nested\": \"map\"\n }\n}\n"; test_all_emits(t.crootref(), expected, expected_json); } { SCOPED_TRACE("no indent"); std::string expected = "{\n0: foo,\n1: bar,\n2: [nested,seq],\n3: {nested: map}\n}\n"; std::string expected_json = "{\n\"0\": \"foo\",\n\"1\": \"bar\",\n\"2\": [\n\"nested\",\n\"seq\"\n],\n\"3\": {\n\"nested\": \"map\"\n}\n}\n"; test_all_emits(t.crootref(), expected, expected_json, EmitOptions{}.indent_flow_ml(false)); } } { SCOPED_TRACE("flow_sl"); TmpContainerStyle tmp(nct.rootref(), FLOW_SL); std::string expected = "{0: foo,1: bar,2: [nested,seq],3: {nested: map}}"; std::string expected_json = R"({"0": "foo","1": "bar","2": ["nested","seq"],"3": {"nested": "map"}})"; test_all_emits(t.crootref(), expected, expected_json); } } { SCOPED_TRACE("full-all"); { SCOPED_TRACE("block"); TmpContainerStyle tmp0(nct.rootref(), BLOCK); TmpContainerStyle tmp2(nct[2], BLOCK); TmpContainerStyle tmp3(nct[3], BLOCK); std::string expected = "0: foo\n1: bar\n2:\n - nested\n - seq\n3:\n nested: map\n"; std::string expected_json = "{\n \"0\": \"foo\",\n \"1\": \"bar\",\n \"2\": [\n \"nested\",\n \"seq\"\n ],\n \"3\": {\n \"nested\": \"map\"\n }\n}\n"; test_all_emits(t.crootref(), expected, expected_json); } { SCOPED_TRACE("flow_ml"); TmpContainerStyle tmp(nct.rootref(), FLOW_ML); TmpContainerStyle tmp2(nct[2], FLOW_ML); TmpContainerStyle tmp3(nct[3], FLOW_ML); { SCOPED_TRACE("indent"); std::string expected = "{\n 0: foo,\n 1: bar,\n 2: [\n nested,\n seq\n ],\n 3: {\n nested: map\n }\n}\n"; std::string expected_json = "{\n \"0\": \"foo\",\n \"1\": \"bar\",\n \"2\": [\n \"nested\",\n \"seq\"\n ],\n \"3\": {\n \"nested\": \"map\"\n }\n}\n"; test_all_emits(t.crootref(), expected, expected_json); } { SCOPED_TRACE("no indent"); std::string expected = "{\n0: foo,\n1: bar,\n2: [\nnested,\nseq\n],\n3: {\nnested: map\n}\n}\n"; std::string expected_json = "{\n\"0\": \"foo\",\n\"1\": \"bar\",\n\"2\": [\n\"nested\",\n\"seq\"\n],\n\"3\": {\n\"nested\": \"map\"\n}\n}\n"; test_all_emits(t.crootref(), expected, expected_json, EmitOptions{}.indent_flow_ml(false)); } } { SCOPED_TRACE("flow_sl"); TmpContainerStyle tmp(nct.rootref(), FLOW_SL); TmpContainerStyle tmp2(nct[2], FLOW_SL); TmpContainerStyle tmp3(nct[3], FLOW_SL); std::string expected = "{0: foo,1: bar,2: [nested,seq],3: {nested: map}}"; std::string expected_json = R"({"0": "foo","1": "bar","2": ["nested","seq"],"3": {"nested": "map"}})"; test_all_emits(t.crootref(), expected, expected_json); } } { SCOPED_TRACE("t[0]"); NodeRef n = nct[0]; { SCOPED_TRACE("block"); TmpContainerStyle tmp(n, BLOCK); std::string expected = "0: foo\n"; std::string expected_json = "\"0\": \"foo\"\n"; test_all_emits(n, expected, expected_json); } { SCOPED_TRACE("flow_ml"); TmpContainerStyle tmp(n, FLOW_ML); std::string expected = "0: foo\n"; std::string expected_json = "\"0\": \"foo\"\n"; test_all_emits(n, expected, expected_json); } { SCOPED_TRACE("flow_sl"); TmpContainerStyle tmp(n, FLOW_SL); std::string expected = "0: foo\n"; std::string expected_json = "\"0\": \"foo\""; // FIXME should have trailing newline test_all_emits(n, expected, expected_json); } } { SCOPED_TRACE("t[1]"); NodeRef n = nct[1]; { SCOPED_TRACE("block"); TmpContainerStyle tmp(n, BLOCK); std::string expected = "1: bar\n"; std::string expected_json = "\"1\": \"bar\"\n"; test_all_emits(n, expected, expected_json); } { SCOPED_TRACE("flow_ml"); TmpContainerStyle tmp(n, FLOW_ML); std::string expected = "1: bar\n"; std::string expected_json = "\"1\": \"bar\"\n"; test_all_emits(n, expected, expected_json); } { SCOPED_TRACE("flow_sl"); TmpContainerStyle tmp(n, FLOW_SL); std::string expected = "1: bar\n"; std::string expected_json = "\"1\": \"bar\""; // FIXME should have trailing newline test_all_emits(n, expected, expected_json); } } { SCOPED_TRACE("t[2]"); NodeRef n = nct[2]; { SCOPED_TRACE("block"); TmpContainerStyle tmp(n, BLOCK); std::string expected = "2:\n - nested\n - seq\n"; std::string expected_json = "\"2\": [\n \"nested\",\n \"seq\"\n]\n"; test_all_emits(n, expected, expected_json); } { SCOPED_TRACE("flow_ml"); TmpContainerStyle tmp(n, FLOW_ML); std::string expected = "2: [\n nested,\n seq\n ]\n"; // FIXME should be indented one level, not two std::string expected_json = "\"2\": [\n \"nested\",\n \"seq\"\n]\n"; test_all_emits(n, expected, expected_json); } { SCOPED_TRACE("flow_sl"); TmpContainerStyle tmp(n, FLOW_SL); std::string expected = "2: [nested,seq]\n"; std::string expected_json = "\"2\": [\"nested\",\"seq\"]"; // FIXME should have trailing newline test_all_emits(n, expected, expected_json); } } { SCOPED_TRACE("t[3]"); NodeRef n = nct[3]; { SCOPED_TRACE("block"); TmpContainerStyle tmp(n, BLOCK); std::string expected = "3:\n nested: map\n"; std::string expected_json = "\"3\": {\n \"nested\": \"map\"\n}\n"; test_all_emits(n, expected, expected_json); } { SCOPED_TRACE("flow_ml"); TmpContainerStyle tmp(n, FLOW_ML); std::string expected = "3: {\n nested: map\n }\n"; // FIXME should be indented one level, not two std::string expected_json = "\"3\": {\n \"nested\": \"map\"\n}\n"; test_all_emits(n, expected, expected_json); } { SCOPED_TRACE("flow_sl"); TmpContainerStyle tmp(n, FLOW_SL); std::string expected = "3: {nested: map}\n"; std::string expected_json = "\"3\": {\"nested\": \"map\"}"; // FIXME should have trailing newline test_all_emits(n, expected, expected_json); } } } TEST(emit, percent_is_quoted) { Tree ti = parse_in_arena("{}"); ASSERT_TRUE(ti.rootref().is_map()); ti["%ROOT"] = "%VAL"; ti["%ROOT2"] |= SEQ; ti["%ROOT2"][0] = "%VAL"; ti["%ROOT2"][1] = "%VAL"; std::string yaml = emitrs_yaml(ti); test_check_emit_check(to_csubstr(yaml), [](Tree const &t){ ASSERT_TRUE(t.rootref().is_map()); ASSERT_TRUE(t.rootref().has_child("%ROOT")); ASSERT_TRUE(t.rootref().has_child("%ROOT2")); ASSERT_EQ(t["%ROOT2"].num_children(), 2u); EXPECT_TRUE(t["%ROOT"].is_key_quoted()); EXPECT_TRUE(t["%ROOT"].is_val_quoted()); EXPECT_TRUE(t["%ROOT2"].is_key_quoted()); EXPECT_TRUE(t["%ROOT2"][0].is_val_quoted()); EXPECT_TRUE(t["%ROOT2"][1].is_val_quoted()); }); } TEST(emit, at_is_quoted__issue_309) { Tree ti = parse_in_arena("{at: [], backtick: []}"); ti["at"][0] << "@test"; ti["at"][1] = "@test2"; ti["at"][2] << "@"; ti["at"][3] = "@"; ti["backtick"][0] << "`test"; ti["backtick"][1] = "`test2"; ti["backtick"][2] << "`"; ti["backtick"][3] = "`"; std::string yaml = emitrs_yaml(ti); test_check_emit_check(to_csubstr(yaml), [](Tree const &t){ ASSERT_TRUE(t.rootref().is_map()); ASSERT_TRUE(t.rootref().has_child("at")); ASSERT_TRUE(t.rootref().has_child("backtick")); ASSERT_EQ(t["at"].num_children(), 4u); ASSERT_EQ(t["backtick"].num_children(), 4u); EXPECT_EQ(t["at"][0].val(), "@test"); EXPECT_EQ(t["at"][1].val(), "@test2"); EXPECT_EQ(t["at"][2].val(), "@"); EXPECT_EQ(t["at"][3].val(), "@"); EXPECT_TRUE(t["at"][0].is_val_quoted()); EXPECT_TRUE(t["at"][1].is_val_quoted()); EXPECT_TRUE(t["at"][2].is_val_quoted()); EXPECT_TRUE(t["at"][3].is_val_quoted()); EXPECT_EQ(t["backtick"][0].val(), "`test"); EXPECT_EQ(t["backtick"][1].val(), "`test2"); EXPECT_EQ(t["backtick"][2].val(), "`"); EXPECT_EQ(t["backtick"][3].val(), "`"); EXPECT_TRUE(t["backtick"][0].is_val_quoted()); EXPECT_TRUE(t["backtick"][1].is_val_quoted()); EXPECT_TRUE(t["backtick"][2].is_val_quoted()); EXPECT_TRUE(t["backtick"][3].is_val_quoted()); }); } TEST(emit, at_is_quoted_only_in_the_beggining__issue_320) { Tree ti = parse_in_arena("{at: [], backtick: []}"); ti["at"].append_child() << "@test"; ti["at"].append_child() << "t@est"; ti["at"].append_child() << "test@"; ti["at"].append_child() = "@test2"; ti["at"].append_child() = "t@est2"; ti["at"].append_child() = "test2@"; ti["backtick"].append_child() << "`test"; ti["backtick"].append_child() << "t`est"; ti["backtick"].append_child() << "test`"; ti["backtick"].append_child() = "`test2"; ti["backtick"].append_child() = "t`est2"; ti["backtick"].append_child() = "test2`"; std::string yaml = emitrs_yaml(ti); test_check_emit_check(to_csubstr(yaml), [](Tree const &t){ ASSERT_TRUE(t.rootref().is_map()); ASSERT_TRUE(t.rootref().has_child("at")); ASSERT_TRUE(t.rootref().has_child("backtick")); ASSERT_EQ(t["at"].num_children(), 6u); ASSERT_EQ(t["backtick"].num_children(), 6u); EXPECT_EQ(t["at"][0].val(), "@test"); EXPECT_EQ(t["at"][1].val(), "t@est"); EXPECT_EQ(t["at"][2].val(), "test@"); EXPECT_EQ(t["at"][3].val(), "@test2"); EXPECT_EQ(t["at"][4].val(), "t@est2"); EXPECT_EQ(t["at"][5].val(), "test2@"); EXPECT_TRUE( t["at"][0].is_val_quoted()); EXPECT_TRUE( ! t["at"][1].is_val_quoted()); EXPECT_TRUE( ! t["at"][2].is_val_quoted()); EXPECT_TRUE( t["at"][3].is_val_quoted()); EXPECT_TRUE( ! t["at"][4].is_val_quoted()); EXPECT_TRUE( ! t["at"][5].is_val_quoted()); EXPECT_EQ(t["backtick"][0].val(), "`test"); EXPECT_EQ(t["backtick"][1].val(), "t`est"); EXPECT_EQ(t["backtick"][2].val(), "test`"); EXPECT_EQ(t["backtick"][3].val(), "`test2"); EXPECT_EQ(t["backtick"][4].val(), "t`est2"); EXPECT_EQ(t["backtick"][5].val(), "test2`"); EXPECT_TRUE( t["backtick"][0].is_val_quoted()); EXPECT_TRUE( ! t["backtick"][1].is_val_quoted()); EXPECT_TRUE( ! t["backtick"][2].is_val_quoted()); EXPECT_TRUE( t["backtick"][3].is_val_quoted()); EXPECT_TRUE( ! t["backtick"][4].is_val_quoted()); EXPECT_TRUE( ! t["backtick"][5].is_val_quoted()); }); } TEST(emit, error_on_emit_yaml_to_short_buffer) { csubstr yaml = "this is: not empty\n"; const Tree tree = parse_in_arena(yaml); { char too_small[2]; ExpectError::check_error_basic([&]{ emit_yaml(tree, too_small); }); ExpectError::check_error_basic([&]{ emit_yaml(tree, too_small, /*error_on_excess*/true); }); substr required = emit_yaml(tree, too_small, /*error_on_excess*/false); EXPECT_EQ(required.str, nullptr); EXPECT_EQ(required.len, yaml.len); } { substr nothing; EXPECT_EQ(nothing.str, nullptr); EXPECT_EQ(nothing.len, 0u); ExpectError::check_error_basic([&]{ emit_yaml(tree, nothing); }); ExpectError::check_error_basic([&]{ emit_yaml(tree, nothing, /*error_on_excess*/true); }); substr required = emit_yaml(tree, nothing, /*error_on_excess*/false); EXPECT_EQ(required.str, nullptr); EXPECT_EQ(required.len, yaml.len); } } TEST(emit, error_on_emit_json_to_short_buffer) { csubstr json = "{\"this is\": \"not empty\"}"; const Tree tree = parse_in_arena(json); { char too_small[2]; ExpectError::check_error_basic([&]{ emit_json(tree, too_small); }); ExpectError::check_error_basic([&]{ emit_json(tree, too_small, /*error_on_excess*/true); }); substr required = emit_json(tree, too_small, /*error_on_excess*/false); EXPECT_EQ(required.str, nullptr); EXPECT_EQ(required.len, json.len); } { substr nothing; EXPECT_EQ(nothing.str, nullptr); EXPECT_EQ(nothing.len, 0u); ExpectError::check_error_basic([&]{ emit_json(tree, nothing); }); ExpectError::check_error_basic([&]{ emit_json(tree, nothing, /*error_on_excess*/true); }); substr required = emit_json(tree, nothing, /*error_on_excess*/false); EXPECT_EQ(required.str, nullptr); EXPECT_EQ(required.len, json.len); } } //------------------------------------------- // this is needed to use the test case library Case const* get_case(csubstr /*name*/) { return nullptr; } } // namespace yml } // namespace c4