Bug 1133939 P2 Add tests validating nsPipeOutputStream AsyncWait behavior. r=froydnj
authorBen Kelly <ben@wanderview.com>
Sat, 21 Feb 2015 09:51:17 -0500
changeset 263767 76e28af52416814caece9ac0b8b08dbee2b9cfb2
parent 263766 516fb2d378b04ae71655b7170e5671f00ae17cbe
child 263768 70bccab35a0286eae6522cb973e70fff71362fb2
push id830
push userraliiev@mozilla.com
push dateFri, 19 Jun 2015 19:24:37 +0000
treeherdermozilla-release@932614382a68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1133939
milestone39.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1133939 P2 Add tests validating nsPipeOutputStream AsyncWait behavior. r=froydnj
xpcom/tests/gtest/Helpers.cpp
xpcom/tests/gtest/Helpers.h
xpcom/tests/gtest/TestPipes.cpp
--- a/xpcom/tests/gtest/Helpers.cpp
+++ b/xpcom/tests/gtest/Helpers.cpp
@@ -89,9 +89,27 @@ ConsumeAndValidateStream(nsIInputStream*
 {
   nsAutoCString outputData;
   nsresult rv = NS_ConsumeStream(aStream, UINT32_MAX, outputData);
   ASSERT_TRUE(NS_SUCCEEDED(rv));
   ASSERT_EQ(aExpectedData.Length(), outputData.Length());
   ASSERT_TRUE(aExpectedData.Equals(outputData));
 }
 
+NS_IMPL_ISUPPORTS(OutputStreamCallback, nsIOutputStreamCallback);
+
+OutputStreamCallback::OutputStreamCallback()
+  : mCalled(false)
+{
+}
+
+OutputStreamCallback::~OutputStreamCallback()
+{
+}
+
+NS_IMETHODIMP
+OutputStreamCallback::OnOutputStreamReady(nsIAsyncOutputStream* aStream)
+{
+  mCalled = true;
+  return NS_OK;
+}
+
 } // namespace testing
--- a/xpcom/tests/gtest/Helpers.h
+++ b/xpcom/tests/gtest/Helpers.h
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef __Helpers_h
 #define __Helpers_h
 
+#include "nsIAsyncOutputStream.h"
 #include "nsString.h"
 #include <stdint.h>
 
 class nsIInputStream;
 class nsIOutputStream;
 template <class T> class nsTArray;
 
 namespace testing {
@@ -29,11 +30,27 @@ WriteAllAndClose(nsIOutputStream* aStrea
 void
 ConsumeAndValidateStream(nsIInputStream* aStream,
                          const nsTArray<char>& aExpectedData);
 
 void
 ConsumeAndValidateStream(nsIInputStream* aStream,
                          const nsACString& aExpectedData);
 
+class OutputStreamCallback MOZ_FINAL : public nsIOutputStreamCallback
+{
+public:
+  OutputStreamCallback();
+
+  bool Called() const { return mCalled; }
+
+private:
+  ~OutputStreamCallback();
+
+  bool mCalled;
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOUTPUTSTREAMCALLBACK
+};
+
 } // namespace testing
 
 #endif // __Helpers_h
--- a/xpcom/tests/gtest/TestPipes.cpp
+++ b/xpcom/tests/gtest/TestPipes.cpp
@@ -655,8 +655,210 @@ TEST(Pipes, Clone_DuringWrite_ReadDuring
 
   TestPipeClone(32 * 1024, // total bytes
                 16,        // num writes
                 1,         // num initial clones
                 1,         // num streams to close after each write
                 2,         // num clones to add after each write
                 3);        // num streams to read after each write
 }
+
+TEST(Pipes, Write_AsyncWait)
+{
+  nsCOMPtr<nsIAsyncInputStream> reader;
+  nsCOMPtr<nsIAsyncOutputStream> writer;
+
+  const uint32_t segmentSize = 1024;
+  const uint32_t numSegments = 1;
+
+  nsresult rv = NS_NewPipe2(getter_AddRefs(reader), getter_AddRefs(writer),
+                            true, true,  // non-blocking - reader, writer
+                            segmentSize, numSegments);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  nsTArray<char> inputData;
+  testing::CreateData(segmentSize, inputData);
+
+  uint32_t numWritten = 0;
+  rv = writer->Write(inputData.Elements(), inputData.Length(), &numWritten);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  rv = writer->Write(inputData.Elements(), inputData.Length(), &numWritten);
+  ASSERT_EQ(NS_BASE_STREAM_WOULD_BLOCK, rv);
+
+  nsRefPtr<testing::OutputStreamCallback> cb =
+    new testing::OutputStreamCallback();
+
+  rv = writer->AsyncWait(cb, 0, 0, nullptr);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  ASSERT_FALSE(cb->Called());
+
+  testing::ConsumeAndValidateStream(reader, inputData);
+
+  ASSERT_TRUE(cb->Called());
+}
+
+TEST(Pipes, Write_AsyncWait_Clone)
+{
+  nsCOMPtr<nsIAsyncInputStream> reader;
+  nsCOMPtr<nsIAsyncOutputStream> writer;
+
+  const uint32_t segmentSize = 1024;
+  const uint32_t numSegments = 1;
+
+  nsresult rv = NS_NewPipe2(getter_AddRefs(reader), getter_AddRefs(writer),
+                            true, true,  // non-blocking - reader, writer
+                            segmentSize, numSegments);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  nsCOMPtr<nsIInputStream> clone;
+  rv = NS_CloneInputStream(reader, getter_AddRefs(clone));
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  nsTArray<char> inputData;
+  testing::CreateData(segmentSize, inputData);
+
+  uint32_t numWritten = 0;
+  rv = writer->Write(inputData.Elements(), inputData.Length(), &numWritten);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  rv = writer->Write(inputData.Elements(), inputData.Length(), &numWritten);
+  ASSERT_EQ(NS_BASE_STREAM_WOULD_BLOCK, rv);
+
+  nsRefPtr<testing::OutputStreamCallback> cb =
+    new testing::OutputStreamCallback();
+
+  rv = writer->AsyncWait(cb, 0, 0, nullptr);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  ASSERT_FALSE(cb->Called());
+
+  testing::ConsumeAndValidateStream(reader, inputData);
+
+  ASSERT_FALSE(cb->Called());
+
+  testing::ConsumeAndValidateStream(clone, inputData);
+
+  ASSERT_TRUE(cb->Called());
+}
+
+TEST(Pipes, Write_AsyncWait_Clone_CloseOriginal)
+{
+  nsCOMPtr<nsIAsyncInputStream> reader;
+  nsCOMPtr<nsIAsyncOutputStream> writer;
+
+  const uint32_t segmentSize = 1024;
+  const uint32_t numSegments = 1;
+
+  nsresult rv = NS_NewPipe2(getter_AddRefs(reader), getter_AddRefs(writer),
+                            true, true,  // non-blocking - reader, writer
+                            segmentSize, numSegments);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  nsCOMPtr<nsIInputStream> clone;
+  rv = NS_CloneInputStream(reader, getter_AddRefs(clone));
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  nsTArray<char> inputData;
+  testing::CreateData(segmentSize, inputData);
+
+  uint32_t numWritten = 0;
+  rv = writer->Write(inputData.Elements(), inputData.Length(), &numWritten);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  rv = writer->Write(inputData.Elements(), inputData.Length(), &numWritten);
+  ASSERT_EQ(NS_BASE_STREAM_WOULD_BLOCK, rv);
+
+  nsRefPtr<testing::OutputStreamCallback> cb =
+    new testing::OutputStreamCallback();
+
+  rv = writer->AsyncWait(cb, 0, 0, nullptr);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  ASSERT_FALSE(cb->Called());
+
+  testing::ConsumeAndValidateStream(clone, inputData);
+
+  ASSERT_FALSE(cb->Called());
+
+  reader->Close();
+
+  ASSERT_TRUE(cb->Called());
+}
+
+namespace {
+
+NS_METHOD
+CloseDuringReadFunc(nsIInputStream *aReader,
+                    void* aClosure,
+                    const char* aFromSegment,
+                    uint32_t aToOffset,
+                    uint32_t aCount,
+                    uint32_t* aWriteCountOut)
+{
+  MOZ_ASSERT(aReader);
+  MOZ_ASSERT(aClosure);
+  MOZ_ASSERT(aFromSegment);
+  MOZ_ASSERT(aWriteCountOut);
+  MOZ_ASSERT(aToOffset == 0);
+
+  // This is insanity and you probably should not do this under normal
+  // conditions.  We want to simulate the case where the pipe is closed
+  // (possibly from other end on another thread) simultaneously with the
+  // read.  This is the easiest way to do trigger this case in a synchronous
+  // gtest.
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aReader->Close()));
+
+  nsTArray<char>* buffer = static_cast<nsTArray<char>*>(aClosure);
+  buffer->AppendElements(aFromSegment, aCount);
+
+  *aWriteCountOut = aCount;
+
+  return NS_OK;
+}
+
+void
+TestCloseDuringRead(uint32_t aSegmentSize, uint32_t aDataSize)
+{
+  nsCOMPtr<nsIInputStream> reader;
+  nsCOMPtr<nsIOutputStream> writer;
+
+  const uint32_t maxSize = aSegmentSize;
+
+  nsresult rv = NS_NewPipe(getter_AddRefs(reader), getter_AddRefs(writer),
+                           aSegmentSize, maxSize);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  nsTArray<char> inputData;
+
+  testing::CreateData(aDataSize, inputData);
+
+  uint32_t numWritten = 0;
+  rv = writer->Write(inputData.Elements(), inputData.Length(), &numWritten);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+  nsTArray<char> outputData;
+
+  uint32_t numRead = 0;
+  rv = reader->ReadSegments(CloseDuringReadFunc, &outputData,
+                            inputData.Length(), &numRead);
+  ASSERT_TRUE(NS_SUCCEEDED(rv));
+  ASSERT_EQ(inputData.Length(), numRead);
+
+  ASSERT_EQ(inputData, outputData);
+
+  uint64_t available;
+  rv = reader->Available(&available);
+  ASSERT_EQ(NS_BASE_STREAM_CLOSED, rv);
+}
+
+} // anonymous namespace
+
+TEST(Pipes, Close_During_Read_Partial_Segment)
+{
+  TestCloseDuringRead(1024, 512);
+}
+
+TEST(Pipes, Close_During_Read_Full_Segment)
+{
+  TestCloseDuringRead(1024, 1024);
+}