diff mbox

[2/2] tests: add file-write-read test

Message ID 1448382858-28616-2-git-send-email-marcandre.lureau@redhat.com
State New
Headers show

Commit Message

Marc-André Lureau Nov. 24, 2015, 4:34 p.m. UTC
From: Marc-André Lureau <marcandre.lureau@redhat.com>

This test exhibits a POSIX behaviour regarding switching between write
and read. It's undefined result if the application doesn't ensure a
flush between the two operations (with glibc, the flush can be implicit
when the buffer size is relatively small). The previous commit fixes
this test.

Related to:
https://bugzilla.redhat.com/show_bug.cgi?id=1210246

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 tests/test-qga.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 91 insertions(+)

Comments

Eric Blake Nov. 24, 2015, 5:29 p.m. UTC | #1
On 11/24/2015 09:34 AM, marcandre.lureau@redhat.com wrote:
> From: Marc-André Lureau <marcandre.lureau@redhat.com>
> 
> This test exhibits a POSIX behaviour regarding switching between write
> and read. It's undefined result if the application doesn't ensure a
> flush between the two operations (with glibc, the flush can be implicit
> when the buffer size is relatively small). The previous commit fixes
> this test.
> 
> Related to:
> https://bugzilla.redhat.com/show_bug.cgi?id=1210246
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>  tests/test-qga.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 91 insertions(+)
> 
> diff --git a/tests/test-qga.c b/tests/test-qga.c
> index 6473846..6b6963e 100644
> --- a/tests/test-qga.c
> +++ b/tests/test-qga.c
> @@ -496,6 +496,96 @@ static void test_qga_file_ops(gconstpointer fix)
>      g_free(cmd);
>  }
>  
> +static void test_qga_file_write_read(gconstpointer fix)
> +{
> +    const TestFixture *fixture = fix;
> +    const guchar helloworld[] = "Hello World!\n";

Do we have to use guchar, or can we just stick with 'char' or 'unsigned
char'?


> +
> +    /* read (check implicit flush) */

Here, the term implicit is okay; while the previous patch is adding an
explicit flush at the low level, it is doing so in order to avoid having
to make clients need an explicit flush operation (clients can rely on an
implicit flush).

> +    cmd = g_strdup_printf("{'execute': 'guest-file-read',"
> +                          " 'arguments': { 'handle': %" PRId64 "} }",
> +                          id);
> +    ret = qmp_fd(fixture->fd, cmd);
> +    val = qdict_get_qdict(ret, "return");
> +    count = qdict_get_int(val, "count");
> +    eof = qdict_get_bool(val, "eof");
> +    b64 = qdict_get_str(val, "buf-b64");
> +    g_assert_cmpint(count, ==, 0);
> +    g_assert(eof);
> +    g_assert_cmpstr(b64, ==, "");
> +    QDECREF(ret);
> +    g_free(cmd);
> +
> +    /* seek to 0*/

Space before */

> +    cmd = g_strdup_printf("{'execute': 'guest-file-seek',"
> +                          " 'arguments': { 'handle': %" PRId64 ", "
> +                          " 'offset': %d, 'whence': %d } }",
> +                          id, 0, SEEK_SET);

EWWWW.  We seriously released this interface as taking an integer for
whence?  SEEK_SET is not required to be the same value on every
platform.  Which is a severe problem if the guest and the host are on
different OS with different choices of values for the constants (if
SEEK_CUR on my host is 1, but 1 maps to SEEK_END on my guest OS, what
behavior am I going to get?).

It would be worth a patch to qga to document the actual integer values
that we have hard-coded (0 for set, 1 for cur, 2 for end; even if that
differs from the guest's local definition of the SEEK_ constants),
and/or to fix the interface to take symbolic names rather than integers
for the whence argument.

Our whole guest-file-* API is lousy.
Marc-Andre Lureau Nov. 24, 2015, 5:58 p.m. UTC | #2
Hi

----- Original Message -----
> On 11/24/2015 09:34 AM, marcandre.lureau@redhat.com wrote:
> > From: Marc-André Lureau <marcandre.lureau@redhat.com>
> > 
> > This test exhibits a POSIX behaviour regarding switching between write
> > and read. It's undefined result if the application doesn't ensure a
> > flush between the two operations (with glibc, the flush can be implicit
> > when the buffer size is relatively small). The previous commit fixes
> > this test.
> > 
> > Related to:
> > https://bugzilla.redhat.com/show_bug.cgi?id=1210246
> > 
> > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> > ---
> >  tests/test-qga.c | 91
> >  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 91 insertions(+)
> > 
> > diff --git a/tests/test-qga.c b/tests/test-qga.c
> > index 6473846..6b6963e 100644
> > --- a/tests/test-qga.c
> > +++ b/tests/test-qga.c
> > @@ -496,6 +496,96 @@ static void test_qga_file_ops(gconstpointer fix)
> >      g_free(cmd);
> >  }
> >  
> > +static void test_qga_file_write_read(gconstpointer fix)
> > +{
> > +    const TestFixture *fixture = fix;
> > +    const guchar helloworld[] = "Hello World!\n";
> 
> Do we have to use guchar, or can we just stick with 'char' or 'unsigned
> char'?

We can switch to "unsigned char"

> 
> 
> > +
> > +    /* read (check implicit flush) */
> 
> Here, the term implicit is okay; while the previous patch is adding an
> explicit flush at the low level, it is doing so in order to avoid having
> to make clients need an explicit flush operation (clients can rely on an
> implicit flush).

ok

> 
> > +    cmd = g_strdup_printf("{'execute': 'guest-file-read',"
> > +                          " 'arguments': { 'handle': %" PRId64 "} }",
> > +                          id);
> > +    ret = qmp_fd(fixture->fd, cmd);
> > +    val = qdict_get_qdict(ret, "return");
> > +    count = qdict_get_int(val, "count");
> > +    eof = qdict_get_bool(val, "eof");
> > +    b64 = qdict_get_str(val, "buf-b64");
> > +    g_assert_cmpint(count, ==, 0);
> > +    g_assert(eof);
> > +    g_assert_cmpstr(b64, ==, "");
> > +    QDECREF(ret);
> > +    g_free(cmd);
> > +
> > +    /* seek to 0*/
> 
> Space before */

fixed

> 
> > +    cmd = g_strdup_printf("{'execute': 'guest-file-seek',"
> > +                          " 'arguments': { 'handle': %" PRId64 ", "
> > +                          " 'offset': %d, 'whence': %d } }",
> > +                          id, 0, SEEK_SET);
> 
> EWWWW.  We seriously released this interface as taking an integer for
> whence?  SEEK_SET is not required to be the same value on every
> platform.  Which is a severe problem if the guest and the host are on
> different OS with different choices of values for the constants (if
> SEEK_CUR on my host is 1, but 1 maps to SEEK_END on my guest OS, what
> behavior am I going to get?).
> 
> It would be worth a patch to qga to document the actual integer values
> that we have hard-coded (0 for set, 1 for cur, 2 for end; even if that
> differs from the guest's local definition of the SEEK_ constants),
> and/or to fix the interface to take symbolic names rather than integers
> for the whence argument.
> 
> Our whole guest-file-* API is lousy.

Are you going to send a patch for this?

(hopefully, we can expose a "real" filesystem protocol in the near future)
Eric Blake Nov. 24, 2015, 6:44 p.m. UTC | #3
On 11/24/2015 10:58 AM, Marc-André Lureau wrote:

>>> +    cmd = g_strdup_printf("{'execute': 'guest-file-seek',"
>>> +                          " 'arguments': { 'handle': %" PRId64 ", "
>>> +                          " 'offset': %d, 'whence': %d } }",
>>> +                          id, 0, SEEK_SET);
>>
>> EWWWW.  We seriously released this interface as taking an integer for
>> whence?  SEEK_SET is not required to be the same value on every
>> platform.  Which is a severe problem if the guest and the host are on
>> different OS with different choices of values for the constants (if
>> SEEK_CUR on my host is 1, but 1 maps to SEEK_END on my guest OS, what
>> behavior am I going to get?).
>>
>> It would be worth a patch to qga to document the actual integer values
>> that we have hard-coded (0 for set, 1 for cur, 2 for end; even if that
>> differs from the guest's local definition of the SEEK_ constants),
>> and/or to fix the interface to take symbolic names rather than integers
>> for the whence argument.
>>
>> Our whole guest-file-* API is lousy.
> 
> Are you going to send a patch for this?

Sure, now that you've asked. For 2.5, it will just be documentation and
mapping integers to the correct constants (any magic of using a qapi
alternate type to support symbolic names would be 2.6 territory).
diff mbox

Patch

diff --git a/tests/test-qga.c b/tests/test-qga.c
index 6473846..6b6963e 100644
--- a/tests/test-qga.c
+++ b/tests/test-qga.c
@@ -496,6 +496,96 @@  static void test_qga_file_ops(gconstpointer fix)
     g_free(cmd);
 }
 
+static void test_qga_file_write_read(gconstpointer fix)
+{
+    const TestFixture *fixture = fix;
+    const guchar helloworld[] = "Hello World!\n";
+    const char *b64;
+    gchar *cmd, *enc;
+    QDict *ret, *val;
+    int64_t id, eof;
+    gsize count;
+
+    /* open */
+    ret = qmp_fd(fixture->fd, "{'execute': 'guest-file-open',"
+                 " 'arguments': { 'path': 'foo', 'mode': 'w+' } }");
+    g_assert_nonnull(ret);
+    qmp_assert_no_error(ret);
+    id = qdict_get_int(ret, "return");
+    QDECREF(ret);
+
+    enc = g_base64_encode(helloworld, sizeof(helloworld));
+    /* write */
+    cmd = g_strdup_printf("{'execute': 'guest-file-write',"
+                          " 'arguments': { 'handle': %" PRId64 ","
+                          " 'buf-b64': '%s' } }", id, enc);
+    ret = qmp_fd(fixture->fd, cmd);
+    g_assert_nonnull(ret);
+    qmp_assert_no_error(ret);
+
+    val = qdict_get_qdict(ret, "return");
+    count = qdict_get_int(val, "count");
+    eof = qdict_get_bool(val, "eof");
+    g_assert_cmpint(count, ==, sizeof(helloworld));
+    g_assert_cmpint(eof, ==, 0);
+    QDECREF(ret);
+    g_free(cmd);
+
+    /* read (check implicit flush) */
+    cmd = g_strdup_printf("{'execute': 'guest-file-read',"
+                          " 'arguments': { 'handle': %" PRId64 "} }",
+                          id);
+    ret = qmp_fd(fixture->fd, cmd);
+    val = qdict_get_qdict(ret, "return");
+    count = qdict_get_int(val, "count");
+    eof = qdict_get_bool(val, "eof");
+    b64 = qdict_get_str(val, "buf-b64");
+    g_assert_cmpint(count, ==, 0);
+    g_assert(eof);
+    g_assert_cmpstr(b64, ==, "");
+    QDECREF(ret);
+    g_free(cmd);
+
+    /* seek to 0*/
+    cmd = g_strdup_printf("{'execute': 'guest-file-seek',"
+                          " 'arguments': { 'handle': %" PRId64 ", "
+                          " 'offset': %d, 'whence': %d } }",
+                          id, 0, SEEK_SET);
+    ret = qmp_fd(fixture->fd, cmd);
+    qmp_assert_no_error(ret);
+    val = qdict_get_qdict(ret, "return");
+    count = qdict_get_int(val, "position");
+    eof = qdict_get_bool(val, "eof");
+    g_assert_cmpint(count, ==, 0);
+    g_assert(!eof);
+    QDECREF(ret);
+    g_free(cmd);
+
+    /* read */
+    cmd = g_strdup_printf("{'execute': 'guest-file-read',"
+                          " 'arguments': { 'handle': %" PRId64 "} }",
+                          id);
+    ret = qmp_fd(fixture->fd, cmd);
+    val = qdict_get_qdict(ret, "return");
+    count = qdict_get_int(val, "count");
+    eof = qdict_get_bool(val, "eof");
+    b64 = qdict_get_str(val, "buf-b64");
+    g_assert_cmpint(count, ==, sizeof(helloworld));
+    g_assert(eof);
+    g_assert_cmpstr(b64, ==, enc);
+    QDECREF(ret);
+    g_free(cmd);
+    g_free(enc);
+
+    /* close */
+    cmd = g_strdup_printf("{'execute': 'guest-file-close',"
+                          " 'arguments': {'handle': %" PRId64 "} }",
+                          id);
+    ret = qmp_fd(fixture->fd, cmd);
+    QDECREF(ret);
+    g_free(cmd);
+}
+
 static void test_qga_get_time(gconstpointer fix)
 {
     const TestFixture *fixture = fix;
@@ -762,6 +852,7 @@  int main(int argc, char **argv)
     g_test_add_data_func("/qga/get-memory-blocks", &fix,
                          test_qga_get_memory_blocks);
     g_test_add_data_func("/qga/file-ops", &fix, test_qga_file_ops);
+    g_test_add_data_func("/qga/file-write-read", &fix, test_qga_file_write_read);
     g_test_add_data_func("/qga/get-time", &fix, test_qga_get_time);
     g_test_add_data_func("/qga/invalid-cmd", &fix, test_qga_invalid_cmd);
     g_test_add_data_func("/qga/fsfreeze-status", &fix,