text: #pragma once
  
  #include <memory>
  
  #include "source/extensions/filters/common/lua/lua.h"
  
  #include "test/mocks/thread_local/mocks.h"
  
  #include "gmock/gmock.h"
  
  using testing::NiceMock;
  
  namespace Envoy {
  namespace Extensions {
  namespace Filters {
  namespace Common {
  namespace Lua {
  
  // A helper to be called inside the registered closure.
  class Printer {
  public:
    MOCK_METHOD(void, testPrint, (const std::string&), (const));
  };
  
  Printer& getPrinter() { MUTABLE_CONSTRUCT_ON_FIRST_USE(Printer); }
  
  template <class T> class LuaWrappersTestBase : public testing::Test {
  public:
    virtual void setup(const std::string& code) {
      coroutine_.reset();
      state_ = std::make_unique<ThreadLocalState>(code, tls_);
      state_->registerType<T>();
      coroutine_ = state_->createCoroutine();
      lua_pushcclosure(coroutine_->luaState(), luaTestPrint, 1);
      lua_setglobal(coroutine_->luaState(), "testPrint");
      testing::Mock::AllowLeak(&printer_);
    }
  
    void TearDown() override { testing::Mock::VerifyAndClear(&printer_); }
  
    void start(const std::string& method) {
      coroutine_->start(state_->getGlobalRef(state_->registerGlobal(method, initializers_)), 1,
                        yield_callback_);
    }
  
    static int luaTestPrint(lua_State* state) {
      const char* message = luaL_checkstring(state, 1);
      getPrinter().testPrint(message);
      return 0;
    }
  
    NiceMock<ThreadLocal::MockInstance> tls_;
    ThreadLocalStatePtr state_;
    std::function<void()> yield_callback_;
    CoroutinePtr coroutine_;
    Printer& printer_{getPrinter()};
    InitializerList initializers_;
  };
  
  } // namespace Lua
  } // namespace Common
  } // namespace Filters
  } // namespace Extensions
  } // namespace Envoy
  