diff --git a/src/tests/cogl-test-utils.c b/src/tests/cogl-test-utils.c new file mode 100644 index 000000000..a4f9ef537 --- /dev/null +++ b/src/tests/cogl-test-utils.c @@ -0,0 +1,404 @@ +/* + * Copyright (C) 2022 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + */ + +#include "config.h" + +#include "tests/cogl-test-utils.h" + +#include "backends/meta-backend-private.h" + +static gboolean cogl_test_is_verbose; +CoglContext *test_ctx; +CoglFramebuffer *test_fb; + +static gboolean +compare_component (int a, int b) +{ + return ABS (a - b) <= 1; +} + +void +test_utils_compare_pixel_and_alpha (const uint8_t *screen_pixel, + uint32_t expected_pixel) +{ + /* Compare each component with a small fuzz factor */ + if (!compare_component (screen_pixel[0], expected_pixel >> 24) || + !compare_component (screen_pixel[1], (expected_pixel >> 16) & 0xff) || + !compare_component (screen_pixel[2], (expected_pixel >> 8) & 0xff) || + !compare_component (screen_pixel[3], (expected_pixel >> 0) & 0xff)) + { + uint32_t screen_pixel_num = GUINT32_FROM_BE (*(uint32_t *) screen_pixel); + char *screen_pixel_string = + g_strdup_printf ("#%08x", screen_pixel_num); + char *expected_pixel_string = + g_strdup_printf ("#%08x", expected_pixel); + + g_assert_cmpstr (screen_pixel_string, ==, expected_pixel_string); + + g_free (screen_pixel_string); + g_free (expected_pixel_string); + } +} + +void +test_utils_compare_pixel (const uint8_t *screen_pixel, + uint32_t expected_pixel) +{ + /* Compare each component with a small fuzz factor */ + if (!compare_component (screen_pixel[0], expected_pixel >> 24) || + !compare_component (screen_pixel[1], (expected_pixel >> 16) & 0xff) || + !compare_component (screen_pixel[2], (expected_pixel >> 8) & 0xff)) + { + uint32_t screen_pixel_num = GUINT32_FROM_BE (*(uint32_t *) screen_pixel); + char *screen_pixel_string = + g_strdup_printf ("#%06x", screen_pixel_num >> 8); + char *expected_pixel_string = + g_strdup_printf ("#%06x", expected_pixel >> 8); + + g_assert_cmpstr (screen_pixel_string, ==, expected_pixel_string); + + g_free (screen_pixel_string); + g_free (expected_pixel_string); + } +} + +void +test_utils_check_pixel (CoglFramebuffer *test_fb, + int x, + int y, + uint32_t expected_pixel) +{ + uint8_t pixel[4]; + + cogl_framebuffer_read_pixels (test_fb, + x, y, 1, 1, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + pixel); + + test_utils_compare_pixel (pixel, expected_pixel); +} + +void +test_utils_check_pixel_and_alpha (CoglFramebuffer *test_fb, + int x, + int y, + uint32_t expected_pixel) +{ + uint8_t pixel[4]; + + cogl_framebuffer_read_pixels (test_fb, + x, y, 1, 1, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + pixel); + + test_utils_compare_pixel_and_alpha (pixel, expected_pixel); +} + +void +test_utils_check_pixel_rgb (CoglFramebuffer *test_fb, + int x, + int y, + int r, + int g, + int b) +{ + g_return_if_fail (r >= 0); + g_return_if_fail (g >= 0); + g_return_if_fail (b >= 0); + g_return_if_fail (r <= 0xFF); + g_return_if_fail (g <= 0xFF); + g_return_if_fail (b <= 0xFF); + + test_utils_check_pixel (test_fb, x, y, + (((guint32) r) << 24) | + (((guint32) g) << 16) | + (((guint32) b) << 8)); +} + +void +test_utils_check_region (CoglFramebuffer *test_fb, + int x, + int y, + int width, + int height, + uint32_t expected_rgba) +{ + uint8_t *pixels, *p; + + pixels = p = g_malloc (width * height * 4); + cogl_framebuffer_read_pixels (test_fb, + x, + y, + width, + height, + COGL_PIXEL_FORMAT_RGBA_8888, + p); + + /* Check whether the center of each division is the right color */ + for (y = 0; y < height; y++) + { + for (x = 0; x < width; x++) + { + test_utils_compare_pixel (p, expected_rgba); + p += 4; + } + } + + g_free (pixels); +} + +CoglTexture * +test_utils_create_color_texture (CoglContext *context, + uint32_t color) +{ + CoglTexture2D *tex_2d; + + color = GUINT32_TO_BE (color); + + tex_2d = cogl_texture_2d_new_from_data (context, + 1, 1, /* width/height */ + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + 4, /* rowstride */ + (uint8_t *) &color, + NULL); + + return COGL_TEXTURE (tex_2d); +} + +gboolean +cogl_test_verbose (void) +{ + return cogl_test_is_verbose; +} + +static void +set_auto_mipmap_cb (CoglTexture *sub_texture, + const float *sub_texture_coords, + const float *meta_coords, + void *user_data) +{ + cogl_primitive_texture_set_auto_mipmap (COGL_PRIMITIVE_TEXTURE (sub_texture), + FALSE); +} + +CoglTexture * +test_utils_texture_new_with_size (CoglContext *ctx, + int width, + int height, + TestUtilsTextureFlags flags, + CoglTextureComponents components) +{ + CoglTexture *tex; + GError *skip_error = NULL; + + /* First try creating a fast-path non-sliced texture */ + tex = COGL_TEXTURE (cogl_texture_2d_new_with_size (ctx, width, height)); + + cogl_texture_set_components (tex, components); + + if (!cogl_texture_allocate (tex, &skip_error)) + { + g_error_free (skip_error); + cogl_object_unref (tex); + tex = NULL; + } + + if (!tex) + { + /* If it fails resort to sliced textures */ + int max_waste = flags & TEST_UTILS_TEXTURE_NO_SLICING ? + -1 : COGL_TEXTURE_MAX_WASTE; + CoglTexture2DSliced *tex_2ds = + cogl_texture_2d_sliced_new_with_size (ctx, + width, + height, + max_waste); + tex = COGL_TEXTURE (tex_2ds); + + cogl_texture_set_components (tex, components); + } + + if (flags & TEST_UTILS_TEXTURE_NO_AUTO_MIPMAP) + { + /* To be able to iterate the slices of a #CoglTexture2DSliced we + * need to ensure the texture is allocated... */ + cogl_texture_allocate (tex, NULL); /* don't catch exceptions */ + + cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (tex), + 0, 0, 1, 1, + COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE, + COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE, + set_auto_mipmap_cb, + NULL); /* don't catch exceptions */ + } + + cogl_texture_allocate (tex, NULL); + + return tex; +} + +CoglTexture * +test_utils_texture_new_from_bitmap (CoglBitmap *bitmap, + TestUtilsTextureFlags flags, + gboolean premultiplied) +{ + CoglAtlasTexture *atlas_tex; + CoglTexture *tex; + GError *internal_error = NULL; + + if (!flags) + { + /* First try putting the texture in the atlas */ + atlas_tex = cogl_atlas_texture_new_from_bitmap (bitmap); + + cogl_texture_set_premultiplied (COGL_TEXTURE (atlas_tex), premultiplied); + + if (cogl_texture_allocate (COGL_TEXTURE (atlas_tex), &internal_error)) + return COGL_TEXTURE (atlas_tex); + + cogl_object_unref (atlas_tex); + } + + g_clear_error (&internal_error); + + /* If that doesn't work try a fast path 2D texture */ + tex = COGL_TEXTURE (cogl_texture_2d_new_from_bitmap (bitmap)); + + cogl_texture_set_premultiplied (tex, premultiplied); + + if (g_error_matches (internal_error, + COGL_SYSTEM_ERROR, + COGL_SYSTEM_ERROR_NO_MEMORY)) + { + g_assert_not_reached (); + return NULL; + } + + g_clear_error (&internal_error); + + if (!tex) + { + /* Otherwise create a sliced texture */ + int max_waste = flags & TEST_UTILS_TEXTURE_NO_SLICING ? + -1 : COGL_TEXTURE_MAX_WASTE; + CoglTexture2DSliced *tex_2ds = + cogl_texture_2d_sliced_new_from_bitmap (bitmap, max_waste); + tex = COGL_TEXTURE (tex_2ds); + + cogl_texture_set_premultiplied (tex, premultiplied); + } + + if (flags & TEST_UTILS_TEXTURE_NO_AUTO_MIPMAP) + { + cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (tex), + 0, 0, 1, 1, + COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE, + COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE, + set_auto_mipmap_cb, + NULL); /* don't catch exceptions */ + } + + cogl_texture_allocate (tex, NULL); + + return tex; +} + +CoglTexture * +test_utils_texture_new_from_data (CoglContext *ctx, + int width, + int height, + TestUtilsTextureFlags flags, + CoglPixelFormat format, + int rowstride, + const uint8_t *data) +{ + CoglBitmap *bmp; + CoglTexture *tex; + + g_assert_cmpint (format, !=, COGL_PIXEL_FORMAT_ANY); + g_assert (data != NULL); + + /* Wrap the data into a bitmap */ + bmp = cogl_bitmap_new_for_data (ctx, + width, height, + format, + rowstride, + (uint8_t *) data); + + tex = test_utils_texture_new_from_bitmap (bmp, flags, TRUE); + + cogl_object_unref (bmp); + + return tex; +} + +static void +on_before_tests (MetaContext *context) +{ + MetaBackend *backend = meta_context_get_backend (context); + ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); + CoglOffscreen *offscreen; + CoglTexture2D *tex; + GError *error = NULL; + + test_ctx = clutter_backend_get_cogl_context (clutter_backend); + + tex = cogl_texture_2d_new_with_size (test_ctx, FB_WIDTH, FB_HEIGHT); + g_assert_nonnull (tex); + offscreen = cogl_offscreen_new_with_texture (COGL_TEXTURE (tex)); + g_assert_nonnull (offscreen); + test_fb = COGL_FRAMEBUFFER (offscreen); + + if (!cogl_framebuffer_allocate (test_fb, &error)) + g_error ("Failed to allocate framebuffer: %s", error->message); + + cogl_framebuffer_clear4f (test_fb, + COGL_BUFFER_BIT_COLOR | + COGL_BUFFER_BIT_DEPTH | + COGL_BUFFER_BIT_STENCIL, + 0, 0, 0, 1); +} + +static void +on_after_tests (MetaContext *context) +{ + g_clear_object (&test_fb); +} + +MetaContext * +meta_create_cogl_test_context (int argc, + char **argv) +{ + MetaContext *context = NULL; + + context = meta_create_test_context (META_CONTEXT_TEST_TYPE_HEADLESS, + META_CONTEXT_TEST_FLAG_NO_X11); + g_assert (meta_context_configure (context, &argc, &argv, NULL)); + + if (g_strcmp0 ("COGL_TEST_VERBOSE", "1") == 0) + cogl_test_is_verbose = TRUE; + + g_signal_connect (context, "before-tests", + G_CALLBACK (on_before_tests), NULL); + g_signal_connect (context, "after-tests", + G_CALLBACK (on_after_tests), NULL); + + return context; +} diff --git a/src/tests/cogl-test-utils.h b/src/tests/cogl-test-utils.h new file mode 100644 index 000000000..868faaa1e --- /dev/null +++ b/src/tests/cogl-test-utils.h @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2022 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + */ + +#ifndef COGL_TEST_UTILS_H +#define COGL_TEST_UTILS_H + +#include "meta-test/meta-context-test.h" + + /** + * TestUtilsTextureFlags: + * @TEST_UTILS_TEXTURE_NONE: No flags specified + * @TEST_UTILS_TEXTURE_NO_AUTO_MIPMAP: Disables the automatic generation of + * the mipmap pyramid from the base level image whenever it is + * updated. The mipmaps are only generated when the texture is + * rendered with a mipmap filter so it should be free to leave out + * this flag when using other filtering modes + * @TEST_UTILS_TEXTURE_NO_SLICING: Disables the slicing of the texture + * @TEST_UTILS_TEXTURE_NO_ATLAS: Disables the insertion of the texture inside + * the texture atlas used by Cogl + * + * Flags to pass to the test_utils_texture_new_* family of functions. + */ +typedef enum +{ + TEST_UTILS_TEXTURE_NONE = 0, + TEST_UTILS_TEXTURE_NO_AUTO_MIPMAP = 1 << 0, + TEST_UTILS_TEXTURE_NO_SLICING = 1 << 1, + TEST_UTILS_TEXTURE_NO_ATLAS = 1 << 2 +} TestUtilsTextureFlags; + +extern CoglContext *test_ctx; +extern CoglFramebuffer *test_fb; + +#define FB_WIDTH 512 +#define FB_HEIGHT 512 + +#define COGL_TEST_SUITE(units) \ +int \ +main (int argc, \ + char **argv) \ +{ \ + g_autoptr (MetaContext) context = NULL; \ +\ + context = meta_create_cogl_test_context (argc, argv); \ +\ + units \ +\ + return meta_context_test_run_tests (META_CONTEXT_TEST (context), \ + META_TEST_RUN_FLAG_NONE); \ +} + +MetaContext * meta_create_cogl_test_context (int argc, + char **argv); + +/* + * test_utils_texture_new_with_size: + * @context: A #CoglContext + * @width: width of texture in pixels. + * @height: height of texture in pixels. + * @flags: Optional flags for the texture, or %TEST_UTILS_TEXTURE_NONE + * @components: What texture components are required + * + * Creates a new #CoglTexture with the specified dimensions and pixel format. + * + * The storage for the texture is not necessarily created before this + * function returns. The storage can be explicitly allocated using + * cogl_texture_allocate() or preferably you can let Cogl + * automatically allocate the storage lazily when uploading data when + * Cogl may know more about how the texture will be used and can + * optimize how it is allocated. + * + * Return value: A newly created #CoglTexture + */ +CoglTexture * test_utils_texture_new_with_size (CoglContext *ctx, + int width, + int height, + TestUtilsTextureFlags flags, + CoglTextureComponents components); + +/* + * test_utils_texture_new_from_data: + * @context: A #CoglContext + * @width: width of texture in pixels + * @height: height of texture in pixels + * @flags: Optional flags for the texture, or %TEST_UTILS_TEXTURE_NONE + * @format: the #CoglPixelFormat the buffer is stored in in RAM + * @rowstride: the memory offset in bytes between the starts of + * scanlines in @data + * @data: pointer the memory region where the source buffer resides + * @error: A #GError to catch exceptional errors or %NULL + * + * Creates a new #CoglTexture based on data residing in memory. + * + * Note: If the given @format has an alpha channel then the data + * will be loaded into a premultiplied internal format. If you want + * to avoid having the source data be premultiplied then you can + * either specify that the data is already premultiplied or use + * test_utils_texture_new_from_bitmap which lets you explicitly + * request whether the data should internally be premultipled or not. + * + * Return value: A newly created #CoglTexture or %NULL on failure + */ +CoglTexture * +test_utils_texture_new_from_data (CoglContext *ctx, + int width, + int height, + TestUtilsTextureFlags flags, + CoglPixelFormat format, + int rowstride, + const uint8_t *data); + +/* + * test_utils_texture_new_from_bitmap: + * @bitmap: A #CoglBitmap pointer + * @flags: Optional flags for the texture, or %TEST_UTILS_TEXTURE_NONE + * @premultiplied: Whether the texture should hold premultipled data. + * (if the bitmap already holds premultiplied data + * and %TRUE is given then no premultiplication will + * be done. The data will be premultipled while + * uploading if the bitmap has an alpha channel but + * does not already have a premultiplied format.) + * + * Creates a #CoglTexture from a #CoglBitmap. + * + * Return value: A newly created #CoglTexture or %NULL on failure + */ +CoglTexture * +test_utils_texture_new_from_bitmap (CoglBitmap *bitmap, + TestUtilsTextureFlags flags, + gboolean premultiplied); + +/* + * test_utils_check_pixel: + * @framebuffer: The #CoglFramebuffer to read from + * @x: x coordinate of the pixel to test + * @y: y coordinate of the pixel to test + * @pixel: An integer of the form 0xRRGGBBAA representing the expected + * pixel value + * + * This performs reads a pixel on the given cogl @framebuffer and + * asserts that it matches the given color. The alpha channel of the + * color is ignored. The pixels are converted to a string and compared + * with g_assert_cmpstr so that if the comparison fails then the + * assert will display a meaningful message + */ +void +test_utils_check_pixel (CoglFramebuffer *framebuffer, + int x, + int y, + uint32_t expected_pixel); + +/** + * @framebuffer: The #CoglFramebuffer to read from + * @x: x coordinate of the pixel to test + * @y: y coordinate of the pixel to test + * @pixel: An integer of the form 0xRRGGBBAA representing the expected + * pixel value + * + * This performs reads a pixel on the given cogl @framebuffer and + * asserts that it matches the given color. The alpha channel is also + * checked unlike with test_utils_check_pixel(). The pixels are + * converted to a string and compared with g_assert_cmpstr so that if + * the comparison fails then the assert will display a meaningful + * message. + */ +void +test_utils_check_pixel_and_alpha (CoglFramebuffer *fb, + int x, + int y, + uint32_t expected_pixel); + +/* + * test_utils_check_pixel: + * @framebuffer: The #CoglFramebuffer to read from + * @x: x coordinate of the pixel to test + * @y: y coordinate of the pixel to test + * @pixel: An integer of the form 0xrrggbb representing the expected pixel value + * + * This performs reads a pixel on the given cogl @framebuffer and + * asserts that it matches the given color. The alpha channel of the + * color is ignored. The pixels are converted to a string and compared + * with g_assert_cmpstr so that if the comparison fails then the + * assert will display a meaningful message + */ +void +test_utils_check_pixel_rgb (CoglFramebuffer *framebuffer, + int x, + int y, + int r, + int g, + int b); + +/* + * test_utils_check_region: + * @framebuffer: The #CoglFramebuffer to read from + * @x: x coordinate of the region to test + * @y: y coordinate of the region to test + * @width: width of the region to test + * @height: height of the region to test + * @pixel: An integer of the form 0xrrggbb representing the expected region color + * + * Performs a read pixel on the specified region of the given cogl + * @framebuffer and asserts that it matches the given color. The alpha + * channel of the color is ignored. The pixels are converted to a + * string and compared with g_assert_cmpstr so that if the comparison + * fails then the assert will display a meaningful message + */ +void +test_utils_check_region (CoglFramebuffer *framebuffer, + int x, + int y, + int width, + int height, + uint32_t expected_rgba); + +/* + * test_utils_compare_pixel: + * @screen_pixel: A pixel stored in memory + * @expected_pixel: The expected RGBA value + * + * Compares a pixel from a buffer to an expected value. The pixels are + * converted to a string and compared with g_assert_cmpstr so that if + * the comparison fails then the assert will display a meaningful + * message. + */ +void +test_utils_compare_pixel (const uint8_t *screen_pixel, + uint32_t expected_pixel); + +/* + * test_utils_compare_pixel_and_alpha: + * @screen_pixel: A pixel stored in memory + * @expected_pixel: The expected RGBA value + * + * Compares a pixel from a buffer to an expected value. This is + * similar to test_utils_compare_pixel() except that it doesn't ignore + * the alpha component. + */ +void +test_utils_compare_pixel_and_alpha (const uint8_t *screen_pixel, + uint32_t expected_pixel); + +/* + * test_utils_create_color_texture: + * @context: A #CoglContext + * @color: A color to put in the texture + * + * Creates a 1x1-pixel RGBA texture filled with the given color. + */ +CoglTexture * +test_utils_create_color_texture (CoglContext *context, + uint32_t color); + +/* cogl_test_verbose: + * + * Queries if the user asked for verbose output or not. + */ +gboolean +cogl_test_verbose (void); + +/* test_util_is_pot: + * @number: A number to test + * + * Returns whether the given integer is a power of two + */ +static inline gboolean +test_utils_is_pot (unsigned int number) +{ + /* Make sure there is only one bit set */ + return (number & (number - 1)) == 0; +} + +#endif /* COGL_TEST_UTILS_H */ diff --git a/src/tests/cogl/conform/meson.build b/src/tests/cogl/conform/meson.build new file mode 100644 index 000000000..2377d9f51 --- /dev/null +++ b/src/tests/cogl/conform/meson.build @@ -0,0 +1,51 @@ +cogl_tests = [ +] + +cogl_test_conformance_includes = [ + tests_includepath, + cogl_includepath, +] + +cogl_test_c_args = [ + cogl_debug_c_args, + '-DCOGL_ENABLE_EXPERIMENTAL_API', + '-DCOGL_DISABLE_DEPRECATED', + '-DCOGL_DISABLE_DEPRECATION_WARNINGS', + '-DTESTS_DATADIR="@0@/tests/data"'.format(cogl_srcdir), + '-DGETTEXT_PACKAGE="@0@"'.format(meson.project_name()), +] + +test_env = environment() +test_env.set('G_TEST_SRCDIR', meson.current_source_dir()) +test_env.set('G_TEST_BUILDDIR', meson.current_build_dir()) +test_env.set('G_ENABLE_DIAGNOSTIC', '0') +test_env.set('MUTTER_TEST_PLUGIN_PATH', '@0@'.format(default_plugin.full_path())) + +cogl_test_variants = [ 'gl', 'gl3', 'gles2' ] + +foreach cogl_test: cogl_tests + test_name = 'cogl-' + cogl_test + test_executable = executable(test_name, + sources: [ + cogl_test_utils, + cogl_test + '.c', + ], + c_args: cogl_test_c_args, + include_directories: cogl_test_conformance_includes, + dependencies: [ + libmutter_test_dep, + ], + install_rpath: pkglibdir, + ) + + foreach variant: cogl_test_variants + variant_test_env = test_env + variant_test_env.set('COGL_DRIVER', variant) + + test(test_name + '-' + variant, test_executable, + suite: ['cogl', 'cogl/conform'], + env: variant_test_env, + is_parallel: false, + ) + endforeach +endforeach diff --git a/src/tests/cogl/meson.build b/src/tests/cogl/meson.build new file mode 100644 index 000000000..3f06af70b --- /dev/null +++ b/src/tests/cogl/meson.build @@ -0,0 +1 @@ +subdir('conform') diff --git a/src/tests/meson.build b/src/tests/meson.build index 33f413dbb..cbd223129 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -18,6 +18,11 @@ mutter_test_sources = [ libmutter_test_name = 'mutter-test-' + libmutter_api_version +cogl_test_utils = files ( + 'cogl-test-utils.c', + 'cogl-test-utils.h', +) + clutter_test_utils = files ( 'clutter-test-utils.c', 'clutter-test-utils.h', @@ -67,6 +72,10 @@ pkg.generate(libmutter_test, install_dir: pcdir, ) +if have_cogl_tests + subdir('cogl') +endif + if have_clutter_tests subdir('clutter') endif