Enable conformance tests over editions protos

We transformed the proto2/proto3 protos to editions, and then run the same set of tests over both.  This will verify that migrating to editions preserves the same proto2/proto3 behavior.  These will not be enabled by default, and require a flag `--maximum_edition=2023`.

Future changes will:
- add more targeted editions-specific tests
- clean up our conformance test framework to allow for more targeted tests
- add wildcards to failure lists in limited cases to reduce noise
- add feature resolution conformance tests

PiperOrigin-RevId: 574570607
diff --git a/conformance/BUILD.bazel b/conformance/BUILD.bazel
index eafd606..e54eab0 100644
--- a/conformance/BUILD.bazel
+++ b/conformance/BUILD.bazel
@@ -130,6 +130,7 @@
 
 cc_library(
     name = "conformance_test",
+    testonly = 1,
     srcs = [
         "conformance_test.cc",
         "conformance_test_runner.cc",
@@ -151,12 +152,15 @@
 
 cc_library(
     name = "binary_json_conformance_suite",
+    testonly = 1,
     srcs = ["binary_json_conformance_suite.cc"],
     hdrs = ["binary_json_conformance_suite.h"],
     deps = [
         ":conformance_test",
         ":test_messages_proto2_proto_cc",
         ":test_messages_proto3_proto_cc",
+        "//src/google/protobuf/editions:test_messages_proto2_editions_cc_proto",
+        "//src/google/protobuf/editions:test_messages_proto3_editions_cc_proto",
         "@com_google_absl//absl/log:die_if_null",
         "@com_google_absl//absl/status",
         "@jsoncpp",
@@ -165,12 +169,15 @@
 
 cc_library(
     name = "text_format_conformance_suite",
+    testonly = 1,
     srcs = ["text_format_conformance_suite.cc"],
     hdrs = ["text_format_conformance_suite.h"],
     deps = [
         ":conformance_test",
         ":test_messages_proto2_proto_cc",
         ":test_messages_proto3_proto_cc",
+        "//src/google/protobuf/editions:test_messages_proto2_editions_cc_proto",
+        "//src/google/protobuf/editions:test_messages_proto3_editions_cc_proto",
         "@com_google_absl//absl/log:absl_log",
         "@com_google_absl//absl/log:die_if_null",
         "@com_google_absl//absl/strings",
@@ -179,6 +186,7 @@
 
 cc_binary(
     name = "conformance_test_runner",
+    testonly = 1,
     srcs = ["conformance_test_main.cc"],
     visibility = ["//visibility:public"],
     deps = [
@@ -199,6 +207,8 @@
         "//:protobuf",
         "//:test_messages_proto2_cc_proto",
         "//:test_messages_proto3_cc_proto",
+        "//src/google/protobuf/editions:test_messages_proto2_editions_cc_proto",
+        "//src/google/protobuf/editions:test_messages_proto3_editions_cc_proto",
         "@com_google_absl//absl/status",
         "@com_google_absl//absl/status:statusor",
     ],
diff --git a/conformance/bazel_conformance_test_runner.sh b/conformance/bazel_conformance_test_runner.sh
index fcf4a48..c434c1f 100755
--- a/conformance/bazel_conformance_test_runner.sh
+++ b/conformance/bazel_conformance_test_runner.sh
@@ -28,9 +28,10 @@
 fi
 # --- end runfiles.bash initialization ---
 
-TESTEE=unset
-FAILURE_LIST=unset
-TEXT_FORMAT_FAILURE_LIST=unset
+TESTEE=
+FAILURE_LIST=
+TEXT_FORMAT_FAILURE_LIST=
+MAXIMUM_EDITION=
 
 while [[ -n "$@" ]]; do
   arg="$1"; shift
@@ -39,6 +40,7 @@
     "--testee") TESTEE="$val" ;;
     "--failure_list") FAILURE_LIST="$val" ;;
     "--text_format_failure_list") TEXT_FORMAT_FAILURE_LIST="$val" ;;
+    "--maximum_edition") MAXIMUM_EDITION="$val" ;;
     *) echo "Flag $arg is not recognized." && exit 1 ;;
   esac
 done
@@ -57,4 +59,8 @@
   args+=(--text_format_failure_list $text_format_failure_list)
 fi
 
+if [ -n "$MAXIMUM_EDITION" ]; then
+  args+=(--maximum_edition $MAXIMUM_EDITION)
+fi
+
 $conformance_test_runner "${args[@]}" $conformance_testee
diff --git a/conformance/binary_json_conformance_suite.cc b/conformance/binary_json_conformance_suite.cc
index dbacc05..628afdc 100644
--- a/conformance/binary_json_conformance_suite.cc
+++ b/conformance/binary_json_conformance_suite.cc
@@ -24,9 +24,12 @@
 #include "absl/status/status.h"
 #include "absl/strings/str_cat.h"
 #include "absl/strings/string_view.h"
+#include "absl/strings/substitute.h"
 #include "json/json.h"
 #include "conformance/conformance.pb.h"
 #include "conformance_test.h"
+#include "google/protobuf/editions/golden/test_messages_proto2_editions.pb.h"
+#include "google/protobuf/editions/golden/test_messages_proto3_editions.pb.h"
 #include "google/protobuf/endian.h"
 #include "google/protobuf/json/json.h"
 #include "google/protobuf/test_messages_proto2.pb.h"
@@ -47,6 +50,10 @@
 using google::protobuf::util::NewTypeResolverForDescriptorPool;
 using protobuf_test_messages::proto2::TestAllTypesProto2;
 using protobuf_test_messages::proto3::TestAllTypesProto3;
+using TestAllTypesProto2Editions =
+    protobuf_test_messages::editions::proto2::TestAllTypesProto2;
+using TestAllTypesProto3Editions =
+    protobuf_test_messages::editions::proto3::TestAllTypesProto3;
 
 namespace {
 
@@ -325,12 +332,17 @@
 void BinaryAndJsonConformanceSuite::RunSuiteImpl() {
   type_resolver_.reset(NewTypeResolverForDescriptorPool(
       kTypeUrlPrefix, DescriptorPool::generated_pool()));
-  type_url_ = GetTypeUrl(TestAllTypesProto3::descriptor());
 
   BinaryAndJsonConformanceSuiteImpl<TestAllTypesProto3>(
       this, /*run_proto3_tests=*/true);
   BinaryAndJsonConformanceSuiteImpl<TestAllTypesProto2>(
       this, /*run_proto3_tests=*/false);
+  if (maximum_edition_ >= Edition::EDITION_2023) {
+    BinaryAndJsonConformanceSuiteImpl<TestAllTypesProto3Editions>(
+        this, /*run_proto3_tests=*/true);
+    BinaryAndJsonConformanceSuiteImpl<TestAllTypesProto2Editions>(
+        this, /*run_proto3_tests=*/false);
+  }
 }
 
 template <typename MessageType>
@@ -413,8 +425,7 @@
 void BinaryAndJsonConformanceSuiteImpl<MessageType>::
     RunValidJsonTestWithProtobufInput(
         const std::string& test_name, ConformanceLevel level,
-        const TestAllTypesProto3& input,
-        const std::string& equivalent_text_format) {
+        const MessageType& input, const std::string& equivalent_text_format) {
   ConformanceRequestSetting setting(
       level, conformance::PROTOBUF, conformance::JSON, conformance::JSON_TEST,
       input, test_name, input.SerializeAsString());
@@ -427,7 +438,7 @@
                                   ConformanceLevel level,
                                   const std::string& input_json,
                                   const std::string& equivalent_text_format) {
-  TestAllTypesProto3 prototype;
+  MessageType prototype;
   ConformanceRequestSetting setting(
       level, conformance::JSON, conformance::PROTOBUF,
       conformance::JSON_IGNORE_UNKNOWN_PARSING_TEST, prototype, test_name,
@@ -560,7 +571,7 @@
 void BinaryAndJsonConformanceSuiteImpl<MessageType>::ExpectParseFailureForJson(
     const std::string& test_name, ConformanceLevel level,
     const std::string& input_json) {
-  TestAllTypesProto3 prototype;
+  MessageType prototype;
   // We don't expect output, but if the program erroneously accepts the protobuf
   // we let it send its response as this.  We must not leave it unspecified.
   ConformanceRequestSetting setting(level, conformance::JSON, conformance::JSON,
@@ -568,8 +579,9 @@
                                     test_name, input_json);
   const ConformanceRequest& request = setting.GetRequest();
   ConformanceResponse response;
-  std::string effective_test_name = absl::StrCat(
-      setting.ConformanceLevelToString(level), ".Proto3.JsonInput.", test_name);
+  std::string effective_test_name =
+      absl::StrCat(setting.ConformanceLevelToString(level), ".",
+                   SyntaxIdentifier(), ".JsonInput.", test_name);
 
   suite_.RunTest(effective_test_name, request, &response);
   if (response.result_case() == ConformanceResponse::kParseError) {
@@ -587,18 +599,19 @@
     ExpectSerializeFailureForJson(const std::string& test_name,
                                   ConformanceLevel level,
                                   const std::string& text_format) {
-  TestAllTypesProto3 payload_message;
+  MessageType payload_message;
   ABSL_CHECK(TextFormat::ParseFromString(text_format, &payload_message))
       << "Failed to parse: " << text_format;
 
-  TestAllTypesProto3 prototype;
+  MessageType prototype;
   ConformanceRequestSetting setting(
       level, conformance::PROTOBUF, conformance::JSON, conformance::JSON_TEST,
       prototype, test_name, payload_message.SerializeAsString());
   const ConformanceRequest& request = setting.GetRequest();
   ConformanceResponse response;
-  std::string effective_test_name = absl::StrCat(
-      setting.ConformanceLevelToString(level), ".", test_name, ".JsonOutput");
+  std::string effective_test_name =
+      absl::StrCat(setting.ConformanceLevelToString(level), ".",
+                   SyntaxIdentifier(), ".", test_name, ".JsonOutput");
 
   suite_.RunTest(effective_test_name, request, &response);
   if (response.result_case() == ConformanceResponse::kSerializeError) {
@@ -1330,6 +1343,7 @@
     BinaryAndJsonConformanceSuiteImpl(BinaryAndJsonConformanceSuite* suite,
                                       bool run_proto3_tests)
     : suite_(*ABSL_DIE_IF_NULL(suite)), run_proto3_tests_(run_proto3_tests) {
+  suite_.SetTypeUrl(GetTypeUrl(MessageType::GetDescriptor()));
   RunAllTests();
 }
 
@@ -1666,15 +1680,18 @@
         "FieldName13": 0
       })",
         [](const Json::Value& value) { return value.isMember("FieldName13"); });
-    RunValidJsonTestWithValidator(
-        "FieldNameExtension", RECOMMENDED,
-        R"({
-        "[protobuf_test_messages.proto2.extension_int32]": 1
+    std::vector<const FieldDescriptor*> extensions;
+    MessageType::GetDescriptor()->file()->pool()->FindAllExtensions(
+        MessageType::GetDescriptor(), &extensions);
+    RunValidJsonTestWithValidator("FieldNameExtension", RECOMMENDED,
+                                  absl::Substitute(R"({
+        "[$0]": 1
       })",
-        [](const Json::Value& value) {
-          return value.isMember(
-              "[protobuf_test_messages.proto2.extension_int32]");
-        });
+                                                   extensions[0]->full_name()),
+                                  [&](const Json::Value& value) {
+                                    return value.isMember(absl::StrCat(
+                                        "[", extensions[0]->full_name(), "]"));
+                                  });
     return;
   }
   RunValidJsonTest("HelloWorld", REQUIRED,
@@ -2175,7 +2192,7 @@
                    R"({"optionalFloat": "-Infinity"})", "optional_float: -inf");
   // Non-canonical Nan will be correctly normalized.
   {
-    TestAllTypesProto3 message;
+    MessageType message;
     // IEEE floating-point standard 32-bit quiet NaN:
     //   0111 1111 1xxx xxxx xxxx xxxx xxxx xxxx
     message.set_optional_float(WireFormatLite::DecodeFloat(0x7FA12345));
@@ -2227,7 +2244,7 @@
                    "optional_double: -inf");
   // Non-canonical Nan will be correctly normalized.
   {
-    TestAllTypesProto3 message;
+    MessageType message;
     message.set_optional_double(
         WireFormatLite::DecodeDouble(int64_t{0x7FFA123456789ABC}));
     RunValidJsonTestWithProtobufInput("DoubleFieldNormalizeQuietNan", REQUIRED,
@@ -3008,54 +3025,61 @@
 
 template <typename MessageType>
 void BinaryAndJsonConformanceSuiteImpl<MessageType>::RunJsonTestsForAny() {
+  std::string type_url = GetTypeUrl(MessageType::GetDescriptor());
   RunValidJsonTest("Any", REQUIRED,
-                   R"({
+                   absl::Substitute(R"({
         "optionalAny": {
-          "@type": "type.googleapis.com/protobuf_test_messages.proto3.TestAllTypesProto3",
+          "@type": "$0",
           "optionalInt32": 12345
   }
       })",
-                   R"(
+                                    GetTypeUrl(MessageType::GetDescriptor())),
+                   absl::Substitute(R"(
         optional_any: {
-    [type.googleapis.com/protobuf_test_messages.proto3.TestAllTypesProto3] {
+          [$0] {
             optional_int32: 12345
-    }
-  }
-      )");
+          }
+        }
+      )",
+                                    type_url));
   RunValidJsonTest("AnyNested", REQUIRED,
-                   R"({
+                   absl::Substitute(R"({
         "optionalAny": {
           "@type": "type.googleapis.com/google.protobuf.Any",
           "value": {
-            "@type": "type.googleapis.com/protobuf_test_messages.proto3.TestAllTypesProto3",
+            "@type": "$0",
             "optionalInt32": 12345
     }
   }
       })",
-                   R"(
+                                    type_url),
+                   absl::Substitute(R"(
         optional_any: {
-    [type.googleapis.com/google.protobuf.Any] {
-      [type.googleapis.com/protobuf_test_messages.proto3.TestAllTypesProto3] {
+          [type.googleapis.com/google.protobuf.Any] {
+            [$0] {
               optional_int32: 12345
-      }
-    }
-  }
-      )");
+            }
+          }
+        }
+      )",
+                                    type_url));
   // The special "@type" tag is not required to appear first.
   RunValidJsonTest("AnyUnorderedTypeTag", REQUIRED,
-                   R"({
+                   absl::Substitute(R"({
         "optionalAny": {
           "optionalInt32": 12345,
-          "@type": "type.googleapis.com/protobuf_test_messages.proto3.TestAllTypesProto3"
-  }
+          "@type": "$0"
+        }
       })",
-                   R"(
+                                    type_url),
+                   absl::Substitute(R"(
         optional_any: {
-    [type.googleapis.com/protobuf_test_messages.proto3.TestAllTypesProto3] {
+          [$0] {
             optional_int32: 12345
-    }
-  }
-      )");
+          }
+        }
+      )",
+                                    type_url));
   // Well-known types in Any.
   RunValidJsonTest("AnyWithInt32ValueWrapper", REQUIRED,
                    R"({
@@ -3253,8 +3277,13 @@
     const {
   if constexpr (std::is_same<MessageType, TestAllTypesProto2>::value) {
     return "Proto2";
-  } else {
+  } else if constexpr (std::is_same<MessageType, TestAllTypesProto3>::value) {
     return "Proto3";
+  } else if constexpr (std::is_same<MessageType,
+                                    TestAllTypesProto2Editions>::value) {
+    return "Editions_Proto2";
+  } else {
+    return "Editions_Proto3";
   }
 }
 
diff --git a/conformance/binary_json_conformance_suite.h b/conformance/binary_json_conformance_suite.h
index f6a8708..d777bba 100644
--- a/conformance/binary_json_conformance_suite.h
+++ b/conformance/binary_json_conformance_suite.h
@@ -14,6 +14,7 @@
 #include <utility>
 #include <vector>
 
+#include "absl/strings/string_view.h"
 #include "json/json.h"
 #include "conformance_test.h"
 #include "google/protobuf/descriptor.h"
@@ -33,6 +34,9 @@
   bool ParseResponse(const conformance::ConformanceResponse& response,
                      const ConformanceRequestSetting& setting,
                      Message* test_message) override;
+  void SetTypeUrl(absl::string_view type_url) {
+    type_url_ = std::string(type_url);
+  }
 
   template <typename MessageType>
   friend class BinaryAndJsonConformanceSuiteImpl;
@@ -44,8 +48,8 @@
 template <typename MessageType>
 class BinaryAndJsonConformanceSuiteImpl {
  public:
-  BinaryAndJsonConformanceSuiteImpl(BinaryAndJsonConformanceSuite* suite,
-                                    bool run_proto3_tests);
+  explicit BinaryAndJsonConformanceSuiteImpl(
+      BinaryAndJsonConformanceSuite* suite, bool run_proto3_tests);
 
  private:
   using ConformanceRequestSetting =
@@ -79,8 +83,7 @@
                                    const Message& prototype);
   void RunValidJsonTestWithProtobufInput(
       const std::string& test_name, ConformanceLevel level,
-      const protobuf_test_messages::proto3::TestAllTypesProto3& input,
-      const std::string& equivalent_text_format);
+      const MessageType& input, const std::string& equivalent_text_format);
   void RunValidJsonIgnoreUnknownTest(const std::string& test_name,
                                      ConformanceLevel level,
                                      const std::string& input_json,
diff --git a/conformance/conformance_cpp.cc b/conformance/conformance_cpp.cc
index 30baafc..86c651c 100644
--- a/conformance/conformance_cpp.cc
+++ b/conformance/conformance_cpp.cc
@@ -21,6 +21,8 @@
 #include "absl/status/statusor.h"
 #include "conformance/conformance.pb.h"
 #include "conformance/conformance.pb.h"
+#include "google/protobuf/editions/golden/test_messages_proto2_editions.pb.h"
+#include "google/protobuf/editions/golden/test_messages_proto3_editions.pb.h"
 #include "google/protobuf/endian.h"
 #include "google/protobuf/message.h"
 #include "google/protobuf/test_messages_proto2.pb.h"
@@ -45,6 +47,10 @@
 using ::google::protobuf::util::TypeResolver;
 using ::protobuf_test_messages::proto2::TestAllTypesProto2;
 using ::protobuf_test_messages::proto3::TestAllTypesProto3;
+using TestAllTypesProto2Editions =
+    ::protobuf_test_messages::editions::proto2::TestAllTypesProto2;
+using TestAllTypesProto3Editions =
+    ::protobuf_test_messages::editions::proto3::TestAllTypesProto3;
 
 absl::Status ReadFd(int fd, char* buf, size_t len) {
   while (len > 0) {
@@ -76,6 +82,8 @@
   Harness() {
     google::protobuf::LinkMessageReflection<TestAllTypesProto2>();
     google::protobuf::LinkMessageReflection<TestAllTypesProto3>();
+    google::protobuf::LinkMessageReflection<TestAllTypesProto2Editions>();
+    google::protobuf::LinkMessageReflection<TestAllTypesProto3Editions>();
 
     resolver_.reset(NewTypeResolverForDescriptorPool(
         "type.googleapis.com", DescriptorPool::generated_pool()));
diff --git a/conformance/conformance_test.cc b/conformance/conformance_test.cc
index 94efbcc..dbcaf45 100644
--- a/conformance/conformance_test.cc
+++ b/conformance/conformance_test.cc
@@ -149,6 +149,16 @@
       return "Proto3";
     case FileDescriptorLegacy::Syntax::SYNTAX_PROTO2:
       return "Proto2";
+    case FileDescriptorLegacy::Syntax::SYNTAX_EDITIONS: {
+      std::string id = "Editions";
+      if (prototype_message_.GetDescriptor()->name() == "TestAllTypesProto2") {
+        absl::StrAppend(&id, "_Proto2");
+      } else if (prototype_message_.GetDescriptor()->name() ==
+                 "TestAllTypesProto3") {
+        absl::StrAppend(&id, "_Proto3");
+      }
+      return id;
+    }
     default:
       return "Unknown";
   }
diff --git a/conformance/conformance_test.h b/conformance/conformance_test.h
index 00559d9..c78f9ea 100644
--- a/conformance/conformance_test.h
+++ b/conformance/conformance_test.h
@@ -132,6 +132,7 @@
       : verbose_(false),
         performance_(false),
         enforce_recommended_(false),
+        maximum_edition_(Edition::EDITION_PROTO3),
         failure_list_flag_name_("--failure_list") {}
   virtual ~ConformanceTestSuite() {}
 
@@ -148,6 +149,9 @@
   // difference between REQUIRED and RECOMMENDED test cases.
   void SetEnforceRecommended(bool value) { enforce_recommended_ = value; }
 
+  // Sets the maximum edition (inclusive) that should be tests for conformance.
+  void SetMaximumEdition(Edition edition) { maximum_edition_ = edition; }
+
   // Gets the flag name to the failure list file.
   // By default, this would return --failure_list
   std::string GetFailureListFlagName() { return failure_list_flag_name_; }
@@ -284,6 +288,7 @@
   bool verbose_;
   bool performance_;
   bool enforce_recommended_;
+  Edition maximum_edition_;
   std::string output_;
   std::string output_dir_;
   std::string failure_list_flag_name_;
diff --git a/conformance/conformance_test_runner.cc b/conformance/conformance_test_runner.cc
index 44b9a09..5edb3c6 100644
--- a/conformance/conformance_test_runner.cc
+++ b/conformance/conformance_test_runner.cc
@@ -37,12 +37,15 @@
 #include <unistd.h>
 
 #include <algorithm>
+#include <cstdio>
 #include <cstdlib>
+#include <cstring>
 #include <fstream>
 #include <future>
 #include <vector>
 
 #include "absl/log/absl_log.h"
+#include "absl/strings/str_cat.h"
 #include "absl/strings/str_format.h"
 #include "conformance/conformance.pb.h"
 #include "conformance_test.h"
@@ -120,6 +123,11 @@
           "                              strictly conforming to protobuf\n");
   fprintf(stderr, "                              spec.\n");
   fprintf(stderr,
+          "  --maximum edition           Only run conformance tests up to \n");
+  fprintf(stderr,
+          "                              and including the specified\n");
+  fprintf(stderr, "                              edition.\n");
+  fprintf(stderr,
           "  --output_dir                <dirname> Directory to write\n"
           "                              output files.\n");
   exit(1);
@@ -200,6 +208,14 @@
         suite->SetVerbose(true);
       } else if (strcmp(argv[arg], "--enforce_recommended") == 0) {
         suite->SetEnforceRecommended(true);
+      } else if (strcmp(argv[arg], "--maximum_edition") == 0) {
+        if (++arg == argc) UsageError();
+        Edition edition = EDITION_UNKNOWN;
+        if (!Edition_Parse(absl::StrCat("EDITION_", argv[arg]), &edition)) {
+          fprintf(stderr, "Unknown edition: %s\n", argv[arg]);
+          UsageError();
+        }
+        suite->SetMaximumEdition(edition);
       } else if (strcmp(argv[arg], "--output_dir") == 0) {
         if (++arg == argc) UsageError();
         suite->SetOutputDir(argv[arg]);
diff --git a/conformance/defs.bzl b/conformance/defs.bzl
index 8daef86..3a7d1d6 100644
--- a/conformance/defs.bzl
+++ b/conformance/defs.bzl
@@ -8,6 +8,7 @@
         testee,
         failure_list = None,
         text_format_failure_list = None,
+        maximum_edition = None,
         **kwargs):
     """Conformance test runner.
 
@@ -27,6 +28,8 @@
     if text_format_failure_list:
         args = args + ["--text_format_failure_list %s" % _strip_bazel(text_format_failure_list)]
         failure_lists = failure_lists + [text_format_failure_list]
+    if maximum_edition:
+        args = args + ["--maximum_edition %s" % maximum_edition]
 
     native.sh_test(
         name = name,
diff --git a/conformance/failure_list_cpp.txt b/conformance/failure_list_cpp.txt
index aa18cea..235ae69 100644
--- a/conformance/failure_list_cpp.txt
+++ b/conformance/failure_list_cpp.txt
@@ -7,30 +7,57 @@
 # TODO: insert links to corresponding bugs tracking the issue.
 # Should we use GitHub issues or the Google-internal bug tracker?
 
-Recommended.FieldMaskNumbersDontRoundTrip.JsonOutput
-Recommended.FieldMaskPathsDontRoundTrip.JsonOutput
-Recommended.FieldMaskTooManyUnderscore.JsonOutput
+Recommended.Proto3.FieldMaskNumbersDontRoundTrip.JsonOutput
+Recommended.Editions_Proto3.FieldMaskNumbersDontRoundTrip.JsonOutput
+Recommended.Proto3.FieldMaskPathsDontRoundTrip.JsonOutput
+Recommended.Editions_Proto3.FieldMaskPathsDontRoundTrip.JsonOutput
+Recommended.Proto3.FieldMaskTooManyUnderscore.JsonOutput
+Recommended.Editions_Proto3.FieldMaskTooManyUnderscore.JsonOutput
 Recommended.Proto3.JsonInput.BoolFieldDoubleQuotedFalse
+Recommended.Editions_Proto3.JsonInput.BoolFieldDoubleQuotedFalse
 Recommended.Proto3.JsonInput.BoolFieldDoubleQuotedTrue
+Recommended.Editions_Proto3.JsonInput.BoolFieldDoubleQuotedTrue
 Recommended.Proto3.JsonInput.FieldMaskInvalidCharacter
+Recommended.Editions_Proto3.JsonInput.FieldMaskInvalidCharacter
 Recommended.Proto3.JsonInput.FieldNameDuplicate
+Recommended.Editions_Proto3.JsonInput.FieldNameDuplicate
 Recommended.Proto3.JsonInput.FieldNameDuplicateDifferentCasing1
+Recommended.Editions_Proto3.JsonInput.FieldNameDuplicateDifferentCasing1
 Recommended.Proto3.JsonInput.FieldNameDuplicateDifferentCasing2
+Recommended.Editions_Proto3.JsonInput.FieldNameDuplicateDifferentCasing2
 Recommended.Proto3.JsonInput.FieldNameNotQuoted
+Recommended.Editions_Proto3.JsonInput.FieldNameNotQuoted
 Recommended.Proto3.JsonInput.IgnoreUnknownEnumStringValueInMapValue.ProtobufOutput
+Recommended.Editions_Proto3.JsonInput.IgnoreUnknownEnumStringValueInMapValue.ProtobufOutput
 Recommended.Proto3.JsonInput.MapFieldValueIsNull
+Recommended.Editions_Proto3.JsonInput.MapFieldValueIsNull
 Recommended.Proto3.JsonInput.RepeatedFieldMessageElementIsNull
+Recommended.Editions_Proto3.JsonInput.RepeatedFieldMessageElementIsNull
 Recommended.Proto3.JsonInput.RepeatedFieldPrimitiveElementIsNull
+Recommended.Editions_Proto3.JsonInput.RepeatedFieldPrimitiveElementIsNull
 Recommended.Proto3.JsonInput.RepeatedFieldTrailingComma
+Recommended.Editions_Proto3.JsonInput.RepeatedFieldTrailingComma
 Recommended.Proto3.JsonInput.RepeatedFieldTrailingCommaWithNewlines
+Recommended.Editions_Proto3.JsonInput.RepeatedFieldTrailingCommaWithNewlines
 Recommended.Proto3.JsonInput.RepeatedFieldTrailingCommaWithSpace
+Recommended.Editions_Proto3.JsonInput.RepeatedFieldTrailingCommaWithSpace
 Recommended.Proto3.JsonInput.RepeatedFieldTrailingCommaWithSpaceCommaSpace
+Recommended.Editions_Proto3.JsonInput.RepeatedFieldTrailingCommaWithSpaceCommaSpace
 Recommended.Proto3.JsonInput.StringFieldSingleQuoteBoth
+Recommended.Editions_Proto3.JsonInput.StringFieldSingleQuoteBoth
 Recommended.Proto3.JsonInput.StringFieldSingleQuoteKey
+Recommended.Editions_Proto3.JsonInput.StringFieldSingleQuoteKey
 Recommended.Proto3.JsonInput.StringFieldSingleQuoteValue
+Recommended.Editions_Proto3.JsonInput.StringFieldSingleQuoteValue
 Recommended.Proto3.JsonInput.StringFieldUppercaseEscapeLetter
+Recommended.Editions_Proto3.JsonInput.StringFieldUppercaseEscapeLetter
 Recommended.Proto3.JsonInput.TrailingCommaInAnObject
+Recommended.Editions_Proto3.JsonInput.TrailingCommaInAnObject
 Recommended.Proto3.JsonInput.TrailingCommaInAnObjectWithNewlines
+Recommended.Editions_Proto3.JsonInput.TrailingCommaInAnObjectWithNewlines
 Recommended.Proto3.JsonInput.TrailingCommaInAnObjectWithSpace
+Recommended.Editions_Proto3.JsonInput.TrailingCommaInAnObjectWithSpace
 Recommended.Proto3.JsonInput.TrailingCommaInAnObjectWithSpaceCommaSpace
+Recommended.Editions_Proto3.JsonInput.TrailingCommaInAnObjectWithSpaceCommaSpace
 Recommended.Proto2.JsonInput.FieldNameExtension.Validator
+Recommended.Editions_Proto2.JsonInput.FieldNameExtension.Validator
diff --git a/conformance/failure_list_csharp.txt b/conformance/failure_list_csharp.txt
index 6999ff0..6c7c0dd 100644
--- a/conformance/failure_list_csharp.txt
+++ b/conformance/failure_list_csharp.txt
@@ -5,5 +5,5 @@
 Required.Proto3.JsonInput.OneofFieldNullFirst.ProtobufOutput
 Required.Proto3.JsonInput.OneofFieldNullSecond.JsonOutput
 Required.Proto3.JsonInput.OneofFieldNullSecond.ProtobufOutput
-Recommended.ValueRejectInfNumberValue.JsonOutput
-Recommended.ValueRejectNanNumberValue.JsonOutput
\ No newline at end of file
+Recommended.Proto3.ValueRejectInfNumberValue.JsonOutput
+Recommended.Proto3.ValueRejectNanNumberValue.JsonOutput
diff --git a/conformance/failure_list_java.txt b/conformance/failure_list_java.txt
index 808e230..8508abd 100644
--- a/conformance/failure_list_java.txt
+++ b/conformance/failure_list_java.txt
@@ -4,9 +4,9 @@
 # By listing them here we can keep tabs on which ones are failing and be sure
 # that we don't introduce regressions in other tests.
 
-Recommended.FieldMaskNumbersDontRoundTrip.JsonOutput
-Recommended.FieldMaskPathsDontRoundTrip.JsonOutput
-Recommended.FieldMaskTooManyUnderscore.JsonOutput
+Recommended.Proto3.FieldMaskNumbersDontRoundTrip.JsonOutput
+Recommended.Proto3.FieldMaskPathsDontRoundTrip.JsonOutput
+Recommended.Proto3.FieldMaskTooManyUnderscore.JsonOutput
 Recommended.Proto3.JsonInput.BoolFieldAllCapitalFalse
 Recommended.Proto3.JsonInput.BoolFieldAllCapitalTrue
 Recommended.Proto3.JsonInput.BoolFieldCamelCaseFalse
diff --git a/conformance/failure_list_jruby.txt b/conformance/failure_list_jruby.txt
index c0d2980..6d511a3 100644
--- a/conformance/failure_list_jruby.txt
+++ b/conformance/failure_list_jruby.txt
@@ -1,6 +1,6 @@
-Recommended.FieldMaskNumbersDontRoundTrip.JsonOutput
-Recommended.FieldMaskPathsDontRoundTrip.JsonOutput
-Recommended.FieldMaskTooManyUnderscore.JsonOutput
+Recommended.Proto3.FieldMaskNumbersDontRoundTrip.JsonOutput
+Recommended.Proto3.FieldMaskPathsDontRoundTrip.JsonOutput
+Recommended.Proto3.FieldMaskTooManyUnderscore.JsonOutput
 Recommended.Proto2.JsonInput.FieldNameExtension.Validator
 Recommended.Proto3.JsonInput.BoolFieldAllCapitalFalse
 Recommended.Proto3.JsonInput.BoolFieldAllCapitalTrue
diff --git a/conformance/text_format_conformance_suite.cc b/conformance/text_format_conformance_suite.cc
index ae39296..1c2c17b 100644
--- a/conformance/text_format_conformance_suite.cc
+++ b/conformance/text_format_conformance_suite.cc
@@ -15,6 +15,8 @@
 #include "absl/log/die_if_null.h"
 #include "absl/strings/str_cat.h"
 #include "conformance_test.h"
+#include "google/protobuf/editions/golden/test_messages_proto2_editions.pb.h"
+#include "google/protobuf/editions/golden/test_messages_proto3_editions.pb.h"
 #include "google/protobuf/test_messages_proto2.pb.h"
 #include "google/protobuf/test_messages_proto3.pb.h"
 #include "google/protobuf/text_format.h"
@@ -25,6 +27,10 @@
 using protobuf_test_messages::proto2::TestAllTypesProto2;
 using protobuf_test_messages::proto2::UnknownToTestAllTypes;
 using protobuf_test_messages::proto3::TestAllTypesProto3;
+using TestAllTypesProto2Editions =
+    protobuf_test_messages::editions::proto2::TestAllTypesProto2;
+using TestAllTypesProto3Editions =
+    protobuf_test_messages::editions::proto3::TestAllTypesProto3;
 
 namespace google {
 namespace protobuf {
@@ -113,6 +119,10 @@
 void TextFormatConformanceTestSuite::RunSuiteImpl() {
   TextFormatConformanceTestSuiteImpl<TestAllTypesProto2>(this);
   TextFormatConformanceTestSuiteImpl<TestAllTypesProto3>(this);
+  if (maximum_edition_ >= Edition::EDITION_2023) {
+    TextFormatConformanceTestSuiteImpl<TestAllTypesProto2Editions>(this);
+    TextFormatConformanceTestSuiteImpl<TestAllTypesProto3Editions>(this);
+  }
 }
 
 template <typename MessageType>
diff --git a/conformance/text_format_failure_list_cpp.txt b/conformance/text_format_failure_list_cpp.txt
index a25f04f..fd2d7ad 100644
--- a/conformance/text_format_failure_list_cpp.txt
+++ b/conformance/text_format_failure_list_cpp.txt
@@ -1,20 +1,40 @@
 Recommended.Proto3.TextFormatInput.StringLiteralLongUnicodeEscapeSurrogateFirstOnlyBytes
+Recommended.Editions_Proto3.TextFormatInput.StringLiteralLongUnicodeEscapeSurrogateFirstOnlyBytes
 Recommended.Proto3.TextFormatInput.StringLiteralLongUnicodeEscapeSurrogateFirstOnlyString
+Recommended.Editions_Proto3.TextFormatInput.StringLiteralLongUnicodeEscapeSurrogateFirstOnlyString
 Recommended.Proto3.TextFormatInput.StringLiteralLongUnicodeEscapeSurrogatePairBytes
+Recommended.Editions_Proto3.TextFormatInput.StringLiteralLongUnicodeEscapeSurrogatePairBytes
 Recommended.Proto3.TextFormatInput.StringLiteralLongUnicodeEscapeSurrogatePairString
+Recommended.Editions_Proto3.TextFormatInput.StringLiteralLongUnicodeEscapeSurrogatePairString
 Recommended.Proto3.TextFormatInput.StringLiteralLongUnicodeEscapeSurrogateSecondOnlyBytes
+Recommended.Editions_Proto3.TextFormatInput.StringLiteralLongUnicodeEscapeSurrogateSecondOnlyBytes
 Recommended.Proto3.TextFormatInput.StringLiteralLongUnicodeEscapeSurrogateSecondOnlyString
+Recommended.Editions_Proto3.TextFormatInput.StringLiteralLongUnicodeEscapeSurrogateSecondOnlyString
 Recommended.Proto3.TextFormatInput.StringLiteralShortUnicodeEscapeSurrogateFirstOnlyBytes
+Recommended.Editions_Proto3.TextFormatInput.StringLiteralShortUnicodeEscapeSurrogateFirstOnlyBytes
 Recommended.Proto3.TextFormatInput.StringLiteralShortUnicodeEscapeSurrogateFirstOnlyString
+Recommended.Editions_Proto3.TextFormatInput.StringLiteralShortUnicodeEscapeSurrogateFirstOnlyString
 Recommended.Proto3.TextFormatInput.StringLiteralShortUnicodeEscapeSurrogatePairBytes
+Recommended.Editions_Proto3.TextFormatInput.StringLiteralShortUnicodeEscapeSurrogatePairBytes
 Recommended.Proto3.TextFormatInput.StringLiteralShortUnicodeEscapeSurrogatePairString
+Recommended.Editions_Proto3.TextFormatInput.StringLiteralShortUnicodeEscapeSurrogatePairString
 Recommended.Proto3.TextFormatInput.StringLiteralShortUnicodeEscapeSurrogateSecondOnlyBytes
+Recommended.Editions_Proto3.TextFormatInput.StringLiteralShortUnicodeEscapeSurrogateSecondOnlyBytes
 Recommended.Proto3.TextFormatInput.StringLiteralShortUnicodeEscapeSurrogateSecondOnlyString
+Recommended.Editions_Proto3.TextFormatInput.StringLiteralShortUnicodeEscapeSurrogateSecondOnlyString
 Recommended.Proto3.TextFormatInput.StringLiteralUnicodeEscapeSurrogatePairLongShortBytes
+Recommended.Editions_Proto3.TextFormatInput.StringLiteralUnicodeEscapeSurrogatePairLongShortBytes
 Recommended.Proto3.TextFormatInput.StringLiteralUnicodeEscapeSurrogatePairLongShortString
+Recommended.Editions_Proto3.TextFormatInput.StringLiteralUnicodeEscapeSurrogatePairLongShortString
 Recommended.Proto3.TextFormatInput.StringLiteralUnicodeEscapeSurrogatePairShortLongBytes
+Recommended.Editions_Proto3.TextFormatInput.StringLiteralUnicodeEscapeSurrogatePairShortLongBytes
 Recommended.Proto3.TextFormatInput.StringLiteralUnicodeEscapeSurrogatePairShortLongString
+Recommended.Editions_Proto3.TextFormatInput.StringLiteralUnicodeEscapeSurrogatePairShortLongString
 Required.Proto3.TextFormatInput.StringFieldBadUTF8Hex
+Required.Editions_Proto3.TextFormatInput.StringFieldBadUTF8Hex
 Required.Proto3.TextFormatInput.StringFieldBadUTF8Octal
+Required.Editions_Proto3.TextFormatInput.StringFieldBadUTF8Octal
 Required.Proto3.TextFormatInput.StringLiteralLongUnicodeEscapeTooLargeBytes
+Required.Editions_Proto3.TextFormatInput.StringLiteralLongUnicodeEscapeTooLargeBytes
 Required.Proto3.TextFormatInput.StringLiteralLongUnicodeEscapeTooLargeString
+Required.Editions_Proto3.TextFormatInput.StringLiteralLongUnicodeEscapeTooLargeString