Author: rinrab
Date: Sun May 18 19:20:48 2025
New Revision: 1925686
URL: http://svn.apache.org/viewvc?rev=1925686&view=rev
Log:
On the 'xml-writer' branch: Implement the xml writer api.
* subversion/include/svn_xml.h
(svn_xml_writer_t,
(svn_xml_writer_create,
svn_xml_writer_close,
svn_xml_writer_flush,
svn_xml_write_open_tag,
svn_xml_write_open_tag_v,
svn_xml_write_open_tag_hash,
svn_xml_write_cdata_cstring,
svn_xml_write_cdata,
svn_xml_write_close_tag,
svn_xml_write_header): Declare symbols.
* subversion/libsvn_subr/xml_writer.c
(xml_stream_write,
xml_ensure_bytes,
xml_write_byte,
xml_write_bytes,
xml_write_cstring): Implement functions for writing data to the buffer.
(svn_xml_writer_create,
svn_xml_writer_close,
svn_xml_writer_flush,
svn_xml_write_open_tag,
svn_xml_write_open_tag_v,
svn_xml_write_open_tag_hash,
svn_xml_write_cdata_cstring,
svn_xml_write_cdata,
svn_xml_write_close_tag,
svn_xml_write_header): Implement symbols.
* subversion/tests/libsvn_subr/xml-test.c
(test_xml_writer,
test_xml_writer_always_flush): New tests.
(test_funcs): Run those tests.
Any kind of feedback will be much appreciated!!
Added:
subversion/branches/xml-writer/subversion/libsvn_subr/xml_writer.c (with
props)
Modified:
subversion/branches/xml-writer/subversion/include/svn_xml.h
subversion/branches/xml-writer/subversion/tests/libsvn_subr/xml-test.c
Modified: subversion/branches/xml-writer/subversion/include/svn_xml.h
URL:
http://svn.apache.org/viewvc/subversion/branches/xml-writer/subversion/include/svn_xml.h?rev=1925686&r1=1925685&r2=1925686&view=diff
==============================================================================
--- subversion/branches/xml-writer/subversion/include/svn_xml.h (original)
+++ subversion/branches/xml-writer/subversion/include/svn_xml.h Sun May 18
19:20:48 2025
@@ -399,6 +399,189 @@ svn_xml_make_close_tag(svn_stringbuf_t *
apr_pool_t *pool,
const char *tagname);
+/*---------------------------------------------------------------*/
+
+/** XML writer, writing XML tags to a generic stream.
+ *
+ * Summary
+ * ---
+ *
+ * The XML writer APIs provides functionality to write the XML tags to
+ * any writable stream, without having a need to manage a stringbuf,
+ * periodically write it to the output, and empty it then -- the XML
+ * writer implements this functionality.
+ *
+ * Usage
+ * ---
+ *
+ * 1. Create the writer using svn_xml_writer_create() before performing any
+ * writes.
+ *
+ * 2. During the lifetime of the XML writer, all the svn_xml_write_* functions
+ * can be safely invoked. Please note that they won't immediately write the
+ * data to the stream (more below), but you may force it using the
+ * svn_xml_writer_flush() function.
+ *
+ * 3. After the operation, the callers MUST close the writer using the
+ * svn_xml_writer_close() function. It will flush the remaining data in
+ * the buffer and close the ostream.
+ *
+ * Buffering
+ * ---
+ *
+ * The XML writer implements a temporary buffer, which will be filled with
+ * the content before it would have been flushed into the stream. This is
+ * used for optimization purposes, so we won't invoke the entire sequence
+ * of stream's write callbacks on each tag we want to write. The callers
+ * may use the svn_xml_writer_flush() function if they want to explicitly
+ * flush it to the stream. Otherwise, the writer will automatically flush
+ * the buffer as soon as it is about to exceed the limit.
+ *
+ * The buffering allows the callers to not care about how much tags do
+ * they want to write. If you are using it in a simple loop, no flushes are
+ * required. You may confidently rely to the automatic flush. However, if
+ * the code following after the write will wait for a long operation, for
+ * example, when the tag has been opened, but we need to do a request to a
+ * server, it's recommended to flush the buffer, so we won't have an opened
+ * tag partially written before the freeze.
+ *
+ * @since New in 1.15.
+ */
+
+typedef struct svn_xml_writer_t svn_xml_writer_t;
+
+/** Create an XML writer for writing to @a ostream. Sets @a writer with
+ * the result, allocated in @a result_pool.
+ *
+ * @a ostream will be closed with the writer, in the svn_xml_writer_close()
+ * function.
+ *
+ * The callers MUST close the writer via svn_xml_writer_close().
+ *
+ * @since New in 1.15.
+ */
+svn_error_t *
+svn_xml_writer_create(svn_xml_writer_t **writer,
+ svn_stream_t *ostream,
+ apr_pool_t *result_pool);
+
+/** Close @a xml_writer. This will also close the stream.
+ *
+ * @since New in 1.15.
+ */
+svn_error_t *
+svn_xml_writer_close(svn_xml_writer_t *xml_writer);
+
+/** Flush the buffer of @a xml_writer to the stream.
+ *
+ * @since New in 1.15.
+ */
+svn_error_t *
+svn_xml_writer_flush(svn_xml_writer_t *xml_writer);
+
+/** Write a new xml tag @a tagname to @a xml_writer.
+ *
+ * Take the tag's attributes from varargs, a SVN_VA_NULL-terminated list of
+ * alternating <tt>char *</tt> key and <tt>char *</tt> val. Do xml-escaping
+ * on each val.
+ *
+ * @a style is one of the enumerated styles in @c svn_xml_open_tag_style.
+ *
+ * Use @a scratch_pool for temporary allocations.
+ *
+ * This function is similar to svn_xml_make_open_tag(), but writes the
+ * result to an XML writer.
+ *
+ * @since New in 1.15.
+ */
+svn_error_t *
+svn_xml_write_open_tag(svn_xml_writer_t *xml_writer,
+ apr_pool_t *scratch_pool,
+ enum svn_xml_open_tag_style style,
+ const char *tagname, ...) SVN_NEEDS_SENTINEL_NULL;
+
+/** Like svn_xml_write_open_tag(), but takes a @c va_list instead of being
+ * variadic.
+ *
+ * This function is similar to svn_xml_make_open_tag_v(), but writes the
+ * result to an XML writer.
+ *
+ * @since New in 1.15.
+ */
+svn_error_t *
+svn_xml_write_open_tag_v(svn_xml_writer_t *xml_writer,
+ apr_pool_t *scratch_pool,
+ enum svn_xml_open_tag_style style,
+ const char *tagname, va_list ap);
+
+/** Like svn_xml_write_open_tag(), but takes a hash table of attributes
+ * (<tt>char *</tt> keys mapping to <tt>char *</tt> values).
+ *
+ * You might ask, why not just provide svn_xml_make_tag_atts()?
+ *
+ * The reason is that a hash table is the most natural interface to an
+ * attribute list; the fact that Expat uses <tt>char **</tt> atts instead is
+ * certainly a defensible implementation decision, but since we'd have
+ * to have special code to support such lists throughout Subversion
+ * anyway, we might as well write that code for the natural interface
+ * (hashes) and then convert in the few cases where conversion is
+ * needed. Someday it might even be nice to change expat-lite to work
+ * with apr hashes.
+ *
+ * See conversion functions svn_xml_make_att_hash() and
+ * svn_xml_make_att_hash_overlaying(). Callers should use those to
+ * convert Expat attr lists into hashes when necessary.
+ *
+ * This function is similar to svn_xml_make_open_tag_hash(), but writes the
+ * result to an XML writer.
+ *
+ * @since New in 1.15.
+ */
+svn_error_t *
+svn_xml_write_open_tag_hash(svn_xml_writer_t *xml_writer,
+ apr_pool_t *scratch_pool,
+ enum svn_xml_open_tag_style style,
+ const char *tagname, apr_hash_t *attributes);
+
+/** Writes and escapes cdata from a NULL-terminated string @a str
+ * to @a xml_writer.
+ *
+ * @since New in 1.15.
+ */
+svn_error_t *
+svn_xml_write_cdata_cstring(svn_xml_writer_t *xml_writer,
+ const char *str);
+
+/** Writes and escapes @a len cdata chars from @a data to @a xml_writer.
+ *
+ * @since New in 1.15.
+ */
+svn_error_t *
+svn_xml_write_cdata(svn_xml_writer_t *xml_writer,
+ const char *data, apr_size_t len);
+
+/** Write @a tagname close tag to @a xml_writer.
+ *
+ * @since New in 1.15.
+ */
+svn_error_t *
+svn_xml_write_close_tag(svn_xml_writer_t *xml_writer,
+ apr_pool_t *scratch_pool,
+ const char *tagname);
+
+/** Write an XML header to @a xml_writer.
+ *
+ * Fully-formed XML documents should start out with a header,
+ * something like <pre>
+ * \<?xml version="1.0" encoding="UTF-8"?\>
+ * </pre>
+ *
+ * @since New in 1.15.
+ */
+svn_error_t *
+svn_xml_write_header(svn_xml_writer_t *xml_writer,
+ const char *encoding,
+ apr_pool_t *scratch_pool);
#ifdef __cplusplus
}
Added: subversion/branches/xml-writer/subversion/libsvn_subr/xml_writer.c
URL:
http://svn.apache.org/viewvc/subversion/branches/xml-writer/subversion/libsvn_subr/xml_writer.c?rev=1925686&view=auto
==============================================================================
--- subversion/branches/xml-writer/subversion/libsvn_subr/xml_writer.c (added)
+++ subversion/branches/xml-writer/subversion/libsvn_subr/xml_writer.c Sun May
18 19:20:48 2025
@@ -0,0 +1,369 @@
+/*
+ * xml_writer.c: svn_xml_writer_t implementation
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+
+
+/*** Includes. ***/
+
+#include <assert.h>
+#include "svn_error.h"
+#include "svn_error_codes.h"
+#include "svn_io.h"
+#include "svn_string.h"
+#include "svn_xml.h"
+
+#include "svn_private_config.h"
+
+#define BUFFER_LENGTH 512
+
+
+/*** svn_xml_writer_t constructor and destructor ***/
+
+struct svn_xml_writer_t
+{
+ /* buffer and its offset */
+ char buffer[BUFFER_LENGTH];
+ apr_size_t offset;
+
+ /* an output stream, to write the XML to. */
+ svn_stream_t *ostream;
+
+ /* where this object is allocated, so we can free it easily */
+ apr_pool_t *pool;
+};
+
+svn_error_t *
+svn_xml_writer_create(svn_xml_writer_t **writer,
+ svn_stream_t *ostream,
+ apr_pool_t *result_pool)
+{
+ svn_xml_writer_t *result = apr_palloc(result_pool, sizeof(*result));
+
+ result->offset = 0;
+ result->ostream = ostream;
+ result->pool = result_pool;
+
+ *writer = result;
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_xml_writer_close(svn_xml_writer_t *writer)
+{
+ if (writer)
+ {
+ SVN_ERR(svn_xml_writer_flush(writer));
+ SVN_ERR(svn_stream_close(writer->ostream));
+ writer->ostream = NULL;
+ }
+
+ return SVN_NO_ERROR;
+}
+
+
+/*** Buffering and writing routines ***/
+
+static svn_error_t *
+xml_stream_write(svn_xml_writer_t *writer, const char *data, apr_size_t len)
+{
+ apr_size_t write_len = len;
+
+ /* We're gonna bail on an incomplete write here only because we know
+ that this stream is really stdout, which should never be blocking
+ on us. */
+ SVN_ERR(svn_stream_write(writer->ostream, data, &write_len));
+ if (write_len != len)
+ return svn_error_create(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
+ _("Error writing to stream"));
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_xml_writer_flush(svn_xml_writer_t *writer)
+{
+ if (writer->offset > 0)
+ {
+ SVN_ERR(xml_stream_write(writer, writer->buffer, writer->offset));
+ writer->offset = 0;
+ }
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+xml_ensure_bytes(svn_xml_writer_t *writer, apr_size_t bytes)
+{
+ if (writer->offset + bytes > BUFFER_LENGTH)
+ SVN_ERR(svn_xml_writer_flush(writer));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+xml_write_byte(svn_xml_writer_t *writer, char b)
+{
+ SVN_ERR(xml_ensure_bytes(writer, 1));
+ writer->buffer[writer->offset++] = b;
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+xml_write_bytes(svn_xml_writer_t *writer, const char *data, apr_size_t len)
+{
+ if (len < BUFFER_LENGTH)
+ {
+ SVN_ERR(xml_ensure_bytes(writer, len));
+ memcpy(writer->buffer + writer->offset, data, len);
+ writer->offset += len;
+ }
+ else
+ {
+ SVN_ERR(svn_xml_writer_flush(writer));
+ SVN_ERR(xml_stream_write(writer, data, len));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+xml_write_cstring(svn_xml_writer_t *writer, const char *str)
+{
+ return svn_error_trace(xml_write_bytes(writer, str, strlen(str)));
+}
+
+svn_error_t *
+svn_xml_write_raw(svn_xml_writer_t *writer,
+ const char *data, apr_size_t len)
+{
+ return svn_error_trace(xml_write_bytes(writer, data, len));
+}
+
+/* XML Escaping */
+
+static svn_error_t *
+xml_write_escaped_attr(svn_xml_writer_t *xml_writer,
+ const char *data, apr_size_t len)
+{
+ const char *end = data + len;
+ const char *p = data, *q;
+
+ while (1)
+ {
+ /* Find a character which needs to be quoted and append bytes up
+ to that point. */
+ q = p;
+ while (q < end && *q != '&' && *q != '<' && *q != '>' && *q != '"' &&
+ *q != '\'' && *q != '\r' && *q != '\n' && *q != '\t')
+ q++;
+ SVN_ERR(xml_write_bytes(xml_writer, p, q - p));
+
+ /* We may already be a winner. */
+ if (q == end)
+ break;
+
+ /* Append the entity reference for the character. */
+ if (*q == '&')
+ SVN_ERR(xml_write_cstring(xml_writer, "&"));
+ else if (*q == '<')
+ SVN_ERR(xml_write_cstring(xml_writer, "<"));
+ else if (*q == '>')
+ SVN_ERR(xml_write_cstring(xml_writer, ">"));
+ else if (*q == '"')
+ SVN_ERR(xml_write_cstring(xml_writer, """));
+ else if (*q == '\'')
+ SVN_ERR(xml_write_cstring(xml_writer, "'"));
+ else if (*q == '\r')
+ SVN_ERR(xml_write_cstring(xml_writer, " "));
+ else if (*q == '\n')
+ SVN_ERR(xml_write_cstring(xml_writer, " "));
+ else if (*q == '\t')
+ SVN_ERR(xml_write_cstring(xml_writer, "	"));
+
+ p = q + 1;
+ }
+
+ return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_xml_write_cdata(svn_xml_writer_t *xml_writer,
+ const char *data, apr_size_t len)
+{
+ const char *end = data + len;
+ const char *p = data, *q;
+
+ while (1)
+ {
+ /* Find a character which needs to be quoted and append bytes up
+ to that point. Strictly speaking, '>' only needs to be
+ quoted if it follows "]]", but it's easier to quote it all
+ the time.
+
+ So, why are we escaping '\r' here? Well, according to the
+ XML spec, '\r\n' gets converted to '\n' during XML parsing.
+ Also, any '\r' not followed by '\n' is converted to '\n'. By
+ golly, if we say we want to escape a '\r', we want to make
+ sure it remains a '\r'! */
+ q = p;
+ while (q < end && *q != '&' && *q != '<' && *q != '>' && *q != '\r')
+ q++;
+ SVN_ERR(xml_write_bytes(xml_writer, p, q - p));
+
+ /* We may already be a winner. */
+ if (q == end)
+ break;
+
+ /* Append the entity reference for the character. */
+ if (*q == '&')
+ SVN_ERR(xml_write_cstring(xml_writer, "&"));
+ else if (*q == '<')
+ SVN_ERR(xml_write_cstring(xml_writer, "<"));
+ else if (*q == '>')
+ SVN_ERR(xml_write_cstring(xml_writer, ">"));
+ else if (*q == '\r')
+ SVN_ERR(xml_write_cstring(xml_writer, " "));
+
+ p = q + 1;
+ }
+
+ return SVN_NO_ERROR;
+}
+
+
+
+/*** Writing an open tag ***/
+
+static svn_error_t *
+xml_write_attribute(svn_xml_writer_t *xml_writer,
+ const char *key, const char *val)
+{
+ SVN_ERR(xml_write_cstring(xml_writer, "\n "));
+ SVN_ERR(xml_write_cstring(xml_writer, key));
+ SVN_ERR(xml_write_cstring(xml_writer, "=\""));
+ SVN_ERR(xml_write_escaped_attr(xml_writer, val, strlen(val)));
+ SVN_ERR(xml_write_byte(xml_writer, '"'));
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_xml_write_open_tag_hash(svn_xml_writer_t *xml_writer,
+ apr_pool_t *scratch_pool,
+ enum svn_xml_open_tag_style style,
+ const char *tagname, apr_hash_t *attributes)
+{
+ apr_hash_index_t *hi;
+
+ SVN_ERR(xml_write_byte(xml_writer, '<'));
+ SVN_ERR(xml_write_cstring(xml_writer, tagname));
+
+ for (hi = apr_hash_first(scratch_pool, attributes);
+ hi;
+ hi = apr_hash_next(hi))
+ {
+ const void *key;
+ void *val;
+
+ apr_hash_this(hi, &key, NULL, &val);
+ assert(val != NULL);
+
+ SVN_ERR(xml_write_attribute(xml_writer, key, val));
+ }
+
+ if (style == svn_xml_self_closing)
+ SVN_ERR(xml_write_byte(xml_writer, '/'));
+ SVN_ERR(xml_write_byte(xml_writer, '>'));
+ if (style != svn_xml_protect_pcdata)
+ SVN_ERR(xml_write_byte(xml_writer, '\n'));
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_xml_write_open_tag_v(svn_xml_writer_t *xml_writer,
+ apr_pool_t *scratch_pool,
+ enum svn_xml_open_tag_style style,
+ const char *tagname,
+ va_list ap)
+{
+ apr_hash_t *ht = svn_xml_ap_to_hash(ap, scratch_pool);
+ return svn_error_trace(svn_xml_write_open_tag_hash(xml_writer, scratch_pool,
+ style, tagname, ht));
+}
+
+svn_error_t *
+svn_xml_write_open_tag(svn_xml_writer_t *xml_writer,
+ apr_pool_t *scratch_pool,
+ enum svn_xml_open_tag_style style,
+ const char *tagname,
+ ...)
+{
+ va_list ap;
+ svn_error_t *err;
+
+ va_start(ap, tagname);
+ err = svn_xml_write_open_tag_v(xml_writer, scratch_pool, style, tagname, ap);
+ va_end(ap);
+
+ return svn_error_trace(err);
+}
+
+
+
+svn_error_t *
+svn_xml_write_cdata_cstring(svn_xml_writer_t *xml_writer, const char *str)
+{
+ return svn_error_trace(svn_xml_write_cdata(xml_writer, str, strlen(str)));
+}
+
+/* close tag */
+
+svn_error_t *
+svn_xml_write_close_tag(svn_xml_writer_t *xml_writer,
+ apr_pool_t *scratch_pool,
+ const char *tagname)
+{
+ SVN_ERR(xml_write_cstring(xml_writer, "</"));
+ SVN_ERR(xml_write_cstring(xml_writer, tagname));
+ SVN_ERR(xml_write_cstring(xml_writer, ">\n"));
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_xml_write_header(svn_xml_writer_t *xml_writer,
+ const char *encoding,
+ apr_pool_t *scratch_pool)
+{
+ SVN_ERR(xml_write_cstring(xml_writer, "<?xml version=\"1.0\""));
+ if (encoding)
+ {
+ SVN_ERR(xml_write_cstring(xml_writer, " encoding=\""));
+ SVN_ERR(xml_write_cstring(xml_writer, encoding));
+ SVN_ERR(xml_write_cstring(xml_writer, "\""));
+ }
+ SVN_ERR(xml_write_cstring(xml_writer, "?>\n"));
+
+ return SVN_NO_ERROR;
+}
Propchange: subversion/branches/xml-writer/subversion/libsvn_subr/xml_writer.c
------------------------------------------------------------------------------
svn:eol-style = native
Modified: subversion/branches/xml-writer/subversion/tests/libsvn_subr/xml-test.c
URL:
http://svn.apache.org/viewvc/subversion/branches/xml-writer/subversion/tests/libsvn_subr/xml-test.c?rev=1925686&r1=1925685&r2=1925686&view=diff
==============================================================================
--- subversion/branches/xml-writer/subversion/tests/libsvn_subr/xml-test.c
(original)
+++ subversion/branches/xml-writer/subversion/tests/libsvn_subr/xml-test.c Sun
May 18 19:20:48 2025
@@ -396,6 +396,85 @@ test_xml_simple_attr_escape(apr_pool_t *
return SVN_NO_ERROR;
}
+static svn_error_t *
+test_xml_writer(apr_pool_t *pool)
+{
+ svn_stringbuf_t *str = svn_stringbuf_create_empty(pool);
+ svn_stream_t *stream = svn_stream_from_stringbuf(str, pool);
+ svn_xml_writer_t *xml_writer;
+
+ SVN_ERR(svn_xml_writer_create(&xml_writer, stream, pool));
+
+ SVN_ERR(svn_xml_write_open_tag(xml_writer, pool, svn_xml_normal, "root",
+ SVN_VA_NULL));
+
+ SVN_ERR(svn_xml_write_open_tag(xml_writer, pool, svn_xml_normal, "tag1",
+ SVN_VA_NULL));
+ SVN_ERR(svn_xml_write_cdata_cstring(xml_writer, "value"));
+ SVN_ERR(svn_xml_write_close_tag(xml_writer, pool, "tag1"));
+
+ SVN_ERR(svn_xml_write_open_tag(xml_writer, pool, svn_xml_normal, "tag2",
+ "a", "v", SVN_VA_NULL));
+ SVN_ERR(svn_xml_write_close_tag(xml_writer, pool, "tag2"));
+
+ SVN_ERR(svn_xml_write_close_tag(xml_writer, pool, "root"));
+
+ SVN_ERR(svn_xml_writer_close(xml_writer));
+
+ SVN_TEST_STRING_ASSERT(str->data, "<root>\n"
+ "<tag1>\n"
+ "value</tag1>\n"
+ "<tag2\n"
+ " a=\"v\">\n"
+ "</tag2>\n"
+ "</root>\n");
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_xml_writer_always_flush(apr_pool_t *pool)
+{
+ svn_stringbuf_t *str = svn_stringbuf_create_empty(pool);
+ svn_stream_t *stream = svn_stream_from_stringbuf(str, pool);
+ svn_xml_writer_t *xml_writer;
+
+ SVN_ERR(svn_xml_writer_create(&xml_writer, stream, pool));
+ SVN_ERR(svn_xml_writer_flush(xml_writer));
+
+ SVN_ERR(svn_xml_write_open_tag(xml_writer, pool, svn_xml_normal, "root",
+ SVN_VA_NULL));
+ SVN_ERR(svn_xml_writer_flush(xml_writer));
+
+ SVN_ERR(svn_xml_write_open_tag(xml_writer, pool, svn_xml_normal, "tag1",
+ SVN_VA_NULL));
+ SVN_ERR(svn_xml_writer_flush(xml_writer));
+ SVN_ERR(svn_xml_write_cdata_cstring(xml_writer, "value"));
+ SVN_ERR(svn_xml_writer_flush(xml_writer));
+ SVN_ERR(svn_xml_write_close_tag(xml_writer, pool, "tag1"));
+ SVN_ERR(svn_xml_writer_flush(xml_writer));
+ SVN_ERR(svn_xml_writer_flush(xml_writer));
+ SVN_ERR(svn_xml_writer_flush(xml_writer));
+
+ SVN_ERR(svn_xml_write_open_tag(xml_writer, pool, svn_xml_normal, "tag2",
+ "a", "v", SVN_VA_NULL));
+ SVN_ERR(svn_xml_writer_flush(xml_writer));
+ SVN_ERR(svn_xml_write_close_tag(xml_writer, pool, "tag2"));
+ SVN_ERR(svn_xml_write_close_tag(xml_writer, pool, "root"));
+
+ SVN_ERR(svn_xml_writer_close(xml_writer));
+
+ SVN_TEST_STRING_ASSERT(str->data, "<root>\n"
+ "<tag1>\n"
+ "value</tag1>\n"
+ "<tag2\n"
+ " a=\"v\">\n"
+ "</tag2>\n"
+ "</root>\n");
+
+ return SVN_NO_ERROR;
+}
+
/* The test table. */
static int max_threads = 1;
@@ -426,6 +505,10 @@ static struct svn_test_descriptor_t test
"simple XML cdata escaping test"),
SVN_TEST_PASS2(test_xml_simple_attr_escape,
"simple XML attribute escaping test"),
+ SVN_TEST_PASS2(test_xml_writer,
+ "test svn_xml_writer_t"),
+ SVN_TEST_PASS2(test_xml_writer_always_flush,
+ "test flush xml-writer after each action"),
SVN_TEST_NULL
};