text: //
  // Copyright (c) 2015 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.
  //
  // UnrollFlatten_test.cpp:
  //   Test for the outputting of [[unroll]] and [[flatten]] for the D3D compiler.
  //   This test can only be enabled when HLSL support is enabled.
  //
  
  #include "angle_gl.h"
  #include "common/angleutils.h"
  #include "gtest/gtest.h"
  #include "GLSLANG/ShaderLang.h"
  #include "tests/test_utils/compiler_test.h"
  
  using namespace sh;
  
  namespace
  {
  
  class UnrollFlattenTest : public testing::Test
  {
    public:
      UnrollFlattenTest() : mInputSpec(SH_GLES2_SPEC) {}
      UnrollFlattenTest(ShShaderSpec inputSpec) : mInputSpec(inputSpec) {}
  
    protected:
      void compile(const std::string &shaderString)
      {
          std::string infoLog;
          bool compilationSuccess =
              compileTestShader(GL_FRAGMENT_SHADER, mInputSpec, SH_HLSL_4_1_OUTPUT, shaderString,
                                SH_VARIABLES, &mTranslatedSource, &infoLog);
          if (!compilationSuccess)
          {
              FAIL() << "Shader compilation failed " << infoLog;
          }
          // Ignore the beginning of the shader to avoid the definitions of LOOP and FLATTEN
          mCurrentPosition = static_cast<int>(mTranslatedSource.find("cbuffer DriverConstants"));
      }
  
      void expect(const char *patterns[], size_t count)
      {
          const char *badPatterns[] = { UNROLL, FLATTEN };
          for (size_t i = 0; i < count; i++)
          {
              const char *pattern = patterns[i];
              auto position = mTranslatedSource.find(pattern, mCurrentPosition);
              if (position == std::string::npos)
              {
                  FAIL() << "Couldn't find '" << pattern << "' after expectations '"
                         << mExpectationList << "' in translated source:\n" << mTranslatedSource;
              }
  
              for (size_t j = 0; j < ArraySize(badPatterns); j++)
              {
                  const char *badPattern = badPatterns[j];
                  if (pattern != badPattern &&
                      mTranslatedSource.find(badPattern, mCurrentPosition) < position)
                  {
                      FAIL() << "Found '" << badPattern << "' before '" << pattern << "' after expectations '"
                             << mExpectationList << "' in translated source:\n" << mTranslatedSource;
                  }
              }
              mExpectationList += " - " + std::string(pattern);
              mCurrentPosition = static_cast<int>(position) + 1;
          }
      }
  
      static const char *UNROLL;
      static const char *FLATTEN;
  
    private:
      ShShaderSpec mInputSpec;
      std::string mTranslatedSource;
  
      int mCurrentPosition;
      std::string mExpectationList;
  };
  
  const char *UnrollFlattenTest::UNROLL = "LOOP";
  const char *UnrollFlattenTest::FLATTEN = "FLATTEN";
  
  // Check that the nothing is added if there is no gradient operation
  // even when there is ifs and discontinuous loops
  TEST_F(UnrollFlattenTest, NoGradient)
  {
      const std::string &shaderString =
          "precision mediump float;\n"
          "uniform float f;\n"
          "float fun(float a){\n" // 1
          "    if (a > 1.0) {return f;}\n" // 2
          "    else {return a + 1.0;}\n"
          "}\n"
          "float fun2(float a){\n" // 3
          "    for (int i = 0; i < 10; i++) {\n" // 4
          "        if (a > 1.0) {break;}\n" // 5
          "        a = fun(a);\n" // 6
          "    }\n"
          "    return a;\n"
          "}\n"
          "void main() {\n"
          "    float accum = 0.0;\n"
          "    if (f < 5.0) {accum = fun2(accum);}\n" // 7
          "    gl_FragColor = vec4(accum);\n"
          "}\n";
      compile(shaderString);
      // 1 - shouldn't get a Lod0 version generated
      // 2 - no FLATTEN because does not contain discont loop
      // 3 - shouldn't get a Lod0 version generated
      // 4 - no LOOP because discont, and also no gradient
      // 5 - no FLATTEN because does not contain loop with a gradient
      // 6 - call non-Lod0 version
      // 7 - no FLATTEN
      const char *expectations[] =
      {
          "fun(", "if",
          "fun2(", "for", "if", "break", "fun(",
          "main(", "if", "fun2("
      };
      expect(expectations, ArraySize(expectations));
  }
  
  // Check that when we have a gradient in a non-discontinuous loop
  // we use the regular version of the functions. Also checks that
  // LOOP is generated for the loop containing the gradient.
  TEST_F(UnrollFlattenTest, GradientNotInDiscont)
  {
      const std::string &shaderString =
          "precision mediump float;\n"
          "uniform float f;\n"
          "uniform sampler2D tex;"
          "float fun(float a){\n" // 1
          "    return texture2D(tex, vec2(0.5, f)).x;\n" // 2
          "}\n"
          "float fun2(float a){\n" // 3
          "    for (int i = 0; i < 10; i++) {\n" // 4
          "        if (a > 1.0) {}\n" // 5
          "        a = fun(a);\n" // 6
          "        a += texture2D(tex, vec2(a, 0.0)).x;" // 7
          "    }\n"
          "    return a;\n"
          "}\n"
          "void main() {\n"
          "    float accum = 0.0;\n"
          "    if (f < 5.0) {accum = fun2(accum);}\n" // 8
          "    gl_FragColor = vec4(accum);\n"
          "}\n";
      // 1 - shouldn't get a Lod0 version generated
      // 2 - no Lod0 version generated
      // 3 - shouldn't get a Lod0 version generated (not in discont loop)
      // 4 - should have LOOP because it contains a gradient operation (even if Lod0)
      // 5 - no FLATTEN because doesn't contain loop with a gradient
      // 6 - call non-Lod0 version
      // 7 - call non-Lod0 version
      // 8 - FLATTEN because it contains a loop with a gradient
      compile(shaderString);
      const char *expectations[] =
      {
          "fun(", "texture2D(",
          "fun2(", "LOOP", "for", "if", "fun(", "texture2D(",
          "main(", "FLATTEN", "if", "fun2("
      };
      expect(expectations, ArraySize(expectations));
  }
  
  // Check that when we have a gradient in a discontinuous loop
  // we use the Lod0 version of the functions.
  TEST_F(UnrollFlattenTest, GradientInDiscont)
  {
      const std::string &shaderString =
          "precision mediump float;\n"
          "uniform float f;\n"
          "uniform sampler2D tex;"
          "float fun(float a){\n" // 1
          "    return texture2D(tex, vec2(0.5, f)).x;\n" // 2
          "}\n"
          "float fun2(float a){\n" // 3
          "    for (int i = 0; i < 10; i++) {\n" // 4
          "        if (a > 1.0) {break;}\n" // 5
          "        a = fun(a);\n" // 6
          "        a += texture2D(tex, vec2(a, 0.0)).x;" // 7
          "    }\n"
          "    return a;\n"
          "}\n"
          "void main() {\n"
          "    float accum = 0.0;\n"
          "    if (f < 5.0) {accum = fun2(accum);}\n" // 8
          "    gl_FragColor = vec4(accum);\n"
          "}\n";
      // 1 - should get a Lod0 version generated (gradient + discont loop)
      // 2 - will get the Lod0 if in funLod0
      // 3 - shouldn't get a Lod0 version generated (not in discont loop)
      // 4 - should have LOOP because it contains a gradient operation (even if Lod0)
      // 5 - no FLATTEN because doesn't contain a loop with a gradient
      // 6 - call Lod0 version
      // 7 - call Lod0 version
      // 8 - FLATTEN because it contains a loop with a gradient
      compile(shaderString);
      const char *expectations[] =
      {
          "fun(", "texture2D(",
          "funLod0(", "texture2DLod0(",
          "fun2(", "LOOP", "for", "if", "break", "funLod0(", "texture2DLod0",
          "main(", "FLATTEN", "if", "fun2("
      };
      expect(expectations, ArraySize(expectations));
  }
  
  class UnrollFlattenTest_ES3 : public UnrollFlattenTest
  {
    public:
      UnrollFlattenTest_ES3() : UnrollFlattenTest(SH_GLES3_SPEC) {}
  };
  
  // Check that we correctly detect the ES3 builtin "texture" function as a gradient operation.
  TEST_F(UnrollFlattenTest_ES3, TextureBuiltin)
  {
      const std::string &shaderString =
          "#version 300 es\n"
          "precision mediump float;\n"
          "uniform sampler2D tex;"
          "out float fragColor;\n"
          "void main() {\n"
          "    float a = 0.0;"
          "    for (int i = 0; i < 10; i++) {\n"
          "        if (a > 1.0) {break;}\n"
          "        a += texture(tex, vec2(a, 0.0)).x;"
          "    }\n"
          "    fragColor = a;\n"
          "}\n";
  
      compile(shaderString);
      const char *expectations[] = {"main(", "LOOP", "Lod0("};
      expect(expectations, ArraySize(expectations));
  }
  }
  