Support optional query on adapter power preference

This patch adds DawnAdapterPropertiesPowerPreferenceDescriptor for
querying adapter power preference which is useful to distinguish
different logical adapters created on same physical device but with
different power preferences.

Bug: dawn:1516
Test: dawn_unittests
Change-Id: I12ed6e370f8b57c860520154565765f0ee894831
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/102780
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/dawn.json b/dawn.json
index d8f8a34..dfe2afe 100644
--- a/dawn.json
+++ b/dawn.json
@@ -2526,7 +2526,8 @@
             {"value": 1002, "name": "dawn toggles device descriptor", "tags": ["dawn", "native"]},
             {"value": 1003, "name": "dawn encoder internal usage descriptor", "tags": ["dawn"]},
             {"value": 1004, "name": "dawn instance descriptor", "tags": ["dawn", "native"]},
-            {"value": 1005, "name": "dawn cache device descriptor", "tags": ["dawn", "native"]}
+            {"value": 1005, "name": "dawn cache device descriptor", "tags": ["dawn", "native"]},
+            {"value": 1006, "name": "dawn adapter properties power preference", "tags": ["dawn", "native"]}
         ]
     },
     "texture": {
@@ -2929,5 +2930,14 @@
         "members": [
             {"name": "use internal usages", "type": "bool", "default": "false"}
         ]
+    },
+    "dawn adapter properties power preference": {
+        "category": "structure",
+        "chained": "out",
+        "chain roots": ["adapter properties"],
+        "tags": ["dawn"],
+        "members": [
+            {"name": "power preference", "type": "power preference", "default": "undefined"}
+        ]
     }
 }
diff --git a/generator/templates/dawn/native/ChainUtils.cpp b/generator/templates/dawn/native/ChainUtils.cpp
index 71f932e..95f5e43 100644
--- a/generator/templates/dawn/native/ChainUtils.cpp
+++ b/generator/templates/dawn/native/ChainUtils.cpp
@@ -25,10 +25,12 @@
 {% set namespace = metadata.namespace %}
 {% for value in types["s type"].values %}
     {% if value.valid %}
-        void FindInChain(const ChainedStruct* chain, const {{as_cppEnum(value.name)}}** out) {
+        {% set const_qualifier = "const " if types[value.name.get()].chained == "in" else "" %}
+        {% set chained_struct_type = "ChainedStruct" if types[value.name.get()].chained == "in" else "ChainedStructOut" %}
+        void FindInChain({{const_qualifier}}{{chained_struct_type}}* chain, {{const_qualifier}}{{as_cppEnum(value.name)}}** out) {
             for (; chain; chain = chain->nextInChain) {
                 if (chain->sType == {{namespace}}::SType::{{as_cppEnum(value.name)}}) {
-                    *out = static_cast<const {{as_cppEnum(value.name)}}*>(chain);
+                    *out = static_cast<{{const_qualifier}}{{as_cppEnum(value.name)}}*>(chain);
                     break;
                 }
             }
@@ -62,4 +64,30 @@
     return {};
 }
 
+MaybeError ValidateSTypes(const ChainedStructOut* chain,
+                          std::vector<std::vector<{{namespace}}::SType>> oneOfConstraints) {
+    std::unordered_set<{{namespace}}::SType> allSTypes;
+    for (; chain; chain = chain->nextInChain) {
+        DAWN_INVALID_IF(allSTypes.find(chain->sType) != allSTypes.end(),
+            "Extension chain has duplicate sType %s.", chain->sType);
+        allSTypes.insert(chain->sType);
+    }
+
+    for (const auto& oneOfConstraint : oneOfConstraints) {
+        bool satisfied = false;
+        for ({{namespace}}::SType oneOfSType : oneOfConstraint) {
+            if (allSTypes.find(oneOfSType) != allSTypes.end()) {
+                DAWN_INVALID_IF(satisfied,
+                    "sType %s is part of a group of exclusive sTypes that is already present.",
+                    oneOfSType);
+                satisfied = true;
+                allSTypes.erase(oneOfSType);
+            }
+        }
+    }
+
+    DAWN_INVALID_IF(!allSTypes.empty(), "Unsupported sType %s.", *allSTypes.begin());
+    return {};
+}
+
 }  // namespace {{native_namespace}}
diff --git a/generator/templates/dawn/native/ChainUtils.h b/generator/templates/dawn/native/ChainUtils.h
index 3377220..3b5a926 100644
--- a/generator/templates/dawn/native/ChainUtils.h
+++ b/generator/templates/dawn/native/ChainUtils.h
@@ -28,7 +28,9 @@
 namespace {{native_namespace}} {
     {% for value in types["s type"].values %}
         {% if value.valid %}
-            void FindInChain(const ChainedStruct* chain, const {{as_cppEnum(value.name)}}** out);
+            {% set const_qualifier = "const " if types[value.name.get()].chained == "in" else "" %}
+            {% set chained_struct_type = "ChainedStruct" if types[value.name.get()].chained == "in" else "ChainedStructOut" %}
+            void FindInChain({{const_qualifier}}{{chained_struct_type}}* chain, {{const_qualifier}}{{as_cppEnum(value.name)}}** out);
         {% endif %}
     {% endfor %}
 
@@ -41,6 +43,8 @@
     {% set namespace = metadata.namespace %}
     MaybeError ValidateSTypes(const ChainedStruct* chain,
                               std::vector<std::vector<{{namespace}}::SType>> oneOfConstraints);
+    MaybeError ValidateSTypes(const ChainedStructOut* chain,
+                              std::vector<std::vector<{{namespace}}::SType>> oneOfConstraints);
 
     template <typename T>
     MaybeError ValidateSingleSTypeInner(const ChainedStruct* chain, T sType) {
@@ -48,6 +52,12 @@
             "Unsupported sType (%s). Expected (%s)", chain->sType, sType);
         return {};
     }
+    template <typename T>
+    MaybeError ValidateSingleSTypeInner(const ChainedStructOut* chain, T sType) {
+        DAWN_INVALID_IF(chain->sType != sType,
+            "Unsupported sType (%s). Expected (%s)", chain->sType, sType);
+        return {};
+    }
 
     template <typename T, typename... Args>
     MaybeError ValidateSingleSTypeInner(const ChainedStruct* chain, T sType, Args... sTypes) {
@@ -56,6 +66,13 @@
         }
         return ValidateSingleSTypeInner(chain, sTypes...);
     }
+    template <typename T, typename... Args>
+    MaybeError ValidateSingleSTypeInner(const ChainedStructOut* chain, T sType, Args... sTypes) {
+        if (chain->sType == sType) {
+            return {};
+        }
+        return ValidateSingleSTypeInner(chain, sTypes...);
+    }
 
     // Verifies that |chain| contains a single ChainedStruct of type |sType| or no ChainedStructs
     // at all.
@@ -68,6 +85,15 @@
             "Chain can only contain a single chained struct.");
         return ValidateSingleSTypeInner(chain, sType);
     }
+    template <typename T>
+    MaybeError ValidateSingleSType(const ChainedStructOut* chain, T sType) {
+        if (chain == nullptr) {
+            return {};
+        }
+        DAWN_INVALID_IF(chain->nextInChain != nullptr,
+            "Chain can only contain a single chained struct.");
+        return ValidateSingleSTypeInner(chain, sType);
+    }
 
     // Verifies that |chain| contains a single ChainedStruct with a type enumerated in the
     // parameter pack or no ChainedStructs at all.
@@ -80,6 +106,15 @@
             "Chain can only contain a single chained struct.");
         return ValidateSingleSTypeInner(chain, sType, sTypes...);
     }
+    template <typename T, typename... Args>
+    MaybeError ValidateSingleSType(const ChainedStructOut* chain, T sType, Args... sTypes) {
+        if (chain == nullptr) {
+            return {};
+        }
+        DAWN_INVALID_IF(chain->nextInChain != nullptr,
+            "Chain can only contain a single chained struct.");
+        return ValidateSingleSTypeInner(chain, sType, sTypes...);
+    }
 
 }  // namespace {{native_namespace}}
 
diff --git a/src/dawn/native/Adapter.cpp b/src/dawn/native/Adapter.cpp
index 0234e2c..c306386 100644
--- a/src/dawn/native/Adapter.cpp
+++ b/src/dawn/native/Adapter.cpp
@@ -86,6 +86,19 @@
 }
 
 void AdapterBase::APIGetProperties(AdapterProperties* properties) const {
+    MaybeError result = ValidateSingleSType(properties->nextInChain,
+                                            wgpu::SType::DawnAdapterPropertiesPowerPreference);
+    if (result.IsError()) {
+        mInstance->ConsumedError(result.AcquireError());
+        return;
+    }
+
+    DawnAdapterPropertiesPowerPreference* powerPreferenceDesc = nullptr;
+    FindInChain(properties->nextInChain, &powerPreferenceDesc);
+    if (powerPreferenceDesc != nullptr) {
+        powerPreferenceDesc->powerPreference = wgpu::PowerPreference::Undefined;
+    }
+
     properties->vendorID = mVendorId;
     properties->vendorName = mVendorName.c_str();
     properties->architecture = mArchitectureName.c_str();
diff --git a/src/dawn/tests/unittests/ChainUtilsTests.cpp b/src/dawn/tests/unittests/ChainUtilsTests.cpp
index c1bea5b..45055f1 100644
--- a/src/dawn/tests/unittests/ChainUtilsTests.cpp
+++ b/src/dawn/tests/unittests/ChainUtilsTests.cpp
@@ -19,65 +19,122 @@
 
 // Checks that we cannot find any structs in an empty chain
 TEST(ChainUtilsTests, FindEmptyChain) {
-    const dawn::native::PrimitiveDepthClipControl* info = nullptr;
-    dawn::native::FindInChain(nullptr, &info);
+    {
+        const dawn::native::PrimitiveDepthClipControl* info = nullptr;
+        const dawn::native::ChainedStruct* chained = nullptr;
+        dawn::native::FindInChain(chained, &info);
 
-    ASSERT_EQ(nullptr, info);
+        ASSERT_EQ(nullptr, info);
+    }
+
+    {
+        dawn::native::DawnAdapterPropertiesPowerPreference* info = nullptr;
+        dawn::native::ChainedStructOut* chained = nullptr;
+        dawn::native::FindInChain(chained, &info);
+
+        ASSERT_EQ(nullptr, info);
+    }
 }
 
 // Checks that searching a chain for a present struct returns that struct
 TEST(ChainUtilsTests, FindPresentInChain) {
-    dawn::native::PrimitiveDepthClipControl chain1;
-    dawn::native::ShaderModuleSPIRVDescriptor chain2;
-    chain1.nextInChain = &chain2;
-    const dawn::native::PrimitiveDepthClipControl* info1 = nullptr;
-    const dawn::native::ShaderModuleSPIRVDescriptor* info2 = nullptr;
-    dawn::native::FindInChain(&chain1, &info1);
-    dawn::native::FindInChain(&chain1, &info2);
+    {
+        dawn::native::PrimitiveDepthClipControl chain1;
+        dawn::native::ShaderModuleSPIRVDescriptor chain2;
+        chain1.nextInChain = &chain2;
+        const dawn::native::PrimitiveDepthClipControl* info1 = nullptr;
+        const dawn::native::ShaderModuleSPIRVDescriptor* info2 = nullptr;
+        dawn::native::FindInChain(&chain1, &info1);
+        dawn::native::FindInChain(&chain1, &info2);
 
-    ASSERT_NE(nullptr, info1);
-    ASSERT_NE(nullptr, info2);
+        ASSERT_NE(nullptr, info1);
+        ASSERT_NE(nullptr, info2);
+    }
+
+    {
+        dawn::native::DawnAdapterPropertiesPowerPreference chain;
+        dawn::native::DawnAdapterPropertiesPowerPreference* output = nullptr;
+        dawn::native::FindInChain(&chain, &output);
+
+        ASSERT_NE(nullptr, output);
+    }
 }
 
 // Checks that searching a chain for a struct that doesn't exist returns a nullptr
 TEST(ChainUtilsTests, FindMissingInChain) {
-    dawn::native::PrimitiveDepthClipControl chain1;
-    dawn::native::ShaderModuleSPIRVDescriptor chain2;
-    chain1.nextInChain = &chain2;
-    const dawn::native::SurfaceDescriptorFromMetalLayer* info = nullptr;
-    dawn::native::FindInChain(&chain1, &info);
+    {
+        dawn::native::PrimitiveDepthClipControl chain1;
+        dawn::native::ShaderModuleSPIRVDescriptor chain2;
+        chain1.nextInChain = &chain2;
+        const dawn::native::SurfaceDescriptorFromMetalLayer* info = nullptr;
+        dawn::native::FindInChain(&chain1, &info);
 
-    ASSERT_EQ(nullptr, info);
+        ASSERT_EQ(nullptr, info);
+    }
+
+    {
+        dawn::native::AdapterProperties adapterProperties;
+        dawn::native::DawnAdapterPropertiesPowerPreference* output = nullptr;
+        dawn::native::FindInChain(adapterProperties.nextInChain, &output);
+
+        ASSERT_EQ(nullptr, output);
+    }
 }
 
 // Checks that validation rejects chains with duplicate STypes
 TEST(ChainUtilsTests, ValidateDuplicateSTypes) {
-    dawn::native::PrimitiveDepthClipControl chain1;
-    dawn::native::ShaderModuleSPIRVDescriptor chain2;
-    dawn::native::PrimitiveDepthClipControl chain3;
-    chain1.nextInChain = &chain2;
-    chain2.nextInChain = &chain3;
+    {
+        dawn::native::PrimitiveDepthClipControl chain1;
+        dawn::native::ShaderModuleSPIRVDescriptor chain2;
+        dawn::native::PrimitiveDepthClipControl chain3;
+        chain1.nextInChain = &chain2;
+        chain2.nextInChain = &chain3;
 
-    dawn::native::MaybeError result = dawn::native::ValidateSTypes(&chain1, {});
-    ASSERT_TRUE(result.IsError());
-    result.AcquireError();
+        dawn::native::MaybeError result = dawn::native::ValidateSTypes(&chain1, {});
+        ASSERT_TRUE(result.IsError());
+        result.AcquireError();
+    }
+
+    {
+        dawn::native::DawnAdapterPropertiesPowerPreference chain1;
+        dawn::native::DawnAdapterPropertiesPowerPreference chain2;
+        chain1.nextInChain = &chain2;
+
+        dawn::native::MaybeError result = dawn::native::ValidateSTypes(&chain1, {});
+        ASSERT_TRUE(result.IsError());
+        result.AcquireError();
+    }
 }
 
 // Checks that validation rejects chains that contain unspecified STypes
 TEST(ChainUtilsTests, ValidateUnspecifiedSTypes) {
-    dawn::native::PrimitiveDepthClipControl chain1;
-    dawn::native::ShaderModuleSPIRVDescriptor chain2;
-    dawn::native::ShaderModuleWGSLDescriptor chain3;
-    chain1.nextInChain = &chain2;
-    chain2.nextInChain = &chain3;
+    {
+        dawn::native::PrimitiveDepthClipControl chain1;
+        dawn::native::ShaderModuleSPIRVDescriptor chain2;
+        dawn::native::ShaderModuleWGSLDescriptor chain3;
+        chain1.nextInChain = &chain2;
+        chain2.nextInChain = &chain3;
 
-    dawn::native::MaybeError result =
-        dawn::native::ValidateSTypes(&chain1, {
-                                                  {wgpu::SType::PrimitiveDepthClipControl},
-                                                  {wgpu::SType::ShaderModuleSPIRVDescriptor},
-                                              });
-    ASSERT_TRUE(result.IsError());
-    result.AcquireError();
+        dawn::native::MaybeError result =
+            dawn::native::ValidateSTypes(&chain1, {
+                                                      {wgpu::SType::PrimitiveDepthClipControl},
+                                                      {wgpu::SType::ShaderModuleSPIRVDescriptor},
+                                                  });
+        ASSERT_TRUE(result.IsError());
+        result.AcquireError();
+    }
+
+    {
+        dawn::native::DawnAdapterPropertiesPowerPreference chain1;
+        dawn::native::ChainedStructOut chain2;
+        chain2.sType = wgpu::SType::RenderPassDescriptorMaxDrawCount;
+        chain1.nextInChain = &chain2;
+
+        dawn::native::MaybeError result = dawn::native::ValidateSTypes(
+            &chain1, {{wgpu::SType::DawnAdapterPropertiesPowerPreference}});
+        ASSERT_TRUE(result.IsError());
+        result.AcquireError();
+    }
 }
 
 // Checks that validation rejects chains that contain multiple STypes from the same oneof
@@ -98,89 +155,162 @@
 
 // Checks that validation accepts chains that match the constraints.
 TEST(ChainUtilsTests, ValidateSuccess) {
-    dawn::native::PrimitiveDepthClipControl chain1;
-    dawn::native::ShaderModuleSPIRVDescriptor chain2;
-    chain1.nextInChain = &chain2;
+    {
+        dawn::native::PrimitiveDepthClipControl chain1;
+        dawn::native::ShaderModuleSPIRVDescriptor chain2;
+        chain1.nextInChain = &chain2;
 
-    dawn::native::MaybeError result = dawn::native::ValidateSTypes(
-        &chain1,
-        {
-            {wgpu::SType::ShaderModuleSPIRVDescriptor, wgpu::SType::ShaderModuleWGSLDescriptor},
-            {wgpu::SType::PrimitiveDepthClipControl},
-            {wgpu::SType::SurfaceDescriptorFromMetalLayer},
-        });
-    ASSERT_TRUE(result.IsSuccess());
+        dawn::native::MaybeError result = dawn::native::ValidateSTypes(
+            &chain1,
+            {
+                {wgpu::SType::ShaderModuleSPIRVDescriptor, wgpu::SType::ShaderModuleWGSLDescriptor},
+                {wgpu::SType::PrimitiveDepthClipControl},
+                {wgpu::SType::SurfaceDescriptorFromMetalLayer},
+            });
+        ASSERT_TRUE(result.IsSuccess());
+    }
+
+    {
+        dawn::native::DawnAdapterPropertiesPowerPreference chain1;
+        dawn::native::MaybeError result = dawn::native::ValidateSTypes(
+            &chain1, {{wgpu::SType::DawnAdapterPropertiesPowerPreference}});
+        ASSERT_TRUE(result.IsSuccess());
+    }
 }
 
 // Checks that validation always passes on empty chains.
 TEST(ChainUtilsTests, ValidateEmptyChain) {
-    dawn::native::MaybeError result =
-        dawn::native::ValidateSTypes(nullptr, {
-                                                  {wgpu::SType::ShaderModuleSPIRVDescriptor},
-                                                  {wgpu::SType::PrimitiveDepthClipControl},
-                                              });
-    ASSERT_TRUE(result.IsSuccess());
+    {
+        const dawn::native::ChainedStruct* chain = nullptr;
+        dawn::native::MaybeError result =
+            dawn::native::ValidateSTypes(chain, {
+                                                    {wgpu::SType::ShaderModuleSPIRVDescriptor},
+                                                    {wgpu::SType::PrimitiveDepthClipControl},
+                                                });
+        ASSERT_TRUE(result.IsSuccess());
 
-    result = dawn::native::ValidateSTypes(nullptr, {});
-    ASSERT_TRUE(result.IsSuccess());
+        result = dawn::native::ValidateSTypes(chain, {});
+        ASSERT_TRUE(result.IsSuccess());
+    }
+
+    {
+        dawn::native::ChainedStructOut* chain = nullptr;
+        dawn::native::MaybeError result = dawn::native::ValidateSTypes(
+            chain, {{wgpu::SType::DawnAdapterPropertiesPowerPreference}});
+        ASSERT_TRUE(result.IsSuccess());
+
+        result = dawn::native::ValidateSTypes(chain, {});
+        ASSERT_TRUE(result.IsSuccess());
+    }
 }
 
 // Checks that singleton validation always passes on empty chains.
 TEST(ChainUtilsTests, ValidateSingleEmptyChain) {
-    dawn::native::MaybeError result =
-        dawn::native::ValidateSingleSType(nullptr, wgpu::SType::ShaderModuleSPIRVDescriptor);
-    ASSERT_TRUE(result.IsSuccess());
+    {
+        const dawn::native::ChainedStruct* chain = nullptr;
+        dawn::native::MaybeError result =
+            dawn::native::ValidateSingleSType(chain, wgpu::SType::ShaderModuleSPIRVDescriptor);
+        ASSERT_TRUE(result.IsSuccess());
 
-    result = dawn::native::ValidateSingleSType(nullptr, wgpu::SType::ShaderModuleSPIRVDescriptor,
-                                               wgpu::SType::PrimitiveDepthClipControl);
-    ASSERT_TRUE(result.IsSuccess());
+        result = dawn::native::ValidateSingleSType(chain, wgpu::SType::ShaderModuleSPIRVDescriptor,
+                                                   wgpu::SType::PrimitiveDepthClipControl);
+        ASSERT_TRUE(result.IsSuccess());
+    }
+
+    {
+        dawn::native::ChainedStructOut* chain = nullptr;
+        dawn::native::MaybeError result = dawn::native::ValidateSingleSType(
+            chain, wgpu::SType::DawnAdapterPropertiesPowerPreference);
+        ASSERT_TRUE(result.IsSuccess());
+
+        result = dawn::native::ValidateSingleSType(
+            chain, wgpu::SType::DawnAdapterPropertiesPowerPreference,
+            wgpu::SType::PrimitiveDepthClipControl);
+        ASSERT_TRUE(result.IsSuccess());
+    }
 }
 
 // Checks that singleton validation always fails on chains with multiple children.
 TEST(ChainUtilsTests, ValidateSingleMultiChain) {
-    dawn::native::PrimitiveDepthClipControl chain1;
-    dawn::native::ShaderModuleSPIRVDescriptor chain2;
-    chain1.nextInChain = &chain2;
+    {
+        dawn::native::PrimitiveDepthClipControl chain1;
+        dawn::native::ShaderModuleSPIRVDescriptor chain2;
+        chain1.nextInChain = &chain2;
 
-    dawn::native::MaybeError result =
-        dawn::native::ValidateSingleSType(&chain1, wgpu::SType::PrimitiveDepthClipControl);
-    ASSERT_TRUE(result.IsError());
-    result.AcquireError();
+        dawn::native::MaybeError result =
+            dawn::native::ValidateSingleSType(&chain1, wgpu::SType::PrimitiveDepthClipControl);
+        ASSERT_TRUE(result.IsError());
+        result.AcquireError();
 
-    result = dawn::native::ValidateSingleSType(&chain1, wgpu::SType::PrimitiveDepthClipControl,
-                                               wgpu::SType::ShaderModuleSPIRVDescriptor);
-    ASSERT_TRUE(result.IsError());
-    result.AcquireError();
+        result = dawn::native::ValidateSingleSType(&chain1, wgpu::SType::PrimitiveDepthClipControl,
+                                                   wgpu::SType::ShaderModuleSPIRVDescriptor);
+        ASSERT_TRUE(result.IsError());
+        result.AcquireError();
+    }
+
+    {
+        dawn::native::DawnAdapterPropertiesPowerPreference chain1;
+        dawn::native::DawnAdapterPropertiesPowerPreference chain2;
+        chain1.nextInChain = &chain2;
+
+        dawn::native::MaybeError result = dawn::native::ValidateSingleSType(
+            &chain1, wgpu::SType::DawnAdapterPropertiesPowerPreference);
+        ASSERT_TRUE(result.IsError());
+        result.AcquireError();
+    }
 }
 
-// Checks that singleton validation passes when the oneof constraint is met.
+// Checks that singleton validation passes when the one of constraint is met.
 TEST(ChainUtilsTests, ValidateSingleSatisfied) {
-    dawn::native::ShaderModuleWGSLDescriptor chain1;
+    {
+        dawn::native::ShaderModuleWGSLDescriptor chain1;
 
-    dawn::native::MaybeError result =
-        dawn::native::ValidateSingleSType(&chain1, wgpu::SType::ShaderModuleWGSLDescriptor);
-    ASSERT_TRUE(result.IsSuccess());
+        dawn::native::MaybeError result =
+            dawn::native::ValidateSingleSType(&chain1, wgpu::SType::ShaderModuleWGSLDescriptor);
+        ASSERT_TRUE(result.IsSuccess());
 
-    result = dawn::native::ValidateSingleSType(&chain1, wgpu::SType::ShaderModuleSPIRVDescriptor,
-                                               wgpu::SType::ShaderModuleWGSLDescriptor);
-    ASSERT_TRUE(result.IsSuccess());
+        result =
+            dawn::native::ValidateSingleSType(&chain1, wgpu::SType::ShaderModuleSPIRVDescriptor,
+                                              wgpu::SType::ShaderModuleWGSLDescriptor);
+        ASSERT_TRUE(result.IsSuccess());
 
-    result = dawn::native::ValidateSingleSType(&chain1, wgpu::SType::ShaderModuleWGSLDescriptor,
-                                               wgpu::SType::ShaderModuleSPIRVDescriptor);
-    ASSERT_TRUE(result.IsSuccess());
+        result = dawn::native::ValidateSingleSType(&chain1, wgpu::SType::ShaderModuleWGSLDescriptor,
+                                                   wgpu::SType::ShaderModuleSPIRVDescriptor);
+        ASSERT_TRUE(result.IsSuccess());
+    }
+
+    {
+        dawn::native::DawnAdapterPropertiesPowerPreference chain1;
+        dawn::native::MaybeError result = dawn::native::ValidateSingleSType(
+            &chain1, wgpu::SType::DawnAdapterPropertiesPowerPreference);
+        ASSERT_TRUE(result.IsSuccess());
+    }
 }
 
 // Checks that singleton validation passes when the oneof constraint is not met.
 TEST(ChainUtilsTests, ValidateSingleUnsatisfied) {
-    dawn::native::PrimitiveDepthClipControl chain1;
+    {
+        dawn::native::PrimitiveDepthClipControl chain1;
 
-    dawn::native::MaybeError result =
-        dawn::native::ValidateSingleSType(&chain1, wgpu::SType::ShaderModuleWGSLDescriptor);
-    ASSERT_TRUE(result.IsError());
-    result.AcquireError();
+        dawn::native::MaybeError result =
+            dawn::native::ValidateSingleSType(&chain1, wgpu::SType::ShaderModuleWGSLDescriptor);
+        ASSERT_TRUE(result.IsError());
+        result.AcquireError();
 
-    result = dawn::native::ValidateSingleSType(&chain1, wgpu::SType::ShaderModuleSPIRVDescriptor,
-                                               wgpu::SType::ShaderModuleWGSLDescriptor);
-    ASSERT_TRUE(result.IsError());
-    result.AcquireError();
+        result =
+            dawn::native::ValidateSingleSType(&chain1, wgpu::SType::ShaderModuleSPIRVDescriptor,
+                                              wgpu::SType::ShaderModuleWGSLDescriptor);
+        ASSERT_TRUE(result.IsError());
+        result.AcquireError();
+    }
+
+    {
+        dawn::native::ChainedStructOut chain1;
+        chain1.sType = wgpu::SType::ShaderModuleWGSLDescriptor;
+
+        dawn::native::MaybeError result = dawn::native::ValidateSingleSType(
+            &chain1, wgpu::SType::DawnAdapterPropertiesPowerPreference);
+        ASSERT_TRUE(result.IsError());
+        result.AcquireError();
+    }
 }