From 0cce5538673148ffcd7aa9479f6b88cf9a641352 Mon Sep 17 00:00:00 2001 From: Ian Romanick Date: Sun, 17 Feb 2013 13:53:19 -0800 Subject: [PATCH] glx/tests: Add unit tests for the GLX part of GLX_MESA_query_renderer These tests primarilly ensure that the functions added by this extension don't abuse other interfaces (e.g., glx_screen::query_renderer_integer) when provided bad data. These tests helped me find a couple small bugs in the initial implementation. Signed-off-by: Ian Romanick --- src/glx/tests/Makefile.am | 3 +- src/glx/tests/fake_glx_screen.cpp | 8 + src/glx/tests/query_renderer_unittest.cpp | 439 ++++++++++++++++++++++ 3 files changed, 449 insertions(+), 1 deletion(-) create mode 100644 src/glx/tests/query_renderer_unittest.cpp diff --git a/src/glx/tests/Makefile.am b/src/glx/tests/Makefile.am index d940712a6c5..6995d987d35 100644 --- a/src/glx/tests/Makefile.am +++ b/src/glx/tests/Makefile.am @@ -16,7 +16,8 @@ glx_test_SOURCES = \ create_context_unittest.cpp \ enum_sizes.cpp \ fake_glx_screen.cpp \ - indirect_api.cpp + indirect_api.cpp \ + query_renderer_unittest.cpp glx_test_LDADD = \ $(top_builddir)/src/glx/libglx.la \ diff --git a/src/glx/tests/fake_glx_screen.cpp b/src/glx/tests/fake_glx_screen.cpp index 7ee9b82e7c7..29f244117cd 100644 --- a/src/glx/tests/fake_glx_screen.cpp +++ b/src/glx/tests/fake_glx_screen.cpp @@ -74,3 +74,11 @@ indirect_create_context_attribs(struct glx_screen *base, return indirect_create_context(base, config_base, shareList, 0); } + +__thread void *__glX_tls_Context = NULL; + +extern "C" struct glx_context * +__glXGetCurrentContext() +{ + return (struct glx_context *) __glX_tls_Context; +} diff --git a/src/glx/tests/query_renderer_unittest.cpp b/src/glx/tests/query_renderer_unittest.cpp new file mode 100644 index 00000000000..f0998b7d7dc --- /dev/null +++ b/src/glx/tests/query_renderer_unittest.cpp @@ -0,0 +1,439 @@ +/* + * Copyright © 2013 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include +#include +#include + +extern "C" { +#include "glxclient.h" +#include "glx_error.h" +} + +extern bool GetGLXScreenConfigs_called; +extern struct glx_screen *psc; + +struct attribute_test_vector { + const char *string; + int value; +}; + +#define E(x) { # x, x } + + + +static bool got_sigsegv; +static jmp_buf jmp; + +static void +sigsegv_handler(int sig) +{ + (void) sig; + got_sigsegv = true; + longjmp(jmp, 1); +} + +static bool query_renderer_string_called = false; +static bool query_renderer_integer_called = false; + +static int +fake_query_renderer_integer(struct glx_screen *psc, int attribute, int *value) +{ + (void) psc; + (void) attribute; + (void) value; + + query_renderer_integer_called = true; + + return -1; +} + +static int +fake_query_renderer_string(struct glx_screen *psc, int attribute, + const char **value) +{ + (void) psc; + (void) attribute; + (void) value; + + query_renderer_string_called = true; + + return -1; +} + +struct glx_screen_vtable fake_vtable = { + NULL, + NULL, + fake_query_renderer_integer, + fake_query_renderer_string +}; + +class query_renderer_string_test : public ::testing::Test { +public: + virtual void SetUp(); + virtual void TearDown(); + + struct glx_screen scr; + struct sigaction sa; + struct sigaction old_sa; + Display dpy; +}; + +class query_renderer_integer_test : public query_renderer_string_test { +}; + +void query_renderer_string_test::SetUp() +{ + memset(&scr, 0, sizeof(scr)); + scr.vtable = &fake_vtable; + psc = &scr; + + got_sigsegv = false; + + sa.sa_handler = sigsegv_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sigaction(SIGSEGV, &sa, &old_sa); +} + +void query_renderer_string_test::TearDown() +{ + sigaction(SIGSEGV, &old_sa, NULL); +} + +/** + * glXQueryRendererStringMESA will return \c NULL if the query_render_string + * vtable entry is \c NULL. It will also not segfault. + */ +TEST_F(query_renderer_string_test, null_query_render_string) +{ + struct glx_screen_vtable vtable = { + NULL, + NULL, + NULL, + NULL + }; + + scr.vtable = &vtable; + + if (setjmp(jmp) == 0) { + const char *str = + glXQueryRendererStringMESA(&dpy, 0, 0, GLX_RENDERER_VENDOR_ID_MESA); + EXPECT_EQ((char *)0, str); + } else { + EXPECT_FALSE(got_sigsegv); + } +} + +/** + * glXQueryRendererStringMESA will not call the screen query_render_string + * function with an invalid GLX enum value, and it will return NULL. + */ +TEST_F(query_renderer_string_test, invalid_attribute) +{ + static const attribute_test_vector invalid_attributes[] = { + /* These values are just plain invalid for use with this extension. + */ + E(0), + E(GLX_VENDOR), + E(GLX_VERSION), + E(GLX_EXTENSIONS), + E(GLX_RENDERER_VENDOR_ID_MESA + 0x10000), + E(GLX_RENDERER_DEVICE_ID_MESA + 0x10000), + + /* These enums are part of the extension, but they are not allowed for + * the string query. + */ + E(GLX_RENDERER_VERSION_MESA), + E(GLX_RENDERER_ACCELERATED_MESA), + E(GLX_RENDERER_VIDEO_MEMORY_MESA), + E(GLX_RENDERER_UNIFIED_MEMORY_ARCHITECTURE_MESA), + E(GLX_RENDERER_PREFERRED_PROFILE_MESA), + E(GLX_RENDERER_OPENGL_CORE_PROFILE_VERSION_MESA), + E(GLX_RENDERER_OPENGL_COMPATIBILITY_PROFILE_VERSION_MESA), + E(GLX_RENDERER_OPENGL_ES_PROFILE_VERSION_MESA), + E(GLX_RENDERER_OPENGL_ES2_PROFILE_VERSION_MESA), + E(GLX_RENDERER_ID_MESA), + }; + + for (unsigned i = 0; i < ARRAY_SIZE(invalid_attributes); i++) { + query_renderer_integer_called = false; + query_renderer_string_called = false; + + const char *str = + glXQueryRendererStringMESA(&dpy, 0, 0, invalid_attributes[i].value); + EXPECT_EQ((char *)0, str) << invalid_attributes[i].string; + EXPECT_FALSE(query_renderer_integer_called) + << invalid_attributes[i].string; + EXPECT_FALSE(query_renderer_string_called) + << invalid_attributes[i].string; + } +} + +/** + * glXQueryRendererStringMESA will not call GetGLXScreenConfigs if the display + * pointer is \c NULL. It will also not segfault. + */ +TEST_F(query_renderer_string_test, null_display_pointer) +{ + if (setjmp(jmp) == 0) { + GetGLXScreenConfigs_called = false; + + const char *str = + glXQueryRendererStringMESA(NULL, 0, 0, GLX_RENDERER_VENDOR_ID_MESA); + EXPECT_EQ((char *)0, str); + EXPECT_FALSE(GetGLXScreenConfigs_called); + } else { + EXPECT_FALSE(got_sigsegv); + } +} + +/** + * glXQueryRendererStringMESA will return error if GetGLXScreenConfigs returns + * NULL. It will also not segfault. + */ +TEST_F(query_renderer_string_test, null_screen_pointer) +{ + psc = NULL; + + if (setjmp(jmp) == 0) { + GetGLXScreenConfigs_called = false; + + const char *str = + glXQueryRendererStringMESA(&dpy, 0, 0, GLX_RENDERER_VENDOR_ID_MESA); + EXPECT_EQ((char *)0, str); + EXPECT_TRUE(GetGLXScreenConfigs_called); + } else { + EXPECT_FALSE(got_sigsegv); + } +} + +/** + * glXQueryRendererStringMESA will not call the screen query_render_string + * function if the renderer is invalid, and it will return NULL. + */ +TEST_F(query_renderer_string_test, invalid_renderer_index) +{ + static const int invalid_renderer_indices[] = { + -1, + 1, + 999, + }; + + if (setjmp(jmp) == 0) { + for (unsigned i = 0; i < ARRAY_SIZE(invalid_renderer_indices); i++) { + const char *str = + glXQueryRendererStringMESA(&dpy, 0, + invalid_renderer_indices[i], + GLX_RENDERER_VENDOR_ID_MESA); + EXPECT_EQ((char *)0, str) << invalid_renderer_indices[i]; + EXPECT_FALSE(query_renderer_integer_called) + << invalid_renderer_indices[i]; + EXPECT_FALSE(query_renderer_string_called) + << invalid_renderer_indices[i]; + } + } else { + EXPECT_FALSE(got_sigsegv); + } +} + +/** + * glXQueryCurrentRendererStringMESA will return error if there is no context + * current. It will also not segfault. + */ +TEST_F(query_renderer_string_test, no_current_context) +{ + if (setjmp(jmp) == 0) { + const char *str = + glXQueryCurrentRendererStringMESA(GLX_RENDERER_VENDOR_ID_MESA); + EXPECT_EQ((char *)0, str); + } else { + EXPECT_FALSE(got_sigsegv); + } +} + +/** + * glXQueryCurrentRendererIntegerMESA will return \c NULL if the + * query_render_string vtable entry is \c NULL. It will also not segfault. + */ +TEST_F(query_renderer_integer_test, null_query_render_string) +{ + struct glx_screen_vtable vtable = { + NULL, + NULL, + NULL, + NULL + }; + + scr.vtable = &vtable; + + if (setjmp(jmp) == 0) { + unsigned value = 0xDEADBEEF; + Bool success = glXQueryRendererIntegerMESA(&dpy, 0, 0, + GLX_RENDERER_VENDOR_ID_MESA, + &value); + EXPECT_FALSE(success); + EXPECT_EQ(0xDEADBEEF, value); + } else { + EXPECT_FALSE(got_sigsegv); + } +} + +/** + * glXQueryCurrentRendererIntegerMESA will not call the screen + * query_render_string function with an invalid GLX enum value, and it will + * return NULL. + */ +TEST_F(query_renderer_integer_test, invalid_attribute) +{ + static const attribute_test_vector invalid_attributes[] = { + /* These values are just plain invalid for use with this extension. + */ + E(0), + E(GLX_VENDOR), + E(GLX_VERSION), + E(GLX_EXTENSIONS), + E(GLX_RENDERER_VENDOR_ID_MESA + 0x10000), + E(GLX_RENDERER_DEVICE_ID_MESA + 0x10000), + E(GLX_RENDERER_VERSION_MESA + 0x10000), + E(GLX_RENDERER_ACCELERATED_MESA + 0x10000), + E(GLX_RENDERER_VIDEO_MEMORY_MESA + 0x10000), + E(GLX_RENDERER_UNIFIED_MEMORY_ARCHITECTURE_MESA + 0x10000), + E(GLX_RENDERER_PREFERRED_PROFILE_MESA + 0x10000), + E(GLX_RENDERER_OPENGL_CORE_PROFILE_VERSION_MESA + 0x10000), + E(GLX_RENDERER_OPENGL_COMPATIBILITY_PROFILE_VERSION_MESA + 0x10000), + E(GLX_RENDERER_OPENGL_ES_PROFILE_VERSION_MESA + 0x10000), + E(GLX_RENDERER_OPENGL_ES2_PROFILE_VERSION_MESA + 0x10000), + E(GLX_RENDERER_ID_MESA + 0x10000), + }; + + for (unsigned i = 0; i < ARRAY_SIZE(invalid_attributes); i++) { + query_renderer_integer_called = false; + query_renderer_string_called = false; + + unsigned value = 0xDEADBEEF; + Bool success = + glXQueryRendererIntegerMESA(&dpy, 0, 0, + invalid_attributes[i].value, + &value); + EXPECT_FALSE(success) << invalid_attributes[i].string; + EXPECT_EQ(0xDEADBEEF, value) << invalid_attributes[i].string; + EXPECT_FALSE(query_renderer_integer_called) + << invalid_attributes[i].string; + EXPECT_FALSE(query_renderer_string_called) + << invalid_attributes[i].string; + } +} + +/** + * glXQueryCurrentRendererIntegerMESA will not call GetGLXScreenConfigs if the + * display pointer is \c NULL. It will also not segfault. + */ +TEST_F(query_renderer_integer_test, null_display_pointer) +{ + if (setjmp(jmp) == 0) { + GetGLXScreenConfigs_called = false; + + unsigned value = 0xDEADBEEF; + Bool success = + glXQueryRendererIntegerMESA(NULL, 0, 0, GLX_RENDERER_VENDOR_ID_MESA, + &value); + EXPECT_FALSE(success); + EXPECT_EQ(0xDEADBEEF, value); + EXPECT_FALSE(GetGLXScreenConfigs_called); + } else { + EXPECT_FALSE(got_sigsegv); + } +} + +/** + * glXQueryCurrentRendererIntegerMESA will return error if GetGLXScreenConfigs + * returns NULL. It will also not segfault. + */ +TEST_F(query_renderer_integer_test, null_screen_pointer) +{ + psc = NULL; + + if (setjmp(jmp) == 0) { + GetGLXScreenConfigs_called = false; + + unsigned value = 0xDEADBEEF; + Bool success = + glXQueryRendererIntegerMESA(&dpy, 0, 0, GLX_RENDERER_VENDOR_ID_MESA, + &value); + EXPECT_FALSE(success); + EXPECT_EQ(0xDEADBEEF, value); + EXPECT_TRUE(GetGLXScreenConfigs_called); + } else { + EXPECT_FALSE(got_sigsegv); + } +} + +/** + * glXQueryRendererIntegerMESA will not call the screen query_render_integer + * function if the renderer is invalid, and it will return NULL. + */ +TEST_F(query_renderer_integer_test, invalid_renderer_index) +{ + static const int invalid_renderer_indices[] = { + -1, + 1, + 999, + }; + + if (setjmp(jmp) == 0) { + for (unsigned i = 0; i < ARRAY_SIZE(invalid_renderer_indices); i++) { + unsigned value = 0xDEADBEEF; + Bool success = + glXQueryRendererIntegerMESA(&dpy, 0, + invalid_renderer_indices[i], + GLX_RENDERER_VENDOR_ID_MESA, + &value); + EXPECT_FALSE(success) << invalid_renderer_indices[i]; + EXPECT_EQ(0xDEADBEEF, value) << invalid_renderer_indices[i]; + EXPECT_FALSE(query_renderer_integer_called) + << invalid_renderer_indices[i]; + EXPECT_FALSE(query_renderer_string_called) + << invalid_renderer_indices[i]; + } + } else { + EXPECT_FALSE(got_sigsegv); + } +} + +/** + * glXQueryCurrentRendererIntegerMESA will return error if there is no context + * current. It will also not segfault. + */ +TEST_F(query_renderer_integer_test, no_current_context) +{ + if (setjmp(jmp) == 0) { + unsigned value = 0xDEADBEEF; + Bool success = + glXQueryCurrentRendererIntegerMESA(GLX_RENDERER_VENDOR_ID_MESA, + &value); + EXPECT_FALSE(success); + EXPECT_EQ(0xDEADBEEF, value); + } else { + EXPECT_FALSE(got_sigsegv); + } +} -- 2.30.2