Support enabling/disabling toggles in unittests

Refactors support for toggle control flags, --enable-toggles= &
--disable-toggles, from the end2end tests into a utility class to make
them available elsewhere. The unittests now uses this utility class to
parse toggle control flags.

For some toggles, like 'use_tint_generator', the unittests are known to
be broken with them turned on. They will be fixed in subsequent CLs.

BUG=dawn:756

Change-Id: Ic283e8a82bedcbf255258cca3e62f58c79d3857b
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/47740
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Auto-Submit: Ryan Harrison <rharrison@chromium.org>
diff --git a/src/tests/BUILD.gn b/src/tests/BUILD.gn
index b025a63..faf4b22 100644
--- a/src/tests/BUILD.gn
+++ b/src/tests/BUILD.gn
@@ -151,6 +151,8 @@
     "${dawn_root}/src/dawn_wire/server/ServerMemoryTransferService_mock.cpp",
     "${dawn_root}/src/dawn_wire/server/ServerMemoryTransferService_mock.h",
     "MockCallback.h",
+    "ToggleParser.cpp",
+    "ToggleParser.h",
     "unittests/BitSetIteratorTests.cpp",
     "unittests/BuddyAllocatorTests.cpp",
     "unittests/BuddyMemoryAllocatorTests.cpp",
@@ -280,6 +282,8 @@
   sources = [
     "DawnTest.h",
     "MockCallback.h",
+    "ToggleParser.cpp",
+    "ToggleParser.h",
     "end2end/BasicTests.cpp",
     "end2end/BindGroupTests.cpp",
     "end2end/BufferTests.cpp",
@@ -404,7 +408,10 @@
     "${dawn_root}/src/utils:dawn_utils",
   ]
 
-  sources = [ "DawnTest.h" ]
+  sources = [
+    "DawnTest.h",
+    "ToggleParser.h",
+  ]
 
   if (dawn_enable_vulkan) {
     deps += [ "${dawn_root}/third_party/khronos:vulkan_headers" ]
@@ -505,6 +512,8 @@
     "DawnTest.cpp",
     "DawnTest.h",
     "ParamGenerator.h",
+    "ToggleParser.cpp",
+    "ToggleParser.h",
     "perf_tests/BufferUploadPerf.cpp",
     "perf_tests/DawnPerfTest.cpp",
     "perf_tests/DawnPerfTest.h",
diff --git a/src/tests/DawnTest.cpp b/src/tests/DawnTest.cpp
index 7bf2825..26bc121 100644
--- a/src/tests/DawnTest.cpp
+++ b/src/tests/DawnTest.cpp
@@ -265,25 +265,11 @@
             continue;
         }
 
-        constexpr const char kEnableTogglesSwitch[] = "--enable-toggles=";
-        argLen = sizeof(kEnableTogglesSwitch) - 1;
-        if (strncmp(argv[i], kEnableTogglesSwitch, argLen) == 0) {
-            std::string toggle;
-            std::stringstream toggles(argv[i] + argLen);
-            while (getline(toggles, toggle, ',')) {
-                mEnabledToggles.push_back(toggle);
-            }
+        if (mToggleParser.ParseEnabledToggles(argv[i])) {
             continue;
         }
 
-        constexpr const char kDisableTogglesSwitch[] = "--disable-toggles=";
-        argLen = sizeof(kDisableTogglesSwitch) - 1;
-        if (strncmp(argv[i], kDisableTogglesSwitch, argLen) == 0) {
-            std::string toggle;
-            std::stringstream toggles(argv[i] + argLen);
-            while (getline(toggles, toggle, ',')) {
-                mDisabledToggles.push_back(toggle);
-            }
+        if (mToggleParser.ParseDisabledToggles(argv[i])) {
             continue;
         }
 
@@ -667,11 +653,11 @@
 }
 
 const std::vector<std::string>& DawnTestEnvironment::GetEnabledToggles() const {
-    return mEnabledToggles;
+    return mToggleParser.GetEnabledToggles();
 }
 
 const std::vector<std::string>& DawnTestEnvironment::GetDisabledToggles() const {
-    return mDisabledToggles;
+    return mToggleParser.GetDisabledToggles();
 }
 
 // Implementation of DawnTest
diff --git a/src/tests/DawnTest.h b/src/tests/DawnTest.h
index de1f017..f10b802 100644
--- a/src/tests/DawnTest.h
+++ b/src/tests/DawnTest.h
@@ -19,6 +19,7 @@
 #include "dawn/dawn_proc_table.h"
 #include "dawn/webgpu_cpp.h"
 #include "dawn_native/DawnNative.h"
+#include "tests/ToggleParser.h"
 
 #include <dawn_platform/DawnPlatform.h>
 #include <gtest/gtest.h>
@@ -232,8 +233,8 @@
     wgpu::BackendType mBackendTypeFilter;
     std::string mWireTraceDir;
 
-    std::vector<std::string> mEnabledToggles;
-    std::vector<std::string> mDisabledToggles;
+    ToggleParser mToggleParser;
+
     std::vector<dawn_native::DeviceType> mDevicePreferences;
     std::vector<TestAdapterProperties> mAdapterProperties;
 
diff --git a/src/tests/ToggleParser.cpp b/src/tests/ToggleParser.cpp
new file mode 100644
index 0000000..3589aea
--- /dev/null
+++ b/src/tests/ToggleParser.cpp
@@ -0,0 +1,57 @@
+// Copyright 2021 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "tests/ToggleParser.h"
+
+#include <cstring>
+#include <sstream>
+
+ToggleParser::ToggleParser() = default;
+ToggleParser::~ToggleParser() = default;
+
+bool ToggleParser::ParseEnabledToggles(char* arg) {
+    constexpr const char kEnableTogglesSwitch[] = "--enable-toggles=";
+    size_t argLen = sizeof(kEnableTogglesSwitch) - 1;
+    if (strncmp(arg, kEnableTogglesSwitch, argLen) == 0) {
+        std::string toggle;
+        std::stringstream toggles(arg + argLen);
+        while (getline(toggles, toggle, ',')) {
+            mEnabledToggles.push_back(toggle);
+        }
+        return true;
+    }
+    return false;
+}
+
+bool ToggleParser::ParseDisabledToggles(char* arg) {
+    constexpr const char kDisableTogglesSwitch[] = "--disable-toggles=";
+    size_t argLDis = sizeof(kDisableTogglesSwitch) - 1;
+    if (strncmp(arg, kDisableTogglesSwitch, argLDis) == 0) {
+        std::string toggle;
+        std::stringstream toggles(arg + argLDis);
+        while (getline(toggles, toggle, ',')) {
+            mDisabledToggles.push_back(toggle);
+        }
+        return true;
+    }
+    return false;
+}
+
+const std::vector<std::string>& ToggleParser::GetEnabledToggles() const {
+    return mEnabledToggles;
+}
+
+const std::vector<std::string>& ToggleParser::GetDisabledToggles() const {
+    return mDisabledToggles;
+}
diff --git a/src/tests/ToggleParser.h b/src/tests/ToggleParser.h
new file mode 100644
index 0000000..d5ff90b
--- /dev/null
+++ b/src/tests/ToggleParser.h
@@ -0,0 +1,37 @@
+// Copyright 2021 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef TESTS_TOGGLEPARSER_H_
+#define TESTS_TOGGLEPARSER_H_
+
+#include <string>
+#include <vector>
+
+class ToggleParser {
+  public:
+    ToggleParser();
+    ~ToggleParser();
+
+    bool ParseEnabledToggles(char* arg);
+    bool ParseDisabledToggles(char* arg);
+
+    const std::vector<std::string>& GetEnabledToggles() const;
+    const std::vector<std::string>& GetDisabledToggles() const;
+
+  private:
+    std::vector<std::string> mEnabledToggles;
+    std::vector<std::string> mDisabledToggles;
+};
+
+#endif  // TESTS_TOGGLEPARSER_H_
diff --git a/src/tests/unittests/validation/BindGroupValidationTests.cpp b/src/tests/unittests/validation/BindGroupValidationTests.cpp
index f2e4be1..7a74399 100644
--- a/src/tests/unittests/validation/BindGroupValidationTests.cpp
+++ b/src/tests/unittests/validation/BindGroupValidationTests.cpp
@@ -1772,6 +1772,8 @@
 // Test that it is invalid to pass a readonly storage buffer in the pipeline layout when the shader
 // uses the binding as a writable storage buffer.
 TEST_F(BindGroupLayoutCompatibilityTest, ROStorageInBGLWithRWStorageInShader) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     // Set up the bind group layout.
     wgpu::BindGroupLayout bgl0 = utils::MakeBindGroupLayout(
         device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment,
@@ -1786,6 +1788,8 @@
 }
 
 TEST_F(BindGroupLayoutCompatibilityTest, TextureViewDimension) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     constexpr char kTexture2DShaderFS[] = R"(
         [[group(0), binding(0)]] var myTexture : texture_2d<f32>;
         [[stage(fragment)]] fn main() {
@@ -1929,6 +1933,8 @@
 // Test that it is invalid to set a pipeline layout that doesn't have all necessary bindings
 // required by the pipeline.
 TEST_F(BindingsValidationTest, PipelineLayoutWithLessBindingsThanPipeline) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     // Set up bind group layout.
     wgpu::BindGroupLayout bgl0 = utils::MakeBindGroupLayout(
         device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment,
diff --git a/src/tests/unittests/validation/GetBindGroupLayoutValidationTests.cpp b/src/tests/unittests/validation/GetBindGroupLayoutValidationTests.cpp
index 8a6f358..0c89ee1 100644
--- a/src/tests/unittests/validation/GetBindGroupLayoutValidationTests.cpp
+++ b/src/tests/unittests/validation/GetBindGroupLayoutValidationTests.cpp
@@ -38,6 +38,8 @@
 // Test that GetBindGroupLayout returns the same object for the same index
 // and for matching layouts.
 TEST_F(GetBindGroupLayoutTests, SameObject) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     // This test works assuming Dawn Native's object deduplication.
     // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
     // Native.
@@ -91,6 +93,8 @@
 // - shader stage visibility is the stage that adds the binding.
 // - dynamic offsets is false
 TEST_F(GetBindGroupLayoutTests, DefaultShaderStageAndDynamicOffsets) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     // This test works assuming Dawn Native's object deduplication.
     // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
     // Native.
@@ -134,6 +138,8 @@
 
 // Test GetBindGroupLayout works with a compute pipeline
 TEST_F(GetBindGroupLayoutTests, ComputePipeline) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     // This test works assuming Dawn Native's object deduplication.
     // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
     // Native.
@@ -171,6 +177,8 @@
 
 // Test that the binding type matches the shader.
 TEST_F(GetBindGroupLayoutTests, BindingType) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     // This test works assuming Dawn Native's object deduplication.
     // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
     // Native.
@@ -262,6 +270,8 @@
 
 // Test that texture view dimension matches the shader.
 TEST_F(GetBindGroupLayoutTests, ViewDimension) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     // This test works assuming Dawn Native's object deduplication.
     // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
     // Native.
@@ -339,6 +349,8 @@
 
 // Test that texture component type matches the shader.
 TEST_F(GetBindGroupLayoutTests, TextureComponentType) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     // This test works assuming Dawn Native's object deduplication.
     // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
     // Native.
@@ -385,6 +397,8 @@
 
 // Test that binding= indices match.
 TEST_F(GetBindGroupLayoutTests, BindingIndices) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     // This test works assuming Dawn Native's object deduplication.
     // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
     // Native.
@@ -471,6 +485,8 @@
 
 // Test that minBufferSize is set on the BGL and that the max of the min buffer sizes is used.
 TEST_F(GetBindGroupLayoutTests, MinBufferSize) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     // This test works assuming Dawn Native's object deduplication.
     // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
     // Native.
@@ -557,6 +573,8 @@
 
 // Test that the visibility is correctly aggregated if two stages have the exact same binding.
 TEST_F(GetBindGroupLayoutTests, StageAggregation) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     // This test works assuming Dawn Native's object deduplication.
     // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
     // Native.
@@ -625,6 +643,8 @@
 
 // Test it is invalid to have conflicting binding types in the shaders.
 TEST_F(GetBindGroupLayoutTests, ConflictingBindingType) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
         [[block]] struct S {
             pos : vec4<f32>;
@@ -653,6 +673,8 @@
 
 // Test it is invalid to have conflicting binding texture multisampling in the shaders.
 TEST_F(GetBindGroupLayoutTests, ConflictingBindingTextureMultisampling) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
         [[group(0), binding(0)]] var myTexture : texture_2d<f32>;
 
@@ -675,6 +697,8 @@
 
 // Test it is invalid to have conflicting binding texture dimension in the shaders.
 TEST_F(GetBindGroupLayoutTests, ConflictingBindingViewDimension) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
         [[group(0), binding(0)]] var myTexture : texture_2d<f32>;
 
@@ -697,6 +721,8 @@
 
 // Test it is invalid to have conflicting binding texture component type in the shaders.
 TEST_F(GetBindGroupLayoutTests, ConflictingBindingTextureComponentType) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
         [[group(0), binding(0)]] var myTexture : texture_2d<f32>;
 
@@ -732,6 +758,8 @@
 
 // Test that unused indices return the empty bind group layout.
 TEST_F(GetBindGroupLayoutTests, UnusedIndex) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     // This test works assuming Dawn Native's object deduplication.
     // Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
     // Native.
diff --git a/src/tests/unittests/validation/MinimumBufferSizeValidationTests.cpp b/src/tests/unittests/validation/MinimumBufferSizeValidationTests.cpp
index 5db20d1..412c1fe 100644
--- a/src/tests/unittests/validation/MinimumBufferSizeValidationTests.cpp
+++ b/src/tests/unittests/validation/MinimumBufferSizeValidationTests.cpp
@@ -315,6 +315,8 @@
 
 // Fail if layout given has non-zero minimum sizes smaller than shader requirements
 TEST_F(MinBufferSizePipelineCreationTests, LayoutSizesTooSmall) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     std::vector<BindingDescriptor> bindings = {{0, 0, "a : f32; b : f32;", 8},
                                                {0, 1, "c : f32;", 4}};
 
@@ -336,6 +338,8 @@
 
 // Fail if layout given has non-zero minimum sizes smaller than shader requirements
 TEST_F(MinBufferSizePipelineCreationTests, LayoutSizesTooSmallMultipleGroups) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     std::vector<BindingDescriptor> bg0Bindings = {{0, 0, "a : f32; b : f32;", 8},
                                                   {0, 1, "c : f32;", 4}};
     std::vector<BindingDescriptor> bg1Bindings = {{1, 0, "d : f32; e : f32; f : f32;", 12},
@@ -398,6 +402,8 @@
 
 // Fail if binding sizes are too small at draw time
 TEST_F(MinBufferSizeDrawTimeValidationTests, ZeroMinSizeAndTooSmallBinding) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     std::vector<BindingDescriptor> bindings = {{0, 0, "a : f32; b : f32;", 8},
                                                {0, 1, "c : f32;", 4}};
 
@@ -419,6 +425,8 @@
 
 // Draw time validation works for non-contiguous bindings
 TEST_F(MinBufferSizeDrawTimeValidationTests, UnorderedBindings) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     std::vector<BindingDescriptor> bindings = {{0, 2, "a : f32; b : f32;", 8},
                                                {0, 0, "c : f32;", 4},
                                                {0, 4, "d : f32; e : f32; f : f32;", 12}};
@@ -441,6 +449,8 @@
 
 // Draw time validation works for multiple bind groups
 TEST_F(MinBufferSizeDrawTimeValidationTests, MultipleGroups) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     std::vector<BindingDescriptor> bg0Bindings = {{0, 0, "a : f32; b : f32;", 8},
                                                   {0, 1, "c : f32;", 4}};
     std::vector<BindingDescriptor> bg1Bindings = {{1, 0, "d : f32; e : f32; f : f32;", 12},
@@ -512,6 +522,8 @@
 
 // Test the minimum size computations for various WGSL types.
 TEST_F(MinBufferSizeDefaultLayoutTests, DefaultLayoutVariousWGSLTypes) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     CheckShaderBindingSizeReflection(
         {{{0, 0, "a : f32;", 4}, {0, 1, "b : array<f32>;", 4}, {0, 2, "c : mat2x2<f32>;", 16}}});
     CheckShaderBindingSizeReflection({{{0, 3, "d : u32; e : array<f32>;", 8},
@@ -521,6 +533,8 @@
 
 // Test the minimum size computations for various buffer binding types.
 TEST_F(MinBufferSizeDefaultLayoutTests, DefaultLayoutVariousBindingTypes) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     CheckShaderBindingSizeReflection(
         {{{0, 0, "a : f32;", 4, wgpu::BufferBindingType::Uniform},
           {0, 1, "a : f32; b : f32;", 8, wgpu::BufferBindingType::Storage},
@@ -529,6 +543,8 @@
 
 // Test the minimum size computations works with multiple bind groups.
 TEST_F(MinBufferSizeDefaultLayoutTests, MultipleBindGroups) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     CheckShaderBindingSizeReflection(
         {{{0, 0, "a : f32;", 4, wgpu::BufferBindingType::Uniform}},
          {{1, 0, "a : f32; b : f32;", 8, wgpu::BufferBindingType::Storage}},
@@ -537,6 +553,8 @@
 
 // Test the minimum size computations with manual size/align/stride decorations.
 TEST_F(MinBufferSizeDefaultLayoutTests, NonDefaultLayout) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     CheckShaderBindingSizeReflection({{{0, 0, "[[size(256)]] a : u32; b : u32;", 260},
                                        {0, 1, "c : u32; [[align(16)]] d : u32;", 20},
                                        {0, 2, "d : [[stride(40)]] array<u32, 3>;", 120},
@@ -545,6 +563,8 @@
 
 // Minimum size should be the max requirement of both vertex and fragment stages.
 TEST_F(MinBufferSizeDefaultLayoutTests, RenderPassConsidersBothStages) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     std::string vertexShader = CreateVertexShaderWithBindings(
         {{0, 0, "a : f32;", 4, wgpu::BufferBindingType::Uniform},
          {0, 1, "b : vec4<f32>;", 16, wgpu::BufferBindingType::Uniform}});
diff --git a/src/tests/unittests/validation/RenderPipelineValidationTests.cpp b/src/tests/unittests/validation/RenderPipelineValidationTests.cpp
index 54e500c..3114d20 100644
--- a/src/tests/unittests/validation/RenderPipelineValidationTests.cpp
+++ b/src/tests/unittests/validation/RenderPipelineValidationTests.cpp
@@ -393,6 +393,8 @@
 
 // Tests that the texture component type in shader must match the bind group layout.
 TEST_F(RenderPipelineValidationTest, TextureComponentTypeCompatibility) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     constexpr uint32_t kNumTextureComponentType = 3u;
     std::array<const char*, kNumTextureComponentType> kScalarTypes = {{"f32", "i32", "u32"}};
     std::array<wgpu::TextureSampleType, kNumTextureComponentType> kTextureComponentTypes = {{
@@ -430,6 +432,8 @@
 
 // Tests that the texture view dimension in shader must match the bind group layout.
 TEST_F(RenderPipelineValidationTest, TextureViewDimensionCompatibility) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     constexpr uint32_t kNumTextureViewDimensions = 6u;
     std::array<const char*, kNumTextureViewDimensions> kTextureKeywords = {{
         "texture_1d",
diff --git a/src/tests/unittests/validation/ShaderModuleValidationTests.cpp b/src/tests/unittests/validation/ShaderModuleValidationTests.cpp
index f3a7a05..b7abc17 100644
--- a/src/tests/unittests/validation/ShaderModuleValidationTests.cpp
+++ b/src/tests/unittests/validation/ShaderModuleValidationTests.cpp
@@ -118,6 +118,8 @@
 // Test that it is not allowed to declare a multisampled-array interface texture.
 // TODO(enga): Also test multisampled cube, cube array, and 3D. These have no GLSL keywords.
 TEST_F(ShaderModuleValidationTest, MultisampledArrayTexture) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     // SPIR-V ASM produced by glslang for the following fragment shader:
     //
     //  #version 450
diff --git a/src/tests/unittests/validation/StorageTextureValidationTests.cpp b/src/tests/unittests/validation/StorageTextureValidationTests.cpp
index aa96915..b7a5973 100644
--- a/src/tests/unittests/validation/StorageTextureValidationTests.cpp
+++ b/src/tests/unittests/validation/StorageTextureValidationTests.cpp
@@ -226,6 +226,8 @@
 
 // Validate read-write storage textures are not currently supported.
 TEST_F(StorageTextureValidationTests, ReadWriteStorageTexture) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     // Read-write storage textures cannot be declared in a vertex shader by default.
     {
         ASSERT_DEVICE_ERROR(utils::CreateShaderModule(device, R"(
@@ -286,6 +288,8 @@
 // Validate it is an error to declare a read-only or write-only storage texture in shaders with any
 // format that doesn't support TextureUsage::Storage texture usages.
 TEST_F(StorageTextureValidationTests, StorageTextureFormatInShaders) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     // Not include RGBA8UnormSrgb, BGRA8Unorm, BGRA8UnormSrgb because they are not related to any
     // SPIR-V Image Formats.
     constexpr std::array<wgpu::TextureFormat, 32> kWGPUTextureFormatSupportedAsSPIRVImageFormats = {
@@ -322,6 +326,8 @@
 // Verify that declaring a storage texture format that is not supported in WebGPU causes validation
 // error.
 TEST_F(StorageTextureValidationTests, UnsupportedWGSLStorageTextureFormat) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     constexpr std::array<wgpu::TextureFormat, 16> kUnsupportedTextureFormats = {
         wgpu::TextureFormat::R8Unorm,      wgpu::TextureFormat::R8Snorm,
         wgpu::TextureFormat::R8Uint,       wgpu::TextureFormat::R8Sint,
@@ -378,6 +384,8 @@
 // render and compute pipeline, the binding type in the bind group layout must match the
 // declaration in the shader.
 TEST_F(StorageTextureValidationTests, BindGroupLayoutEntryTypeMatchesShaderDeclaration) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     constexpr wgpu::TextureFormat kStorageTextureFormat = wgpu::TextureFormat::R32Float;
 
     std::initializer_list<utils::BindingLayoutEntryInitializationHelper> kSupportedBindingTypes = {
@@ -461,6 +469,8 @@
 
 // Verify the storage texture format in the bind group layout must match the declaration in shader.
 TEST_F(StorageTextureValidationTests, BindGroupLayoutStorageTextureFormatMatchesShaderDeclaration) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     for (wgpu::StorageTextureAccess bindingType : kSupportedStorageTextureAccess) {
         for (wgpu::TextureFormat storageTextureFormatInShader : utils::kAllTextureFormats) {
             if (!utils::TextureFormatSupportsStorageTexture(storageTextureFormatInShader)) {
@@ -517,6 +527,8 @@
 // Verify the dimension of the bind group layout with storage textures must match the one declared
 // in shader.
 TEST_F(StorageTextureValidationTests, BindGroupLayoutViewDimensionMatchesShaderDeclaration) {
+    DAWN_SKIP_TEST_IF(HasToggleEnabled("use_tint_generator"));
+
     constexpr std::array<wgpu::TextureViewDimension, 4> kSupportedDimensions = {
         wgpu::TextureViewDimension::e1D, wgpu::TextureViewDimension::e2D,
         wgpu::TextureViewDimension::e2DArray, wgpu::TextureViewDimension::e3D};
diff --git a/src/tests/unittests/validation/ValidationTest.cpp b/src/tests/unittests/validation/ValidationTest.cpp
index 73f6c11..9a215ee 100644
--- a/src/tests/unittests/validation/ValidationTest.cpp
+++ b/src/tests/unittests/validation/ValidationTest.cpp
@@ -19,6 +19,7 @@
 #include "dawn/dawn_proc.h"
 #include "dawn/webgpu.h"
 #include "dawn_native/NullBackend.h"
+#include "tests/ToggleParser.h"
 #include "utils/WireHelper.h"
 
 #include <algorithm>
@@ -27,10 +28,13 @@
 
     bool gUseWire = false;
     std::string gWireTraceDir = "";
+    std::unique_ptr<ToggleParser> gToggleParser = nullptr;
 
 }  // namespace
 
 void InitDawnValidationTestEnvironment(int argc, char** argv) {
+    gToggleParser = std::make_unique<ToggleParser>();
+
     for (int i = 1; i < argc; ++i) {
         if (strcmp("-w", argv[i]) == 0 || strcmp("--use-wire", argv[i]) == 0) {
             gUseWire = true;
@@ -44,6 +48,26 @@
             continue;
         }
 
+        if (gToggleParser->ParseEnabledToggles(argv[i])) {
+            continue;
+        }
+
+        if (gToggleParser->ParseDisabledToggles(argv[i])) {
+            continue;
+        }
+
+        if (strcmp("-h", argv[i]) == 0 || strcmp("--help", argv[i]) == 0) {
+            dawn::InfoLog()
+                << "\n\nUsage: " << argv[0]
+                << " [GTEST_FLAGS...] [-w]\n"
+                   "    [--enable-toggles=toggles] [--disable-toggles=toggles]\n"
+                   "  -w, --use-wire: Run the tests through the wire (defaults to no wire)\n"
+                   "  --enable-toggles: Comma-delimited list of Dawn toggles to enable.\n"
+                   "    ex.) skip_validation,use_tint_generator,disable_robustness,turn_off_vsync\n"
+                   "  --disable-toggles: Comma-delimited list of Dawn toggles to disable\n";
+            continue;
+        }
+
         // Skip over args that look like they're for Googletest.
         constexpr const char kGtestArgPrefix[] = "--gtest_";
         if (strncmp(kGtestArgPrefix, argv[i], sizeof(kGtestArgPrefix) - 1) == 0) {
@@ -159,6 +183,15 @@
     // Disabled disallowing unsafe APIs so we can test them.
     dawn_native::DeviceDescriptor deviceDescriptor;
     deviceDescriptor.forceDisabledToggles.push_back("disallow_unsafe_apis");
+
+    for (const std::string& toggle : gToggleParser->GetEnabledToggles()) {
+        deviceDescriptor.forceEnabledToggles.push_back(toggle.c_str());
+    }
+
+    for (const std::string& toggle : gToggleParser->GetDisabledToggles()) {
+        deviceDescriptor.forceDisabledToggles.push_back(toggle.c_str());
+    }
+
     return adapter.CreateDevice(&deviceDescriptor);
 }
 
diff --git a/src/tests/unittests/validation/ValidationTest.h b/src/tests/unittests/validation/ValidationTest.h
index 47a9419..3f33912 100644
--- a/src/tests/unittests/validation/ValidationTest.h
+++ b/src/tests/unittests/validation/ValidationTest.h
@@ -18,7 +18,8 @@
 #include "common/Log.h"
 #include "dawn/webgpu_cpp.h"
 #include "dawn_native/DawnNative.h"
-#include "gtest/gtest.h"
+
+#include <gtest/gtest.h>
 
 #define ASSERT_DEVICE_ERROR(statement)                          \
     FlushWire();                                                \