Add index file for amber tests
authorJari Komppa <jari.komppa@siru.fi>
Mon, 4 Nov 2019 13:51:27 +0000 (15:51 +0200)
committerAlexander Galazin <Alexander.Galazin@arm.com>
Tue, 19 Nov 2019 11:11:03 +0000 (06:11 -0500)
This change adds index file for amber tests, making it possible to add
new amber tests without having to recompile the cts binary itself.
This change does not affect the tests themselves, just the way they are
discovered.

Affects: none

Components: Vulkan

VK-GL-CTS issue: 2086

Change-Id: Iba1d2b6cfe8816360fe520dec90621a71a4d2769

external/vulkancts/data/vulkan/amber/graphicsfuzz/index.txt [new file with mode: 0644]
external/vulkancts/modules/vulkan/amber/vktAmberGraphicsFuzzTests.cpp
external/vulkancts/modules/vulkan/amber/vktAmberTestCase.hpp
external/vulkancts/modules/vulkan/amber/vktAmberTestCaseUtil.cpp

diff --git a/external/vulkancts/data/vulkan/amber/graphicsfuzz/index.txt b/external/vulkancts/data/vulkan/amber/graphicsfuzz/index.txt
new file mode 100644 (file)
index 0000000..f446007
--- /dev/null
@@ -0,0 +1,70 @@
+{      "access-new-vector-inside-if-condition.amber",  "access-new-vector-inside-if-condition","A shader that accesses a new vector within an if condition"                                                    },
+{      "always-false-if-in-do-while.amber",                    "always-false-if-in-do-while",                  "A fragment shader with an always false if."                                                                                    },
+{      "always-false-if-with-discard-return.amber",    "always-false-if-with-discard-return",  "A fragment shader with discard keyword and a return"                                                                   },
+{      "barrier-in-loop-with-break.amber",                             "barrier-in-loop-with-break",                   "A compute shader with a barrier in a loop with a break"                                                                },
+{      "call-if-while-switch.amber",                                   "call-if-while-switch",                                 "A fragment shader with a call, if, while, switch"                                                                              },
+{      "color-write-in-loop.amber",                                    "color-write-in-loop",                                  "A fragment shader that writes to color in a loop"                                                                              },
+{      "complex-nested-loops-and-call.amber",                  "complex-nested-loops-and-call",                "A fragment shader with complex nested loops, breaks, etc."                                                             },
+{      "conditional-return-in-infinite-while.amber",   "conditional-return-in-infinite-while", "A shader with conditional return inside an infinite loop"                                                              },
+{      "continue-and-merge.amber",                                             "continue-and-merge",                                   "A fragment shader with two nested loops"                                                                                               },
+{      "control-flow-in-function.amber",                               "control-flow-in-function",                             "A fragment shader with a lot of control flow"                                                                                  },
+{      "control-flow-switch.amber",                                    "control-flow-switch",                                  "A fragment shader with somewhat complex control flow and a switch"                                             },
+{      "dead-barriers-in-loops.amber",                                 "dead-barriers-in-loops",                               "A compute shader with dead barriers"                                                                                                   },
+{      "dead-struct-init.amber",                                               "dead-struct-init",                                             "A fragment shader that uses struct initializers"                                                                               },
+{      "disc-and-add-in-func-in-loop.amber",                   "disc-and-add-in-func-in-loop",                 "A fragment shader with discard and add in function in loop"                                                    },
+{      "discard-continue-return.amber",                                "discard-continue-return",                              "A fragment shader with a discard, continue, and return"                                                                },
+{      "discard-in-array-manipulating-loop.amber",             "discard-in-array-manipulating-loop",   "An array-manipulating fragment shader with a discard"                                                                  },
+{      "do-while-loop-in-conditionals.amber",                  "do-while-loop-in-conditionals",                "A fragment shader with do-while loop in conditional nest"                                                              },
+{      "do-while-with-always-true-if.amber",                   "do-while-with-always-true-if",                 "A fragment shader with a do while that always returns"                                                                 },
+{      "early-return-and-barrier.amber",                               "early-return-and-barrier",                             "A compute shader with an early return and a barrier"                                                                   },
+{      "for-condition-always-false.amber",                             "for-condition-always-false",                   "A fragment shader that uses a for loop with condition always false"                                    },
+{      "for-loop-with-return.amber",                                   "for-loop-with-return",                                 "A fragment shader with a for loop that loops only once"                                                                },
+{      "for-with-ifs-and-return.amber",                                "for-with-ifs-and-return",                              "A fragment shader with two ifs and return/continue inside a for loop"                                  },
+{      "fragcoord-control-flow.amber",                                 "fragcoord-control-flow",                               "A fragment shader that uses FragCoord and somewhat complex control flow"                               },
+{      "fragcoord-control-flow-2.amber",                               "fragcoord-control-flow-2",                             "A fragment shader that uses FragCoord and somewhat complex control flow"                               },
+{      "if-and-switch.amber",                                                  "if-and-switch",                                                "A fragment shader with a switch and some data flow"                                                                    },
+{      "loop-call-discard.amber",                                              "loop-call-discard",                                    "A fragment shader with nested loops and a function call"                                                               },
+{      "loop-dead-if-loop.amber",                                              "loop-dead-if-loop",                                    "A fragment shader with a loop, dead if, and a loop"                                                                    },
+{      "loop-nested-ifs.amber",                                                "loop-nested-ifs",                                              "A fragment shader with a for loop containing nested ifs"                                                               },
+{      "loops-breaks-returns.amber",                                   "loops-breaks-returns",                                 "A compute shader with loops, breaks, returns"                                                                                  },
+{      "loops-ifs-continues-call.amber",                               "loops-ifs-continues-call",                             "A fragment shader with nested control flow and a call"                                                                 },
+{      "mat-array-deep-control-flow.amber",                    "mat-array-deep-control-flow",                  "A fragment shader that uses an array of matrices and has deep control flow"                    },
+{      "mat-array-distance.amber",                                             "mat-array-distance",                                   "A fragment shader that uses an array of matrices and distance"                                                 },
+{      "matrices-and-return-in-loop.amber",                    "matrices-and-return-in-loop",                  "A fragment shader with matrices and a return in a loop"                                                                },
+{      "max-mix-conditional-discard.amber",                    "max-mix-conditional-discard",                  "A fragment shader with an expression used in two discard guards"                                               },
+{      "mix-floor-add.amber",                                                  "mix-floor-add",                                                "A fragment shader with mix, uintBitsToFloat, and floor"                                                                },
+{      "nested-for-loops-with-return.amber",                   "nested-for-loops-with-return",                 "A fragment shader with two nested for loops with return"                                                               },
+{      "nested-ifs-and-return-in-for-loop.amber",              "nested-ifs-and-return-in-for-loop",    "A fragment shader with return in nest of ifs, inside loop"                                                             },
+{      "nested-loops-switch.amber",                                    "nested-loops-switch",                                  "A fragment shader with nested loops and a switch"                                                                              },
+{      "pow-vec4.amber",                                                               "pow-vec4",                                                             "A fragment shader that uses pow"                                                                                                               },
+{      "return-before-writing-wrong-color.amber",              "return-before-writing-wrong-color",    "A fragment shader with return before writing wrong color"                                                              },
+{      "return-float-from-while-loop.amber",                   "return-float-from-while-loop",                 "A fragment shader with unreachable while loop"                                                                                 },
+{      "return-in-loop-in-function.amber",                             "return-in-loop-in-function",                   "A fragment shader with early return from loop in function"                                                             },
+{      "returned-boolean-in-vector.amber",                             "returned-boolean-in-vector",                   "A fragment shader with returned boolean in vector"                                                                             },
+{      "similar-nested-ifs.amber",                                             "similar-nested-ifs",                                   "A fragment shader with similar nested ifs and loops"                                                                   },
+{      "struct-used-as-temporary.amber",                               "struct-used-as-temporary",                             "A fragment shader that uses a temporary struct variable"                                                               },
+{      "switch-if-discard.amber",                                              "switch-if-discard",                                    "A fragment shader with a switch, if, and discard"                                                                              },
+{      "switch-with-empty-if-false.amber",                             "switch-with-empty-if-false",                   "A fragment shader with always false if in switch statement"                                                    },
+{      "swizzle-struct-init-min.amber",                                "swizzle-struct-init-min",                              "A fragment shader that uses vector swizzles, struct initializers, and min"                             },
+{      "two-for-loops-with-barrier-function.amber",    "two-for-loops-with-barrier-function",  "A compute shader with two barrier functions"                                                                                   },
+{      "two-loops-matrix.amber",                                               "two-loops-matrix",                                             "A fragment shader with two loops and some matrices"                                                                    },
+{      "two-loops-set-struct.amber",                                   "two-loops-set-struct",                                 "A fragment shader with two loops that write to a struct"                                                               },
+{      "two-loops-with-break.amber",                                   "two-loops-with-break",                                 "A fragment shader with two loops with breaks"                                                                                  },
+{      "two-nested-do-whiles.amber",                                   "two-nested-do-whiles",                                 "A fragment shader with nested do while"                                                                                                },
+{      "two-nested-for-loops-with-returns.amber",              "two-nested-for-loops-with-returns",    "A compute shader with two nested for loops"                                                                                    },
+{      "two-nested-infinite-loops-discard.amber",              "two-nested-infinite-loops-discard",    "A fragment shader with an always false if function"                                                                    },
+{      "undefined-integer-in-function.amber",                  "undefined-integer-in-function",                "A fragment shader with nested do while and undefined int"                                                              },
+{      "uninitialized-var-decrement-and-add.amber",    "uninitialized-var-decrement-and-add",  "A fragment shader that uses an uninitialized variable"                                                                 },
+{      "undefined-assign-in-infinite-loop.amber",              "undefined-assign-in-infinite-loop",    "A fragment shader with uninitialized read in infinite loop"                                                    },
+{      "unreachable-barrier-in-loops.amber",                   "unreachable-barrier-in-loops",                 "A compute shader with an unreachable barrier in a loop nest"                                                   },
+{      "unreachable-continue-statement.amber",                 "unreachable-continue-statement",               "A fragment shader with unreachable continue statement"                                                                 },
+{      "unreachable-discard-statement-in-if.amber",    "unreachable-discard-statement-in-if",  "A fragment shader with discard keyword and a return"                                                                   },
+{      "unreachable-discard-statement.amber",                  "unreachable-discard-statement",                "A fragment shader with unreachable discard statement"                                                                  },
+{      "unreachable-loops.amber",                                              "unreachable-loops",                                    "Fragment shader that writes red despite unreachable loops"                                                             },
+{      "unreachable-loops-in-switch.amber",                    "unreachable-loops-in-switch",                  "A fragment shader with unreachable loops in a switch"                                                                  },
+{      "unreachable-return-in-loop.amber",                             "unreachable-return-in-loop",                   "A fragment shader with an unreachable return in a loop"                                                                },
+{      "while-function-always-false.amber",                    "while-function-always-false",                  "A fragment shader with an always false while function"                                                                 },
+{      "while-inside-switch.amber",                                    "while-inside-switch",                                  "A fragment shader that uses a while loop inside a switch"                                                              },
+{      "write-before-break.amber",                                             "write-before-break",                                   "Fragment shader that writes red before loop break"                                                                             },
+{      "write-red-in-loop-nest.amber",                                 "write-red-in-loop-nest",                               "A fragment shader that writes red in a nest of loops"                                                                  },
+{      "wrong-color-in-always-false-if.amber",                 "wrong-color-in-always-false-if",               "A fragment shader with wrong color write in false if"                                                                  },
index e3e14b8..93b5035 100644 (file)
@@ -38,88 +38,7 @@ void createAmberTests (tcu::TestCaseGroup* group)
 {
        tcu::TestContext& testCtx = group->getTestContext();
 
-       static const struct
-       {
-               const std::string       filename;
-               const char*                     name;
-               const char*                     description;
-       }
-       tests[] =
-       {
-               {       "access-new-vector-inside-if-condition.amber",  "access-new-vector-inside-if-condition","A shader that accesses a new vector within an if condition"                                                    },
-               {       "always-false-if-in-do-while.amber",                    "always-false-if-in-do-while",                  "A fragment shader with an always false if."                                                                                    },
-               {       "always-false-if-with-discard-return.amber",    "always-false-if-with-discard-return",  "A fragment shader with discard keyword and a return"                                                                   },
-               {       "barrier-in-loop-with-break.amber",                             "barrier-in-loop-with-break",                   "A compute shader with a barrier in a loop with a break"                                                                },
-               {       "call-if-while-switch.amber",                                   "call-if-while-switch",                                 "A fragment shader with a call, if, while, switch"                                                                              },
-               {       "color-write-in-loop.amber",                                    "color-write-in-loop",                                  "A fragment shader that writes to color in a loop"                                                                              },
-               {       "complex-nested-loops-and-call.amber",                  "complex-nested-loops-and-call",                "A fragment shader with complex nested loops, breaks, etc."                                                             },
-               {       "conditional-return-in-infinite-while.amber",   "conditional-return-in-infinite-while", "A shader with conditional return inside an infinite loop"                                                              },
-               {       "continue-and-merge.amber",                                             "continue-and-merge",                                   "A fragment shader with two nested loops"                                                                                               },
-               {       "control-flow-in-function.amber",                               "control-flow-in-function",                             "A fragment shader with a lot of control flow"                                                                                  },
-               {       "control-flow-switch.amber",                                    "control-flow-switch",                                  "A fragment shader with somewhat complex control flow and a switch"                                             },
-               {       "dead-barriers-in-loops.amber",                                 "dead-barriers-in-loops",                               "A compute shader with dead barriers"                                                                                                   },
-               {       "dead-struct-init.amber",                                               "dead-struct-init",                                             "A fragment shader that uses struct initializers"                                                                               },
-               {       "disc-and-add-in-func-in-loop.amber",                   "disc-and-add-in-func-in-loop",                 "A fragment shader with discard and add in function in loop"                                                    },
-               {       "discard-continue-return.amber",                                "discard-continue-return",                              "A fragment shader with a discard, continue, and return"                                                                },
-               {       "discard-in-array-manipulating-loop.amber",             "discard-in-array-manipulating-loop",   "An array-manipulating fragment shader with a discard"                                                                  },
-               {       "do-while-loop-in-conditionals.amber",                  "do-while-loop-in-conditionals",                "A fragment shader with do-while loop in conditional nest"                                                              },
-               {       "do-while-with-always-true-if.amber",                   "do-while-with-always-true-if",                 "A fragment shader with a do while that always returns"                                                                 },
-               {       "early-return-and-barrier.amber",                               "early-return-and-barrier",                             "A compute shader with an early return and a barrier"                                                                   },
-               {       "for-condition-always-false.amber",                             "for-condition-always-false",                   "A fragment shader that uses a for loop with condition always false"                                    },
-               {       "for-loop-with-return.amber",                                   "for-loop-with-return",                                 "A fragment shader with a for loop that loops only once"                                                                },
-               {       "for-with-ifs-and-return.amber",                                "for-with-ifs-and-return",                              "A fragment shader with two ifs and return/continue inside a for loop"                                  },
-               {       "fragcoord-control-flow.amber",                                 "fragcoord-control-flow",                               "A fragment shader that uses FragCoord and somewhat complex control flow"                               },
-               {       "fragcoord-control-flow-2.amber",                               "fragcoord-control-flow-2",                             "A fragment shader that uses FragCoord and somewhat complex control flow"                               },
-               {       "if-and-switch.amber",                                                  "if-and-switch",                                                "A fragment shader with a switch and some data flow"                                                                    },
-               {       "loop-call-discard.amber",                                              "loop-call-discard",                                    "A fragment shader with nested loops and a function call"                                                               },
-               {       "loop-dead-if-loop.amber",                                              "loop-dead-if-loop",                                    "A fragment shader with a loop, dead if, and a loop"                                                                    },
-               {       "loop-nested-ifs.amber",                                                "loop-nested-ifs",                                              "A fragment shader with a for loop containing nested ifs"                                                               },
-               {       "loops-breaks-returns.amber",                                   "loops-breaks-returns",                                 "A compute shader with loops, breaks, returns"                                                                                  },
-               {       "loops-ifs-continues-call.amber",                               "loops-ifs-continues-call",                             "A fragment shader with nested control flow and a call"                                                                 },
-               {       "mat-array-deep-control-flow.amber",                    "mat-array-deep-control-flow",                  "A fragment shader that uses an array of matrices and has deep control flow"                    },
-               {       "mat-array-distance.amber",                                             "mat-array-distance",                                   "A fragment shader that uses an array of matrices and distance"                                                 },
-               {       "matrices-and-return-in-loop.amber",                    "matrices-and-return-in-loop",                  "A fragment shader with matrices and a return in a loop"                                                                },
-               {       "max-mix-conditional-discard.amber",                    "max-mix-conditional-discard",                  "A fragment shader with an expression used in two discard guards"                                               },
-               {       "mix-floor-add.amber",                                                  "mix-floor-add",                                                "A fragment shader with mix, uintBitsToFloat, and floor"                                                                },
-               {       "nested-for-loops-with-return.amber",                   "nested-for-loops-with-return",                 "A fragment shader with two nested for loops with return"                                                               },
-               {       "nested-ifs-and-return-in-for-loop.amber",              "nested-ifs-and-return-in-for-loop",    "A fragment shader with return in nest of ifs, inside loop"                                                             },
-               {       "nested-loops-switch.amber",                                    "nested-loops-switch",                                  "A fragment shader with nested loops and a switch"                                                                              },
-               {       "pow-vec4.amber",                                                               "pow-vec4",                                                             "A fragment shader that uses pow"                                                                                                               },
-               {       "return-before-writing-wrong-color.amber",              "return-before-writing-wrong-color",    "A fragment shader with return before writing wrong color"                                                              },
-               {       "return-float-from-while-loop.amber",                   "return-float-from-while-loop",                 "A fragment shader with unreachable while loop"                                                                                 },
-               {       "return-in-loop-in-function.amber",                             "return-in-loop-in-function",                   "A fragment shader with early return from loop in function"                                                             },
-               {       "returned-boolean-in-vector.amber",                             "returned-boolean-in-vector",                   "A fragment shader with returned boolean in vector"                                                                             },
-               {       "similar-nested-ifs.amber",                                             "similar-nested-ifs",                                   "A fragment shader with similar nested ifs and loops"                                                                   },
-               {       "struct-used-as-temporary.amber",                               "struct-used-as-temporary",                             "A fragment shader that uses a temporary struct variable"                                                               },
-               {       "switch-if-discard.amber",                                              "switch-if-discard",                                    "A fragment shader with a switch, if, and discard"                                                                              },
-               {       "switch-with-empty-if-false.amber",                             "switch-with-empty-if-false",                   "A fragment shader with always false if in switch statement"                                                    },
-               {       "swizzle-struct-init-min.amber",                                "swizzle-struct-init-min",                              "A fragment shader that uses vector swizzles, struct initializers, and min"                             },
-               {       "two-for-loops-with-barrier-function.amber",    "two-for-loops-with-barrier-function",  "A compute shader with two barrier functions"                                                                                   },
-               {       "two-loops-matrix.amber",                                               "two-loops-matrix",                                             "A fragment shader with two loops and some matrices"                                                                    },
-               {       "two-loops-set-struct.amber",                                   "two-loops-set-struct",                                 "A fragment shader with two loops that write to a struct"                                                               },
-               {       "two-loops-with-break.amber",                                   "two-loops-with-break",                                 "A fragment shader with two loops with breaks"                                                                                  },
-               {       "two-nested-do-whiles.amber",                                   "two-nested-do-whiles",                                 "A fragment shader with nested do while"                                                                                                },
-               {       "two-nested-for-loops-with-returns.amber",              "two-nested-for-loops-with-returns",    "A compute shader with two nested for loops"                                                                                    },
-               {       "two-nested-infinite-loops-discard.amber",              "two-nested-infinite-loops-discard",    "A fragment shader with an always false if function"                                                                    },
-               {       "undefined-integer-in-function.amber",                  "undefined-integer-in-function",                "A fragment shader with nested do while and undefined int"                                                              },
-               {       "uninitialized-var-decrement-and-add.amber",    "uninitialized-var-decrement-and-add",  "A fragment shader that uses an uninitialized variable"                                                                 },
-               {       "undefined-assign-in-infinite-loop.amber",              "undefined-assign-in-infinite-loop",    "A fragment shader with uninitialized read in infinite loop"                                                    },
-               {       "unreachable-barrier-in-loops.amber",                   "unreachable-barrier-in-loops",                 "A compute shader with an unreachable barrier in a loop nest"                                                   },
-               {       "unreachable-continue-statement.amber",                 "unreachable-continue-statement",               "A fragment shader with unreachable continue statement"                                                                 },
-               {       "unreachable-discard-statement-in-if.amber",    "unreachable-discard-statement-in-if",  "A fragment shader with discard keyword and a return"                                                                   },
-               {       "unreachable-discard-statement.amber",                  "unreachable-discard-statement",                "A fragment shader with unreachable discard statement"                                                                  },
-               {       "unreachable-loops.amber",                                              "unreachable-loops",                                    "Fragment shader that writes red despite unreachable loops"                                                             },
-               {       "unreachable-loops-in-switch.amber",                    "unreachable-loops-in-switch",                  "A fragment shader with unreachable loops in a switch"                                                                  },
-               {       "unreachable-return-in-loop.amber",                             "unreachable-return-in-loop",                   "A fragment shader with an unreachable return in a loop"                                                                },
-               {       "while-function-always-false.amber",                    "while-function-always-false",                  "A fragment shader with an always false while function"                                                                 },
-               {       "while-inside-switch.amber",                                    "while-inside-switch",                                  "A fragment shader that uses a while loop inside a switch"                                                              },
-               {       "write-before-break.amber",                                             "write-before-break",                                   "Fragment shader that writes red before loop break"                                                                             },
-               {       "write-red-in-loop-nest.amber",                                 "write-red-in-loop-nest",                               "A fragment shader that writes red in a nest of loops"                                                                  },
-               {       "wrong-color-in-always-false-if.amber",                 "wrong-color-in-always-false-if",               "A fragment shader with wrong color write in false if"                                                                  },
-       };
-
-       for (size_t i = 0; i < sizeof tests / sizeof tests[0]; i++)
-               group->addChild(createAmberTestCase(testCtx, tests[i].name, tests[i].description, "graphicsfuzz", tests[i].filename));
+       createAmberTestsFromIndexFile(testCtx, group, "index.txt", "graphicsfuzz");
 }
 
 } // anonymous
index 8a04c9c..8027d29 100644 (file)
@@ -118,6 +118,11 @@ AmberTestCase* createAmberTestCase (tcu::TestContext&                              testCtx,
                                                                        const std::string&                              filename,
                                                                        const std::vector<std::string>  requirements = std::vector<std::string>());
 
+void createAmberTestsFromIndexFile (tcu::TestContext&  testCtx,
+                                                                       tcu::TestCaseGroup*     group,
+                                                                       const std::string       filename,
+                                                                       const char*                     category);
+
 } // cts_amber
 } // vkt
 
index d585441..01553da 100644 (file)
@@ -27,6 +27,180 @@ namespace vkt
 namespace cts_amber
 {
 
+class AmberIndexFileParser
+{
+       char*                           m_buf;
+       size_t                          m_idx;
+       size_t                          m_len;
+       static const int        m_fieldLen = 256;
+       char                            m_scratch[m_fieldLen];
+       char                            m_filenameField[m_fieldLen];
+       char                            m_testnameField[m_fieldLen];
+       char                            m_descField[m_fieldLen];
+
+       bool isWhitespace (char c)
+       {
+               if (c == ' '  ||
+                       c == '\t' ||
+                       c == '\r' ||
+                       c == '\n')
+               {
+                       return true;
+               }
+               return false;
+       }
+
+       void skipWhitespace (void)
+       {
+               while (m_idx < m_len && isWhitespace(m_buf[m_idx]))
+                       m_idx++;
+       }
+
+       void accept (char c)
+       {
+               if (m_buf[m_idx] == c)
+                       m_idx++;
+       }
+
+       void expect (char c)
+       {
+               if (m_buf[m_idx] != c || m_idx >= m_len)
+                       TCU_THROW(ResourceError, "Error parsing amber index file");
+
+               m_idx++;
+       }
+
+       void captureString (char* field)
+       {
+               int i = 0;
+
+               while (m_idx < m_len && i < m_fieldLen && m_buf[m_idx] != '"')
+               {
+                       field[i] = m_buf[m_idx];
+                       i++;
+                       m_idx++;
+               }
+
+               field[i] = 0;
+               m_idx++;
+       }
+
+       char* loadFile (const char* filename, size_t& len)
+       {
+               FILE* f = fopen(filename, "rb");
+
+               if (f == 0)
+               {
+                       std::string error("Unable to open index file ");
+                       error.append(filename);
+                       TCU_THROW(ResourceError, error.c_str());
+               }
+
+               fseek(f, 0, SEEK_END);
+               len = ftell(f);
+               fseek(f, 0, SEEK_SET);
+               char* buf = new char[len + 1];
+
+               if (fread(buf, 1, len, f) != len)
+               {
+                       delete[] buf;
+                       fclose(f);
+                       std::string error("File i/o error reading index file ");
+                       error.append(filename);
+                       TCU_THROW(ResourceError, error.c_str());
+               }
+
+               buf[len] = 0;
+               fclose(f);
+               return buf;
+       }
+
+public:
+       AmberIndexFileParser (const char* filename, const char* category)
+       {
+               std::string     indexFilename("vulkan/amber/");
+               indexFilename.append(category);
+               indexFilename.append("/");
+               indexFilename.append(filename);
+
+               m_buf = loadFile(indexFilename.c_str(), m_len);
+               m_idx = 0;
+       }
+
+       ~AmberIndexFileParser (void)
+       {
+               delete[] m_buf;
+       }
+
+       AmberTestCase* parse (const char* category, tcu::TestContext& testCtx)
+       {
+               // Format:
+               // {"filename","test name","description"[,requirement[,requirement[,requirement..]]]}[,]
+               // Things inside [] are optional. Whitespace is allowed everywhere.
+               //
+               // For example, test without requirements might be:
+               // {"testname.amber","test name","test description"},
+
+               if (m_idx < m_len)
+               {
+                       skipWhitespace();
+                       expect('{');
+                       skipWhitespace();
+                       expect('"');
+                       captureString(m_filenameField);
+                       skipWhitespace();
+                       expect(',');
+                       skipWhitespace();
+                       expect('"');
+                       captureString(m_testnameField);
+                       skipWhitespace();
+                       expect(',');
+                       skipWhitespace();
+                       expect('"');
+                       captureString(m_descField);
+                       skipWhitespace();
+
+                       std::string testFilename("vulkan/amber/");
+                       testFilename.append(category);
+                       testFilename.append("/");
+                       testFilename.append(m_filenameField);
+                       AmberTestCase *testCase = new AmberTestCase(testCtx, m_testnameField, m_descField, testFilename);
+
+                       while (m_idx < m_len && m_buf[m_idx] == ',')
+                       {
+                               accept(',');
+                               skipWhitespace();
+                               expect('"');
+                               captureString(m_scratch);
+                               skipWhitespace();
+                               testCase->addRequirement(m_scratch);
+                       }
+
+                       expect('}');
+                       skipWhitespace();
+                       accept(',');
+                       skipWhitespace();
+                       return testCase;
+               }
+               return 0;
+       }
+};
+
+void createAmberTestsFromIndexFile (tcu::TestContext& testCtx, tcu::TestCaseGroup* group, const std::string filename, const char* category)
+{
+       AmberTestCase*                  testCase = 0;
+       AmberIndexFileParser    parser(filename.c_str(), category);
+
+       do
+       {
+               testCase = parser.parse(category, testCtx);
+               if (testCase)
+               {
+                       group->addChild(testCase);
+               }
+       } while (testCase);
+}
+
 AmberTestCase* createAmberTestCase (tcu::TestContext&                          testCtx,
                                                                        const char*                                             name,
                                                                        const char*                                             description,