diff --git a/src/c4/yml/emit.def.hpp b/src/c4/yml/emit.def.hpp index 13a46805..95ace367 100644 --- a/src/c4/yml/emit.def.hpp +++ b/src/c4/yml/emit.def.hpp @@ -29,7 +29,7 @@ substr Emitter::emit_as(EmitType_e type, Tree const& t, id_type id, bool if(type == EMIT_YAML) _emit_yaml(id); else if(type == EMIT_JSON) - _do_visit_json(id); + _do_visit_json(id, 0); else _RYML_CB_ERR(m_tree->callbacks(), "unknown emit type"); m_tree = nullptr; @@ -535,9 +535,11 @@ C4_SUPPRESS_WARNING_MSVC_POP template -void Emitter::_do_visit_json(id_type id) +void Emitter::_do_visit_json(id_type id, id_type depth) { _RYML_CB_CHECK(m_tree->callbacks(), !m_tree->is_stream(id)); // JSON does not have streams + if(C4_UNLIKELY(depth > m_opts.max_depth())) + _RYML_CB_ERR(m_tree->callbacks(), "max depth exceeded"); if(m_tree->is_keyval(id)) { _writek_json(id); @@ -565,7 +567,7 @@ void Emitter::_do_visit_json(id_type id) { if(ich != m_tree->first_child(id)) this->Writer::_do_write(','); - _do_visit_json(ich); + _do_visit_json(ich, depth+1); } if(m_tree->is_seq(id)) diff --git a/src/c4/yml/emit.hpp b/src/c4/yml/emit.hpp index df271f1e..9b18784c 100644 --- a/src/c4/yml/emit.hpp +++ b/src/c4/yml/emit.hpp @@ -134,6 +134,8 @@ class Emitter : public Writer /** emit starting at the given node */ substr emit_as(EmitType_e type, ConstNodeRef const& n, bool error_on_excess=true) { + if(!n.tree() || n.id() == NONE) + return {}; _RYML_CB_CHECK(n.tree()->callbacks(), n.readable()); return this->emit_as(type, *n.tree(), n.id(), error_on_excess); } @@ -161,7 +163,7 @@ class Emitter : public Writer void _do_visit_flow_ml(id_type id, id_type ilevel=0, id_type do_indent=1); void _do_visit_block(id_type id, id_type ilevel=0, id_type do_indent=1); void _do_visit_block_container(id_type id, id_type next_level, bool do_indent); - void _do_visit_json(id_type id); + void _do_visit_json(id_type id, id_type depth); private: @@ -414,13 +416,13 @@ inline OStream& operator<< (OStream& s, as_yaml const& y) /** (1) emit YAML to the given buffer. Return a substr trimmed to the emitted YAML. * @param t the tree to emit. * @param id the node where to start emitting. - * @param buf the output buffer. * @param opts emit options. + * @param buf the output buffer. * @param error_on_excess Raise an error if the space in the buffer is insufficient. * @return a substr trimmed to the result in the output buffer. If the buffer is * insufficient (when error_on_excess is false), the string pointer of the * result will be set to null, and the length will report the required buffer size. */ -inline substr emit_yaml(Tree const& t, id_type id, substr buf, EmitOptions const& opts, bool error_on_excess=true) +inline substr emit_yaml(Tree const& t, id_type id, EmitOptions const& opts, substr buf, bool error_on_excess=true) { EmitterBuf em(opts, buf); return em.emit_as(EMIT_YAML, t, id, error_on_excess); @@ -434,13 +436,13 @@ inline substr emit_yaml(Tree const& t, id_type id, substr buf, bool error_on_exc /** (1) emit JSON to the given buffer. Return a substr trimmed to the emitted JSON. * @param t the tree to emit. * @param id the node where to start emitting. - * @param buf the output buffer. * @param opts emit options. + * @param buf the output buffer. * @param error_on_excess Raise an error if the space in the buffer is insufficient. * @return a substr trimmed to the result in the output buffer. If the buffer is * insufficient (when error_on_excess is false), the string pointer of the * result will be set to null, and the length will report the required buffer size. */ -inline substr emit_json(Tree const& t, id_type id, substr buf, EmitOptions const& opts, bool error_on_excess=true) +inline substr emit_json(Tree const& t, id_type id, EmitOptions const& opts, substr buf, bool error_on_excess=true) { EmitterBuf em(opts, buf); return em.emit_as(EMIT_JSON, t, id, error_on_excess); @@ -462,7 +464,7 @@ inline substr emit_json(Tree const& t, id_type id, substr buf, bool error_on_exc * @return a substr trimmed to the result in the output buffer. If the buffer is * insufficient (when error_on_excess is false), the string pointer of the * result will be set to null, and the length will report the required buffer size. */ -inline substr emit_yaml(Tree const& t, substr buf, EmitOptions const& opts, bool error_on_excess=true) +inline substr emit_yaml(Tree const& t, EmitOptions const& opts, substr buf, bool error_on_excess=true) { EmitterBuf em(opts, buf); return em.emit_as(EMIT_YAML, t, error_on_excess); @@ -480,7 +482,7 @@ inline substr emit_yaml(Tree const& t, substr buf, bool error_on_excess=true) * @return a substr trimmed to the result in the output buffer. If the buffer is * insufficient (when error_on_excess is false), the string pointer of the * result will be set to null, and the length will report the required buffer size. */ -inline substr emit_json(Tree const& t, substr buf, EmitOptions const& opts, bool error_on_excess=true) +inline substr emit_json(Tree const& t, EmitOptions const& opts, substr buf, bool error_on_excess=true) { EmitterBuf em(opts, buf); return em.emit_as(EMIT_JSON, t, error_on_excess); @@ -503,7 +505,7 @@ inline substr emit_json(Tree const& t, substr buf, bool error_on_excess=true) * @return a substr trimmed to the result in the output buffer. If the buffer is * insufficient (when error_on_excess is false), the string pointer of the * result will be set to null, and the length will report the required buffer size. */ -inline substr emit_yaml(ConstNodeRef const& r, substr buf, EmitOptions const& opts, bool error_on_excess=true) +inline substr emit_yaml(ConstNodeRef const& r, EmitOptions const& opts, substr buf, bool error_on_excess=true) { EmitterBuf em(opts, buf); return em.emit_as(EMIT_YAML, r, error_on_excess); @@ -522,7 +524,7 @@ inline substr emit_yaml(ConstNodeRef const& r, substr buf, bool error_on_excess= * @return a substr trimmed to the result in the output buffer. If the buffer is * insufficient (when error_on_excess is false), the string pointer of the * result will be set to null, and the length will report the required buffer size. */ -inline substr emit_json(ConstNodeRef const& r, substr buf, EmitOptions const& opts, bool error_on_excess=true) +inline substr emit_json(ConstNodeRef const& r, EmitOptions const& opts, substr buf, bool error_on_excess=true) { EmitterBuf em(opts, buf); return em.emit_as(EMIT_JSON, r, error_on_excess); @@ -552,15 +554,15 @@ inline substr emit_json(ConstNodeRef const& r, substr buf, bool error_on_excess= template substr emitrs_yaml(Tree const& t, id_type id, EmitOptions const& opts, CharOwningContainer * cont, bool append=false) { - const size_t startpos = append ? cont->size() : 0u; + size_t startpos = append ? cont->size() : 0u; cont->resize(cont->capacity()); // otherwise the first emit would be certain to fail substr buf = to_substr(*cont).sub(startpos); - substr ret = emit_yaml(t, id, buf, opts, /*error_on_excess*/false); + substr ret = emit_yaml(t, id, opts, buf, /*error_on_excess*/false); if(ret.str == nullptr && ret.len > 0) { cont->resize(startpos + ret.len); buf = to_substr(*cont).sub(startpos); - ret = emit_yaml(t, id, buf, opts, /*error_on_excess*/true); + ret = emit_yaml(t, id, opts, buf, /*error_on_excess*/true); } else { @@ -585,12 +587,13 @@ substr emitrs_json(Tree const& t, id_type id, EmitOptions const& opts, CharOwnin const size_t startpos = append ? cont->size() : 0u; cont->resize(cont->capacity()); // otherwise the first emit would be certain to fail substr buf = to_substr(*cont).sub(startpos); - substr ret = emit_json(t, id, buf, opts, /*error_on_excess*/false); + EmitterBuf em(opts, buf); + substr ret = emit_json(t, id, opts, buf, /*error_on_excess*/false); if(ret.str == nullptr && ret.len > 0) { cont->resize(startpos + ret.len); buf = to_substr(*cont).sub(startpos); - ret = emit_json(t, id, buf, opts, /*error_on_excess*/true); + ret = emit_json(t, id, opts, buf, /*error_on_excess*/true); } else { @@ -608,18 +611,18 @@ substr emitrs_json(Tree const& t, id_type id, CharOwningContainer * cont, bool a /** (3) emit+resize: YAML to a newly-created `std::string`/`std::vector`-like container. */ template -CharOwningContainer emitrs_yaml(Tree const& t, id_type id, EmitOptions const& opts={}, bool append=false) +CharOwningContainer emitrs_yaml(Tree const& t, id_type id, EmitOptions const& opts={}) { CharOwningContainer c; - emitrs_yaml(t, id, opts, &c, append); + emitrs_yaml(t, id, opts, &c); return c; } /** (3) emit+resize: JSON to a newly-created `std::string`/`std::vector`-like container. */ template -CharOwningContainer emitrs_json(Tree const& t, id_type id, EmitOptions const& opts={}, bool append=false) +CharOwningContainer emitrs_json(Tree const& t, id_type id, EmitOptions const& opts={}) { CharOwningContainer c; - emitrs_json(t, id, opts, &c, append); + emitrs_json(t, id, opts, &c); return c; } @@ -666,22 +669,22 @@ substr emitrs_json(Tree const& t, CharOwningContainer * cont, bool append=false) /** (3) emit+resize: YAML to a newly-created `std::string`/`std::vector`-like container. */ template -CharOwningContainer emitrs_yaml(Tree const& t, EmitOptions const& opts={}, bool append=false) +CharOwningContainer emitrs_yaml(Tree const& t, EmitOptions const& opts={}) { CharOwningContainer c; if(t.empty()) return c; - emitrs_yaml(t, t.root_id(), opts, &c, append); + emitrs_yaml(t, t.root_id(), opts, &c); return c; } /** (3) emit+resize: JSON to a newly-created `std::string`/`std::vector`-like container. */ template -CharOwningContainer emitrs_json(Tree const& t, EmitOptions const& opts={}, bool append=false) +CharOwningContainer emitrs_json(Tree const& t, EmitOptions const& opts={}) { CharOwningContainer c; if(t.empty()) return c; - emitrs_json(t, t.root_id(), opts, &c, append); + emitrs_json(t, t.root_id(), opts, &c); return c; } @@ -725,20 +728,20 @@ substr emitrs_json(ConstNodeRef const& n, CharOwningContainer * cont, bool appen /** (3) emit+resize: YAML to a newly-created `std::string`/`std::vector`-like container. */ template -CharOwningContainer emitrs_yaml(ConstNodeRef const& n, EmitOptions const& opts={}, bool append=false) +CharOwningContainer emitrs_yaml(ConstNodeRef const& n, EmitOptions const& opts={}) { _RYML_CB_CHECK(n.tree()->callbacks(), n.readable()); CharOwningContainer c; - emitrs_yaml(*n.tree(), n.id(), opts, &c, append); + emitrs_yaml(*n.tree(), n.id(), opts, &c); return c; } /** (3) emit+resize: JSON to a newly-created `std::string`/`std::vector`-like container. */ template -CharOwningContainer emitrs_json(ConstNodeRef const& n, EmitOptions const& opts={}, bool append=false) +CharOwningContainer emitrs_json(ConstNodeRef const& n, EmitOptions const& opts={}) { _RYML_CB_CHECK(n.tree()->callbacks(), n.readable()); CharOwningContainer c; - emitrs_json(*n.tree(), n.id(), opts, &c, append); + emitrs_json(*n.tree(), n.id(), opts, &c); return c; } diff --git a/test/test_emit.cpp b/test/test_emit.cpp index 09fd967f..16a66cd9 100644 --- a/test/test_emit.cpp +++ b/test/test_emit.cpp @@ -54,6 +54,14 @@ std::string emit2buf(Emit &&fn) return buf; } +template +std::string emitrs_append(csubstr first_part, Emit &&fn) +{ + std::string buf(first_part.begin(), first_part.end()); + fn(&buf); + return buf; +} + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- @@ -89,41 +97,144 @@ TEST(as_json, basic) //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- -template -void test_emits(Tree const& t, size_t id, std::string const& expected, std::string const& expected_json) + +id_type getdepth(Tree const& t, id_type id, id_type currdepth=0, id_type maxdepth=0) { - 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(emitrs_yaml(t, id), expected); - EXPECT_EQ(emitrs_json(t, id), expected_json); + maxdepth = currdepth > maxdepth ? currdepth : maxdepth; + for(id_type child = t.first_child(id); child != NONE; child = t.next_sibling(child)) + { + const id_type d = getdepth(t, child, currdepth+1, maxdepth); + maxdepth = d > maxdepth ? d : maxdepth; + } + return maxdepth; +} + +void test_emits(Tree const& t, id_type id, std::string const& expected, std::string const& expected_json) +{ + EXPECT_EQ(emit2buf([&](substr buf){ return emit_yaml(t, id, buf); }), expected); + EXPECT_EQ(emit2buf([&](substr buf){ return emit_yaml(t, id, EmitOptions{}, buf); }), expected); + EXPECT_EQ(emit2buf([&](substr buf){ return emit_json(t, id, buf); }), expected_json); + EXPECT_EQ(emit2buf([&](substr buf){ return emit_json(t, id, EmitOptions{}, buf); }), expected_json); + EXPECT_EQ(emit2file([&](FILE *f){ return emit_yaml(t, id, f); }), expected); + EXPECT_EQ(emit2file([&](FILE *f){ return emit_yaml(t, id, EmitOptions{}, f); }), expected); + EXPECT_EQ(emit2file([&](FILE *f){ return emit_json(t, id, f); }), expected_json); + EXPECT_EQ(emit2file([&](FILE *f){ return emit_json(t, id, EmitOptions{}, f); }), expected_json); + if(id != NONE) + { + EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ oss << ConstNodeRef(&t, id); }), expected); + EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ oss << as_yaml(ConstNodeRef(&t, id)); }), expected); + EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ oss << as_yaml(ConstNodeRef(&t, id), EmitOptions{}); }), expected); + EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ oss << as_json(ConstNodeRef(&t, id)); }), expected_json); + EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ oss << as_json(ConstNodeRef(&t, id), EmitOptions{}); }), expected_json); + } + EXPECT_EQ(emitrs_yaml(t, id), expected); + EXPECT_EQ(emitrs_yaml(t, id, EmitOptions{}), expected); + EXPECT_EQ(emitrs_json(t, id), expected_json); + EXPECT_EQ(emitrs_json(t, id, EmitOptions{}), expected_json); + std::string append_prefix = "#before\n"; + 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_yaml(t, id, EmitOptions{}, 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(emitrs_append(to_csubstr(append_prefix), [&](std::string *s) { emitrs_json(t, id, EmitOptions{}, s, /*append*/true); } ), append_prefix + expected_json); + // error on max depth + if(id == NONE) + return; + id_type max_depth = getdepth(t, id); + if(max_depth > 1) + { + EmitOptions opts = EmitOptions{}.max_depth(0); + ExpectError::do_check(&t, [&]{ C4_DONT_OPTIMIZE(emit2buf([&](substr buf){ return emit_yaml(t, id, opts, buf); })); }); + ExpectError::do_check(&t, [&]{ C4_DONT_OPTIMIZE(emit2buf([&](substr buf){ return emit_json(t, id, opts, buf); })); }); + ExpectError::do_check(&t, [&]{ C4_DONT_OPTIMIZE(emit2file([&](FILE *f){ return emit_yaml(t, id, opts, f); })); }); + ExpectError::do_check(&t, [&]{ C4_DONT_OPTIMIZE(emit2file([&](FILE *f){ return emit_json(t, id, opts, f); })); }); + ExpectError::do_check(&t, [&]{ C4_DONT_OPTIMIZE(emit2stream([&](std::ostringstream &oss){ oss << as_yaml(ConstNodeRef(&t, id), opts); })); }); + ExpectError::do_check(&t, [&]{ C4_DONT_OPTIMIZE(emit2stream([&](std::ostringstream &oss){ oss << as_json(ConstNodeRef(&t, id), opts); })); }); + ExpectError::do_check(&t, [&]{ C4_DONT_OPTIMIZE(emitrs_yaml(t, id, opts)); }); + ExpectError::do_check(&t, [&]{ C4_DONT_OPTIMIZE(emitrs_json(t, id, opts)); }); + } } -template void test_emits(Tree const& t, std::string const& expected, std::string const& expected_json) { - 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(emit2stream([&](std::ostream& s){ s << t; }), expected); - EXPECT_EQ(emit2stream([&](std::ostream& s){ s << as_json(t); }), expected_json); - EXPECT_EQ(emitrs_yaml(t), expected); - EXPECT_EQ(emitrs_json(t), expected_json); + EXPECT_EQ(emit2buf([&](substr buf){ return emit_yaml(t, buf); }), expected); + EXPECT_EQ(emit2buf([&](substr buf){ return emit_yaml(t, EmitOptions{}, buf); }), expected); + EXPECT_EQ(emit2buf([&](substr buf){ return emit_json(t, buf); }), expected_json); + EXPECT_EQ(emit2buf([&](substr buf){ return emit_json(t, EmitOptions{}, buf); }), expected_json); + EXPECT_EQ(emit2file([&](FILE *f){ return emit_yaml(t, f); }), expected); + EXPECT_EQ(emit2file([&](FILE *f){ return emit_yaml(t, EmitOptions{}, f); }), expected); + EXPECT_EQ(emit2file([&](FILE *f){ return emit_json(t, f); }), expected_json); + EXPECT_EQ(emit2file([&](FILE *f){ return emit_json(t, EmitOptions{}, f); }), expected_json); + if(!t.empty()) + { + EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ oss << ConstNodeRef(&t); }), expected); + EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ oss << as_yaml(ConstNodeRef(&t)); }), expected); + EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ oss << as_yaml(ConstNodeRef(&t), EmitOptions{}); }), expected); + EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ oss << as_json(ConstNodeRef(&t)); }), expected_json); + EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ oss << as_json(ConstNodeRef(&t), EmitOptions{}); }), expected_json); + } + EXPECT_EQ(emitrs_yaml(t), expected); + EXPECT_EQ(emitrs_yaml(t, EmitOptions{}), expected); + EXPECT_EQ(emitrs_json(t), expected_json); + EXPECT_EQ(emitrs_json(t, EmitOptions{}), expected_json); + std::string append_prefix = "#before\n"; + 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_yaml(t, EmitOptions{}, 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(emitrs_append(to_csubstr(append_prefix), [&](std::string *s) { emitrs_json(t, EmitOptions{}, s, /*append*/true); } ), append_prefix + expected_json); + // error on max depth + id_type max_depth = t.empty() ? 0 : getdepth(t, t.root_id()); + if(max_depth > 1) + { + EmitOptions opts = EmitOptions{}.max_depth(0); + ExpectError::do_check(&t, [&]{ C4_DONT_OPTIMIZE(emit2buf([&](substr buf){ return emit_yaml(t, opts, buf); })); }); + ExpectError::do_check(&t, [&]{ C4_DONT_OPTIMIZE(emit2buf([&](substr buf){ return emit_json(t, opts, buf); })); }); + ExpectError::do_check(&t, [&]{ C4_DONT_OPTIMIZE(emit2file([&](FILE *f){ return emit_yaml(t, opts, f); })); }); + ExpectError::do_check(&t, [&]{ C4_DONT_OPTIMIZE(emit2file([&](FILE *f){ return emit_json(t, opts, f); })); }); + ExpectError::do_check(&t, [&]{ C4_DONT_OPTIMIZE(emit2stream([&](std::ostringstream &oss){ oss << as_yaml(ConstNodeRef(&t), opts); })); }); + ExpectError::do_check(&t, [&]{ C4_DONT_OPTIMIZE(emit2stream([&](std::ostringstream &oss){ oss << as_json(ConstNodeRef(&t), opts); })); }); + ExpectError::do_check(&t, [&]{ C4_DONT_OPTIMIZE(emitrs_yaml(t, opts)); }); + ExpectError::do_check(&t, [&]{ C4_DONT_OPTIMIZE(emitrs_json(t, opts)); }); + } } -template -void test_emits(ConstNodeRef t, std::string const& expected, std::string const& expected_json) +void test_emits(ConstNodeRef n, std::string const& expected, std::string const& expected_json) { - 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(emit2stream([&](std::ostream& s){ s << t; }), expected); - EXPECT_EQ(emit2stream([&](std::ostream& s){ s << as_json(t); }), expected_json); - EXPECT_EQ(emitrs_yaml(t), expected); - EXPECT_EQ(emitrs_json(t), expected_json); + EXPECT_EQ(emit2buf([&](substr buf){ return emit_yaml(n, buf); }), expected); + EXPECT_EQ(emit2buf([&](substr buf){ return emit_yaml(n, EmitOptions{}, buf); }), expected); + EXPECT_EQ(emit2buf([&](substr buf){ return emit_json(n, buf); }), expected_json); + EXPECT_EQ(emit2buf([&](substr buf){ return emit_json(n, EmitOptions{}, buf); }), expected_json); + EXPECT_EQ(emit2file([&](FILE *f){ return emit_yaml(n, f); }), expected); + EXPECT_EQ(emit2file([&](FILE *f){ return emit_yaml(n, EmitOptions{}, f); }), expected); + EXPECT_EQ(emit2file([&](FILE *f){ return emit_json(n, f); }), expected_json); + EXPECT_EQ(emit2file([&](FILE *f){ return emit_json(n, EmitOptions{}, f); }), 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_yaml(n, EmitOptions{}); }), expected); + EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ oss << as_json(n); }), expected_json); + EXPECT_EQ(emit2stream([&](std::ostringstream &oss){ oss << as_json(n, EmitOptions{}); }), expected_json); + EXPECT_EQ(emitrs_yaml(n), expected); + EXPECT_EQ(emitrs_yaml(n, EmitOptions{}), expected); + EXPECT_EQ(emitrs_json(n), expected_json); + EXPECT_EQ(emitrs_json(n, EmitOptions{}), expected_json); + std::string append_prefix = "#before\n"; + 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_yaml(n, EmitOptions{}, 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(emitrs_append(to_csubstr(append_prefix), [&](std::string *s) { emitrs_json(n, EmitOptions{}, s, /*append*/true); } ), append_prefix + expected_json); + // error on max depth + id_type max_depth = getdepth(*n.tree(), n.id()); + if(max_depth > 1) + { + EmitOptions opts = EmitOptions{}.max_depth(0); + ExpectError::do_check(n.tree(), [&]{ C4_DONT_OPTIMIZE(emit2buf([&](substr buf){ return emit_yaml(n, opts, buf); })); }); + ExpectError::do_check(n.tree(), [&]{ C4_DONT_OPTIMIZE(emit2buf([&](substr buf){ return emit_json(n, opts, buf); })); }); + ExpectError::do_check(n.tree(), [&]{ C4_DONT_OPTIMIZE(emit2file([&](FILE *f){ return emit_yaml(n, opts, f); })); }); + ExpectError::do_check(n.tree(), [&]{ C4_DONT_OPTIMIZE(emit2file([&](FILE *f){ return emit_json(n, opts, f); })); }); + ExpectError::do_check(n.tree(), [&]{ C4_DONT_OPTIMIZE(emit2stream([&](std::ostringstream &oss){ oss << as_yaml(n, opts); })); }); + ExpectError::do_check(n.tree(), [&]{ C4_DONT_OPTIMIZE(emit2stream([&](std::ostringstream &oss){ oss << as_json(n, opts); })); }); + ExpectError::do_check(n.tree(), [&]{ C4_DONT_OPTIMIZE(emitrs_yaml(n, opts)); }); + ExpectError::do_check(n.tree(), [&]{ C4_DONT_OPTIMIZE(emitrs_json(n, opts)); }); + } } @@ -187,7 +298,7 @@ TEST(emit, existing_seq_node) nct._add_flags(n.id(), FLOW_SL); expected = "foo"; { - SCOPED_TRACE("t, id"); + SCOPED_TRACE("noderef"); test_emits(n, expected, expected_json); } { @@ -211,7 +322,7 @@ TEST(emit, existing_seq_node) nct._add_flags(n.id(), FLOW_SL); expected = "bar"; { - SCOPED_TRACE("t, id"); + SCOPED_TRACE("noderef"); test_emits(n, expected, expected_json); } { @@ -236,7 +347,7 @@ TEST(emit, existing_seq_node) expected = "[nested,seq]"; nct._add_flags(n.id(), FLOW_SL); { - SCOPED_TRACE("t, id"); + SCOPED_TRACE("noderef"); test_emits(n, expected, expected_json); } { @@ -260,7 +371,7 @@ TEST(emit, existing_seq_node) expected = "{nested: map}"; nct._add_flags(n.id(), FLOW_SL); { - SCOPED_TRACE("t, id"); + SCOPED_TRACE("noderef"); test_emits(n, expected, expected_json); } { diff --git a/test/test_lib/test_case.cpp b/test/test_lib/test_case.cpp index d27e6d91..a3b368b9 100644 --- a/test/test_lib/test_case.cpp +++ b/test/test_lib/test_case.cpp @@ -262,6 +262,11 @@ void ExpectError::check_success(Tree *tree, std::function fn) EXPECT_FALSE(context.m_got_an_error); } +void ExpectError::do_check(Tree const* tree, std::function fn, Location expected_location) +{ + do_check(const_cast(tree), fn, expected_location); +} + void ExpectError::do_check(Tree *tree, std::function fn, Location expected_location) { auto context = ExpectError(tree, expected_location); diff --git a/test/test_lib/test_case.hpp b/test/test_lib/test_case.hpp index bc2a9939..e85e964b 100644 --- a/test/test_lib/test_case.hpp +++ b/test/test_lib/test_case.hpp @@ -203,8 +203,9 @@ struct ExpectError ExpectError(Tree *tree, Location loc={}); ~ExpectError(); - static void do_check( std::function fn, Location expected={}) { do_check(nullptr, fn, expected); } + static void do_check( std::function fn, Location expected={}) { do_check((const Tree*)nullptr, fn, expected); } static void do_check(Tree *tree, std::function fn, Location expected={}); + static void do_check(Tree const *tree, std::function fn, Location expected={}); static void check_assertion( std::function fn, Location expected={}) { check_assertion(nullptr, fn, expected); } static void check_assertion(Tree *tree, std::function fn, Location expected={}); static void check_success( std::function fn) { check_success(nullptr, fn); };