Add In/Out variables to returned EntryPoint
BUG=tint:257
Change-Id: Iec0dca854dfa6380991c04544848c24f21496a93
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/29524
Commit-Queue: Ryan Harrison <rharrison@chromium.org>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/src/ast/function.h b/src/ast/function.h
index 7e21c28..25bb059 100644
--- a/src/ast/function.h
+++ b/src/ast/function.h
@@ -113,6 +113,8 @@
/// is not already included.
/// @param var the module variable to add
void add_referenced_module_variable(Variable* var);
+ /// Note: If this function calls other functions, the return will also include
+ /// all of the referenced variables from the callees.
/// @returns the referenced module variables
const std::vector<Variable*>& referenced_module_variables() const {
return referenced_module_vars_;
diff --git a/src/inspector.cc b/src/inspector.cc
index bdcb4df..cfe9d50 100644
--- a/src/inspector.cc
+++ b/src/inspector.cc
@@ -14,22 +14,41 @@
#include "src/inspector.h"
+#include <algorithm>
+
#include "src/ast/function.h"
namespace tint {
namespace inspector {
+EntryPoint::EntryPoint() = default;
+EntryPoint::EntryPoint(EntryPoint&) = default;
+EntryPoint::EntryPoint(EntryPoint&&) = default;
+EntryPoint::~EntryPoint() = default;
+
Inspector::Inspector(const ast::Module& module) : module_(module) {}
Inspector::~Inspector() = default;
std::vector<EntryPoint> Inspector::GetEntryPoints() {
std::vector<EntryPoint> result;
+
for (const auto& func : module_.functions()) {
if (func->IsEntryPoint()) {
- uint32_t x, y, z;
- std::tie(x, y, z) = func->workgroup_size();
- result.push_back({func->name(), func->pipeline_stage(), x, y, z});
+ EntryPoint entry_point;
+ entry_point.name = func->name();
+ entry_point.stage = func->pipeline_stage();
+ std::tie(entry_point.workgroup_size_x, entry_point.workgroup_size_y,
+ entry_point.workgroup_size_z) = func->workgroup_size();
+
+ for (auto* var : func->referenced_module_variables()) {
+ if (var->storage_class() == ast::StorageClass::kInput) {
+ entry_point.input_variables.push_back(var->name());
+ } else {
+ entry_point.output_variables.push_back(var->name());
+ }
+ }
+ result.push_back(std::move(entry_point));
}
}
diff --git a/src/inspector.h b/src/inspector.h
index 6631765..75f9ead 100644
--- a/src/inspector.h
+++ b/src/inspector.h
@@ -28,6 +28,12 @@
/// Container of reflection data for an entry point in the shader.
struct EntryPoint {
+ /// Constructors
+ EntryPoint();
+ EntryPoint(EntryPoint&);
+ EntryPoint(EntryPoint&&);
+ ~EntryPoint();
+
/// The entry point name
std::string name;
/// The entry point stage
@@ -38,6 +44,10 @@
uint32_t workgroup_size_y;
/// The workgroup z size
uint32_t workgroup_size_z;
+ /// List of the input variable accessed via this entry point.
+ std::vector<std::string> input_variables;
+ /// List of the output variable accessed via this entry point.
+ std::vector<std::string> output_variables;
/// @returns the size of the workgroup in {x,y,z} format
std::tuple<uint32_t, uint32_t, uint32_t> workgroup_size() {
diff --git a/src/inspector_test.cc b/src/inspector_test.cc
index 7bd3b4f..d0a8f96 100644
--- a/src/inspector_test.cc
+++ b/src/inspector_test.cc
@@ -15,12 +15,19 @@
#include "src/inspector.h"
#include "gtest/gtest.h"
+#include "src/ast/assignment_statement.h"
+#include "src/ast/call_expression.h"
+#include "src/ast/call_statement.h"
#include "src/ast/function.h"
+#include "src/ast/identifier_expression.h"
#include "src/ast/pipeline_stage.h"
+#include "src/ast/return_statement.h"
#include "src/ast/stage_decoration.h"
+#include "src/ast/type/u32_type.h"
#include "src/ast/type/void_type.h"
#include "src/ast/workgroup_decoration.h"
#include "src/context.h"
+#include "src/type_determiner.h"
namespace tint {
namespace inspector {
@@ -29,33 +36,135 @@
class InspectorHelper {
public:
InspectorHelper()
- : mod_(std::make_unique<ast::Module>()),
- inspector_(std::make_unique<Inspector>(*mod_)) {}
+ : td_(std::make_unique<TypeDeterminer>(&ctx_, &mod_)),
+ inspector_(std::make_unique<Inspector>(mod_)) {}
- void AddFunction(const std::string& name, ast::PipelineStage stage) {
- auto func = std::make_unique<ast::Function>(
- name, ast::VariableList{},
- ctx_.type_mgr().Get(std::make_unique<ast::type::VoidType>()));
- if (stage != ast::PipelineStage::kNone) {
- func->add_decoration(std::make_unique<ast::StageDecoration>(stage));
+ /// Generates an empty function
+ /// @param name name of the function created
+ /// @returns a function object
+ std::unique_ptr<ast::Function> GenerateEmptyBodyFunction(std::string name) {
+ auto body = std::make_unique<ast::BlockStatement>();
+ body->append(std::make_unique<ast::ReturnStatement>());
+ std::unique_ptr<ast::Function> func =
+ std::make_unique<ast::Function>(name, ast::VariableList(), void_type());
+ func->set_body(std::move(body));
+ return func;
+ }
+
+ /// Generates a function that calls another
+ /// @param caller name of the function created
+ /// @param callee name of the function to be called
+ /// @returns a function object
+ std::unique_ptr<ast::Function> GenerateCallerBodyFunction(
+ std::string caller,
+ std::string callee) {
+ auto body = std::make_unique<ast::BlockStatement>();
+ auto ident_expr = std::make_unique<ast::IdentifierExpression>(callee);
+ auto call_expr = std::make_unique<ast::CallExpression>(
+ std::move(ident_expr), ast::ExpressionList());
+ body->append(std::make_unique<ast::CallStatement>(std::move(call_expr)));
+ body->append(std::make_unique<ast::ReturnStatement>());
+ std::unique_ptr<ast::Function> func = std::make_unique<ast::Function>(
+ caller, ast::VariableList(), void_type());
+ func->set_body(std::move(body));
+ return func;
+ }
+
+ /// Add In/Out variables to the global variables
+ /// @param inout_vars tuples of {in, out} that will be added as entries to the
+ /// global variables
+ void CreateInOutVariables(
+ std::vector<std::tuple<std::string, std::string>> inout_vars) {
+ for (auto inout : inout_vars) {
+ std::string in, out;
+ std::tie(in, out) = inout;
+ auto in_var = std::make_unique<ast::Variable>(
+ in, ast::StorageClass::kInput, u32_type());
+ auto out_var = std::make_unique<ast::Variable>(
+ out, ast::StorageClass::kOutput, u32_type());
+ mod()->AddGlobalVariable(std::move(in_var));
+ mod()->AddGlobalVariable(std::move(out_var));
}
- last_function_ = func.get();
- mod()->AddFunction(std::move(func));
}
- void AddWorkGroupSizeToLastFunction(uint32_t x, uint32_t y, uint32_t z) {
- last_function_->add_decoration(
- std::make_unique<ast::WorkgroupDecoration>(x, y, z));
+ /// Generates a function that references in/out variables
+ /// @param name name of the function created
+ /// @param inout_vars tuples of {in, out} that will be converted into out = in
+ /// calls in the function body
+ /// @returns a function object
+ std::unique_ptr<ast::Function> GenerateInOutVariableBodyFunction(
+ std::string name,
+ std::vector<std::tuple<std::string, std::string>> inout_vars) {
+ auto body = std::make_unique<ast::BlockStatement>();
+ for (auto inout : inout_vars) {
+ std::string in, out;
+ std::tie(in, out) = inout;
+ body->append(std::make_unique<ast::AssignmentStatement>(
+ std::make_unique<ast::IdentifierExpression>(out),
+ std::make_unique<ast::IdentifierExpression>(in)));
+ }
+ body->append(std::make_unique<ast::ReturnStatement>());
+ auto func =
+ std::make_unique<ast::Function>(name, ast::VariableList(), void_type());
+ func->set_body(std::move(body));
+ return func;
}
- ast::Module* mod() { return mod_.get(); }
+ /// Generates a function that references in/out variables and calls another
+ /// function.
+ /// @param caller name of the function created
+ /// @param callee name of the function to be called
+ /// @param inout_vars tuples of {in, out} that will be converted into out = in
+ /// calls in the function body
+ /// @returns a function object
+ std::unique_ptr<ast::Function> GenerateInOutVariableCallerBodyFunction(
+ std::string caller,
+ std::string callee,
+ std::vector<std::tuple<std::string, std::string>> inout_vars) {
+ auto body = std::make_unique<ast::BlockStatement>();
+ for (auto inout : inout_vars) {
+ std::string in, out;
+ std::tie(in, out) = inout;
+ body->append(std::make_unique<ast::AssignmentStatement>(
+ std::make_unique<ast::IdentifierExpression>(out),
+ std::make_unique<ast::IdentifierExpression>(in)));
+ }
+ auto ident_expr = std::make_unique<ast::IdentifierExpression>(callee);
+ auto call_expr = std::make_unique<ast::CallExpression>(
+ std::move(ident_expr), ast::ExpressionList());
+ body->append(std::make_unique<ast::CallStatement>(std::move(call_expr)));
+ body->append(std::make_unique<ast::ReturnStatement>());
+ auto func = std::make_unique<ast::Function>(caller, ast::VariableList(),
+ void_type());
+ func->set_body(std::move(body));
+ return func;
+ }
+
+ bool ContainsString(const std::vector<std::string>& vec,
+ const std::string& str) {
+ for (auto& s : vec) {
+ if (s == str) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ ast::Module* mod() { return &mod_; }
+ TypeDeterminer* td() { return td_.get(); }
Inspector* inspector() { return inspector_.get(); }
+ ast::type::VoidType* void_type() { return &void_type_; }
+ ast::type::U32Type* u32_type() { return &u32_type_; }
+
private:
Context ctx_;
- std::unique_ptr<ast::Module> mod_;
+ ast::Module mod_;
+ std::unique_ptr<TypeDeterminer> td_;
std::unique_ptr<Inspector> inspector_;
- ast::Function* last_function_;
+
+ ast::type::VoidType void_type_;
+ ast::type::U32Type u32_type_;
};
class InspectorTest : public InspectorHelper, public testing::Test {};
@@ -70,7 +179,7 @@
}
TEST_F(InspectorGetEntryPointTest, NoEntryPoints) {
- AddFunction("foo", ast::PipelineStage::kNone);
+ mod()->AddFunction(GenerateEmptyBodyFunction("foo"));
auto result = inspector()->GetEntryPoints();
ASSERT_FALSE(inspector()->has_error());
@@ -79,24 +188,34 @@
}
TEST_F(InspectorGetEntryPointTest, OneEntryPoint) {
- AddFunction("foo", ast::PipelineStage::kVertex);
+ auto foo = GenerateEmptyBodyFunction("foo");
+ foo->add_decoration(
+ std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
+ mod()->AddFunction(std::move(foo));
auto result = inspector()->GetEntryPoints();
ASSERT_FALSE(inspector()->has_error());
- EXPECT_EQ(1u, result.size());
+ ASSERT_EQ(1u, result.size());
EXPECT_EQ("foo", result[0].name);
EXPECT_EQ(ast::PipelineStage::kVertex, result[0].stage);
}
TEST_F(InspectorGetEntryPointTest, MultipleEntryPoints) {
- AddFunction("foo", ast::PipelineStage::kVertex);
- AddFunction("bar", ast::PipelineStage::kCompute);
+ auto foo = GenerateEmptyBodyFunction("foo");
+ foo->add_decoration(
+ std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
+ mod()->AddFunction(std::move(foo));
+
+ auto bar = GenerateEmptyBodyFunction("bar");
+ bar->add_decoration(
+ std::make_unique<ast::StageDecoration>(ast::PipelineStage::kCompute));
+ mod()->AddFunction(std::move(bar));
auto result = inspector()->GetEntryPoints();
ASSERT_FALSE(inspector()->has_error());
- EXPECT_EQ(2u, result.size());
+ ASSERT_EQ(2u, result.size());
EXPECT_EQ("foo", result[0].name);
EXPECT_EQ(ast::PipelineStage::kVertex, result[0].stage);
EXPECT_EQ("bar", result[1].name);
@@ -104,22 +223,34 @@
}
TEST_F(InspectorGetEntryPointTest, MixFunctionsAndEntryPoints) {
- AddFunction("foo", ast::PipelineStage::kVertex);
- AddFunction("func", ast::PipelineStage::kNone);
- AddFunction("bar", ast::PipelineStage::kCompute);
+ auto func = GenerateEmptyBodyFunction("func");
+ mod()->AddFunction(std::move(func));
+
+ auto foo = GenerateCallerBodyFunction("foo", "func");
+ foo->add_decoration(
+ std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
+ mod()->AddFunction(std::move(foo));
+
+ auto bar = GenerateCallerBodyFunction("bar", "func");
+ bar->add_decoration(
+ std::make_unique<ast::StageDecoration>(ast::PipelineStage::kFragment));
+ mod()->AddFunction(std::move(bar));
auto result = inspector()->GetEntryPoints();
EXPECT_FALSE(inspector()->has_error());
- EXPECT_EQ(2u, result.size());
+ ASSERT_EQ(2u, result.size());
EXPECT_EQ("foo", result[0].name);
EXPECT_EQ(ast::PipelineStage::kVertex, result[0].stage);
EXPECT_EQ("bar", result[1].name);
- EXPECT_EQ(ast::PipelineStage::kCompute, result[1].stage);
+ EXPECT_EQ(ast::PipelineStage::kFragment, result[1].stage);
}
TEST_F(InspectorGetEntryPointTest, DefaultWorkgroupSize) {
- AddFunction("foo", ast::PipelineStage::kVertex);
+ auto foo = GenerateCallerBodyFunction("foo", "func");
+ foo->add_decoration(
+ std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
+ mod()->AddFunction(std::move(foo));
auto result = inspector()->GetEntryPoints();
ASSERT_FALSE(inspector()->has_error());
@@ -133,8 +264,12 @@
}
TEST_F(InspectorGetEntryPointTest, NonDefaultWorkgroupSize) {
- AddFunction("foo", ast::PipelineStage::kCompute);
- AddWorkGroupSizeToLastFunction(8u, 2u, 1u);
+ auto foo = GenerateEmptyBodyFunction("foo");
+ foo->add_decoration(
+ std::make_unique<ast::StageDecoration>(ast::PipelineStage::kCompute));
+ foo->add_decoration(std::make_unique<ast::WorkgroupDecoration>(8u, 2u, 1u));
+ mod()->AddFunction(std::move(foo));
+
auto result = inspector()->GetEntryPoints();
ASSERT_FALSE(inspector()->has_error());
@@ -146,6 +281,219 @@
EXPECT_EQ(1u, z);
}
+TEST_F(InspectorGetEntryPointTest, NoInOutVariables) {
+ auto func = GenerateEmptyBodyFunction("func");
+ mod()->AddFunction(std::move(func));
+
+ auto foo = GenerateCallerBodyFunction("foo", "func");
+ foo->add_decoration(
+ std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
+ mod()->AddFunction(std::move(foo));
+
+ auto result = inspector()->GetEntryPoints();
+ ASSERT_FALSE(inspector()->has_error());
+
+ ASSERT_EQ(1u, result.size());
+ EXPECT_EQ(0u, result[0].input_variables.size());
+ EXPECT_EQ(0u, result[0].output_variables.size());
+}
+
+TEST_F(InspectorGetEntryPointTest, EntryPointInOutVariables) {
+ CreateInOutVariables({{"in_var", "out_var"}});
+
+ auto foo = GenerateInOutVariableBodyFunction("foo", {{"in_var", "out_var"}});
+ foo->add_decoration(
+ std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
+ mod()->AddFunction(std::move(foo));
+
+ ASSERT_TRUE(td()->Determine()) << td()->error();
+
+ auto result = inspector()->GetEntryPoints();
+ ASSERT_FALSE(inspector()->has_error());
+
+ ASSERT_EQ(1u, result.size());
+
+ ASSERT_EQ(1u, result[0].input_variables.size());
+ EXPECT_EQ("in_var", result[0].input_variables[0]);
+ ASSERT_EQ(1u, result[0].output_variables.size());
+ EXPECT_EQ("out_var", result[0].output_variables[0]);
+}
+
+TEST_F(InspectorGetEntryPointTest, FunctionInOutVariables) {
+ CreateInOutVariables({{"in_var", "out_var"}});
+
+ auto func =
+ GenerateInOutVariableBodyFunction("func", {{"in_var", "out_var"}});
+ mod()->AddFunction(std::move(func));
+
+ auto foo = GenerateCallerBodyFunction("foo", "func");
+ foo->add_decoration(
+ std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
+ mod()->AddFunction(std::move(foo));
+
+ ASSERT_TRUE(td()->Determine()) << td()->error();
+
+ auto result = inspector()->GetEntryPoints();
+ ASSERT_FALSE(inspector()->has_error());
+
+ ASSERT_EQ(1u, result.size());
+
+ ASSERT_EQ(1u, result[0].input_variables.size());
+ EXPECT_EQ("in_var", result[0].input_variables[0]);
+ ASSERT_EQ(1u, result[0].output_variables.size());
+ EXPECT_EQ("out_var", result[0].output_variables[0]);
+}
+
+TEST_F(InspectorGetEntryPointTest, RepeatedInOutVariables) {
+ CreateInOutVariables({{"in_var", "out_var"}});
+
+ auto func =
+ GenerateInOutVariableBodyFunction("func", {{"in_var", "out_var"}});
+ mod()->AddFunction(std::move(func));
+
+ auto foo = GenerateInOutVariableCallerBodyFunction("foo", "func",
+ {{"in_var", "out_var"}});
+ foo->add_decoration(
+ std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
+ mod()->AddFunction(std::move(foo));
+
+ ASSERT_TRUE(td()->Determine()) << td()->error();
+
+ auto result = inspector()->GetEntryPoints();
+ ASSERT_FALSE(inspector()->has_error());
+
+ ASSERT_EQ(1u, result.size());
+
+ ASSERT_EQ(1u, result[0].input_variables.size());
+ EXPECT_EQ("in_var", result[0].input_variables[0]);
+ ASSERT_EQ(1u, result[0].output_variables.size());
+ EXPECT_EQ("out_var", result[0].output_variables[0]);
+}
+
+TEST_F(InspectorGetEntryPointTest, EntryPointMultipleInOutVariables) {
+ CreateInOutVariables({{"in_var", "out_var"}, {"in2_var", "out2_var"}});
+
+ auto foo = GenerateInOutVariableBodyFunction(
+ "foo", {{"in_var", "out_var"}, {"in2_var", "out2_var"}});
+ foo->add_decoration(
+ std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
+ mod()->AddFunction(std::move(foo));
+
+ ASSERT_TRUE(td()->Determine()) << td()->error();
+
+ auto result = inspector()->GetEntryPoints();
+ ASSERT_FALSE(inspector()->has_error());
+
+ ASSERT_EQ(1u, result.size());
+
+ ASSERT_EQ(2u, result[0].input_variables.size());
+ EXPECT_TRUE(ContainsString(result[0].input_variables, "in_var"));
+ EXPECT_TRUE(ContainsString(result[0].input_variables, "in2_var"));
+ ASSERT_EQ(2u, result[0].output_variables.size());
+ EXPECT_TRUE(ContainsString(result[0].output_variables, "out_var"));
+ EXPECT_TRUE(ContainsString(result[0].output_variables, "out2_var"));
+}
+
+TEST_F(InspectorGetEntryPointTest, FunctionMultipleInOutVariables) {
+ CreateInOutVariables({{"in_var", "out_var"}, {"in2_var", "out2_var"}});
+
+ auto func = GenerateInOutVariableBodyFunction(
+ "func", {{"in_var", "out_var"}, {"in2_var", "out2_var"}});
+ mod()->AddFunction(std::move(func));
+
+ auto foo = GenerateCallerBodyFunction("foo", "func");
+ foo->add_decoration(
+ std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
+ mod()->AddFunction(std::move(foo));
+
+ ASSERT_TRUE(td()->Determine()) << td()->error();
+
+ auto result = inspector()->GetEntryPoints();
+ ASSERT_FALSE(inspector()->has_error());
+
+ ASSERT_EQ(1u, result.size());
+
+ ASSERT_EQ(2u, result[0].input_variables.size());
+ EXPECT_TRUE(ContainsString(result[0].input_variables, "in_var"));
+ EXPECT_TRUE(ContainsString(result[0].input_variables, "in2_var"));
+ ASSERT_EQ(2u, result[0].output_variables.size());
+ EXPECT_TRUE(ContainsString(result[0].output_variables, "out_var"));
+ EXPECT_TRUE(ContainsString(result[0].output_variables, "out2_var"));
+}
+
+TEST_F(InspectorGetEntryPointTest, MultipleEntryPointsInOutVariables) {
+ CreateInOutVariables({{"in_var", "out_var"}, {"in2_var", "out2_var"}});
+
+ auto foo = GenerateInOutVariableBodyFunction("foo", {{"in_var", "out2_var"}});
+ foo->add_decoration(
+ std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
+ mod()->AddFunction(std::move(foo));
+
+ auto bar = GenerateInOutVariableBodyFunction("bar", {{"in2_var", "out_var"}});
+ bar->add_decoration(
+ std::make_unique<ast::StageDecoration>(ast::PipelineStage::kCompute));
+ mod()->AddFunction(std::move(bar));
+
+ ASSERT_TRUE(td()->Determine()) << td()->error();
+
+ auto result = inspector()->GetEntryPoints();
+ ASSERT_FALSE(inspector()->has_error());
+
+ ASSERT_EQ(2u, result.size());
+
+ ASSERT_EQ("foo", result[0].name);
+ ASSERT_EQ(1u, result[0].input_variables.size());
+ EXPECT_EQ("in_var", result[0].input_variables[0]);
+ ASSERT_EQ(1u, result[0].output_variables.size());
+ EXPECT_EQ("out2_var", result[0].output_variables[0]);
+
+ ASSERT_EQ("bar", result[1].name);
+ ASSERT_EQ(1u, result[1].input_variables.size());
+ EXPECT_EQ("in2_var", result[1].input_variables[0]);
+ ASSERT_EQ(1u, result[1].output_variables.size());
+ EXPECT_EQ("out_var", result[1].output_variables[0]);
+}
+
+TEST_F(InspectorGetEntryPointTest, MultipleEntryPointsSharedInOutVariables) {
+ CreateInOutVariables({{"in_var", "out_var"}, {"in2_var", "out2_var"}});
+
+ auto func =
+ GenerateInOutVariableBodyFunction("func", {{"in2_var", "out2_var"}});
+ mod()->AddFunction(std::move(func));
+
+ auto foo = GenerateInOutVariableCallerBodyFunction("foo", "func",
+ {{"in_var", "out_var"}});
+ foo->add_decoration(
+ std::make_unique<ast::StageDecoration>(ast::PipelineStage::kVertex));
+ mod()->AddFunction(std::move(foo));
+
+ auto bar = GenerateCallerBodyFunction("bar", "func");
+ bar->add_decoration(
+ std::make_unique<ast::StageDecoration>(ast::PipelineStage::kCompute));
+ mod()->AddFunction(std::move(bar));
+
+ ASSERT_TRUE(td()->Determine()) << td()->error();
+
+ auto result = inspector()->GetEntryPoints();
+ ASSERT_FALSE(inspector()->has_error());
+
+ ASSERT_EQ(2u, result.size());
+
+ ASSERT_EQ("foo", result[0].name);
+ EXPECT_EQ(2u, result[0].input_variables.size());
+ EXPECT_TRUE(ContainsString(result[0].input_variables, "in_var"));
+ EXPECT_TRUE(ContainsString(result[0].input_variables, "in2_var"));
+ EXPECT_EQ(2u, result[0].output_variables.size());
+ EXPECT_TRUE(ContainsString(result[0].output_variables, "out_var"));
+ EXPECT_TRUE(ContainsString(result[0].output_variables, "out2_var"));
+
+ ASSERT_EQ("bar", result[1].name);
+ EXPECT_EQ(1u, result[1].input_variables.size());
+ EXPECT_EQ("in2_var", result[1].input_variables[0]);
+ EXPECT_EQ(1u, result[1].output_variables.size());
+ EXPECT_EQ("out2_var", result[1].output_variables[0]);
+}
+
} // namespace
} // namespace inspector
} // namespace tint