[libvirt] [PATCH v2 04/12] tests: Add virfilemock -- the new super mock

Martin Kletzander posted 12 patches 8 years, 10 months ago
There is a newer version of this series
[libvirt] [PATCH v2 04/12] tests: Add virfilemock -- the new super mock
Posted by Martin Kletzander 8 years, 10 months ago
This mock (which is actually not mock at all, see later) can redirect
all accesses to a path into another path.  There is no need to
create mocks for particular directories, you just create a directory
with all the data a redirect the test there.

In the future, this should also be able to register callbacks for
calls/paths, e.g. when the test is going to write into anything under
"/sys/devices", call function fce();  Then in the open() call we would
add information about the fd into some structure and in write() we
would call fce() with parameters like @path to write to, @data to
be written and pointer to optional return value, so that fce() itself
could stop the call from happening or change its behaviour.  But
that's an idea for a latter day.

This is not a mock because it will not be preloaded, but compiled in
the test itself.  See future patches for usage.

Signed-off-by: Martin Kletzander <mkletzan@redhat.com>
---
 cfg.mk              |   2 +-
 tests/virfilemock.c | 281 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 tests/virfilemock.h |  31 ++++++
 3 files changed, 313 insertions(+), 1 deletion(-)
 create mode 100644 tests/virfilemock.c
 create mode 100644 tests/virfilemock.h

diff --git a/cfg.mk b/cfg.mk
index 89e03ca0e32c..52a684462457 100644
--- a/cfg.mk
+++ b/cfg.mk
@@ -1117,7 +1117,7 @@ exclude_file_name_regexp--sc_copyright_usage = \
   ^COPYING(|\.LESSER)$$

 exclude_file_name_regexp--sc_flags_usage = \
-  ^(cfg\.mk|docs/|src/util/virnetdevtap\.c$$|tests/(vir(cgroup|pci|test|usb)|nss|qemuxml2argv)mock\.c$$)
+  ^(cfg\.mk|docs/|src/util/virnetdevtap\.c$$|tests/(vir(cgroup|pci|test|usb|file)|nss|qemuxml2argv)mock\.c$$)

 exclude_file_name_regexp--sc_libvirt_unmarked_diagnostics = \
   ^(src/rpc/gendispatch\.pl$$|tests/)
diff --git a/tests/virfilemock.c b/tests/virfilemock.c
new file mode 100644
index 000000000000..b199a64188a1
--- /dev/null
+++ b/tests/virfilemock.c
@@ -0,0 +1,281 @@
+/*
+ * virfilemock.c: Mock for universal file access mocking
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include "virmock.h"
+#include "virfilemock.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+#include "viralloc.h"
+#include "virstring.h"
+#include "virfile.h"
+
+
+/* Mapping for prefix overrides */
+static size_t noverrides;
+static const char **overrides;
+
+/* nprefixes == noverrides, but two variables make it easier to use
+ * VIR_*_ELEMENT macros */
+static size_t nprefixes;
+static const char **prefixes;
+
+/* TODO: callbacks */
+
+
+static int (*real_open)(const char *path, int flags, ...);
+static FILE *(*real_fopen)(const char *path, const char *mode);
+static int (*real_access)(const char *path, int mode);
+static int (*real_stat)(const char *path, struct stat *sb);
+static int (*real___xstat)(int ver, const char *path, struct stat *sb);
+static int (*real_lstat)(const char *path, struct stat *sb);
+static int (*real___lxstat)(int ver, const char *path, struct stat *sb);
+static int (*real_mkdir)(const char *path, mode_t mode);
+static DIR *(*real_opendir)(const char *path);
+
+static void init_syms(void)
+{
+    if (real_fopen)
+        return;
+
+    VIR_MOCK_REAL_INIT(fopen);
+    VIR_MOCK_REAL_INIT(access);
+    VIR_MOCK_REAL_INIT_ALT(lstat, __lxstat);
+    VIR_MOCK_REAL_INIT_ALT(stat, __xstat);
+    VIR_MOCK_REAL_INIT(mkdir);
+    VIR_MOCK_REAL_INIT(open);
+    VIR_MOCK_REAL_INIT(opendir);
+}
+
+
+int
+virFileMockAddPrefix(const char *prefix,
+                     const char *override)
+{
+    /* Both parameters are mandatory */
+    if (!prefix || !override)
+        return -1;
+
+    init_syms();
+
+    if (VIR_APPEND_ELEMENT_QUIET(prefixes, nprefixes, prefix) < 0 ||
+        VIR_APPEND_ELEMENT_QUIET(overrides, noverrides, override) < 0) {
+        VIR_FREE(prefixes);
+        VIR_FREE(overrides);
+        return -1;
+    }
+
+    return 0;
+}
+
+
+void
+virFileMockRemovePrefix(const char *prefix)
+{
+    size_t i = 0;
+
+    for (i = 0; i < noverrides; i++) {
+        if (STREQ(prefixes[i], prefix))
+            break;
+    }
+
+    if (i == noverrides)
+        return;
+
+    VIR_DELETE_ELEMENT(overrides, i, noverrides);
+    VIR_DELETE_ELEMENT(prefixes, i, nprefixes);
+}
+
+void
+virFileMockClearPrefixes(void)
+{
+    nprefixes = 0;
+    noverrides = 0;
+
+    VIR_FREE(prefixes);
+    VIR_FREE(overrides);
+}
+
+static char *
+virFileMockOverridePrefix(const char *path)
+{
+    char *ret = NULL;
+    size_t i = 0;
+
+    for (i = 0; i < noverrides; i++) {
+        const char *tmp = STRSKIP(path, prefixes[i]);
+
+        if (!tmp)
+            continue;
+
+        if (virAsprintfQuiet(&ret, "%s%s", overrides[i], tmp) < 0)
+            return NULL;
+
+        break;
+    }
+
+    if (!ret)
+        ignore_value(VIR_STRDUP_QUIET(ret, path));
+
+    return ret;
+}
+
+
+#define PATH_OVERRIDE(newpath, path)                    \
+    do {                                                \
+        init_syms();                                    \
+                                                        \
+        newpath = virFileMockOverridePrefix(path);      \
+        if (!newpath)                                   \
+            abort();                                    \
+    } while (0)
+
+
+FILE *fopen(const char *path, const char *mode)
+{
+    FILE *ret = NULL;
+    char *newpath = NULL;
+
+    PATH_OVERRIDE(newpath, path);
+
+    ret = real_fopen(newpath, mode);
+
+    VIR_FREE(newpath);
+
+    return ret;
+}
+
+int access(const char *path, int mode)
+{
+    int ret = -1;
+    char *newpath = NULL;
+
+    PATH_OVERRIDE(newpath, path);
+
+    ret = real_access(newpath, mode);
+
+    VIR_FREE(newpath);
+
+    return ret;
+}
+
+int __lxstat(int ver, const char *path, struct stat *sb)
+{
+    int ret = -1;
+    char *newpath = NULL;
+
+    PATH_OVERRIDE(newpath, path);
+
+    ret = real___lxstat(ver, newpath, sb);
+
+    VIR_FREE(newpath);
+
+    return ret;
+}
+
+int lstat(const char *path, struct stat *sb)
+{
+    int ret = -1;
+    char *newpath = NULL;
+
+    PATH_OVERRIDE(newpath, path);
+
+    ret = real_lstat(newpath, sb);
+
+    VIR_FREE(newpath);
+
+    return ret;
+}
+
+int __xstat(int ver, const char *path, struct stat *sb)
+{
+    int ret = -1;
+    char *newpath = NULL;
+
+    PATH_OVERRIDE(newpath, path);
+
+    ret = real___xstat(ver, newpath, sb);
+
+    VIR_FREE(newpath);
+
+    return ret;
+}
+
+int stat(const char *path, struct stat *sb)
+{
+    int ret = -1;
+    char *newpath = NULL;
+
+    PATH_OVERRIDE(newpath, path);
+
+    ret = real_stat(newpath, sb);
+
+    VIR_FREE(newpath);
+
+    return ret;
+}
+
+int mkdir(const char *path, mode_t mode)
+{
+    int ret = -1;
+    char *newpath = NULL;
+
+    PATH_OVERRIDE(newpath, path);
+
+    ret = real_mkdir(newpath, mode);
+
+    VIR_FREE(newpath);
+
+    return ret;
+}
+
+int open(const char *path, int flags, ...)
+{
+    int ret = -1;
+    char *newpath = NULL;
+
+    PATH_OVERRIDE(newpath, path);
+
+    ret = real_open(newpath, flags);
+
+    VIR_FREE(newpath);
+
+    return ret;
+}
+
+DIR *opendir(const char *path)
+{
+    DIR *ret = NULL;
+    char *newpath = NULL;
+
+    PATH_OVERRIDE(newpath, path);
+
+    ret = real_opendir(newpath);
+
+    VIR_FREE(newpath);
+
+    return ret;
+}
diff --git a/tests/virfilemock.h b/tests/virfilemock.h
new file mode 100644
index 000000000000..eeca7d8b125a
--- /dev/null
+++ b/tests/virfilemock.h
@@ -0,0 +1,31 @@
+/*
+ * virfilemock.h: Mock for universal file access mocking
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __VIR_FILE_MOCK_H__
+
+int
+virFileMockAddPrefix(const char *prefix,
+                     const char *override);
+
+void
+virFileMockRemovePrefix(const char *prefix);
+
+void
+virFileMockClearPrefixes(void);
+
+#endif /* __VIR_FILE_MOCK_H__ */
-- 
2.12.2

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH v2 04/12] tests: Add virfilemock -- the new super mock
Posted by Erik Skultety 8 years, 10 months ago
> +#include <config.h>
> +
> +#include "virmock.h"
> +#include "virfilemock.h"

^^These are local, could you group them with the rest below the system ones?
(This was pointed out to me during review once or twice).

> +
> +#include <stdio.h>
> +#include <stdlib.h>
^^These get pulled in by virmock.h, but it's always nice to be explicit about
being able to use "printf" :P.

> +#include <unistd.h>

I've successfully built the patch without ^^this header. You can drop it.

> +#include <fcntl.h>

...

> +#include <sys/stat.h>
> +#include <sys/types.h>

It built without ^^these 2 as well.

> +#include <dirent.h>

^This one gets pulled in by virfile.h

> +
> +#include "viralloc.h"
> +#include "virstring.h"
> +#include "virfile.h"
> +
> +
> +/* Mapping for prefix overrides */
> +static size_t noverrides;
> +static const char **overrides;
> +
> +/* nprefixes == noverrides, but two variables make it easier to use
> + * VIR_*_ELEMENT macros */
> +static size_t nprefixes;
> +static const char **prefixes;
> +
> +/* TODO: callbacks */

Just out of curiosity, what callbacks?

[...]

> +
> +
> +void
> +virFileMockRemovePrefix(const char *prefix)
> +{
> +    size_t i = 0;
> +
> +    for (i = 0; i < noverrides; i++) {
> +        if (STREQ(prefixes[i], prefix))

Since you're removing a single element, you can just delete the prefix here and
then break from the loop.

> +            break;
> +    }
> +
> +    if (i == noverrides)
> +        return;

You won't be needing ^^this then.

> +
> +    VIR_DELETE_ELEMENT(overrides, i, noverrides);
> +    VIR_DELETE_ELEMENT(prefixes, i, nprefixes);
> +}
> +

ACK with the adjustments (with the exception if it would somehow break BSD
again :)).

Erik

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH v2 04/12] tests: Add virfilemock -- the new super mock
Posted by Erik Skultety 8 years, 10 months ago
On Fri, Apr 07, 2017 at 10:08:35AM +0200, Erik Skultety wrote:
> > +#include <config.h>
> > +
> > +#include "virmock.h"
> > +#include "virfilemock.h"
>
> ^^These are local, could you group them with the rest below the system ones?
> (This was pointed out to me during review once or twice).
>
> > +
> > +#include <stdio.h>
> > +#include <stdlib.h>
> ^^These get pulled in by virmock.h, but it's always nice to be explicit about
> being able to use "printf" :P.
>
> > +#include <unistd.h>
>
> I've successfully built the patch without ^^this header. You can drop it.
>
> > +#include <fcntl.h>
>
> ...
>
> > +#include <sys/stat.h>
> > +#include <sys/types.h>
>
> It built without ^^these 2 as well.
>
> > +#include <dirent.h>
>
> ^This one gets pulled in by virfile.h
>
> > +
> > +#include "viralloc.h"
> > +#include "virstring.h"
> > +#include "virfile.h"
> > +
> > +
> > +/* Mapping for prefix overrides */
> > +static size_t noverrides;
> > +static const char **overrides;
> > +
> > +/* nprefixes == noverrides, but two variables make it easier to use
> > + * VIR_*_ELEMENT macros */
> > +static size_t nprefixes;
> > +static const char **prefixes;
> > +
> > +/* TODO: callbacks */
>
> Just out of curiosity, what callbacks?
>
> [...]
>
> > +
> > +
> > +void
> > +virFileMockRemovePrefix(const char *prefix)
> > +{
> > +    size_t i = 0;
> > +
> > +    for (i = 0; i < noverrides; i++) {
> > +        if (STREQ(prefixes[i], prefix))
>
> Since you're removing a single element, you can just delete the prefix here and
> then break from the loop.
>
> > +            break;
> > +    }
> > +
> > +    if (i == noverrides)
> > +        return;
>
> You won't be needing ^^this then.
>
> > +
> > +    VIR_DELETE_ELEMENT(overrides, i, noverrides);
> > +    VIR_DELETE_ELEMENT(prefixes, i, nprefixes);
> > +}
> > +
>
> ACK with the adjustments (with the exception if it would somehow break BSD
> again :)).

You'll also need to add virfilemock.h to tests/Makefile.am EXTRA_DIST otherwise
make distcheck fails with "No such file or directory".

Erik

>
> Erik
>
> --
> libvir-list mailing list
> libvir-list@redhat.com
> https://www.redhat.com/mailman/listinfo/libvir-list

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH v2 04/12] tests: Add virfilemock -- the new super mock
Posted by Martin Kletzander 8 years, 10 months ago
On Fri, Apr 07, 2017 at 10:08:35AM +0200, Erik Skultety wrote:
>> +#include <config.h>
>> +
>> +#include "virmock.h"
>> +#include "virfilemock.h"
>
>^^These are local, could you group them with the rest below the system ones?
>(This was pointed out to me during review once or twice).
>

Yeah, I forgot to get to the header cleaning part.  Most of this was
copy-paste from cgroupmock and then I just added my new header.

>> +
>> +#include <stdio.h>
>> +#include <stdlib.h>
>^^These get pulled in by virmock.h, but it's always nice to be explicit about
>being able to use "printf" :P.
>
>> +#include <unistd.h>
>
>I've successfully built the patch without ^^this header. You can drop it.
>
>> +#include <fcntl.h>
>
>...
>
>> +#include <sys/stat.h>
>> +#include <sys/types.h>
>
>It built without ^^these 2 as well.
>
>> +#include <dirent.h>
>
>^This one gets pulled in by virfile.h
>
>> +
>> +#include "viralloc.h"
>> +#include "virstring.h"
>> +#include "virfile.h"
>> +
>> +
>> +/* Mapping for prefix overrides */
>> +static size_t noverrides;
>> +static const char **overrides;
>> +
>> +/* nprefixes == noverrides, but two variables make it easier to use
>> + * VIR_*_ELEMENT macros */
>> +static size_t nprefixes;
>> +static const char **prefixes;
>> +
>> +/* TODO: callbacks */
>
>Just out of curiosity, what callbacks?
>

See commit message.  I don't like repeating myself.  Or should I copy
the commit message to the comment?

>[...]
>
>> +
>> +
>> +void
>> +virFileMockRemovePrefix(const char *prefix)
>> +{
>> +    size_t i = 0;
>> +
>> +    for (i = 0; i < noverrides; i++) {
>> +        if (STREQ(prefixes[i], prefix))
>
>Since you're removing a single element, you can just delete the prefix here and
>then break from the loop.
>

Yeah, thta was the first variant I went with, but then I didn't like the
extra indentation level, so I changed it to this.  Can we get a hacking
file section talking about this?  I don't like repainting the shed every
new series.

>> +            break;
>> +    }
>> +
>> +    if (i == noverrides)
>> +        return;
>
>You won't be needing ^^this then.
>
>> +
>> +    VIR_DELETE_ELEMENT(overrides, i, noverrides);
>> +    VIR_DELETE_ELEMENT(prefixes, i, nprefixes);
>> +}
>> +
>
>ACK with the adjustments (with the exception if it would somehow break BSD
>again :)).
>

I'll try to check this one (and the following ones as well) on BSD
before pushing.  Hopefully I'll manage to install the machine before
going home.

>Erik
--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list