Move class EmbedderTestEnvironment to standalone V8TestEnvironment.

This will then allow tests involving garbage-collected objects to
be lighter-weight unittests, rather than embeddertests. In particular,
an object with an existing unit test shouldn't have to become an
embeddertest when it becomes gc'd.

-- add environment setup to unittest main.
-- move two tests back to unittest as a result.

Change-Id: I25c205f3a9dc12854230ed851aebf7d3cb553ace
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/71810
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index f22617e..24d61e1 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -254,6 +254,7 @@
     configs += [ "//v8:external_startup_data" ]
     deps += [
       "fxjs:unittests",
+      "testing:pdfium_v8_gtest_support",
       "//v8",
     ]
   }
@@ -289,6 +290,9 @@
     "fxjs/*",
     "xfa/*",
   ]
+  if (pdf_enable_v8) {
+    public_deps += [ "testing:pdfium_v8_gtest_support" ]
+  }
 }
 
 test("pdfium_embeddertests") {
diff --git a/fxjs/BUILD.gn b/fxjs/BUILD.gn
index c7f42d7..bfff222 100644
--- a/fxjs/BUILD.gn
+++ b/fxjs/BUILD.gn
@@ -229,6 +229,19 @@
     configs = [ "//v8:external_startup_data" ]
     deps = [ ":fxjs" ]
     pdfium_root_dir = "../"
+    if (pdf_enable_xfa) {
+      sources += [
+        "gc/fxgc_unittest.cpp",
+        "gc/fxgc_unittest.h",
+        "gc/gced_tree_node_unittest.cpp",
+        "gc/heap_unittest.cpp",
+      ]
+      deps += [
+        "../testing:pdfium_v8_gtest_support",
+        "../testing:unit_test_support",
+        "//v8:cppgc",
+      ]
+    }
   }
 
   pdfium_embeddertest_source_set("embeddertests") {
@@ -244,8 +257,6 @@
     pdfium_root_dir = "../"
     if (pdf_enable_xfa) {
       sources += [
-        "gc/gced_tree_node_embeddertest.cpp",
-        "gc/heap_embeddertest.cpp",
         "xfa/cfxjse_app_embeddertest.cpp",
         "xfa/cfxjse_formcalc_context_embeddertest.cpp",
         "xfa/cfxjse_value_embeddertest.cpp",
diff --git a/fxjs/cfx_v8_unittest.cpp b/fxjs/cfx_v8_unittest.cpp
index 226a8e2..c969009 100644
--- a/fxjs/cfx_v8_unittest.cpp
+++ b/fxjs/cfx_v8_unittest.cpp
@@ -20,6 +20,7 @@
   CFXV8UnitTest() = default;
   ~CFXV8UnitTest() override = default;
 
+  // FXV8UnitTest:
   void SetUp() override {
     FXV8UnitTest::SetUp();
     cfx_v8_ = std::make_unique<CFX_V8>(isolate());
diff --git a/fxjs/cfxjs_engine_unittest.cpp b/fxjs/cfxjs_engine_unittest.cpp
index 6566d82..b1a2f85 100644
--- a/fxjs/cfxjs_engine_unittest.cpp
+++ b/fxjs/cfxjs_engine_unittest.cpp
@@ -15,12 +15,12 @@
   FXJSEngineUnitTest() = default;
   ~FXJSEngineUnitTest() override = default;
 
+  // FXV8UnitTest:
   void SetUp() override {
     FXV8UnitTest::SetUp();
     FXJS_Initialize(1, isolate());
     engine_ = std::make_unique<CFXJS_Engine>(isolate());
   }
-
   void TearDown() override { FXJS_Release(); }
 
   CFXJS_Engine* engine() const { return engine_.get(); }
diff --git a/fxjs/gc/fxgc_unittest.cpp b/fxjs/gc/fxgc_unittest.cpp
new file mode 100644
index 0000000..74a329b
--- /dev/null
+++ b/fxjs/gc/fxgc_unittest.cpp
@@ -0,0 +1,26 @@
+// Copyright 2020 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "fxjs/gc/fxgc_unittest.h"
+
+#include "fxjs/gc/heap.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/v8_test_environment.h"
+
+FXGCUnitTest::FXGCUnitTest() = default;
+
+FXGCUnitTest::~FXGCUnitTest() = default;
+
+void FXGCUnitTest::SetUp() {
+  FXV8UnitTest::SetUp();
+  FXGC_Initialize(V8TestEnvironment::GetInstance()->platform());
+  heap_ = FXGC_CreateHeap();
+  ASSERT_TRUE(heap_);
+}
+
+void FXGCUnitTest::TearDown() {
+  heap_.reset();
+  FXGC_Release();
+  FXV8UnitTest::TearDown();
+}
diff --git a/fxjs/gc/fxgc_unittest.h b/fxjs/gc/fxgc_unittest.h
new file mode 100644
index 0000000..6467e3b
--- /dev/null
+++ b/fxjs/gc/fxgc_unittest.h
@@ -0,0 +1,26 @@
+// Copyright 2020 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FXJS_GC_FXGC_UNITTEST_H_
+#define FXJS_GC_FXGC_UNITTEST_H_
+
+#include "fxjs/fxv8_unittest.h"
+#include "fxjs/gc/heap.h"
+
+class FXGCUnitTest : public FXV8UnitTest {
+ public:
+  FXGCUnitTest();
+  ~FXGCUnitTest() override;
+
+  // FXV8UnitTest:
+  void SetUp() override;
+  void TearDown() override;
+
+  cppgc::Heap* heap() const { return heap_.get(); }
+
+ private:
+  FXGCScopedHeap heap_;
+};
+
+#endif  // FXJS_GC_FXGC_UNITTEST_H_
diff --git a/fxjs/gc/gced_tree_node_embeddertest.cpp b/fxjs/gc/gced_tree_node_unittest.cpp
similarity index 79%
rename from fxjs/gc/gced_tree_node_embeddertest.cpp
rename to fxjs/gc/gced_tree_node_unittest.cpp
index cbda307..2fbc592 100644
--- a/fxjs/gc/gced_tree_node_embeddertest.cpp
+++ b/fxjs/gc/gced_tree_node_unittest.cpp
@@ -7,9 +7,10 @@
 #include <map>
 
 #include "core/fxcrt/observed_ptr.h"
+#include "fxjs/gc/fxgc_unittest.h"
 #include "fxjs/gc/heap.h"
-#include "testing/gced_embeddertest.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "testing/v8_test_environment.h"
 #include "third_party/base/stl_util.h"
 #include "v8/include/cppgc/allocation.h"
 #include "v8/include/cppgc/persistent.h"
@@ -28,39 +29,36 @@
 
 }  // namespace
 
-class GCedTreeNodeEmbedderTest : public GCedEmbedderTest {
+class GCedTreeNodeUnitTest : public FXGCUnitTest {
  public:
   static cppgc::Persistent<ObservableGCedTreeNodeForTest> s_root;
 
-  void SetUp() override {
-    GCedEmbedderTest::SetUp();
-    heap_ = FXGC_CreateHeap();
-  }
+  GCedTreeNodeUnitTest() = default;
+  ~GCedTreeNodeUnitTest() override = default;
 
+  // FXGCUnitTest:
   void TearDown() override {
     s_root = nullptr;  // Can't (yet) outlive |heap_|.
-    heap_.reset();
-    GCedEmbedderTest::TearDown();
+    FXGCUnitTest::TearDown();
   }
 
-  cppgc::Heap* heap() const { return heap_.get(); }
   ObservableGCedTreeNodeForTest* CreateNode() {
     return cppgc::MakeGarbageCollected<ObservableGCedTreeNodeForTest>(
-        heap_->GetAllocationHandle());
+        heap()->GetAllocationHandle());
   }
 
   void ForceGCAndPump() {
     heap()->ForceGarbageCollectionSlow(
-        "GCedTreeNodeEmbedderTest", "test",
+        "GCedTreeNodeUnitTest", "test",
         cppgc::Heap::StackState::kNoHeapPointers);
-    PumpPlatformMessageLoop();
+    V8TestEnvironment::PumpPlatformMessageLoop(isolate());
   }
 
   void AddClutterToFront(ObservableGCedTreeNodeForTest* parent) {
     for (int i = 0; i < 4; ++i) {
       parent->AppendFirstChild(
           cppgc::MakeGarbageCollected<ObservableGCedTreeNodeForTest>(
-              heap_->GetAllocationHandle()));
+              heap()->GetAllocationHandle()));
     }
   }
 
@@ -68,7 +66,7 @@
     for (int i = 0; i < 4; ++i) {
       parent->AppendLastChild(
           cppgc::MakeGarbageCollected<ObservableGCedTreeNodeForTest>(
-              heap_->GetAllocationHandle()));
+              heap()->GetAllocationHandle()));
     }
   }
 
@@ -76,23 +74,22 @@
   FXGCScopedHeap heap_;
 };
 
-cppgc::Persistent<ObservableGCedTreeNodeForTest>
-    GCedTreeNodeEmbedderTest::s_root;
+cppgc::Persistent<ObservableGCedTreeNodeForTest> GCedTreeNodeUnitTest::s_root;
 
-TEST_F(GCedTreeNodeEmbedderTest, OneRefence) {
+TEST_F(GCedTreeNodeUnitTest, OneRefence) {
   s_root = CreateNode();
   ObservedPtr<ObservableGCedTreeNodeForTest> watcher(s_root);
   ForceGCAndPump();
   EXPECT_TRUE(watcher);
 }
 
-TEST_F(GCedTreeNodeEmbedderTest, NoReferences) {
+TEST_F(GCedTreeNodeUnitTest, NoReferences) {
   ObservedPtr<ObservableGCedTreeNodeForTest> watcher(CreateNode());
   ForceGCAndPump();
   EXPECT_FALSE(watcher);
 }
 
-TEST_F(GCedTreeNodeEmbedderTest, FirstHasParent) {
+TEST_F(GCedTreeNodeUnitTest, FirstHasParent) {
   s_root = CreateNode();
   ObservedPtr<ObservableGCedTreeNodeForTest> watcher(CreateNode());
   s_root->AppendFirstChild(watcher.Get());
@@ -118,7 +115,7 @@
   EXPECT_FALSE(watcher);
 }
 
-TEST_F(GCedTreeNodeEmbedderTest, RemoveSelf) {
+TEST_F(GCedTreeNodeUnitTest, RemoveSelf) {
   s_root = CreateNode();
   ObservedPtr<ObservableGCedTreeNodeForTest> watcher(CreateNode());
   s_root->AppendFirstChild(watcher.Get());
@@ -131,7 +128,7 @@
   EXPECT_FALSE(watcher);
 }
 
-TEST_F(GCedTreeNodeEmbedderTest, InsertBeforeAfter) {
+TEST_F(GCedTreeNodeUnitTest, InsertBeforeAfter) {
   s_root = CreateNode();
   AddClutterToFront(s_root);
   ObservedPtr<ObservableGCedTreeNodeForTest> watcher(CreateNode());
@@ -147,7 +144,7 @@
   EXPECT_FALSE(watcher);
 }
 
-TEST_F(GCedTreeNodeEmbedderTest, AsMapKey) {
+TEST_F(GCedTreeNodeUnitTest, AsMapKey) {
   std::map<cppgc::Persistent<ObservableGCedTreeNodeForTest>, int> score;
   ObservableGCedTreeNodeForTest* node = CreateNode();
   score[node] = 100;
diff --git a/fxjs/gc/heap_embeddertest.cpp b/fxjs/gc/heap_unittest.cpp
similarity index 91%
rename from fxjs/gc/heap_embeddertest.cpp
rename to fxjs/gc/heap_unittest.cpp
index b7cac35..33e1bb1 100644
--- a/fxjs/gc/heap_embeddertest.cpp
+++ b/fxjs/gc/heap_unittest.cpp
@@ -8,7 +8,7 @@
 #include <set>
 
 #include "core/fxcrt/autorestorer.h"
-#include "testing/gced_embeddertest.h"
+#include "fxjs/gc/fxgc_unittest.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/base/stl_util.h"
 #include "v8/include/cppgc/allocation.h"
@@ -48,15 +48,19 @@
 
 }  // namespace
 
-class HeapEmbedderTest : public GCedEmbedderTest {
+class HeapUnitTest : public FXGCUnitTest {
  public:
+  HeapUnitTest() = default;
+  ~HeapUnitTest() override = default;
+
+  // FXGCUnitTest:
   void TearDown() override {
     PseudoCollectible::ClearCounts();
-    GCedEmbedderTest::TearDown();
+    FXGCUnitTest::TearDown();
   }
 };
 
-TEST_F(HeapEmbedderTest, SeveralHeaps) {
+TEST_F(HeapUnitTest, SeveralHeaps) {
   FXGCScopedHeap heap1 = FXGC_CreateHeap();
   EXPECT_TRUE(heap1);
 
@@ -73,7 +77,7 @@
   EXPECT_FALSE(heap3);
 }
 
-TEST_F(HeapEmbedderTest, NoReferences) {
+TEST_F(HeapUnitTest, NoReferences) {
   FXGCScopedHeap heap1 = FXGC_CreateHeap();
   ASSERT_TRUE(heap1);
   {
@@ -95,7 +99,7 @@
   EXPECT_EQ(1u, PseudoCollectible::DeadCount());
 }
 
-TEST_F(HeapEmbedderTest, HasReferences) {
+TEST_F(HeapUnitTest, HasReferences) {
   FXGCScopedHeap heap1 = FXGC_CreateHeap();
   ASSERT_TRUE(heap1);
   {
@@ -121,7 +125,7 @@
 }
 
 // TODO(tsepez): enable when CPPGC fixes this segv.
-TEST_F(HeapEmbedderTest, DISABLED_DeleteHeapHasReferences) {
+TEST_F(HeapUnitTest, DISABLED_DeleteHeapHasReferences) {
   FXGCScopedHeap heap1 = FXGC_CreateHeap();
   ASSERT_TRUE(heap1);
   {
@@ -144,7 +148,7 @@
   }
 }
 
-TEST_F(HeapEmbedderTest, DeleteHeapNoReferences) {
+TEST_F(HeapUnitTest, DeleteHeapNoReferences) {
   FXGCScopedHeap heap1 = FXGC_CreateHeap();
   ASSERT_TRUE(heap1);
   {
diff --git a/testing/BUILD.gn b/testing/BUILD.gn
index 33ee6ec..4f04ce0 100644
--- a/testing/BUILD.gn
+++ b/testing/BUILD.gn
@@ -51,6 +51,27 @@
   }
 }
 
+if (pdf_enable_v8) {
+  source_set("pdfium_v8_gtest_support") {
+    testonly = true
+    sources = [
+      "v8_test_environment.cpp",
+      "v8_test_environment.h",
+    ]
+    deps = [
+      ":test_support",
+      "../core/fxcrt",
+      "//testing/gtest",
+      "//v8",
+      "//v8:v8_libplatform",
+    ]
+    configs += [
+      "../:pdfium_core_config",
+      "//v8:external_startup_data",
+    ]
+  }
+}
+
 source_set("unit_test_support") {
   testonly = true
   sources = []
@@ -58,17 +79,21 @@
   configs += [ "../:pdfium_core_config" ]
   visibility = [ "../*" ]
 
-  if (pdf_enable_xfa) {
-    sources += [
-      "xfa_unit_test_support.cpp",
-      "xfa_unit_test_support.h",
-    ]
-    deps += [
-      "../:pdfium",
-      "../core/fxge",
-      "../xfa/fgas",
-      "//testing/gtest",
-    ]
+  if (pdf_enable_v8) {
+    deps += [ ":pdfium_v8_gtest_support" ]
+
+    if (pdf_enable_xfa) {
+      sources += [
+        "xfa_unit_test_support.cpp",
+        "xfa_unit_test_support.h",
+      ]
+      deps += [
+        "../:pdfium",
+        "../core/fxge",
+        "../xfa/fgas",
+        "//testing/gtest",
+      ]
+    }
   }
 }
 
@@ -105,13 +130,14 @@
       "js_embedder_test.cpp",
       "js_embedder_test.h",
     ]
-    deps += [ "../fxjs" ]
+    deps += [
+      ":pdfium_v8_gtest_support",
+      "../fxjs",
+    ]
     configs += [ "//v8:external_startup_data" ]
 
     if (pdf_enable_xfa) {
       sources += [
-        "gced_embeddertest.cpp",
-        "gced_embeddertest.h",
         "xfa_js_embedder_test.cpp",
         "xfa_js_embedder_test.h",
       ]
diff --git a/testing/embedder_test.cpp b/testing/embedder_test.cpp
index ae09cf9..ccfd40c 100644
--- a/testing/embedder_test.cpp
+++ b/testing/embedder_test.cpp
@@ -27,15 +27,13 @@
 #include "third_party/base/stl_util.h"
 
 #ifdef PDF_ENABLE_V8
-#include "testing/v8_initializer.h"
+#include "testing/v8_test_environment.h"
 #include "v8/include/v8-platform.h"
 #include "v8/include/v8.h"
 #endif  // PDF_ENABLE_V8
 
 namespace {
 
-EmbedderTestEnvironment* g_environment = nullptr;
-
 int GetBitmapBytesPerPixel(FPDF_BITMAP bitmap) {
   return EmbedderTest::BytesPerPixelForFormat(FPDFBitmap_GetFormat(bitmap));
 }
@@ -54,55 +52,6 @@
 
 }  // namespace
 
-EmbedderTestEnvironment::EmbedderTestEnvironment(const char* exe_name)
-#ifdef PDF_ENABLE_V8
-    : exe_path_(exe_name)
-#endif
-{
-  ASSERT(!g_environment);
-  g_environment = this;
-}
-
-EmbedderTestEnvironment::~EmbedderTestEnvironment() {
-  ASSERT(g_environment);
-  g_environment = nullptr;
-
-#ifdef PDF_ENABLE_V8
-#ifdef V8_USE_EXTERNAL_STARTUP_DATA
-  if (v8_snapshot_)
-    free(const_cast<char*>(v8_snapshot_->data));
-#endif  // V8_USE_EXTERNAL_STARTUP_DATA
-#endif  // PDF_ENABLE_V8
-}
-
-// static
-EmbedderTestEnvironment* EmbedderTestEnvironment::GetInstance() {
-  return g_environment;
-}
-
-void EmbedderTestEnvironment::SetUp() {
-#ifdef PDF_ENABLE_V8
-#ifdef V8_USE_EXTERNAL_STARTUP_DATA
-  if (v8_snapshot_) {
-    platform_ =
-        InitializeV8ForPDFiumWithStartupData(exe_path_, std::string(), nullptr);
-  } else {
-    v8_snapshot_ = std::make_unique<v8::StartupData>();
-    platform_ = InitializeV8ForPDFiumWithStartupData(exe_path_, std::string(),
-                                                     v8_snapshot_.get());
-  }
-#else
-  platform_ = InitializeV8ForPDFium(exe_path_);
-#endif  // V8_USE_EXTERNAL_STARTUP_DATA
-#endif  // FPDF_ENABLE_V8
-}
-
-void EmbedderTestEnvironment::TearDown() {
-#ifdef PDF_ENABLE_V8
-  v8::V8::ShutdownPlatform();
-#endif  // PDF_ENABLE_V8
-}
-
 EmbedderTest::EmbedderTest()
     : default_delegate_(std::make_unique<EmbedderTest::Delegate>()),
       delegate_(default_delegate_.get()) {
@@ -119,7 +68,7 @@
   config.m_v8EmbedderSlot = 0;
   config.m_pIsolate = external_isolate_;
 #ifdef PDF_ENABLE_V8
-  config.m_pPlatform = EmbedderTestEnvironment::GetInstance()->platform();
+  config.m_pPlatform = V8TestEnvironment::GetInstance()->platform();
 #else   // PDF_ENABLE_V8
   config.m_pPlatform = nullptr;
 #endif  // PDF_ENABLE_V8
diff --git a/testing/embedder_test.h b/testing/embedder_test.h
index 45e2a5d..a1fbac8 100644
--- a/testing/embedder_test.h
+++ b/testing/embedder_test.h
@@ -23,44 +23,11 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/base/span.h"
 
-#ifdef PDF_ENABLE_V8
-namespace v8 {
-class Platform;
-#ifdef V8_USE_EXTERNAL_STARTUP_DATA
-class StartupData;
-#endif  // V8_USE_EXTERNAL_STARTUP_DATA
-}  // namespace v8
-#endif  // PDF_ENABLE_V8
-
 class TestLoader;
 
 // The loading time of the CFGAS_FontMgr is linear in the number of times it is
 // loaded. So, if a test suite has a lot of tests that need a font manager they
 // can end up executing very, very slowly.
-class EmbedderTestEnvironment final : public testing::Environment {
- public:
-  explicit EmbedderTestEnvironment(const char* exe_path);
-  ~EmbedderTestEnvironment() override;
-
-  // Note: does not create one if it does not exist.
-  static EmbedderTestEnvironment* GetInstance();
-
-  void SetUp() override;
-  void TearDown() override;
-
-#ifdef PDF_ENABLE_V8
-  v8::Platform* platform() const { return platform_.get(); }
-#endif  // PDF_ENABLE_V8
-
- private:
-#ifdef PDF_ENABLE_V8
-  const char* const exe_path_;
-#ifdef V8_USE_EXTERNAL_STARTUP_DATA
-  std::unique_ptr<v8::StartupData> v8_snapshot_;
-#endif  // V8_USE_EXTERNAL_STARTUP_DATA
-  std::unique_ptr<v8::Platform> platform_;
-#endif  // PDF_ENABLE_V8
-};
 
 // This class is used to load a PDF document, and then run programatic
 // API tests against it.
diff --git a/testing/embedder_test_main.cpp b/testing/embedder_test_main.cpp
index d4318d1..798c9cf 100644
--- a/testing/embedder_test_main.cpp
+++ b/testing/embedder_test_main.cpp
@@ -2,19 +2,25 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "build/build_config.h"
 #include "core/fxcrt/fx_memory.h"
-#include "testing/embedder_test.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+#ifdef PDF_ENABLE_V8
+#include "testing/v8_test_environment.h"
+#endif  // PDF_ENABLE_V8
+
 // Can't use gtest-provided main since we need to create our own
 // testing environment which needs the executable path in order to
 // find the external V8 binary data files.
 int main(int argc, char** argv) {
   FXMEM_InitializePartitionAlloc();
 
+#ifdef PDF_ENABLE_V8
   // The env will be deleted by gtest.
-  AddGlobalTestEnvironment(new EmbedderTestEnvironment(argv[0]));
+  AddGlobalTestEnvironment(new V8TestEnvironment(argv[0]));
+#endif  // PDF_ENABLE_V8
 
   testing::InitGoogleTest(&argc, argv);
   testing::InitGoogleMock(&argc, argv);
diff --git a/testing/gced_embeddertest.cpp b/testing/gced_embeddertest.cpp
deleted file mode 100644
index fb8d9c1..0000000
--- a/testing/gced_embeddertest.cpp
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2020 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "testing/gced_embeddertest.h"
-
-#include "public/fpdfview.h"
-#include "v8/include/cppgc/allocation.h"
-#include "v8/include/cppgc/persistent.h"
-#include "v8/include/libplatform/libplatform.h"
-#include "v8/include/v8.h"
-
-void GCedEmbedderTest::PumpPlatformMessageLoop() {
-  v8::Platform* platform = EmbedderTestEnvironment::GetInstance()->platform();
-  while (v8::platform::PumpMessageLoop(platform, isolate()))
-    continue;
-}
diff --git a/testing/gced_embeddertest.h b/testing/gced_embeddertest.h
deleted file mode 100644
index 2295bc0..0000000
--- a/testing/gced_embeddertest.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2020 PDFium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef TESTING_GCED_EMBEDDER_TEST_H_
-#define TESTING_GCED_EMBEDDER_TEST_H_
-
-#include <memory>
-
-#include "testing/js_embedder_test.h"
-
-class GCedEmbedderTest : public JSEmbedderTest {
- public:
-  void PumpPlatformMessageLoop();
-};
-
-#endif  // TESTING_GCED_EMBEDDER_TEST_H_
diff --git a/testing/unit_test_main.cpp b/testing/unit_test_main.cpp
index 5d50249..b36ac80 100644
--- a/testing/unit_test_main.cpp
+++ b/testing/unit_test_main.cpp
@@ -11,7 +11,7 @@
 #include "testing/test_support.h"
 
 #ifdef PDF_ENABLE_V8
-#include "testing/v8_initializer.h"
+#include "testing/v8_test_environment.h"
 #include "v8/include/v8-platform.h"
 #include "v8/include/v8.h"
 #endif  // PDF_ENABLE_V8
@@ -26,18 +26,14 @@
   FXMEM_InitializePartitionAlloc();
 
 #ifdef PDF_ENABLE_V8
-  std::unique_ptr<v8::Platform> platform;
-#ifdef V8_USE_EXTERNAL_STARTUP_DATA
-  static v8::StartupData* snapshot = new v8::StartupData;
-  platform =
-      InitializeV8ForPDFiumWithStartupData(argv[0], std::string(), snapshot);
-#else  // V8_USE_EXTERNAL_STARTUP_DATA
-  platform = InitializeV8ForPDFium(argv[0]);
-#endif  // V8_USE_EXTERNAL_STARTUP_DATA
+  // V8 test environment will be deleted by gtest.
+  AddGlobalTestEnvironment(new V8TestEnvironment(argv[0]));
 #endif  // PDF_ENABLE_V8
 
   InitializePDFTestEnvironment();
+
 #ifdef PDF_ENABLE_XFA
+  // XFA test environment will be deleted by gtest.
   InitializeXFATestEnvironment();
 #endif  // PDF_ENABLE_XFA
 
@@ -47,11 +43,7 @@
   int ret_val = RUN_ALL_TESTS();
 
   DestroyPDFTestEnvironment();
-  // NOTE: XFA test environment, if present, destroyed by gtest.
 
-#ifdef PDF_ENABLE_V8
-  v8::V8::ShutdownPlatform();
-#endif  // PDF_ENABLE_V8
 
   return ret_val;
 }
diff --git a/testing/v8_test_environment.cpp b/testing/v8_test_environment.cpp
new file mode 100644
index 0000000..08a8840
--- /dev/null
+++ b/testing/v8_test_environment.cpp
@@ -0,0 +1,66 @@
+// Copyright 2020 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/v8_test_environment.h"
+
+#include <string>
+
+#include "core/fxcrt/fx_system.h"
+#include "testing/v8_initializer.h"
+#include "v8/include/libplatform/libplatform.h"
+#include "v8/include/v8-platform.h"
+#include "v8/include/v8.h"
+
+namespace {
+
+V8TestEnvironment* g_environment = nullptr;
+
+}  // namespace
+
+V8TestEnvironment::V8TestEnvironment(const char* exe_name)
+    : exe_path_(exe_name) {
+  ASSERT(!g_environment);
+  g_environment = this;
+}
+
+V8TestEnvironment::~V8TestEnvironment() {
+  ASSERT(g_environment);
+  g_environment = nullptr;
+
+#ifdef V8_USE_EXTERNAL_STARTUP_DATA
+  if (v8_snapshot_)
+    free(const_cast<char*>(v8_snapshot_->data));
+#endif  // V8_USE_EXTERNAL_STARTUP_DATA
+}
+
+// static
+V8TestEnvironment* V8TestEnvironment::GetInstance() {
+  return g_environment;
+}
+
+// static
+void V8TestEnvironment::PumpPlatformMessageLoop(v8::Isolate* isolate) {
+  v8::Platform* platform = GetInstance()->platform();
+  while (v8::platform::PumpMessageLoop(platform, isolate))
+    continue;
+}
+
+void V8TestEnvironment::SetUp() {
+#ifdef V8_USE_EXTERNAL_STARTUP_DATA
+  if (v8_snapshot_) {
+    platform_ =
+        InitializeV8ForPDFiumWithStartupData(exe_path_, std::string(), nullptr);
+  } else {
+    v8_snapshot_ = std::make_unique<v8::StartupData>();
+    platform_ = InitializeV8ForPDFiumWithStartupData(exe_path_, std::string(),
+                                                     v8_snapshot_.get());
+  }
+#else
+  platform_ = InitializeV8ForPDFium(exe_path_);
+#endif  // V8_USE_EXTERNAL_STARTUP_DATA
+}
+
+void V8TestEnvironment::TearDown() {
+  v8::V8::ShutdownPlatform();
+}
diff --git a/testing/v8_test_environment.h b/testing/v8_test_environment.h
new file mode 100644
index 0000000..3cce117
--- /dev/null
+++ b/testing/v8_test_environment.h
@@ -0,0 +1,45 @@
+// Copyright 2020 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_V8_TEST_ENVIRONMENT_H_
+#define TESTING_V8_TEST_ENVIRONMENT_H_
+
+#include <memory>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace v8 {
+class Isolate;
+class Platform;
+#ifdef V8_USE_EXTERNAL_STARTUP_DATA
+class StartupData;
+#endif  // V8_USE_EXTERNAL_STARTUP_DATA
+}  // namespace v8
+
+class TestLoader;
+
+class V8TestEnvironment : public testing::Environment {
+ public:
+  explicit V8TestEnvironment(const char* exe_path);
+  ~V8TestEnvironment() override;
+
+  // Note: GetInstance() does not create one if it does not exist.
+  static V8TestEnvironment* GetInstance();
+  static void PumpPlatformMessageLoop(v8::Isolate* pIsolate);
+
+  // testing::Environment:
+  void SetUp() override;
+  void TearDown() override;
+
+  v8::Platform* platform() const { return platform_.get(); }
+
+ private:
+  const char* const exe_path_;
+#ifdef V8_USE_EXTERNAL_STARTUP_DATA
+  std::unique_ptr<v8::StartupData> v8_snapshot_;
+#endif  // V8_USE_EXTERNAL_STARTUP_DATA
+  std::unique_ptr<v8::Platform> platform_;
+};
+
+#endif  // TESTING_V8_TEST_ENVIRONMENT_H_