Handle nullptr in GetQueryObjectParameter()

There are applications that disable validation using
EGL_CONTEXT_OPENGL_NO_ERROR_KHR extension. In such usecases
the GetQueryObjectParameter() method needs to account for the
possibility that the query object has not yet been created.

Bug: angleproject:5704
Tests: angle_end2end_tests --gtest_filter=QueryObjectTest*
Change-Id: Ib9e1cb32a6d64f2772124178223cf07cbb84691b
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2729298
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
Commit-Queue: Mohan Maiya <m.maiya@samsung.com>
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
index 2cb5cbf..ea42d24 100644
--- a/src/libANGLE/Context.cpp
+++ b/src/libANGLE/Context.cpp
@@ -67,7 +67,25 @@
 template <typename T>
 angle::Result GetQueryObjectParameter(const Context *context, Query *query, GLenum pname, T *params)
 {
-    ASSERT(query != nullptr || pname == GL_QUERY_RESULT_AVAILABLE_EXT);
+    if (!query)
+    {
+        // Some applications call into glGetQueryObjectuiv(...) prior to calling glBeginQuery(...)
+        // This wouldn't be an issue since the validation layer will handle such a usecases but when
+        // the app enables EGL_KHR_create_context_no_error extension, we skip the validation layer.
+        switch (pname)
+        {
+            case GL_QUERY_RESULT_EXT:
+                *params = 0;
+                break;
+            case GL_QUERY_RESULT_AVAILABLE_EXT:
+                *params = GL_FALSE;
+                break;
+            default:
+                UNREACHABLE();
+                return angle::Result::Stop;
+        }
+        return angle::Result::Continue;
+    }
 
     switch (pname)
     {
diff --git a/src/tests/angle_end2end_tests.gni b/src/tests/angle_end2end_tests.gni
index 16c9afb..5b20401 100644
--- a/src/tests/angle_end2end_tests.gni
+++ b/src/tests/angle_end2end_tests.gni
@@ -108,6 +108,7 @@
   "gl_tests/ProgramParameterTest.cpp",
   "gl_tests/ProgramPipelineTest.cpp",
   "gl_tests/ProvokingVertexTest.cpp",
+  "gl_tests/QueryObjectValidation.cpp",
   "gl_tests/ReadOnlyFeedbackLoopTest.cpp",
   "gl_tests/ReadPixelsTest.cpp",
   "gl_tests/RenderbufferMultisampleTest.cpp",
diff --git a/src/tests/gl_tests/QueryObjectValidation.cpp b/src/tests/gl_tests/QueryObjectValidation.cpp
new file mode 100644
index 0000000..ea11e2c
--- /dev/null
+++ b/src/tests/gl_tests/QueryObjectValidation.cpp
@@ -0,0 +1,157 @@
+//
+// Copyright 2021 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// QueryObjectValidation.cpp : Tests between gl.*Query.* functions and interactions with
+// EGL_CONTEXT_OPENGL_NO_ERROR_KHR
+#include <gtest/gtest.h>
+
+#include "test_utils/ANGLETest.h"
+#include "util/EGLWindow.h"
+#include "util/test_utils.h"
+
+namespace angle
+{
+
+using QueryObjectTestParams = std::tuple<angle::PlatformParameters, bool>;
+
+std::string PrintToStringParamName(const ::testing::TestParamInfo<QueryObjectTestParams> &info)
+{
+    std::stringstream ss;
+    ss << std::get<0>(info.param);
+    if (std::get<1>(info.param))
+    {
+        ss << "__ValidationDisabled";
+    }
+    else
+    {
+        ss << "__ValidationEnabled";
+    }
+    return ss.str();
+}
+
+class QueryObjectTest : public ANGLETestWithParam<QueryObjectTestParams>
+{
+  protected:
+    QueryObjectTest() : mQueryObjectName(0), mQueryResult(false)
+    {
+        setNoErrorEnabled(testing::get<1>(GetParam()));
+    }
+
+    void createQuery()
+    {
+        glGenQueries(1, &mQueryObjectName);
+        ASSERT_NE(mQueryObjectName, (GLuint)0u);
+        ASSERT_GL_NO_ERROR();
+    }
+
+    void testSetUp() override { createQuery(); }
+
+    void testTearDown() override
+    {
+        if (mQueryObjectName)
+        {
+            glDeleteQueries(1, &mQueryObjectName);
+        }
+    }
+
+    GLuint mQueryObjectName = 0;
+    GLuint mQueryResult     = 0;
+};
+
+class QueryObjectTestES32 : public QueryObjectTest
+{};
+
+// Test if a generated query is a query before glBeginQuery
+TEST_P(QueryObjectTest, QueryObjectIsQuery)
+{
+    GLboolean isQueryResult = glIsQuery(mQueryObjectName);
+    ASSERT_GL_NO_ERROR();
+    ASSERT_FALSE(isQueryResult);
+}
+
+// Negative test for glGetQueryObjectuiv before glBegin with GL_QUERY_RESULT
+TEST_P(QueryObjectTest, QueryObjectResultBeforeBegin)
+{
+    glGetQueryObjectuiv(mQueryObjectName, GL_QUERY_RESULT, &mQueryResult);
+
+    bool isNoError = testing::get<1>(GetParam());
+    if (isNoError)
+    {
+        ASSERT_GL_NO_ERROR();
+    }
+    else
+    {
+        ASSERT_EQ(glGetError(), (GLenum)GL_INVALID_OPERATION);
+    }
+}
+
+// Negative test for glGetQueryObjectuiv before glBegin with GL_QUERY_RESULT_AVAILABLE
+TEST_P(QueryObjectTest, QueryObjectResultAvailableBeforeBegin)
+{
+    glGetQueryObjectuiv(mQueryObjectName, GL_QUERY_RESULT_AVAILABLE, &mQueryResult);
+
+    bool isNoError = testing::get<1>(GetParam());
+    if (isNoError)
+    {
+        ASSERT_GL_NO_ERROR();
+    }
+    else
+    {
+        ASSERT_EQ(glGetError(), (GLenum)GL_INVALID_OPERATION);
+    }
+}
+
+// Test glGetQueryObjectuiv after glEndQuery
+TEST_P(QueryObjectTest, QueryObjectResultAfterEnd)
+{
+    glBeginQuery(GL_ANY_SAMPLES_PASSED, mQueryObjectName);
+    ASSERT_GL_NO_ERROR();
+
+    glEndQuery(GL_ANY_SAMPLES_PASSED);
+    ASSERT_GL_NO_ERROR();
+
+    glGetQueryObjectuiv(mQueryObjectName, GL_QUERY_RESULT_AVAILABLE, &mQueryResult);
+}
+
+// Test glGetQueryObjectuiv after glEndQuery with GL_PRIMITIVES_GENERATED
+TEST_P(QueryObjectTestES32, QueryObjectResultAfterEndPrimitivesGenerated)
+{
+    // TODO(anglebug.com/5430): Allow GL_PRIMITIVES_GENERATED query objects
+    // when transform feedback is not active
+    ANGLE_SKIP_TEST_IF(IsVulkan());
+    glBeginQuery(GL_PRIMITIVES_GENERATED, mQueryObjectName);
+    ASSERT_GL_NO_ERROR();
+
+    glEndQuery(GL_PRIMITIVES_GENERATED);
+    ASSERT_GL_NO_ERROR();
+
+    while (mQueryResult != GL_TRUE)
+    {
+        glGetQueryObjectuiv(mQueryObjectName, GL_QUERY_RESULT_AVAILABLE, &mQueryResult);
+        ASSERT_GL_NO_ERROR();
+        angle::Sleep(50);
+    }
+
+    GLboolean isQueryResult = glIsQuery(mQueryObjectName);
+    ASSERT_GL_NO_ERROR();
+    ASSERT_TRUE(isQueryResult);
+}
+
+static const bool noErrorFlags[] = {true, false};
+
+ANGLE_INSTANTIATE_TEST_COMBINE_1(QueryObjectTest,
+                                 PrintToStringParamName,
+                                 testing::ValuesIn(noErrorFlags),
+                                 ANGLE_ALL_TEST_PLATFORMS_ES3);
+
+ANGLE_INSTANTIATE_TEST_COMBINE_1(QueryObjectTestES32,
+                                 PrintToStringParamName,
+                                 testing::ValuesIn(noErrorFlags),
+                                 ANGLE_ALL_TEST_PLATFORMS_ES32);
+
+// This test suite is not instantiated on some OSes.
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(QueryObjectTestES32);
+
+}  // namespace angle