chore(php): conformance testing for edition (#16712)

COPYBARA_INTEGRATE_REVIEW=https://github.com/protocolbuffers/protobuf/pull/16712 from bshaffer:php-editions a1c41add7d
PiperOrigin-RevId: 631824623
pull/16762/head
Brent Shaffer 12 months ago committed by Copybara-Service
parent b77343aa2c
commit 01744cccae
  1. 3
      conformance/BUILD.bazel
  2. 214
      conformance/conformance_php.php
  3. 8
      conformance/text_format_failure_list_php.txt
  4. 27
      editions/BUILD
  5. 2
      php/BUILD.bazel
  6. 8
      protobuf.bzl
  7. 8
      src/google/protobuf/compiler/php/php_generator.h

@ -332,13 +332,14 @@ inline_sh_binary(
], ],
cmd = """ cmd = """
php -dextension=$(rootpath //php:extension) \\ php -dextension=$(rootpath //php:extension) \\
-d include_path=conformance:src/google/protobuf \\ -d include_path=conformance:src/google/protobuf:editions/golden \\
$(rootpath conformance_php.php) $(rootpath conformance_php.php)
""", """,
visibility = ["//php:__subpackages__"], visibility = ["//php:__subpackages__"],
deps = [ deps = [
":conformance_php_proto", ":conformance_php_proto",
"//:test_messages_proto3_php_proto", "//:test_messages_proto3_php_proto",
"//editions:test_messages_proto3_editions_php_proto",
], ],
) )

@ -1,80 +1,134 @@
<?php <?php
require_once("Conformance/WireFormat.php"); require_once('Conformance/WireFormat.php');
require_once("Conformance/ConformanceResponse.php"); require_once('Conformance/ConformanceResponse.php');
require_once("Conformance/ConformanceRequest.php"); require_once('Conformance/ConformanceRequest.php');
require_once("Conformance/FailureSet.php"); require_once('Conformance/FailureSet.php');
require_once("Conformance/JspbEncodingConfig.php"); require_once('Conformance/JspbEncodingConfig.php');
require_once("Conformance/TestCategory.php"); require_once('Conformance/TestCategory.php');
require_once("Protobuf_test_messages/Proto3/ForeignMessage.php"); require_once('Protobuf_test_messages/Proto3/ForeignMessage.php');
require_once("Protobuf_test_messages/Proto3/ForeignEnum.php"); require_once('Protobuf_test_messages/Proto3/ForeignEnum.php');
require_once("Protobuf_test_messages/Proto3/TestAllTypesProto3.php"); require_once('Protobuf_test_messages/Proto3/TestAllTypesProto3.php');
require_once("Protobuf_test_messages/Proto3/TestAllTypesProto3/AliasedEnum.php"); require_once('Protobuf_test_messages/Proto3/TestAllTypesProto3/AliasedEnum.php');
require_once("Protobuf_test_messages/Proto3/TestAllTypesProto3/NestedMessage.php"); require_once('Protobuf_test_messages/Proto3/TestAllTypesProto3/NestedMessage.php');
require_once("Protobuf_test_messages/Proto3/TestAllTypesProto3/NestedEnum.php"); require_once('Protobuf_test_messages/Proto3/TestAllTypesProto3/NestedEnum.php');
require_once('Protobuf_test_messages/Editions/Proto3/EnumOnlyProto3/PBBool.php');
require_once("GPBMetadata/Conformance.php"); require_once('Protobuf_test_messages/Editions/Proto3/EnumOnlyProto3.php');
require_once("GPBMetadata/TestMessagesProto3.php"); require_once('Protobuf_test_messages/Editions/Proto3/TestAllTypesProto3/NestedMessage.php');
require_once('Protobuf_test_messages/Editions/Proto3/TestAllTypesProto3/NestedEnum.php');
use \Conformance\TestCategory; require_once('Protobuf_test_messages/Editions/Proto3/TestAllTypesProto3/AliasedEnum.php');
use \Conformance\WireFormat; require_once('Protobuf_test_messages/Editions/Proto3/TestAllTypesProto3.php');
require_once('Protobuf_test_messages/Editions/Proto3/NullHypothesisProto3.php');
if (!ini_get("date.timezone")) { require_once('Protobuf_test_messages/Editions/Proto3/ForeignEnum.php');
ini_set("date.timezone", "UTC"); require_once('Protobuf_test_messages/Editions/Proto3/ForeignMessage.php');
require_once('GPBMetadata/Conformance.php');
require_once('GPBMetadata/TestMessagesProto3.php');
require_once('GPBMetadata/TestMessagesProto3Editions.php');
use Conformance\ConformanceRequest;
use Conformance\ConformanceResponse;
use Conformance\TestCategory;
use Conformance\WireFormat;
use Protobuf_test_messages\Proto3\TestAllTypesProto3;
use Protobuf_test_messages\Editions\Proto3\TestAllTypesProto3 as TestAllTypesProto3Editions;
if (!ini_get('date.timezone')) {
ini_set('date.timezone', 'UTC');
} }
$test_count = 0; $test_count = 0;
function doTest($request) function doTest($request)
{ {
$test_message = new \Protobuf_test_messages\Proto3\TestAllTypesProto3(); $response = new ConformanceResponse();
$response = new \Conformance\ConformanceResponse();
if ($request->getPayload() == "protobuf_payload") { switch ($request->getPayload()) {
if ($request->getMessageType() == "conformance.FailureSet") { case 'protobuf_payload':
$response->setProtobufPayload(""); switch ($request->getMessageType()) {
return $response; case 'protobuf_test_messages.proto3.TestAllTypesProto3':
} elseif ($request->getMessageType() == "protobuf_test_messages.proto3.TestAllTypesProto3") { $test_message = new TestAllTypesProto3();
try { break;
$test_message->mergeFromString($request->getProtobufPayload()); case 'protobuf_test_messages.editions.proto3.TestAllTypesProto3':
} catch (Exception $e) { $test_message = new TestAllTypesProto3Editions();
$response->setParseError($e->getMessage()); break;
return $response; case 'conformance.FailureSet':
} $response->setProtobufPayload('');
} elseif ($request->getMessageType() == "protobuf_test_messages.proto2.TestAllTypesProto2") { return $response;
$response->setSkipped("PHP doesn't support proto2"); case 'protobuf_test_messages.proto2.TestAllTypesProto2':
return $response; case 'protobuf_test_messages.editions.proto2.TestAllTypesProto2':
} else { $response->setSkipped('PHP doesn\'t support proto2');
trigger_error("Protobuf request doesn't have specific payload type", E_USER_ERROR); return $response;
} case 'protobuf_test_messages.editions.TestAllTypesEdition2023':
} elseif ($request->getPayload() == "json_payload") { $response->setSkipped('PHP doesn\'t support editions-specific features yet');
$ignore_json_unknown = return $response;
($request->getTestCategory() == case '':
TestCategory::JSON_IGNORE_UNKNOWN_PARSING_TEST); trigger_error(
try { 'Protobuf request doesn\'t have specific payload type',
$test_message->mergeFromJsonString($request->getJsonPayload(), E_USER_ERROR
$ignore_json_unknown); );
} catch (Exception $e) { default:
$response->setParseError($e->getMessage()); trigger_error(sprintf(
return $response; 'Protobuf request doesn\'t support %s message type',
} $request->getMessageType(),
} elseif ($request->getPayload() == "text_payload") { ), E_USER_ERROR);
$response->setSkipped("PHP doesn't support text format yet"); }
return $response;
} else { try {
trigger_error("Request didn't have payload.", E_USER_ERROR); $test_message->mergeFromString($request->getProtobufPayload());
} catch (Exception $e) {
$response->setParseError($e->getMessage());
return $response;
}
break;
case 'json_payload':
switch ($request->getMessageType()) {
case 'protobuf_test_messages.editions.proto3.TestAllTypesProto3':
$test_message = new TestAllTypesProto3Editions();
break;
case 'protobuf_test_messages.editions.proto2.TestAllTypesProto2':
$response->setSkipped('PHP doesn\'t support proto2');
return $response;
default:
$test_message = new TestAllTypesProto3();
}
$ignore_json_unknown =
($request->getTestCategory() == TestCategory::JSON_IGNORE_UNKNOWN_PARSING_TEST);
try {
$test_message->mergeFromJsonString(
$request->getJsonPayload(),
$ignore_json_unknown
);
} catch (Exception $e) {
$response->setParseError($e->getMessage());
return $response;
}
break;
case 'text_payload':
$response->setSkipped('PHP doesn\'t support text format yet');
return $response;
default:
trigger_error('Request didn\'t have payload.', E_USER_ERROR);
} }
if ($request->getRequestedOutputFormat() == WireFormat::UNSPECIFIED) { switch ($request->getRequestedOutputFormat()) {
trigger_error("Unspecified output format.", E_USER_ERROR); case WireFormat::TEXT_FORMAT:
} elseif ($request->getRequestedOutputFormat() == WireFormat::PROTOBUF) { $response->setSkipped('PHP doesn\'t support text format yet');
$response->setProtobufPayload($test_message->serializeToString()); return $response;
} elseif ($request->getRequestedOutputFormat() == WireFormat::JSON) { case WireFormat::UNSPECIFIED:
try { trigger_error('Unspecified output format.', E_USER_ERROR);
$response->setJsonPayload($test_message->serializeToJsonString()); case WireFormat::PROTOBUF:
} catch (Exception $e) { $response->setProtobufPayload($test_message->serializeToString());
$response->setSerializeError($e->getMessage()); break;
return $response; case WireFormat::JSON:
} try {
$response->setJsonPayload($test_message->serializeToJsonString());
} catch (Exception $e) {
$response->setSerializeError($e->getMessage());
return $response;
}
} }
return $response; return $response;
@ -84,25 +138,25 @@ function doTestIO()
{ {
$length_bytes = fread(STDIN, 4); $length_bytes = fread(STDIN, 4);
if (strlen($length_bytes) == 0) { if (strlen($length_bytes) == 0) {
return false; # EOF return false; # EOF
} elseif (strlen($length_bytes) != 4) { } elseif (strlen($length_bytes) != 4) {
fwrite(STDERR, "I/O error\n"); fwrite(STDERR, "I/O error\n");
return false; return false;
} }
$length = unpack("V", $length_bytes)[1]; $length = unpack('V', $length_bytes)[1];
$serialized_request = fread(STDIN, $length); $serialized_request = fread(STDIN, $length);
if (strlen($serialized_request) != $length) { if (strlen($serialized_request) != $length) {
trigger_error("I/O error", E_USER_ERROR); trigger_error('I/O error', E_USER_ERROR);
} }
$request = new \Conformance\ConformanceRequest(); $request = new ConformanceRequest();
$request->mergeFromString($serialized_request); $request->mergeFromString($serialized_request);
$response = doTest($request); $response = doTest($request);
$serialized_response = $response->serializeToString(); $serialized_response = $response->serializeToString();
fwrite(STDOUT, pack("V", strlen($serialized_response))); fwrite(STDOUT, pack('V', strlen($serialized_response)));
fwrite(STDOUT, $serialized_response); fwrite(STDOUT, $serialized_response);
$GLOBALS['test_count'] += 1; $GLOBALS['test_count'] += 1;
@ -111,10 +165,10 @@ function doTestIO()
} }
while(true){ while(true){
if (!doTestIO()) { if (!doTestIO()) {
fprintf(STDERR, fprintf(STDERR,
"conformance_php: received EOF from test runner " . 'conformance_php: received EOF from test runner ' .
"after %d tests, exiting\n", $test_count); "after %d tests, exiting\n", $test_count);
exit; exit;
} }
} }

@ -1,8 +0,0 @@
Recommended.Proto3.ProtobufInput.GroupUnknownFields_Drop.TextFormatOutput
Recommended.Proto3.ProtobufInput.GroupUnknownFields_Print.TextFormatOutput
Recommended.Proto3.ProtobufInput.MessageUnknownFields_Drop.TextFormatOutput
Recommended.Proto3.ProtobufInput.MessageUnknownFields_Print.TextFormatOutput
Recommended.Proto3.ProtobufInput.RepeatedUnknownFields_Drop.TextFormatOutput
Recommended.Proto3.ProtobufInput.RepeatedUnknownFields_Print.TextFormatOutput
Recommended.Proto3.ProtobufInput.ScalarUnknownFields_Drop.TextFormatOutput
Recommended.Proto3.ProtobufInput.ScalarUnknownFields_Print.TextFormatOutput

@ -1,5 +1,5 @@
load("@bazel_skylib//:bzl_library.bzl", "bzl_library") load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
load("//:protobuf.bzl", "internal_objc_proto_library", "internal_py_proto_library") load("//:protobuf.bzl", "internal_objc_proto_library", "internal_php_proto_library", "internal_py_proto_library")
load("//bazel:cc_proto_library.bzl", "cc_proto_library") load("//bazel:cc_proto_library.bzl", "cc_proto_library")
load("//bazel:upb_proto_library.bzl", "upb_c_proto_library", "upb_proto_reflection_library") load("//bazel:upb_proto_library.bzl", "upb_c_proto_library", "upb_proto_reflection_library")
load(":defaults.bzl", "compile_edition_defaults", "embed_edition_defaults") load(":defaults.bzl", "compile_edition_defaults", "embed_edition_defaults")
@ -236,6 +236,31 @@ java_lite_proto_library(
deps = [":test_messages_proto3_editions_proto"], deps = [":test_messages_proto3_editions_proto"],
) )
internal_php_proto_library(
name = "test_messages_proto3_editions_php_proto",
testonly = 1,
srcs = ["golden/test_messages_proto3_editions.proto"],
outs = [
"GPBMetadata/TestMessagesProto3Editions.php",
"Protobuf_test_messages/Editions/Proto3/EnumOnlyProto3.php",
"Protobuf_test_messages/Editions/Proto3/EnumOnlyProto3/PBBool.php",
"Protobuf_test_messages/Editions/Proto3/ForeignEnum.php",
"Protobuf_test_messages/Editions/Proto3/ForeignMessage.php",
"Protobuf_test_messages/Editions/Proto3/NullHypothesisProto3.php",
"Protobuf_test_messages/Editions/Proto3/TestAllTypesProto3.php",
"Protobuf_test_messages/Editions/Proto3/TestAllTypesProto3/AliasedEnum.php",
"Protobuf_test_messages/Editions/Proto3/TestAllTypesProto3/NestedEnum.php",
"Protobuf_test_messages/Editions/Proto3/TestAllTypesProto3/NestedMessage.php",
],
enable_editions = True,
includes = [
"golden",
"src",
],
proto_deps = ["//:well_known_protos"],
visibility = ["//conformance:__pkg__"],
)
internal_py_proto_library( internal_py_proto_library(
name = "test_messages_proto3_editions_py_pb2", name = "test_messages_proto3_editions_py_pb2",
testonly = True, testonly = True,

@ -64,6 +64,7 @@ genrule(
conformance_test( conformance_test(
name = "conformance_test", name = "conformance_test",
failure_list = "//conformance:failure_list_php.txt", failure_list = "//conformance:failure_list_php.txt",
maximum_edition = "2023",
target_compatible_with = select({ target_compatible_with = select({
"@platforms//os:osx": ["@platforms//:incompatible"], "@platforms//os:osx": ["@platforms//:incompatible"],
"//conditions:default": [], "//conditions:default": [],
@ -75,6 +76,7 @@ conformance_test(
conformance_test( conformance_test(
name = "conformance_test_c", name = "conformance_test_c",
failure_list = "//conformance:failure_list_php_c.txt", failure_list = "//conformance:failure_list_php_c.txt",
maximum_edition = "2023",
target_compatible_with = select({ target_compatible_with = select({
"@platforms//os:osx": [], "@platforms//os:osx": [],
"//conditions:default": ["@platforms//:incompatible"], "//conditions:default": ["@platforms//:incompatible"],

@ -80,6 +80,7 @@ def _proto_gen_impl(ctx):
srcs = ctx.files.srcs srcs = ctx.files.srcs
langs = ctx.attr.langs or [] langs = ctx.attr.langs or []
out_type = ctx.attr.out_type out_type = ctx.attr.out_type
enable_editions = ctx.attr.enable_editions
deps = depset(direct = ctx.files.srcs) deps = depset(direct = ctx.files.srcs)
source_dir = _SourceDir(ctx) source_dir = _SourceDir(ctx)
gen_dir = _GenDir(ctx).rstrip("/") gen_dir = _GenDir(ctx).rstrip("/")
@ -130,6 +131,8 @@ def _proto_gen_impl(ctx):
generated_files = [] generated_files = []
for src in srcs: for src in srcs:
args = [] args = []
if enable_editions:
args.append("--experimental_editions")
in_gen_dir = src.root.path == gen_dir in_gen_dir = src.root.path == gen_dir
if in_gen_dir: if in_gen_dir:
@ -231,6 +234,7 @@ Args:
srcs: Protocol Buffers definition files (.proto) to run the protocol compiler srcs: Protocol Buffers definition files (.proto) to run the protocol compiler
against. against.
deps: a list of dependency labels; must be other proto libraries. deps: a list of dependency labels; must be other proto libraries.
enable_editions: if true, sets the --experimental_editions flag.
includes: a list of include paths to .proto files. includes: a list of include paths to .proto files.
protoc: the label of the protocol compiler to generate the sources. protoc: the label of the protocol compiler to generate the sources.
plugin: the label of the protocol compiler plugin to be passed to the protocol plugin: the label of the protocol compiler plugin to be passed to the protocol
@ -247,6 +251,7 @@ _proto_gen = rule(
attrs = { attrs = {
"srcs": attr.label_list(allow_files = True), "srcs": attr.label_list(allow_files = True),
"deps": attr.label_list(providers = [ProtoGenInfo]), "deps": attr.label_list(providers = [ProtoGenInfo]),
"enable_editions": attr.bool(),
"includes": attr.string_list(), "includes": attr.string_list(),
"protoc": attr.label( "protoc": attr.label(
cfg = "exec", cfg = "exec",
@ -655,6 +660,7 @@ def _source_proto_library(
protoc = Label("//:protoc"), protoc = Label("//:protoc"),
testonly = None, testonly = None,
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
enable_editions = False,
**kwargs): **kwargs):
"""Bazel rule to create generated protobuf code from proto source files for """Bazel rule to create generated protobuf code from proto source files for
languages not well supported by Bazel yet. This will output the generated languages not well supported by Bazel yet. This will output the generated
@ -699,6 +705,7 @@ def _source_proto_library(
srcs = proto_deps, srcs = proto_deps,
protoc = protoc, protoc = protoc,
includes = includes, includes = includes,
enable_editions = enable_editions,
) )
full_deps.append(":%s_deps_genproto" % name) full_deps.append(":%s_deps_genproto" % name)
@ -712,6 +719,7 @@ def _source_proto_library(
protoc = protoc, protoc = protoc,
testonly = testonly, testonly = testonly,
visibility = visibility, visibility = visibility,
enable_editions = enable_editions,
) )
native.filegroup( native.filegroup(

@ -37,7 +37,13 @@ class PROTOC_EXPORT Generator : public CodeGenerator {
std::string* error) const override; std::string* error) const override;
uint64_t GetSupportedFeatures() const override { uint64_t GetSupportedFeatures() const override {
return FEATURE_PROTO3_OPTIONAL; return Feature::FEATURE_PROTO3_OPTIONAL;
}
Edition GetMinimumEdition() const override { return Edition::EDITION_PROTO2; }
Edition GetMaximumEdition() const override { return Edition::EDITION_2023; }
std::vector<const FieldDescriptor*> GetFeatureExtensions() const override {
return {};
} }
private: private:

Loading…
Cancel
Save