Add support for the conformance test for objc when run on OS X
diff --git a/.gitignore b/.gitignore
index b0a92f8..19e5212 100644
--- a/.gitignore
+++ b/.gitignore
@@ -103,9 +103,12 @@
 conformance/conformance-cpp
 conformance/conformance-csharp
 conformance/conformance-java
+conformance/conformance-objc
 conformance/conformance-test-runner
 conformance/conformance.pb.cc
 conformance/conformance.pb.h
+conformance/Conformance.pbobjc.h
+conformance/Conformance.pbobjc.m
 conformance/conformance.rb
 conformance/javac_middleman
 conformance/protoc_middleman
diff --git a/configure.ac b/configure.ac
index 0e58789..42605ae 100644
--- a/configure.ac
+++ b/configure.ac
@@ -59,6 +59,7 @@
 ACX_USE_SYSTEM_EXTENSIONS
 m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
 AM_CONDITIONAL(GCC, test "$GCC" = yes)   # let the Makefile know if we're gcc
+AC_PROG_OBJC
 
 # test_util.cc takes forever to compile with GCC and optimization turned on.
 AC_MSG_CHECKING([C++ compiler flags...])
@@ -163,6 +164,15 @@
     ;;
 esac
 
+# Enable ObjC support for conformance directory on OS X.
+OBJC_CONFORMANCE_TEST=0
+case "$target_os" in
+  darwin*)
+    OBJC_CONFORMANCE_TEST=1
+    ;;
+esac
+AM_CONDITIONAL([OBJC_CONFORMANCE_TEST], [test $OBJC_CONFORMANCE_TEST = 1])
+
 # HACK:  Make gmock's configure script pick up our copy of CFLAGS and CXXFLAGS,
 #   since the flags added by ACX_CHECK_SUNCC must be used when compiling gmock
 #   too.
diff --git a/conformance/Makefile.am b/conformance/Makefile.am
index b6fda2a..d7bb939 100644
--- a/conformance/Makefile.am
+++ b/conformance/Makefile.am
@@ -9,7 +9,9 @@
 
 other_language_protoc_outputs =                                \
   conformance.rb                                               \
-  com/google/protobuf/conformance/Conformance.java
+  com/google/protobuf/conformance/Conformance.java             \
+  Conformance.pbobjc.h                                         \
+  Conformance.pbobjc.m
 
 bin_PROGRAMS = conformance-test-runner conformance-cpp
 
@@ -17,16 +19,37 @@
 conformance_test_runner_SOURCES = conformance_test.cc conformance_test_runner.cc
 nodist_conformance_test_runner_SOURCES = conformance.pb.cc
 conformance_test_runner_CPPFLAGS = -I$(top_srcdir)/src
+# Explicit deps beacuse BUILT_SOURCES are only done before a "make all/check"
+# so a direct "make test_cpp" could fail if parallel enough.
+conformance_test_runner-conformance_test.$(OBJEXT): conformance.pb.h
+conformance_test_runner-conformance_test_runner.$(OBJEXT): conformance.pb.h
 
 conformance_cpp_LDADD = $(top_srcdir)/src/libprotobuf.la
 conformance_cpp_SOURCES = conformance_cpp.cc
 nodist_conformance_cpp_SOURCES = conformance.pb.cc
 conformance_cpp_CPPFLAGS = -I$(top_srcdir)/src
+# Explicit dep beacuse BUILT_SOURCES are only done before a "make all/check"
+# so a direct "make test_cpp" could fail if parallel enough.
+conformance_cpp-conformance_cpp.$(OBJEXT): conformance.pb.h
+
+if OBJC_CONFORMANCE_TEST
+
+bin_PROGRAMS += conformance-objc
+
+conformance_objc_SOURCES = conformance_objc.m ../objectivec/GPBProtocolBuffers.m
+nodist_conformance_objc_SOURCES = Conformance.pbobjc.m
+conformance_objc_CPPFLAGS = -I$(top_srcdir)/objectivec
+conformance_objc_LDFLAGS = -framework Foundation
+# Explicit dep beacuse BUILT_SOURCES are only done before a "make all/check"
+# so a direct "make test_objc" could fail if parallel enough.
+conformance_objc-conformance_objc.$(OBJEXT): Conformance.pbobjc.h
+
+endif
 
 if USE_EXTERNAL_PROTOC
 
 protoc_middleman: $(protoc_inputs)
-	$(PROTOC) -I$(srcdir) --cpp_out=. --java_out=. --ruby_out=. $^
+	$(PROTOC) -I$(srcdir) --cpp_out=. --java_out=. --ruby_out=. --objc_out=. $^
 	touch protoc_middleman
 
 else
@@ -35,7 +58,7 @@
 # relative to srcdir, which may not be the same as the current directory when
 # building out-of-tree.
 protoc_middleman: $(top_srcdir)/src/protoc$(EXEEXT) $(protoc_inputs)
-	oldpwd=`pwd` && ( cd $(srcdir) && $$oldpwd/../src/protoc$(EXEEXT) -I. --cpp_out=$$oldpwd --java_out=$$oldpwd --ruby_out=$$oldpwd $(protoc_inputs) )
+	oldpwd=`pwd` && ( cd $(srcdir) && $$oldpwd/../src/protoc$(EXEEXT) -I. --cpp_out=$$oldpwd --java_out=$$oldpwd --ruby_out=$$oldpwd --objc_out=$$oldpwd $(protoc_inputs) )
 	touch protoc_middleman
 
 endif
@@ -44,7 +67,7 @@
 
 $(other_language_protoc_outputs): protoc_middleman
 
-BUILT_SOURCES = $(protoc_outputs)
+BUILT_SOURCES = $(protoc_outputs) $(other_language_protoc_outputs)
 
 CLEANFILES = $(protoc_outputs) protoc_middleman javac_middleman conformance-java conformance-csharp $(other_language_protoc_outputs)
 
@@ -82,3 +105,10 @@
 
 test_ruby: protoc_middleman conformance-test-runner $(other_language_protoc_outputs)
 	RUBYLIB=../ruby/lib:. ./conformance-test-runner --failure_list failure_list_ruby.txt ./conformance_ruby.rb
+
+if OBJC_CONFORMANCE_TEST
+
+test_objc: protoc_middleman conformance-test-runner conformance-objc
+	./conformance-test-runner --failure_list failure_list_objc.txt ./conformance-objc
+
+endif
diff --git a/conformance/conformance_objc.m b/conformance/conformance_objc.m
new file mode 100644
index 0000000..06c97a8
--- /dev/null
+++ b/conformance/conformance_objc.m
@@ -0,0 +1,179 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Foundation/Foundation.h>
+
+#import "Conformance.pbobjc.h"
+
+static void Die(NSString *format, ...) __dead2;
+
+static BOOL verbose = NO;
+static int32_t testCount = 0;
+
+static void Die(NSString *format, ...) {
+  va_list args;
+  va_start(args, format);
+  NSString *msg = [[NSString alloc] initWithFormat:format arguments:args];
+  NSLog(@"%@", msg);
+  va_end(args);
+  [msg release];
+  exit(66);
+}
+
+static NSData *CheckedReadDataOfLength(NSFileHandle *handle, NSUInteger numBytes) {
+  NSData *data = [handle readDataOfLength:numBytes];
+  NSUInteger dataLen = data.length;
+  if (dataLen == 0) {
+    return nil;  // EOF.
+  }
+  if (dataLen != numBytes) {
+    Die(@"Failed to read the request length (%d), only got: %@",
+        numBytes, data);
+  }
+  return data;
+}
+
+static ConformanceResponse *DoTest(ConformanceRequest *request) {
+  ConformanceResponse *response = [ConformanceResponse message];
+  TestAllTypes *testMessage = nil;
+
+  switch (request.payloadOneOfCase) {
+    case ConformanceRequest_Payload_OneOfCase_GPBUnsetOneOfCase:
+      Die(@"Request didn't have a payload: %@", request);
+      break;
+
+    case ConformanceRequest_Payload_OneOfCase_ProtobufPayload: {
+      NSError *error = nil;
+      testMessage = [TestAllTypes parseFromData:request.protobufPayload
+                                          error:&error];
+      if (!testMessage) {
+        response.parseError =
+            [NSString stringWithFormat:@"Parse error: %@", error];
+      }
+      break;
+    }
+
+    case ConformanceRequest_Payload_OneOfCase_JsonPayload:
+      response.skipped = @"ObjC doesn't support parsing JSON";
+      break;
+  }
+
+  if (testMessage) {
+    switch (request.requestedOutputFormat) {
+      case WireFormat_GPBUnrecognizedEnumeratorValue:
+      case WireFormat_Unspecified:
+        Die(@"Unrecognized/unspecified output format: %@", request);
+        break;
+
+      case WireFormat_Protobuf:
+        response.protobufPayload = testMessage.data;
+        if (!response.protobufPayload) {
+          response.runtimeError =
+            [NSString stringWithFormat:@"Failed to make data from: %@", testMessage];
+        }
+        break;
+
+      case WireFormat_Json:
+        response.skipped = @"ObjC doesn't support generating JSON";
+        break;
+    }
+  }
+
+  return response;
+}
+
+static uint32_t UInt32FromLittleEndianData(NSData *data) {
+  if (data.length != sizeof(uint32_t)) {
+    Die(@"Data not the right size for uint32_t: %@", data);
+  }
+  uint32_t value;
+  memcpy(&value, data.bytes, sizeof(uint32_t));
+  return CFSwapInt32LittleToHost(value);
+}
+
+static NSData *UInt32ToLittleEndianData(uint32_t num) {
+  uint32_t value = CFSwapInt32HostToLittle(num);
+  return [NSData dataWithBytes:&value length:sizeof(uint32_t)];
+}
+
+static BOOL DoTestIo(NSFileHandle *input, NSFileHandle *output) {
+  // See conformance_test_runner.cc for the wire format.
+  NSData *data = CheckedReadDataOfLength(input, sizeof(uint32_t));
+  if (!data) {
+    // EOF.
+    return NO;
+  }
+  uint32_t numBytes = UInt32FromLittleEndianData(data);
+  data = CheckedReadDataOfLength(input, numBytes);
+  if (!data) {
+    Die(@"Failed to read request");
+  }
+
+  NSError *error = nil;
+  ConformanceRequest *request = [ConformanceRequest parseFromData:data
+                                                            error:&error];
+  if (!request) {
+    Die(@"Failed to parse the message data: %@", error);
+  }
+
+  ConformanceResponse *response = DoTest(request);
+  if (!response) {
+    Die(@"Failed to make a reply from %@", request);
+  }
+
+  data = response.data;
+  [output writeData:UInt32ToLittleEndianData((int32_t)data.length)];
+  [output writeData:data];
+
+  if (verbose) {
+    NSLog(@"Request: %@", request);
+    NSLog(@"Response: %@", response);
+  }
+
+  ++testCount;
+  return YES;
+}
+
+int main(int argc, const char *argv[]) {
+  @autoreleasepool {
+    NSFileHandle *input = [[NSFileHandle fileHandleWithStandardInput] retain];
+    NSFileHandle *output = [[NSFileHandle fileHandleWithStandardOutput] retain];
+
+    BOOL notDone = YES;
+    while (notDone) {
+      @autoreleasepool {
+        notDone = DoTestIo(input, output);
+      }
+    }
+
+    NSLog(@"Received EOF from test runner after %d tests, exiting.", testCount);
+  }
+  return 0;
+}
diff --git a/conformance/failure_list_objc.txt b/conformance/failure_list_objc.txt
new file mode 100644
index 0000000..a34b3f5
--- /dev/null
+++ b/conformance/failure_list_objc.txt
@@ -0,0 +1,2 @@
+ProtobufInput.PrematureEofInDelimitedDataForKnownNonRepeatedValue.MESSAGE
+ProtobufInput.PrematureEofInDelimitedDataForKnownRepeatedValue.MESSAGE
diff --git a/objectivec/DevTools/full_mac_build.sh b/objectivec/DevTools/full_mac_build.sh
index 71d3fa4..251f259 100755
--- a/objectivec/DevTools/full_mac_build.sh
+++ b/objectivec/DevTools/full_mac_build.sh
@@ -38,6 +38,8 @@
          Skip the invoke of Xcode to test the runtime on iOS.
    --skip-xcode-osx
          Skip the invoke of Xcode to test the runtime on OS X.
+   --skip-objc-conformance
+         Skip the Objective C conformance tests (run on OS X).
 
 EOF
 }
@@ -73,6 +75,7 @@
 CORE_ONLY=no
 DO_XCODE_IOS_TESTS=yes
 DO_XCODE_OSX_TESTS=yes
+DO_OBJC_CONFORMANCE_TESTS=yes
 while [[ $# != 0 ]]; do
   case "${1}" in
     -h | --help )
@@ -105,6 +108,9 @@
     --skip-xcode-osx )
       DO_XCODE_OSX_TESTS=no
       ;;
+    --skip-objc-conformance )
+      DO_OBJC_CONFORMANCE_TESTS=no
+      ;;
     -*)
       echo "ERROR: Unknown option: ${1}" 1>&2
       printUsage
@@ -172,7 +178,7 @@
   wrapped_make -j "${NUM_MAKE_JOBS}" check
   # Fire off the conformance tests also.
   cd conformance
-  wrapped_make -j "${NUM_MAKE_JOBS}"
+  wrapped_make -j "${NUM_MAKE_JOBS}" test_cpp
   cd ..
 fi
 
@@ -264,3 +270,9 @@
   header "Doing Xcode OS X build/tests - Release"
   "${XCODEBUILD_TEST_BASE_OSX[@]}" -configuration Release test
 fi
+
+if [[ "${DO_OBJC_CONFORMANCE_TESTS}" == "yes" ]] ; then
+  cd conformance
+  wrapped_make -j "${NUM_MAKE_JOBS}" test_objc
+  cd ..
+fi
diff --git a/travis.sh b/travis.sh
index 3b6ec33..459f83f 100755
--- a/travis.sh
+++ b/travis.sh
@@ -136,8 +136,9 @@
   brew update
   brew outdated xctool || brew upgrade xctool
   # Reused the build script that takes care of configuring and ensuring things
-  # are up to date.
-  objectivec/DevTools/full_mac_build.sh --core-only --skip-xcode
+  # are up to date. Xcode and conformance tests will be directly invoked.
+  objectivec/DevTools/full_mac_build.sh \
+      --core-only --skip-xcode --skip-objc-conformance
 }
 
 internal_xctool_debug_and_release() {
@@ -178,6 +179,7 @@
     -scheme ProtocolBuffers \
     -destination "platform=OS X,arch=x86_64" \
     test
+  cd conformance && make test_objc && cd ..
 }
 
 build_python() {