integration_test: add conformance tests

Change-Id: I3c21e5069c34a6417c9177ae5f1bdcb801418c8a
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/173665
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/internal/cmd/conformance/conformance.sh b/internal/cmd/conformance/conformance.sh
new file mode 100755
index 0000000..c82ad6a
--- /dev/null
+++ b/internal/cmd/conformance/conformance.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+# Copyright 2019 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+cd $(dirname $0)
+exec go run main.go $@
diff --git a/internal/cmd/conformance/failure_list_go.txt b/internal/cmd/conformance/failure_list_go.txt
new file mode 100644
index 0000000..f30c4c6
--- /dev/null
+++ b/internal/cmd/conformance/failure_list_go.txt
@@ -0,0 +1,7 @@
+Required.Proto3.JsonInput.IgnoreUnknownJsonFalse.ProtobufOutput
+Required.Proto3.JsonInput.IgnoreUnknownJsonNull.ProtobufOutput
+Required.Proto3.JsonInput.IgnoreUnknownJsonNumber.ProtobufOutput
+Required.Proto3.JsonInput.IgnoreUnknownJsonObject.ProtobufOutput
+Required.Proto3.JsonInput.IgnoreUnknownJsonString.ProtobufOutput
+Required.Proto3.JsonInput.IgnoreUnknownJsonTrue.ProtobufOutput
+Recommended.Proto3.JsonInput.FieldMaskInvalidCharacter
diff --git a/internal/cmd/conformance/main.go b/internal/cmd/conformance/main.go
new file mode 100644
index 0000000..a779929
--- /dev/null
+++ b/internal/cmd/conformance/main.go
@@ -0,0 +1,124 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This binary implements the conformance test subprocess protocol as documented
+// in conformance.proto.
+package main
+
+import (
+	"encoding/binary"
+	"io"
+	"log"
+	"os"
+
+	"github.com/golang/protobuf/v2/encoding/jsonpb"
+	"github.com/golang/protobuf/v2/proto"
+
+	pb "github.com/golang/protobuf/v2/internal/testprotos/conformance"
+)
+
+func main() {
+	var sizeBuf [4]byte
+	inbuf := make([]byte, 0, 4096)
+	for {
+		_, err := io.ReadFull(os.Stdin, sizeBuf[:])
+		if err == io.EOF {
+			break
+		}
+		if err != nil {
+			log.Fatalf("conformance: read request: %v", err)
+		}
+		size := binary.LittleEndian.Uint32(sizeBuf[:])
+		if int(size) > cap(inbuf) {
+			inbuf = make([]byte, size)
+		}
+		inbuf = inbuf[:size]
+		if _, err := io.ReadFull(os.Stdin, inbuf); err != nil {
+			log.Fatalf("conformance: read request: %v", err)
+		}
+
+		req := &pb.ConformanceRequest{}
+		if err := proto.Unmarshal(inbuf, req); err != nil {
+			log.Fatalf("conformance: parse request: %v", err)
+		}
+		res := handle(req)
+
+		out, err := proto.Marshal(res)
+		if err != nil {
+			log.Fatalf("conformance: marshal response: %v", err)
+		}
+		binary.LittleEndian.PutUint32(sizeBuf[:], uint32(len(out)))
+		if _, err := os.Stdout.Write(sizeBuf[:]); err != nil {
+			log.Fatalf("conformance: write response: %v", err)
+		}
+		if _, err := os.Stdout.Write(out); err != nil {
+			log.Fatalf("conformance: write response: %v", err)
+		}
+	}
+}
+
+func handle(req *pb.ConformanceRequest) *pb.ConformanceResponse {
+	var err error
+	var msg proto.Message = &pb.TestAllTypesProto2{}
+	if req.GetMessageType() == "protobuf_test_messages.proto3.TestAllTypesProto3" {
+		msg = &pb.TestAllTypesProto3{}
+	}
+
+	switch p := req.Payload.(type) {
+	case *pb.ConformanceRequest_ProtobufPayload:
+		err = proto.Unmarshal(p.ProtobufPayload, msg)
+	case *pb.ConformanceRequest_JsonPayload:
+		err = jsonpb.Unmarshal(msg, []byte(p.JsonPayload))
+	default:
+		return &pb.ConformanceResponse{
+			Result: &pb.ConformanceResponse_RuntimeError{
+				RuntimeError: "unknown request payload type",
+			},
+		}
+	}
+	if err != nil {
+		return &pb.ConformanceResponse{
+			Result: &pb.ConformanceResponse_ParseError{
+				ParseError: err.Error(),
+			},
+		}
+	}
+
+	switch req.RequestedOutputFormat {
+	case pb.WireFormat_PROTOBUF:
+		p, err := proto.Marshal(msg)
+		if err != nil {
+			return &pb.ConformanceResponse{
+				Result: &pb.ConformanceResponse_SerializeError{
+					SerializeError: err.Error(),
+				},
+			}
+		}
+		return &pb.ConformanceResponse{
+			Result: &pb.ConformanceResponse_ProtobufPayload{
+				ProtobufPayload: p,
+			},
+		}
+	case pb.WireFormat_JSON:
+		p, err := jsonpb.Marshal(msg)
+		if err != nil {
+			return &pb.ConformanceResponse{
+				Result: &pb.ConformanceResponse_SerializeError{
+					SerializeError: err.Error(),
+				},
+			}
+		}
+		return &pb.ConformanceResponse{
+			Result: &pb.ConformanceResponse_JsonPayload{
+				JsonPayload: string(p),
+			},
+		}
+	default:
+		return &pb.ConformanceResponse{
+			Result: &pb.ConformanceResponse_RuntimeError{
+				RuntimeError: "unknown output format",
+			},
+		}
+	}
+}
diff --git a/internal/cmd/generate-protos/main.go b/internal/cmd/generate-protos/main.go
index 78e3c19..2d03750 100644
--- a/internal/cmd/generate-protos/main.go
+++ b/internal/cmd/generate-protos/main.go
@@ -185,6 +185,8 @@
 		{"src", "google/protobuf/field_mask.proto"},
 		{"src", "google/protobuf/source_context.proto"},
 		{"src", "google/protobuf/struct.proto"},
+		{"src", "google/protobuf/test_messages_proto2.proto"},
+		{"src", "google/protobuf/test_messages_proto3.proto"},
 		{"src", "google/protobuf/timestamp.proto"},
 		{"src", "google/protobuf/type.proto"},
 		{"src", "google/protobuf/wrappers.proto"},