Factor out the InspectorHelper from the Inspector test file

This class is very large, so factoring it out into its own seperate
file.

Also renaming it to InspectorBuilder, in anticipation of adding tests
that don't build their state through explicitly calling into the
ProgramBuilder.

BUG=tint:967

Change-Id: I316458a969479200edf6962ab2008180f9a1e7e2
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/57800
Auto-Submit: Ryan Harrison <rharrison@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ryan Harrison <rharrison@chromium.org>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 9761dc1..9a98007 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -615,6 +615,8 @@
     diagnostic/formatter_test.cc
     diagnostic/printer_test.cc
     inspector/inspector_test.cc
+    inspector/test_inspector_builder.cc
+    inspector/test_inspector_builder.h
     intrinsic_table_test.cc
     program_test.cc
     resolver/assignment_validation_test.cc
diff --git a/src/inspector/inspector_test.cc b/src/inspector/inspector_test.cc
index 2aa1c49..70d5de6 100644
--- a/src/inspector/inspector_test.cc
+++ b/src/inspector/inspector_test.cc
@@ -19,6 +19,7 @@
 #include "src/ast/stage_decoration.h"
 #include "src/ast/struct_block_decoration.h"
 #include "src/ast/workgroup_decoration.h"
+#include "src/inspector/test_inspector_builder.h"
 #include "src/program_builder.h"
 #include "src/sem/depth_texture_type.h"
 #include "src/sem/external_texture_type.h"
@@ -31,683 +32,17 @@
 namespace inspector {
 namespace {
 
-class InspectorHelper : public ProgramBuilder {
- public:
-  InspectorHelper() {}
-
-  /// Generates an empty function
-  /// @param name name of the function created
-  /// @param decorations the function decorations
-  void MakeEmptyBodyFunction(std::string name,
-                             ast::DecorationList decorations) {
-    Func(name, ast::VariableList(), ty.void_(), ast::StatementList{Return()},
-         decorations);
-  }
-
-  /// Generates a function that calls other functions
-  /// @param caller name of the function created
-  /// @param callees names of the functions to be called
-  /// @param decorations the function decorations
-  void MakeCallerBodyFunction(std::string caller,
-                              std::vector<std::string> callees,
-                              ast::DecorationList decorations) {
-    ast::StatementList body;
-    body.reserve(callees.size() + 1);
-    for (auto callee : callees) {
-      body.push_back(create<ast::CallStatement>(Call(callee)));
-    }
-    body.push_back(Return());
-
-    Func(caller, ast::VariableList(), ty.void_(), body, decorations);
-  }
-
-  /// Generates a struct that contains user-defined IO members
-  /// @param name the name of the generated struct
-  /// @param inout_vars tuples of {name, loc} that will be the struct members
-  ast::Struct* MakeInOutStruct(
-      std::string name,
-      std::vector<std::tuple<std::string, uint32_t>> inout_vars) {
-    ast::StructMemberList members;
-    for (auto var : inout_vars) {
-      std::string member_name;
-      uint32_t location;
-      std::tie(member_name, location) = var;
-      members.push_back(Member(member_name, ty.u32(), {Location(location)}));
-    }
-    return Structure(name, members);
-  }
-
-  // TODO(crbug.com/tint/697): Remove this.
-  /// 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 AddInOutVariables(
-      std::vector<std::tuple<std::string, std::string>> inout_vars) {
-    uint32_t location = 0;
-    for (auto inout : inout_vars) {
-      std::string in, out;
-      std::tie(in, out) = inout;
-
-      Global(in, ty.u32(), ast::StorageClass::kInput, nullptr,
-             ast::DecorationList{
-                 Location(location++),
-                 ASTNodes().Create<ast::DisableValidationDecoration>(
-                     ID(), ast::DisabledValidation::kIgnoreStorageClass)});
-      Global(out, ty.u32(), ast::StorageClass::kOutput, nullptr,
-             ast::DecorationList{
-                 Location(location++),
-                 ASTNodes().Create<ast::DisableValidationDecoration>(
-                     ID(), ast::DisabledValidation::kIgnoreStorageClass)});
-    }
-  }
-
-  // TODO(crbug.com/tint/697): Remove this.
-  /// 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
-  /// @param decorations the function decorations
-  void MakeInOutVariableBodyFunction(
-      std::string name,
-      std::vector<std::tuple<std::string, std::string>> inout_vars,
-      ast::DecorationList decorations) {
-    ast::StatementList stmts;
-    for (auto inout : inout_vars) {
-      std::string in, out;
-      std::tie(in, out) = inout;
-      stmts.emplace_back(Assign(out, in));
-    }
-    stmts.emplace_back(Return());
-    Func(name, ast::VariableList(), ty.void_(), stmts, decorations);
-  }
-
-  // TODO(crbug.com/tint/697): Remove this.
-  /// 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
-  /// @param decorations the function decorations
-  /// @returns a function object
-  ast::Function* MakeInOutVariableCallerBodyFunction(
-      std::string caller,
-      std::string callee,
-      std::vector<std::tuple<std::string, std::string>> inout_vars,
-      ast::DecorationList decorations) {
-    ast::StatementList stmts;
-    for (auto inout : inout_vars) {
-      std::string in, out;
-      std::tie(in, out) = inout;
-      stmts.emplace_back(Assign(out, in));
-    }
-    stmts.emplace_back(create<ast::CallStatement>(Call(callee)));
-    stmts.emplace_back(Return());
-
-    return Func(caller, ast::VariableList(), ty.void_(), stmts, decorations);
-  }
-
-  /// Add a pipeline constant to the global variables, with a specific ID.
-  /// @param name name of the variable to add
-  /// @param id id number for the constant id
-  /// @param type type of the variable
-  /// @param val value to initialize the variable with, if NULL no initializer
-  ///            will be added.
-  /// @returns the constant that was created
-  template <class T>
-  ast::Variable* AddOverridableConstantWithID(std::string name,
-                                              uint32_t id,
-                                              ast::Type* type,
-                                              T* val) {
-    ast::Expression* constructor = nullptr;
-    if (val) {
-      constructor = Expr(*val);
-    }
-    return GlobalConst(name, type, constructor,
-                       ast::DecorationList{
-                           Override(id),
-                       });
-  }
-
-  /// Add a pipeline constant to the global variables, without a specific ID.
-  /// @param name name of the variable to add
-  /// @param type type of the variable
-  /// @param val value to initialize the variable with, if NULL no initializer
-  ///            will be added.
-  /// @returns the constant that was created
-  template <class T>
-  ast::Variable* AddOverridableConstantWithoutID(std::string name,
-                                                 ast::Type* type,
-                                                 T* val) {
-    ast::Expression* constructor = nullptr;
-    if (val) {
-      constructor = Expr(*val);
-    }
-    return GlobalConst(name, type, constructor,
-                       ast::DecorationList{
-                           Override(),
-                       });
-  }
-
-  /// Generates a function that references module constant
-  /// @param func name of the function created
-  /// @param var name of the constant to be reference
-  /// @param type type of the const being referenced
-  /// @param decorations the function decorations
-  /// @returns a function object
-  ast::Function* MakeConstReferenceBodyFunction(
-      std::string func,
-      std::string var,
-      ast::Type* type,
-      ast::DecorationList decorations) {
-    ast::StatementList stmts;
-    stmts.emplace_back(Decl(Var("local_" + var, type)));
-    stmts.emplace_back(Assign("local_" + var, var));
-    stmts.emplace_back(Return());
-
-    return Func(func, ast::VariableList(), ty.void_(), stmts, decorations);
-  }
-
-  /// @param vec Vector of StageVariable to be searched
-  /// @param name Name to be searching for
-  /// @returns true if name is in vec, otherwise false
-  bool ContainsName(const std::vector<StageVariable>& vec,
-                    const std::string& name) {
-    for (auto& s : vec) {
-      if (s.name == name) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  /// Builds a string for accessing a member in a generated struct
-  /// @param idx index of member
-  /// @param type type of member
-  /// @returns a string for the member
-  std::string StructMemberName(size_t idx, ast::Type* type) {
-    return std::to_string(idx) + type->type_name();
-  }
-
-  /// Generates a struct type
-  /// @param name name for the type
-  /// @param member_types a vector of member types
-  /// @param is_block whether or not to decorate as a Block
-  /// @returns a struct type
-  ast::Struct* MakeStructType(const std::string& name,
-                              std::vector<ast::Type*> member_types,
-                              bool is_block) {
-    ast::StructMemberList members;
-    for (auto* type : member_types) {
-      members.push_back(Member(StructMemberName(members.size(), type), type));
-    }
-
-    ast::DecorationList decos;
-    if (is_block) {
-      decos.push_back(create<ast::StructBlockDecoration>());
-    }
-
-    return Structure(name, members, decos);
-  }
-
-  /// Generates types appropriate for using in an uniform buffer
-  /// @param name name for the type
-  /// @param member_types a vector of member types
-  /// @returns a struct type that has the layout for an uniform buffer.
-  ast::Struct* MakeUniformBufferType(const std::string& name,
-                                     std::vector<ast::Type*> member_types) {
-    return MakeStructType(name, member_types, true);
-  }
-
-  /// Generates types appropriate for using in a storage buffer
-  /// @param name name for the type
-  /// @param member_types a vector of member types
-  /// @returns a function that returns the created structure.
-  std::function<ast::TypeName*()> MakeStorageBufferTypes(
-      const std::string& name,
-      std::vector<ast::Type*> member_types) {
-    MakeStructType(name, member_types, true);
-    return [this, name] { return ty.type_name(name); };
-  }
-
-  /// Adds an uniform buffer variable to the program
-  /// @param name the name of the variable
-  /// @param type the type to use
-  /// @param group the binding/group/ to use for the uniform buffer
-  /// @param binding the binding number to use for the uniform buffer
-  void AddUniformBuffer(const std::string& name,
-                        ast::Type* type,
-                        uint32_t group,
-                        uint32_t binding) {
-    Global(name, type, ast::StorageClass::kUniform,
-           ast::DecorationList{
-               create<ast::BindingDecoration>(binding),
-               create<ast::GroupDecoration>(group),
-           });
-  }
-
-  /// Adds a storage buffer variable to the program
-  /// @param name the name of the variable
-  /// @param type the type to use
-  /// @param access the storage buffer access control
-  /// @param group the binding/group to use for the storage buffer
-  /// @param binding the binding number to use for the storage buffer
-  void AddStorageBuffer(const std::string& name,
-                        ast::Type* type,
-                        ast::Access access,
-                        uint32_t group,
-                        uint32_t binding) {
-    Global(name, type, ast::StorageClass::kStorage, access,
-           ast::DecorationList{
-               create<ast::BindingDecoration>(binding),
-               create<ast::GroupDecoration>(group),
-           });
-  }
-
-  /// Generates a function that references a specific struct variable
-  /// @param func_name name of the function created
-  /// @param struct_name name of the struct variabler to be accessed
-  /// @param members list of members to access, by index and type
-  void MakeStructVariableReferenceBodyFunction(
-      std::string func_name,
-      std::string struct_name,
-      std::vector<std::tuple<size_t, ast::Type*>> members) {
-    ast::StatementList stmts;
-    for (auto member : members) {
-      size_t member_idx;
-      ast::Type* member_type;
-      std::tie(member_idx, member_type) = member;
-      std::string member_name = StructMemberName(member_idx, member_type);
-
-      stmts.emplace_back(Decl(Var("local" + member_name, member_type)));
-    }
-
-    for (auto member : members) {
-      size_t member_idx;
-      ast::Type* member_type;
-      std::tie(member_idx, member_type) = member;
-      std::string member_name = StructMemberName(member_idx, member_type);
-
-      stmts.emplace_back(Assign("local" + member_name,
-                                MemberAccessor(struct_name, member_name)));
-    }
-
-    stmts.emplace_back(Return());
-
-    Func(func_name, ast::VariableList(), ty.void_(), stmts,
-         ast::DecorationList{});
-  }
-
-  /// Adds a regular sampler variable to the program
-  /// @param name the name of the variable
-  /// @param group the binding/group to use for the storage buffer
-  /// @param binding the binding number to use for the storage buffer
-  void AddSampler(const std::string& name, uint32_t group, uint32_t binding) {
-    Global(name, sampler_type(),
-           ast::DecorationList{
-               create<ast::BindingDecoration>(binding),
-               create<ast::GroupDecoration>(group),
-           });
-  }
-
-  /// Adds a comparison sampler variable to the program
-  /// @param name the name of the variable
-  /// @param group the binding/group to use for the storage buffer
-  /// @param binding the binding number to use for the storage buffer
-  void AddComparisonSampler(const std::string& name,
-                            uint32_t group,
-                            uint32_t binding) {
-    Global(name, comparison_sampler_type(),
-           ast::DecorationList{
-               create<ast::BindingDecoration>(binding),
-               create<ast::GroupDecoration>(group),
-           });
-  }
-
-  /// Generates a SampledTexture appropriate for the params
-  /// @param dim the dimensions of the texture
-  /// @param type the data type of the sampled texture
-  /// @returns the generated SampleTextureType
-  ast::SampledTexture* MakeSampledTextureType(ast::TextureDimension dim,
-                                              ast::Type* type) {
-    return ty.sampled_texture(dim, type);
-  }
-
-  /// Generates a DepthTexture appropriate for the params
-  /// @param dim the dimensions of the texture
-  /// @returns the generated DepthTexture
-  ast::DepthTexture* MakeDepthTextureType(ast::TextureDimension dim) {
-    return ty.depth_texture(dim);
-  }
-
-  /// Generates a MultisampledTexture appropriate for the params
-  /// @param dim the dimensions of the texture
-  /// @param type the data type of the sampled texture
-  /// @returns the generated SampleTextureType
-  ast::MultisampledTexture* MakeMultisampledTextureType(
-      ast::TextureDimension dim,
-      ast::Type* type) {
-    return ty.multisampled_texture(dim, type);
-  }
-
-  /// Generates an ExternalTexture appropriate for the params
-  /// @returns the generated ExternalTexture
-  ast::ExternalTexture* MakeExternalTextureType() {
-    return ty.external_texture();
-  }
-
-  /// Adds a sampled texture variable to the program
-  /// @param name the name of the variable
-  /// @param type the type to use
-  /// @param group the binding/group to use for the sampled texture
-  /// @param binding the binding number to use for the sampled texture
-  void AddSampledTexture(const std::string& name,
-                         ast::Type* type,
-                         uint32_t group,
-                         uint32_t binding) {
-    Global(name, type,
-           ast::DecorationList{
-               create<ast::BindingDecoration>(binding),
-               create<ast::GroupDecoration>(group),
-           });
-  }
-
-  /// Adds a multi-sampled texture variable to the program
-  /// @param name the name of the variable
-  /// @param type the type to use
-  /// @param group the binding/group to use for the multi-sampled texture
-  /// @param binding the binding number to use for the multi-sampled texture
-  void AddMultisampledTexture(const std::string& name,
-                              ast::Type* type,
-                              uint32_t group,
-                              uint32_t binding) {
-    Global(name, type,
-           ast::DecorationList{
-               create<ast::BindingDecoration>(binding),
-               create<ast::GroupDecoration>(group),
-           });
-  }
-
-  void AddGlobalVariable(const std::string& name, ast::Type* type) {
-    Global(name, type, ast::StorageClass::kPrivate);
-  }
-
-  /// Adds a depth texture variable to the program
-  /// @param name the name of the variable
-  /// @param type the type to use
-  /// @param group the binding/group to use for the depth texture
-  /// @param binding the binding number to use for the depth texture
-  void AddDepthTexture(const std::string& name,
-                       ast::Type* type,
-                       uint32_t group,
-                       uint32_t binding) {
-    Global(name, type,
-           ast::DecorationList{
-               create<ast::BindingDecoration>(binding),
-               create<ast::GroupDecoration>(group),
-           });
-  }
-
-  /// Adds an external texture variable to the program
-  /// @param name the name of the variable
-  /// @param type the type to use
-  /// @param group the binding/group to use for the external texture
-  /// @param binding the binding number to use for the external texture
-  void AddExternalTexture(const std::string& name,
-                          ast::Type* type,
-                          uint32_t group,
-                          uint32_t binding) {
-    Global(name, type,
-           ast::DecorationList{
-               create<ast::BindingDecoration>(binding),
-               create<ast::GroupDecoration>(group),
-           });
-  }
-
-  /// Generates a function that references a specific sampler variable
-  /// @param func_name name of the function created
-  /// @param texture_name name of the texture to be sampled
-  /// @param sampler_name name of the sampler to use
-  /// @param coords_name name of the coords variable to use
-  /// @param base_type sampler base type
-  /// @param decorations the function decorations
-  /// @returns a function that references all of the values specified
-  ast::Function* MakeSamplerReferenceBodyFunction(
-      const std::string& func_name,
-      const std::string& texture_name,
-      const std::string& sampler_name,
-      const std::string& coords_name,
-      ast::Type* base_type,
-      ast::DecorationList decorations) {
-    std::string result_name = "sampler_result";
-
-    ast::StatementList stmts;
-    stmts.emplace_back(Decl(Var(result_name, ty.vec(base_type, 4))));
-
-    stmts.emplace_back(Assign(result_name, Call("textureSample", texture_name,
-                                                sampler_name, coords_name)));
-    stmts.emplace_back(Return());
-
-    return Func(func_name, ast::VariableList(), ty.void_(), stmts, decorations);
-  }
-
-  /// Generates a function that references a specific sampler variable
-  /// @param func_name name of the function created
-  /// @param texture_name name of the texture to be sampled
-  /// @param sampler_name name of the sampler to use
-  /// @param coords_name name of the coords variable to use
-  /// @param array_index name of the array index variable to use
-  /// @param base_type sampler base type
-  /// @param decorations the function decorations
-  /// @returns a function that references all of the values specified
-  ast::Function* MakeSamplerReferenceBodyFunction(
-      const std::string& func_name,
-      const std::string& texture_name,
-      const std::string& sampler_name,
-      const std::string& coords_name,
-      const std::string& array_index,
-      ast::Type* base_type,
-      ast::DecorationList decorations) {
-    std::string result_name = "sampler_result";
-
-    ast::StatementList stmts;
-
-    stmts.emplace_back(Decl(Var("sampler_result", ty.vec(base_type, 4))));
-
-    stmts.emplace_back(
-        Assign("sampler_result", Call("textureSample", texture_name,
-                                      sampler_name, coords_name, array_index)));
-    stmts.emplace_back(Return());
-
-    return Func(func_name, ast::VariableList(), ty.void_(), stmts, decorations);
-  }
-
-  /// Generates a function that references a specific comparison sampler
-  /// variable.
-  /// @param func_name name of the function created
-  /// @param texture_name name of the depth texture to  use
-  /// @param sampler_name name of the sampler to use
-  /// @param coords_name name of the coords variable to use
-  /// @param depth_name name of the depth reference to use
-  /// @param base_type sampler base type
-  /// @param decorations the function decorations
-  /// @returns a function that references all of the values specified
-  ast::Function* MakeComparisonSamplerReferenceBodyFunction(
-      const std::string& func_name,
-      const std::string& texture_name,
-      const std::string& sampler_name,
-      const std::string& coords_name,
-      const std::string& depth_name,
-      ast::Type* base_type,
-      ast::DecorationList decorations) {
-    std::string result_name = "sampler_result";
-
-    ast::StatementList stmts;
-
-    stmts.emplace_back(Decl(Var("sampler_result", base_type)));
-    stmts.emplace_back(
-        Assign("sampler_result", Call("textureSampleCompare", texture_name,
-                                      sampler_name, coords_name, depth_name)));
-    stmts.emplace_back(Return());
-
-    return Func(func_name, ast::VariableList(), ty.void_(), stmts, decorations);
-  }
-
-  /// Gets an appropriate type for the data in a given texture type.
-  /// @param sampled_kind type of in the texture
-  /// @returns a pointer to a type appropriate for the coord param
-  ast::Type* GetBaseType(ResourceBinding::SampledKind sampled_kind) {
-    switch (sampled_kind) {
-      case ResourceBinding::SampledKind::kFloat:
-        return ty.f32();
-      case ResourceBinding::SampledKind::kSInt:
-        return ty.i32();
-      case ResourceBinding::SampledKind::kUInt:
-        return ty.u32();
-      default:
-        return nullptr;
-    }
-  }
-
-  /// Gets an appropriate type for the coords parameter depending the the
-  /// dimensionality of the texture being sampled.
-  /// @param dim dimensionality of the texture being sampled
-  /// @param scalar the scalar type
-  /// @returns a pointer to a type appropriate for the coord param
-  ast::Type* GetCoordsType(ast::TextureDimension dim, ast::Type* scalar) {
-    switch (dim) {
-      case ast::TextureDimension::k1d:
-        return scalar;
-      case ast::TextureDimension::k2d:
-      case ast::TextureDimension::k2dArray:
-        return create<ast::Vector>(scalar, 2);
-      case ast::TextureDimension::k3d:
-      case ast::TextureDimension::kCube:
-      case ast::TextureDimension::kCubeArray:
-        return create<ast::Vector>(scalar, 3);
-      default:
-        [=]() { FAIL() << "Unsupported texture dimension: " << dim; }();
-    }
-    return nullptr;
-  }
-
-  /// Generates appropriate types for a Read-Only StorageTexture
-  /// @param dim the texture dimension of the storage texture
-  /// @param format the image format of the storage texture
-  /// @param read_only should the access type be read only, otherwise write only
-  /// @returns the storage texture type
-  ast::Type* MakeStorageTextureTypes(ast::TextureDimension dim,
-                                     ast::ImageFormat format,
-                                     bool read_only) {
-    auto access = read_only ? ast::Access::kRead : ast::Access::kWrite;
-    return ty.storage_texture(dim, format, access);
-  }
-
-  /// Adds a storage texture variable to the program
-  /// @param name the name of the variable
-  /// @param type the type to use
-  /// @param group the binding/group to use for the sampled texture
-  /// @param binding the binding number to use for the sampled texture
-  void AddStorageTexture(const std::string& name,
-                         ast::Type* type,
-                         uint32_t group,
-                         uint32_t binding) {
-    Global(name, type,
-           ast::DecorationList{
-               create<ast::BindingDecoration>(binding),
-               create<ast::GroupDecoration>(group),
-           });
-  }
-
-  /// Generates a function that references a storage texture variable.
-  /// @param func_name name of the function created
-  /// @param st_name name of the storage texture to use
-  /// @param dim_type type expected by textureDimensons to return
-  /// @param decorations the function decorations
-  /// @returns a function that references all of the values specified
-  ast::Function* MakeStorageTextureBodyFunction(
-      const std::string& func_name,
-      const std::string& st_name,
-      ast::Type* dim_type,
-      ast::DecorationList decorations) {
-    ast::StatementList stmts;
-
-    stmts.emplace_back(Decl(Var("dim", dim_type)));
-    stmts.emplace_back(Assign("dim", Call("textureDimensions", st_name)));
-    stmts.emplace_back(Return());
-
-    return Func(func_name, ast::VariableList(), ty.void_(), stmts, decorations);
-  }
-
-  std::function<ast::Type*()> GetTypeFunction(ComponentType component,
-                                              CompositionType composition) {
-    std::function<ast::Type*()> func;
-    switch (component) {
-      case ComponentType::kFloat:
-        func = [this]() -> ast::Type* { return ty.f32(); };
-        break;
-      case ComponentType::kSInt:
-        func = [this]() -> ast::Type* { return ty.i32(); };
-        break;
-      case ComponentType::kUInt:
-        func = [this]() -> ast::Type* { return ty.u32(); };
-        break;
-      case ComponentType::kUnknown:
-        return []() -> ast::Type* { return nullptr; };
-    }
-
-    uint32_t n;
-    switch (composition) {
-      case CompositionType::kScalar:
-        return func;
-      case CompositionType::kVec2:
-        n = 2;
-        break;
-      case CompositionType::kVec3:
-        n = 3;
-        break;
-      case CompositionType::kVec4:
-        n = 4;
-        break;
-      default:
-        return []() -> ast::Type* { return nullptr; };
-    }
-
-    return [this, func, n]() -> ast::Type* { return ty.vec(func(), n); };
-  }
-
-  Inspector& Build() {
-    if (inspector_) {
-      return *inspector_;
-    }
-    program_ = std::make_unique<Program>(std::move(*this));
-    [&]() {
-      ASSERT_TRUE(program_->IsValid())
-          << diag::Formatter().format(program_->Diagnostics());
-    }();
-    inspector_ = std::make_unique<Inspector>(program_.get());
-    return *inspector_;
-  }
-
-  ast::Sampler* sampler_type() {
-    return ty.sampler(ast::SamplerKind::kSampler);
-  }
-  ast::Sampler* comparison_sampler_type() {
-    return ty.sampler(ast::SamplerKind::kComparisonSampler);
-  }
-
- protected:
-  std::unique_ptr<Program> program_;
-  std::unique_ptr<Inspector> inspector_;
-};
-
-class InspectorGetEntryPointTest : public InspectorHelper,
+// All the tests that descend from InspectorBuilder are expected to define their
+// test state via building up the AST through InspectorBuilder and then generate
+// the program with ::Build().
+// The returned Inspector from ::Build() can then be used to test expecations.
+class InspectorGetEntryPointTest : public InspectorBuilder,
                                    public testing::Test {};
 
 typedef std::tuple<inspector::ComponentType, inspector::CompositionType>
     InspectorGetEntryPointComponentAndCompositionTestParams;
 class InspectorGetEntryPointComponentAndCompositionTest
-    : public InspectorHelper,
+    : public InspectorBuilder,
       public testing::TestWithParam<
           InspectorGetEntryPointComponentAndCompositionTestParams> {};
 struct InspectorGetEntryPointInterpolateTestParams {
@@ -717,35 +52,35 @@
   inspector::InterpolationSampling out_sampling;
 };
 class InspectorGetEntryPointInterpolateTest
-    : public InspectorHelper,
+    : public InspectorBuilder,
       public testing::TestWithParam<
           InspectorGetEntryPointInterpolateTestParams> {};
-class InspectorGetRemappedNameForEntryPointTest : public InspectorHelper,
+class InspectorGetRemappedNameForEntryPointTest : public InspectorBuilder,
                                                   public testing::Test {};
-class InspectorGetConstantIDsTest : public InspectorHelper,
+class InspectorGetConstantIDsTest : public InspectorBuilder,
                                     public testing::Test {};
-class InspectorGetConstantNameToIdMapTest : public InspectorHelper,
+class InspectorGetConstantNameToIdMapTest : public InspectorBuilder,
                                             public testing::Test {};
-class InspectorGetStorageSizeTest : public InspectorHelper,
+class InspectorGetStorageSizeTest : public InspectorBuilder,
                                     public testing::Test {};
-class InspectorGetResourceBindingsTest : public InspectorHelper,
+class InspectorGetResourceBindingsTest : public InspectorBuilder,
                                          public testing::Test {};
-class InspectorGetUniformBufferResourceBindingsTest : public InspectorHelper,
+class InspectorGetUniformBufferResourceBindingsTest : public InspectorBuilder,
                                                       public testing::Test {};
-class InspectorGetStorageBufferResourceBindingsTest : public InspectorHelper,
+class InspectorGetStorageBufferResourceBindingsTest : public InspectorBuilder,
                                                       public testing::Test {};
 class InspectorGetReadOnlyStorageBufferResourceBindingsTest
-    : public InspectorHelper,
+    : public InspectorBuilder,
       public testing::Test {};
-class InspectorGetSamplerResourceBindingsTest : public InspectorHelper,
+class InspectorGetSamplerResourceBindingsTest : public InspectorBuilder,
                                                 public testing::Test {};
 class InspectorGetComparisonSamplerResourceBindingsTest
-    : public InspectorHelper,
+    : public InspectorBuilder,
       public testing::Test {};
-class InspectorGetSampledTextureResourceBindingsTest : public InspectorHelper,
+class InspectorGetSampledTextureResourceBindingsTest : public InspectorBuilder,
                                                        public testing::Test {};
 class InspectorGetSampledArrayTextureResourceBindingsTest
-    : public InspectorHelper,
+    : public InspectorBuilder,
       public testing::Test {};
 struct GetSampledTextureTestParams {
   ast::TextureDimension type_dim;
@@ -753,32 +88,32 @@
   inspector::ResourceBinding::SampledKind sampled_kind;
 };
 class InspectorGetSampledTextureResourceBindingsTestWithParam
-    : public InspectorHelper,
+    : public InspectorBuilder,
       public testing::TestWithParam<GetSampledTextureTestParams> {};
 class InspectorGetSampledArrayTextureResourceBindingsTestWithParam
-    : public InspectorHelper,
+    : public InspectorBuilder,
       public testing::TestWithParam<GetSampledTextureTestParams> {};
 class InspectorGetMultisampledTextureResourceBindingsTest
-    : public InspectorHelper,
+    : public InspectorBuilder,
       public testing::Test {};
 class InspectorGetMultisampledArrayTextureResourceBindingsTest
-    : public InspectorHelper,
+    : public InspectorBuilder,
       public testing::Test {};
 typedef GetSampledTextureTestParams GetMultisampledTextureTestParams;
 class InspectorGetMultisampledArrayTextureResourceBindingsTestWithParam
-    : public InspectorHelper,
+    : public InspectorBuilder,
       public testing::TestWithParam<GetMultisampledTextureTestParams> {};
 class InspectorGetMultisampledTextureResourceBindingsTestWithParam
-    : public InspectorHelper,
+    : public InspectorBuilder,
       public testing::TestWithParam<GetMultisampledTextureTestParams> {};
-class InspectorGetStorageTextureResourceBindingsTest : public InspectorHelper,
+class InspectorGetStorageTextureResourceBindingsTest : public InspectorBuilder,
                                                        public testing::Test {};
 struct GetDepthTextureTestParams {
   ast::TextureDimension type_dim;
   inspector::ResourceBinding::TextureDimension inspector_dim;
 };
 class InspectorGetDepthTextureResourceBindingsTestWithParam
-    : public InspectorHelper,
+    : public InspectorBuilder,
       public testing::TestWithParam<GetDepthTextureTestParams> {};
 
 typedef std::tuple<ast::TextureDimension, ResourceBinding::TextureDimension>
@@ -790,13 +125,13 @@
 typedef std::tuple<bool, DimensionParams, ImageFormatParams>
     GetStorageTextureTestParams;
 class InspectorGetStorageTextureResourceBindingsTestWithParam
-    : public InspectorHelper,
+    : public InspectorBuilder,
       public testing::TestWithParam<GetStorageTextureTestParams> {};
 
-class InspectorGetExternalTextureResourceBindingsTest : public InspectorHelper,
+class InspectorGetExternalTextureResourceBindingsTest : public InspectorBuilder,
                                                         public testing::Test {};
 
-class InspectorGetSamplerTextureUsesTest : public InspectorHelper,
+class InspectorGetSamplerTextureUsesTest : public InspectorBuilder,
                                            public testing::Test {};
 
 TEST_F(InspectorGetEntryPointTest, NoFunctions) {
diff --git a/src/inspector/test_inspector_builder.cc b/src/inspector/test_inspector_builder.cc
new file mode 100644
index 0000000..8e1912c
--- /dev/null
+++ b/src/inspector/test_inspector_builder.cc
@@ -0,0 +1,495 @@
+// Copyright 2021 The Tint 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 "src/inspector/test_inspector_builder.h"
+
+#include <memory>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "gtest/gtest.h"
+
+namespace tint {
+namespace inspector {
+
+InspectorBuilder::InspectorBuilder() = default;
+InspectorBuilder::~InspectorBuilder() = default;
+
+void InspectorBuilder::MakeEmptyBodyFunction(std::string name,
+                                             ast::DecorationList decorations) {
+  Func(name, ast::VariableList(), ty.void_(), ast::StatementList{Return()},
+       decorations);
+}
+
+void InspectorBuilder::MakeCallerBodyFunction(std::string caller,
+                                              std::vector<std::string> callees,
+                                              ast::DecorationList decorations) {
+  ast::StatementList body;
+  body.reserve(callees.size() + 1);
+  for (auto callee : callees) {
+    body.push_back(create<ast::CallStatement>(Call(callee)));
+  }
+  body.push_back(Return());
+
+  Func(caller, ast::VariableList(), ty.void_(), body, decorations);
+}
+
+ast::Struct* InspectorBuilder::MakeInOutStruct(
+    std::string name,
+    std::vector<std::tuple<std::string, uint32_t>> inout_vars) {
+  ast::StructMemberList members;
+  for (auto var : inout_vars) {
+    std::string member_name;
+    uint32_t location;
+    std::tie(member_name, location) = var;
+    members.push_back(Member(member_name, ty.u32(), {Location(location)}));
+  }
+  return Structure(name, members);
+}
+
+// TODO(crbug.com/tint/697): Remove this.
+void InspectorBuilder::AddInOutVariables(
+    std::vector<std::tuple<std::string, std::string>> inout_vars) {
+  uint32_t location = 0;
+  for (auto inout : inout_vars) {
+    std::string in, out;
+    std::tie(in, out) = inout;
+
+    Global(in, ty.u32(), ast::StorageClass::kInput, nullptr,
+           ast::DecorationList{
+               Location(location++),
+               ASTNodes().Create<ast::DisableValidationDecoration>(
+                   ID(), ast::DisabledValidation::kIgnoreStorageClass)});
+    Global(out, ty.u32(), ast::StorageClass::kOutput, nullptr,
+           ast::DecorationList{
+               Location(location++),
+               ASTNodes().Create<ast::DisableValidationDecoration>(
+                   ID(), ast::DisabledValidation::kIgnoreStorageClass)});
+  }
+}
+
+// TODO(crbug.com/tint/697): Remove this.
+void InspectorBuilder::MakeInOutVariableBodyFunction(
+    std::string name,
+    std::vector<std::tuple<std::string, std::string>> inout_vars,
+    ast::DecorationList decorations) {
+  ast::StatementList stmts;
+  for (auto inout : inout_vars) {
+    std::string in, out;
+    std::tie(in, out) = inout;
+    stmts.emplace_back(Assign(out, in));
+  }
+  stmts.emplace_back(Return());
+  Func(name, ast::VariableList(), ty.void_(), stmts, decorations);
+}
+
+// TODO(crbug.com/tint/697): Remove this.
+ast::Function* InspectorBuilder::MakeInOutVariableCallerBodyFunction(
+    std::string caller,
+    std::string callee,
+    std::vector<std::tuple<std::string, std::string>> inout_vars,
+    ast::DecorationList decorations) {
+  ast::StatementList stmts;
+  for (auto inout : inout_vars) {
+    std::string in, out;
+    std::tie(in, out) = inout;
+    stmts.emplace_back(Assign(out, in));
+  }
+  stmts.emplace_back(create<ast::CallStatement>(Call(callee)));
+  stmts.emplace_back(Return());
+
+  return Func(caller, ast::VariableList(), ty.void_(), stmts, decorations);
+}
+
+ast::Function* InspectorBuilder::MakeConstReferenceBodyFunction(
+    std::string func,
+    std::string var,
+    ast::Type* type,
+    ast::DecorationList decorations) {
+  ast::StatementList stmts;
+  stmts.emplace_back(Decl(Var("local_" + var, type)));
+  stmts.emplace_back(Assign("local_" + var, var));
+  stmts.emplace_back(Return());
+
+  return Func(func, ast::VariableList(), ty.void_(), stmts, decorations);
+}
+
+bool InspectorBuilder::ContainsName(const std::vector<StageVariable>& vec,
+                                    const std::string& name) {
+  for (auto& s : vec) {
+    if (s.name == name) {
+      return true;
+    }
+  }
+  return false;
+}
+
+std::string InspectorBuilder::StructMemberName(size_t idx, ast::Type* type) {
+  return std::to_string(idx) + type->type_name();
+}
+
+ast::Struct* InspectorBuilder::MakeStructType(
+    const std::string& name,
+    std::vector<ast::Type*> member_types,
+    bool is_block) {
+  ast::StructMemberList members;
+  for (auto* type : member_types) {
+    members.push_back(Member(StructMemberName(members.size(), type), type));
+  }
+
+  ast::DecorationList decos;
+  if (is_block) {
+    decos.push_back(create<ast::StructBlockDecoration>());
+  }
+
+  return Structure(name, members, decos);
+}
+
+ast::Struct* InspectorBuilder::MakeUniformBufferType(
+    const std::string& name,
+    std::vector<ast::Type*> member_types) {
+  return MakeStructType(name, member_types, true);
+}
+
+std::function<ast::TypeName*()> InspectorBuilder::MakeStorageBufferTypes(
+    const std::string& name,
+    std::vector<ast::Type*> member_types) {
+  MakeStructType(name, member_types, true);
+  return [this, name] { return ty.type_name(name); };
+}
+
+void InspectorBuilder::AddUniformBuffer(const std::string& name,
+                                        ast::Type* type,
+                                        uint32_t group,
+                                        uint32_t binding) {
+  Global(name, type, ast::StorageClass::kUniform,
+         ast::DecorationList{
+             create<ast::BindingDecoration>(binding),
+             create<ast::GroupDecoration>(group),
+         });
+}
+
+void InspectorBuilder::AddStorageBuffer(const std::string& name,
+                                        ast::Type* type,
+                                        ast::Access access,
+                                        uint32_t group,
+                                        uint32_t binding) {
+  Global(name, type, ast::StorageClass::kStorage, access,
+         ast::DecorationList{
+             create<ast::BindingDecoration>(binding),
+             create<ast::GroupDecoration>(group),
+         });
+}
+
+void InspectorBuilder::MakeStructVariableReferenceBodyFunction(
+    std::string func_name,
+    std::string struct_name,
+    std::vector<std::tuple<size_t, ast::Type*>> members) {
+  ast::StatementList stmts;
+  for (auto member : members) {
+    size_t member_idx;
+    ast::Type* member_type;
+    std::tie(member_idx, member_type) = member;
+    std::string member_name = StructMemberName(member_idx, member_type);
+
+    stmts.emplace_back(Decl(Var("local" + member_name, member_type)));
+  }
+
+  for (auto member : members) {
+    size_t member_idx;
+    ast::Type* member_type;
+    std::tie(member_idx, member_type) = member;
+    std::string member_name = StructMemberName(member_idx, member_type);
+
+    stmts.emplace_back(Assign("local" + member_name,
+                              MemberAccessor(struct_name, member_name)));
+  }
+
+  stmts.emplace_back(Return());
+
+  Func(func_name, ast::VariableList(), ty.void_(), stmts,
+       ast::DecorationList{});
+}
+
+void InspectorBuilder::AddSampler(const std::string& name,
+                                  uint32_t group,
+                                  uint32_t binding) {
+  Global(name, sampler_type(),
+         ast::DecorationList{
+             create<ast::BindingDecoration>(binding),
+             create<ast::GroupDecoration>(group),
+         });
+}
+
+void InspectorBuilder::AddComparisonSampler(const std::string& name,
+                                            uint32_t group,
+                                            uint32_t binding) {
+  Global(name, comparison_sampler_type(),
+         ast::DecorationList{
+             create<ast::BindingDecoration>(binding),
+             create<ast::GroupDecoration>(group),
+         });
+}
+
+ast::SampledTexture* InspectorBuilder::MakeSampledTextureType(
+    ast::TextureDimension dim,
+    ast::Type* type) {
+  return ty.sampled_texture(dim, type);
+}
+
+ast::DepthTexture* InspectorBuilder::MakeDepthTextureType(
+    ast::TextureDimension dim) {
+  return ty.depth_texture(dim);
+}
+
+ast::MultisampledTexture* InspectorBuilder::MakeMultisampledTextureType(
+    ast::TextureDimension dim,
+    ast::Type* type) {
+  return ty.multisampled_texture(dim, type);
+}
+
+ast::ExternalTexture* InspectorBuilder::MakeExternalTextureType() {
+  return ty.external_texture();
+}
+
+void InspectorBuilder::AddSampledTexture(const std::string& name,
+                                         ast::Type* type,
+                                         uint32_t group,
+                                         uint32_t binding) {
+  Global(name, type,
+         ast::DecorationList{
+             create<ast::BindingDecoration>(binding),
+             create<ast::GroupDecoration>(group),
+         });
+}
+
+void InspectorBuilder::AddMultisampledTexture(const std::string& name,
+                                              ast::Type* type,
+                                              uint32_t group,
+                                              uint32_t binding) {
+  Global(name, type,
+         ast::DecorationList{
+             create<ast::BindingDecoration>(binding),
+             create<ast::GroupDecoration>(group),
+         });
+}
+
+void InspectorBuilder::AddGlobalVariable(const std::string& name,
+                                         ast::Type* type) {
+  Global(name, type, ast::StorageClass::kPrivate);
+}
+
+void InspectorBuilder::AddDepthTexture(const std::string& name,
+                                       ast::Type* type,
+                                       uint32_t group,
+                                       uint32_t binding) {
+  Global(name, type,
+         ast::DecorationList{
+             create<ast::BindingDecoration>(binding),
+             create<ast::GroupDecoration>(group),
+         });
+}
+
+void InspectorBuilder::AddExternalTexture(const std::string& name,
+                                          ast::Type* type,
+                                          uint32_t group,
+                                          uint32_t binding) {
+  Global(name, type,
+         ast::DecorationList{
+             create<ast::BindingDecoration>(binding),
+             create<ast::GroupDecoration>(group),
+         });
+}
+
+ast::Function* InspectorBuilder::MakeSamplerReferenceBodyFunction(
+    const std::string& func_name,
+    const std::string& texture_name,
+    const std::string& sampler_name,
+    const std::string& coords_name,
+    ast::Type* base_type,
+    ast::DecorationList decorations) {
+  std::string result_name = "sampler_result";
+
+  ast::StatementList stmts;
+  stmts.emplace_back(Decl(Var(result_name, ty.vec(base_type, 4))));
+
+  stmts.emplace_back(Assign(result_name, Call("textureSample", texture_name,
+                                              sampler_name, coords_name)));
+  stmts.emplace_back(Return());
+
+  return Func(func_name, ast::VariableList(), ty.void_(), stmts, decorations);
+}
+
+ast::Function* InspectorBuilder::MakeSamplerReferenceBodyFunction(
+    const std::string& func_name,
+    const std::string& texture_name,
+    const std::string& sampler_name,
+    const std::string& coords_name,
+    const std::string& array_index,
+    ast::Type* base_type,
+    ast::DecorationList decorations) {
+  std::string result_name = "sampler_result";
+
+  ast::StatementList stmts;
+
+  stmts.emplace_back(Decl(Var("sampler_result", ty.vec(base_type, 4))));
+
+  stmts.emplace_back(
+      Assign("sampler_result", Call("textureSample", texture_name, sampler_name,
+                                    coords_name, array_index)));
+  stmts.emplace_back(Return());
+
+  return Func(func_name, ast::VariableList(), ty.void_(), stmts, decorations);
+}
+
+ast::Function* InspectorBuilder::MakeComparisonSamplerReferenceBodyFunction(
+    const std::string& func_name,
+    const std::string& texture_name,
+    const std::string& sampler_name,
+    const std::string& coords_name,
+    const std::string& depth_name,
+    ast::Type* base_type,
+    ast::DecorationList decorations) {
+  std::string result_name = "sampler_result";
+
+  ast::StatementList stmts;
+
+  stmts.emplace_back(Decl(Var("sampler_result", base_type)));
+  stmts.emplace_back(
+      Assign("sampler_result", Call("textureSampleCompare", texture_name,
+                                    sampler_name, coords_name, depth_name)));
+  stmts.emplace_back(Return());
+
+  return Func(func_name, ast::VariableList(), ty.void_(), stmts, decorations);
+}
+
+ast::Type* InspectorBuilder::GetBaseType(
+    ResourceBinding::SampledKind sampled_kind) {
+  switch (sampled_kind) {
+    case ResourceBinding::SampledKind::kFloat:
+      return ty.f32();
+    case ResourceBinding::SampledKind::kSInt:
+      return ty.i32();
+    case ResourceBinding::SampledKind::kUInt:
+      return ty.u32();
+    default:
+      return nullptr;
+  }
+}
+
+ast::Type* InspectorBuilder::GetCoordsType(ast::TextureDimension dim,
+                                           ast::Type* scalar) {
+  switch (dim) {
+    case ast::TextureDimension::k1d:
+      return scalar;
+    case ast::TextureDimension::k2d:
+    case ast::TextureDimension::k2dArray:
+      return create<ast::Vector>(scalar, 2);
+    case ast::TextureDimension::k3d:
+    case ast::TextureDimension::kCube:
+    case ast::TextureDimension::kCubeArray:
+      return create<ast::Vector>(scalar, 3);
+    default:
+      [=]() { FAIL() << "Unsupported texture dimension: " << dim; }();
+  }
+  return nullptr;
+}
+
+ast::Type* InspectorBuilder::MakeStorageTextureTypes(ast::TextureDimension dim,
+                                                     ast::ImageFormat format,
+                                                     bool read_only) {
+  auto access = read_only ? ast::Access::kRead : ast::Access::kWrite;
+  return ty.storage_texture(dim, format, access);
+}
+
+void InspectorBuilder::AddStorageTexture(const std::string& name,
+                                         ast::Type* type,
+                                         uint32_t group,
+                                         uint32_t binding) {
+  Global(name, type,
+         ast::DecorationList{
+             create<ast::BindingDecoration>(binding),
+             create<ast::GroupDecoration>(group),
+         });
+}
+
+ast::Function* InspectorBuilder::MakeStorageTextureBodyFunction(
+    const std::string& func_name,
+    const std::string& st_name,
+    ast::Type* dim_type,
+    ast::DecorationList decorations) {
+  ast::StatementList stmts;
+
+  stmts.emplace_back(Decl(Var("dim", dim_type)));
+  stmts.emplace_back(Assign("dim", Call("textureDimensions", st_name)));
+  stmts.emplace_back(Return());
+
+  return Func(func_name, ast::VariableList(), ty.void_(), stmts, decorations);
+}
+
+std::function<ast::Type*()> InspectorBuilder::GetTypeFunction(
+    ComponentType component,
+    CompositionType composition) {
+  std::function<ast::Type*()> func;
+  switch (component) {
+    case ComponentType::kFloat:
+      func = [this]() -> ast::Type* { return ty.f32(); };
+      break;
+    case ComponentType::kSInt:
+      func = [this]() -> ast::Type* { return ty.i32(); };
+      break;
+    case ComponentType::kUInt:
+      func = [this]() -> ast::Type* { return ty.u32(); };
+      break;
+    case ComponentType::kUnknown:
+      return []() -> ast::Type* { return nullptr; };
+  }
+
+  uint32_t n;
+  switch (composition) {
+    case CompositionType::kScalar:
+      return func;
+    case CompositionType::kVec2:
+      n = 2;
+      break;
+    case CompositionType::kVec3:
+      n = 3;
+      break;
+    case CompositionType::kVec4:
+      n = 4;
+      break;
+    default:
+      return []() -> ast::Type* { return nullptr; };
+  }
+
+  return [this, func, n]() -> ast::Type* { return ty.vec(func(), n); };
+}
+
+Inspector& InspectorBuilder::Build() {
+  if (inspector_) {
+    return *inspector_;
+  }
+  program_ = std::make_unique<Program>(std::move(*this));
+  [&]() {
+    ASSERT_TRUE(program_->IsValid())
+        << diag::Formatter().format(program_->Diagnostics());
+  }();
+  inspector_ = std::make_unique<Inspector>(program_.get());
+  return *inspector_;
+}
+
+}  // namespace inspector
+}  // namespace tint
diff --git a/src/inspector/test_inspector_builder.h b/src/inspector/test_inspector_builder.h
new file mode 100644
index 0000000..3d1353d
--- /dev/null
+++ b/src/inspector/test_inspector_builder.h
@@ -0,0 +1,434 @@
+// Copyright 2021 The Tint 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 SRC_INSPECTOR_TEST_INSPECTOR_BUILDER_H_
+#define SRC_INSPECTOR_TEST_INSPECTOR_BUILDER_H_
+
+#include <memory>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include "src/ast/call_statement.h"
+#include "src/ast/disable_validation_decoration.h"
+#include "src/ast/override_decoration.h"
+#include "src/ast/stage_decoration.h"
+#include "src/ast/struct_block_decoration.h"
+#include "src/ast/workgroup_decoration.h"
+#include "src/program_builder.h"
+#include "src/sem/depth_texture_type.h"
+#include "src/sem/external_texture_type.h"
+#include "src/sem/multisampled_texture_type.h"
+#include "src/sem/sampled_texture_type.h"
+#include "src/sem/variable.h"
+#include "tint/tint.h"
+
+namespace tint {
+namespace inspector {
+
+/// Utility class for building programs in inspector tests
+class InspectorBuilder : public ProgramBuilder {
+ public:
+  InspectorBuilder();
+  ~InspectorBuilder() override;
+
+  /// Generates an empty function
+  /// @param name name of the function created
+  /// @param decorations the function decorations
+  void MakeEmptyBodyFunction(std::string name, ast::DecorationList decorations);
+
+  /// Generates a function that calls other functions
+  /// @param caller name of the function created
+  /// @param callees names of the functions to be called
+  /// @param decorations the function decorations
+  void MakeCallerBodyFunction(std::string caller,
+                              std::vector<std::string> callees,
+                              ast::DecorationList decorations);
+
+  /// Generates a struct that contains user-defined IO members
+  /// @param name the name of the generated struct
+  /// @param inout_vars tuples of {name, loc} that will be the struct members
+  /// @returns a structure object
+  ast::Struct* MakeInOutStruct(
+      std::string name,
+      std::vector<std::tuple<std::string, uint32_t>> inout_vars);
+
+  // TODO(crbug.com/tint/697): Remove this.
+  /// 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 AddInOutVariables(
+      std::vector<std::tuple<std::string, std::string>> inout_vars);
+
+  // TODO(crbug.com/tint/697): Remove this.
+  /// 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
+  /// @param decorations the function decorations
+  void MakeInOutVariableBodyFunction(
+      std::string name,
+      std::vector<std::tuple<std::string, std::string>> inout_vars,
+      ast::DecorationList decorations);
+
+  // TODO(crbug.com/tint/697): Remove this.
+  /// 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
+  /// @param decorations the function decorations
+  /// @returns a function object
+  ast::Function* MakeInOutVariableCallerBodyFunction(
+      std::string caller,
+      std::string callee,
+      std::vector<std::tuple<std::string, std::string>> inout_vars,
+      ast::DecorationList decorations);
+
+  /// Add a pipeline constant to the global variables, with a specific ID.
+  /// @param name name of the variable to add
+  /// @param id id number for the constant id
+  /// @param type type of the variable
+  /// @param val value to initialize the variable with, if NULL no initializer
+  ///            will be added.
+  /// @returns the constant that was created
+  template <class T>
+  ast::Variable* AddOverridableConstantWithID(std::string name,
+                                              uint32_t id,
+                                              ast::Type* type,
+                                              T* val) {
+    ast::Expression* constructor = nullptr;
+    if (val) {
+      constructor = Expr(*val);
+    }
+    return GlobalConst(name, type, constructor,
+                       ast::DecorationList{
+                           Override(id),
+                       });
+  }
+
+  /// Add a pipeline constant to the global variables, without a specific ID.
+  /// @param name name of the variable to add
+  /// @param type type of the variable
+  /// @param val value to initialize the variable with, if NULL no initializer
+  ///            will be added.
+  /// @returns the constant that was created
+  template <class T>
+  ast::Variable* AddOverridableConstantWithoutID(std::string name,
+                                                 ast::Type* type,
+                                                 T* val) {
+    ast::Expression* constructor = nullptr;
+    if (val) {
+      constructor = Expr(*val);
+    }
+    return GlobalConst(name, type, constructor,
+                       ast::DecorationList{
+                           Override(),
+                       });
+  }
+
+  /// Generates a function that references module constant
+  /// @param func name of the function created
+  /// @param var name of the constant to be reference
+  /// @param type type of the const being referenced
+  /// @param decorations the function decorations
+  /// @returns a function object
+  ast::Function* MakeConstReferenceBodyFunction(
+      std::string func,
+      std::string var,
+      ast::Type* type,
+      ast::DecorationList decorations);
+
+  /// @param vec Vector of StageVariable to be searched
+  /// @param name Name to be searching for
+  /// @returns true if name is in vec, otherwise false
+  bool ContainsName(const std::vector<StageVariable>& vec,
+                    const std::string& name);
+
+  /// Builds a string for accessing a member in a generated struct
+  /// @param idx index of member
+  /// @param type type of member
+  /// @returns a string for the member
+  std::string StructMemberName(size_t idx, ast::Type* type);
+
+  /// Generates a struct type
+  /// @param name name for the type
+  /// @param member_types a vector of member types
+  /// @param is_block whether or not to decorate as a Block
+  /// @returns a struct type
+  ast::Struct* MakeStructType(const std::string& name,
+                              std::vector<ast::Type*> member_types,
+                              bool is_block);
+
+  /// Generates types appropriate for using in an uniform buffer
+  /// @param name name for the type
+  /// @param member_types a vector of member types
+  /// @returns a struct type that has the layout for an uniform buffer.
+  ast::Struct* MakeUniformBufferType(const std::string& name,
+                                     std::vector<ast::Type*> member_types);
+
+  /// Generates types appropriate for using in a storage buffer
+  /// @param name name for the type
+  /// @param member_types a vector of member types
+  /// @returns a function that returns the created structure.
+  std::function<ast::TypeName*()> MakeStorageBufferTypes(
+      const std::string& name,
+      std::vector<ast::Type*> member_types);
+
+  /// Adds an uniform buffer variable to the program
+  /// @param name the name of the variable
+  /// @param type the type to use
+  /// @param group the binding/group/ to use for the uniform buffer
+  /// @param binding the binding number to use for the uniform buffer
+  void AddUniformBuffer(const std::string& name,
+                        ast::Type* type,
+                        uint32_t group,
+                        uint32_t binding);
+
+  /// Adds a storage buffer variable to the program
+  /// @param name the name of the variable
+  /// @param type the type to use
+  /// @param access the storage buffer access control
+  /// @param group the binding/group to use for the storage buffer
+  /// @param binding the binding number to use for the storage buffer
+  void AddStorageBuffer(const std::string& name,
+                        ast::Type* type,
+                        ast::Access access,
+                        uint32_t group,
+                        uint32_t binding);
+
+  /// Generates a function that references a specific struct variable
+  /// @param func_name name of the function created
+  /// @param struct_name name of the struct variabler to be accessed
+  /// @param members list of members to access, by index and type
+  void MakeStructVariableReferenceBodyFunction(
+      std::string func_name,
+      std::string struct_name,
+      std::vector<std::tuple<size_t, ast::Type*>> members);
+
+  /// Adds a regular sampler variable to the program
+  /// @param name the name of the variable
+  /// @param group the binding/group to use for the storage buffer
+  /// @param binding the binding number to use for the storage buffer
+  void AddSampler(const std::string& name, uint32_t group, uint32_t binding);
+
+  /// Adds a comparison sampler variable to the program
+  /// @param name the name of the variable
+  /// @param group the binding/group to use for the storage buffer
+  /// @param binding the binding number to use for the storage buffer
+  void AddComparisonSampler(const std::string& name,
+                            uint32_t group,
+                            uint32_t binding);
+
+  /// Generates a SampledTexture appropriate for the params
+  /// @param dim the dimensions of the texture
+  /// @param type the data type of the sampled texture
+  /// @returns the generated SampleTextureType
+  ast::SampledTexture* MakeSampledTextureType(ast::TextureDimension dim,
+                                              ast::Type* type);
+
+  /// Generates a DepthTexture appropriate for the params
+  /// @param dim the dimensions of the texture
+  /// @returns the generated DepthTexture
+  ast::DepthTexture* MakeDepthTextureType(ast::TextureDimension dim);
+
+  /// Generates a MultisampledTexture appropriate for the params
+  /// @param dim the dimensions of the texture
+  /// @param type the data type of the sampled texture
+  /// @returns the generated SampleTextureType
+  ast::MultisampledTexture* MakeMultisampledTextureType(
+      ast::TextureDimension dim,
+      ast::Type* type);
+
+  /// Generates an ExternalTexture appropriate for the params
+  /// @returns the generated ExternalTexture
+  ast::ExternalTexture* MakeExternalTextureType();
+
+  /// Adds a sampled texture variable to the program
+  /// @param name the name of the variable
+  /// @param type the type to use
+  /// @param group the binding/group to use for the sampled texture
+  /// @param binding the binding number to use for the sampled texture
+  void AddSampledTexture(const std::string& name,
+                         ast::Type* type,
+                         uint32_t group,
+                         uint32_t binding);
+
+  /// Adds a multi-sampled texture variable to the program
+  /// @param name the name of the variable
+  /// @param type the type to use
+  /// @param group the binding/group to use for the multi-sampled texture
+  /// @param binding the binding number to use for the multi-sampled texture
+  void AddMultisampledTexture(const std::string& name,
+                              ast::Type* type,
+                              uint32_t group,
+                              uint32_t binding);
+
+  /// Add a module scope private variable to the progames
+  /// @param name the name of the variable
+  /// @param type the type to use
+  void AddGlobalVariable(const std::string& name, ast::Type* type);
+
+  /// Adds a depth texture variable to the program
+  /// @param name the name of the variable
+  /// @param type the type to use
+  /// @param group the binding/group to use for the depth texture
+  /// @param binding the binding number to use for the depth texture
+  void AddDepthTexture(const std::string& name,
+                       ast::Type* type,
+                       uint32_t group,
+                       uint32_t binding);
+
+  /// Adds an external texture variable to the program
+  /// @param name the name of the variable
+  /// @param type the type to use
+  /// @param group the binding/group to use for the external texture
+  /// @param binding the binding number to use for the external texture
+  void AddExternalTexture(const std::string& name,
+                          ast::Type* type,
+                          uint32_t group,
+                          uint32_t binding);
+
+  /// Generates a function that references a specific sampler variable
+  /// @param func_name name of the function created
+  /// @param texture_name name of the texture to be sampled
+  /// @param sampler_name name of the sampler to use
+  /// @param coords_name name of the coords variable to use
+  /// @param base_type sampler base type
+  /// @param decorations the function decorations
+  /// @returns a function that references all of the values specified
+  ast::Function* MakeSamplerReferenceBodyFunction(
+      const std::string& func_name,
+      const std::string& texture_name,
+      const std::string& sampler_name,
+      const std::string& coords_name,
+      ast::Type* base_type,
+      ast::DecorationList decorations);
+
+  /// Generates a function that references a specific sampler variable
+  /// @param func_name name of the function created
+  /// @param texture_name name of the texture to be sampled
+  /// @param sampler_name name of the sampler to use
+  /// @param coords_name name of the coords variable to use
+  /// @param array_index name of the array index variable to use
+  /// @param base_type sampler base type
+  /// @param decorations the function decorations
+  /// @returns a function that references all of the values specified
+  ast::Function* MakeSamplerReferenceBodyFunction(
+      const std::string& func_name,
+      const std::string& texture_name,
+      const std::string& sampler_name,
+      const std::string& coords_name,
+      const std::string& array_index,
+      ast::Type* base_type,
+      ast::DecorationList decorations);
+
+  /// Generates a function that references a specific comparison sampler
+  /// variable.
+  /// @param func_name name of the function created
+  /// @param texture_name name of the depth texture to  use
+  /// @param sampler_name name of the sampler to use
+  /// @param coords_name name of the coords variable to use
+  /// @param depth_name name of the depth reference to use
+  /// @param base_type sampler base type
+  /// @param decorations the function decorations
+  /// @returns a function that references all of the values specified
+  ast::Function* MakeComparisonSamplerReferenceBodyFunction(
+      const std::string& func_name,
+      const std::string& texture_name,
+      const std::string& sampler_name,
+      const std::string& coords_name,
+      const std::string& depth_name,
+      ast::Type* base_type,
+      ast::DecorationList decorations);
+
+  /// Gets an appropriate type for the data in a given texture type.
+  /// @param sampled_kind type of in the texture
+  /// @returns a pointer to a type appropriate for the coord param
+  ast::Type* GetBaseType(ResourceBinding::SampledKind sampled_kind);
+
+  /// Gets an appropriate type for the coords parameter depending the the
+  /// dimensionality of the texture being sampled.
+  /// @param dim dimensionality of the texture being sampled
+  /// @param scalar the scalar type
+  /// @returns a pointer to a type appropriate for the coord param
+  ast::Type* GetCoordsType(ast::TextureDimension dim, ast::Type* scalar);
+
+  /// Generates appropriate types for a Read-Only StorageTexture
+  /// @param dim the texture dimension of the storage texture
+  /// @param format the image format of the storage texture
+  /// @param read_only should the access type be read only, otherwise write only
+  /// @returns the storage texture type
+  ast::Type* MakeStorageTextureTypes(ast::TextureDimension dim,
+                                     ast::ImageFormat format,
+                                     bool read_only);
+
+  /// Adds a storage texture variable to the program
+  /// @param name the name of the variable
+  /// @param type the type to use
+  /// @param group the binding/group to use for the sampled texture
+  /// @param binding the binding57 number to use for the sampled texture
+  void AddStorageTexture(const std::string& name,
+                         ast::Type* type,
+                         uint32_t group,
+                         uint32_t binding);
+
+  /// Generates a function that references a storage texture variable.
+  /// @param func_name name of the function created
+  /// @param st_name name of the storage texture to use
+  /// @param dim_type type expected by textureDimensons to return
+  /// @param decorations the function decorations
+  /// @returns a function that references all of the values specified
+  ast::Function* MakeStorageTextureBodyFunction(
+      const std::string& func_name,
+      const std::string& st_name,
+      ast::Type* dim_type,
+      ast::DecorationList decorations);
+
+  /// Get a generator function that returns a type appropriate for a stage
+  /// variable with the given combination of component and composition type.
+  /// @param component component type of the stage variable
+  /// @param composition composition type of the stage variable
+  /// @returns a generator function for the stage variable's type.
+  std::function<ast::Type*()> GetTypeFunction(ComponentType component,
+                                              CompositionType composition);
+
+  /// Build the Program given all of the previous methods called and return an
+  /// Inspector for it.
+  /// Should only be called once per test.
+  /// @returns a reference to the Inspector for the built Program.
+  Inspector& Build();
+
+  /// @returns the type for a SamplerKind::kSampler
+  ast::Sampler* sampler_type() {
+    return ty.sampler(ast::SamplerKind::kSampler);
+  }
+
+  /// @returns the type for a SamplerKind::kComparison
+  ast::Sampler* comparison_sampler_type() {
+    return ty.sampler(ast::SamplerKind::kComparisonSampler);
+  }
+
+ protected:
+  /// Program built by this builder.
+  std::unique_ptr<Program> program_;
+  /// Inspector for |program_|
+  std::unique_ptr<Inspector> inspector_;
+};
+
+}  // namespace inspector
+}  // namespace tint
+
+#endif  // SRC_INSPECTOR_TEST_INSPECTOR_BUILDER_H_
diff --git a/test/BUILD.gn b/test/BUILD.gn
index 2fd1d31..f80e072 100644
--- a/test/BUILD.gn
+++ b/test/BUILD.gn
@@ -221,6 +221,8 @@
     "../src/diagnostic/formatter_test.cc",
     "../src/diagnostic/printer_test.cc",
     "../src/inspector/inspector_test.cc",
+    "../src/inspector/test_inspector_builder.cc",
+    "../src/inspector/test_inspector_builder.h",
     "../src/intrinsic_table_test.cc",
     "../src/program_builder_test.cc",
     "../src/program_test.cc",