Bug 1286165 (Part 3) - Add StreamingLexer tests for the SourceBufferIterator version of Lex(). r=njn
authorSeth Fowler <mark.seth.fowler@gmail.com>
Tue, 12 Jul 2016 00:23:30 -0700
changeset 305178 bf3df70f738edf245f4d1bc9bdc88efd881460e5
parent 305177 e2069b87af4c2291c9d2ef391c7759c3464d0341
child 305179 46e298b95ec13cf4a9f20462089a6f552b3d2db9
push id30455
push usercbook@mozilla.com
push dateSun, 17 Jul 2016 08:08:29 +0000
treeherdermozilla-central@711963e8daa3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnjn
bugs1286165
milestone50.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 1286165 (Part 3) - Add StreamingLexer tests for the SourceBufferIterator version of Lex(). r=njn
image/test/gtest/Common.h
image/test/gtest/TestStreamingLexer.cpp
--- a/image/test/gtest/Common.h
+++ b/image/test/gtest/Common.h
@@ -183,16 +183,47 @@ bool PalettedRectIsSolidColor(Decoder* a
 /**
  * @returns true if the pixels in @aRow of @aSurface match the pixels given in
  * @aPixels.
  */
 bool RowHasPixels(gfx::SourceSurface* aSurface,
                   int32_t aRow,
                   const std::vector<BGRAColor>& aPixels);
 
+// ExpectNoResume is an IResumable implementation for use by tests that expect
+// Resume() to never get called.
+class ExpectNoResume final : public IResumable
+{
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ExpectNoResume, override)
+
+  void Resume() override { FAIL() << "Resume() should not get called"; }
+
+private:
+  ~ExpectNoResume() override { }
+};
+
+// CountResumes is an IResumable implementation for use by tests that expect
+// Resume() to get called a certain number of times.
+class CountResumes : public IResumable
+{
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CountResumes, override)
+
+  CountResumes() : mCount(0) { }
+
+  void Resume() override { mCount++; }
+  uint32_t Count() const { return mCount; }
+
+private:
+  ~CountResumes() override { }
+
+  uint32_t mCount;
+};
+
 
 ///////////////////////////////////////////////////////////////////////////////
 // SurfacePipe Helpers
 ///////////////////////////////////////////////////////////////////////////////
 
 /**
  * Creates a decoder with no data associated with, suitable for testing code
  * that requires a decoder to initialize or to allocate surfaces but doesn't
--- a/image/test/gtest/TestStreamingLexer.cpp
+++ b/image/test/gtest/TestStreamingLexer.cpp
@@ -77,185 +77,465 @@ DoLexWithUnbufferedTerminate(TestState a
       return Transition::ToUnbuffered(TestState::TWO, TestState::UNBUFFERED, 3);
     case TestState::UNBUFFERED:
       return Transition::TerminateSuccess();
     default:
       MOZ_CRASH("Unknown TestState");
   }
 }
 
-TEST(ImageStreamingLexer, SingleChunk)
+class ImageStreamingLexer : public ::testing::Test
 {
-  StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3));
-  char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
+public:
+  ImageStreamingLexer()
+    : mLexer(Transition::To(TestState::ONE, 3))
+    , mSourceBuffer(new SourceBuffer)
+    , mIterator(mSourceBuffer->Iterator())
+    , mExpectNoResume(new ExpectNoResume)
+    , mCountResumes(new CountResumes)
+  { }
 
+protected:
+  AutoInitializeImageLib mInit;
+  const char mData[9] { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
+  StreamingLexer<TestState> mLexer;
+  RefPtr<SourceBuffer> mSourceBuffer;
+  SourceBufferIterator mIterator;
+  RefPtr<ExpectNoResume> mExpectNoResume;
+  RefPtr<CountResumes> mCountResumes;
+};
+
+TEST_F(ImageStreamingLexer, ZeroLengthData)
+{
+  // Test delivering a zero-length piece of data.
+  Maybe<TerminalState> result = mLexer.Lex(mData, 0, DoLex);
+  EXPECT_TRUE(result.isNothing());
+}
+
+TEST_F(ImageStreamingLexer, SingleChunk)
+{
   // Test delivering all the data at once.
-  Maybe<TerminalState> result = lexer.Lex(data, sizeof(data), DoLex);
+  Maybe<TerminalState> result = mLexer.Lex(mData, sizeof(mData), DoLex);
   EXPECT_TRUE(result.isSome());
   EXPECT_EQ(Some(TerminalState::SUCCESS), result);
 }
 
-TEST(ImageStreamingLexer, SingleChunkWithUnbuffered)
+TEST_F(ImageStreamingLexer, SingleChunkFromSourceBuffer)
 {
-  StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3));
-  char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
+  // Test delivering all the data at once.
+  mSourceBuffer->Append(mData, sizeof(mData));
+  mSourceBuffer->Complete(NS_OK);
+
+  Maybe<TerminalState> result = mLexer.Lex(mIterator, mExpectNoResume, DoLex);
+
+  EXPECT_TRUE(result.isSome());
+  EXPECT_EQ(Some(TerminalState::SUCCESS), result);
+}
+
+TEST_F(ImageStreamingLexer, SingleChunkWithUnbuffered)
+{
   Vector<char> unbufferedVector;
 
   // Test delivering all the data at once.
   Maybe<TerminalState> result =
-    lexer.Lex(data, sizeof(data),
-              [&](TestState aState, const char* aData, size_t aLength) {
+    mLexer.Lex(mData, sizeof(mData),
+               [&](TestState aState, const char* aData, size_t aLength) {
       return DoLexWithUnbuffered(aState, aData, aLength, unbufferedVector);
   });
   EXPECT_TRUE(result.isSome());
   EXPECT_EQ(Some(TerminalState::SUCCESS), result);
 }
 
-TEST(ImageStreamingLexer, ChunkPerState)
+TEST_F(ImageStreamingLexer, SingleChunkWithUnbufferedFromSourceBuffer)
 {
-  StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3));
-  char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
+  Vector<char> unbufferedVector;
+
+  // Test delivering all the data at once.
+  mSourceBuffer->Append(mData, sizeof(mData));
+  mSourceBuffer->Complete(NS_OK);
 
+  Maybe<TerminalState> result =
+    mLexer.Lex(mIterator, mExpectNoResume,
+               [&](TestState aState, const char* aData, size_t aLength) {
+      return DoLexWithUnbuffered(aState, aData, aLength, unbufferedVector);
+  });
+
+  EXPECT_TRUE(result.isSome());
+  EXPECT_EQ(Some(TerminalState::SUCCESS), result);
+}
+
+TEST_F(ImageStreamingLexer, ChunkPerState)
+{
   // Test delivering in perfectly-sized chunks, one per state.
   for (unsigned i = 0; i < 3; ++i) {
-    Maybe<TerminalState> result = lexer.Lex(data + 3 * i, 3, DoLex);
+    Maybe<TerminalState> result = mLexer.Lex(mData + 3 * i, 3, DoLex);
 
     if (i == 2) {
       EXPECT_TRUE(result.isSome());
       EXPECT_EQ(Some(TerminalState::SUCCESS), result);
     } else {
       EXPECT_TRUE(result.isNothing());
     }
   }
 }
 
-TEST(ImageStreamingLexer, ChunkPerStateWithUnbuffered)
+TEST_F(ImageStreamingLexer, ChunkPerStateFromSourceBuffer)
 {
-  StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3));
-  char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
+  // Test delivering in perfectly-sized chunks, one per state.
+  for (unsigned i = 0; i < 3; ++i) {
+    mSourceBuffer->Append(mData + 3 * i, 3);
+    Maybe<TerminalState> result = mLexer.Lex(mIterator, mCountResumes, DoLex);
+
+    if (i == 2) {
+      EXPECT_TRUE(result.isSome());
+      EXPECT_EQ(Some(TerminalState::SUCCESS), result);
+    } else {
+      EXPECT_TRUE(result.isNothing());
+    }
+  }
+
+  EXPECT_EQ(2u, mCountResumes->Count());
+  mSourceBuffer->Complete(NS_OK);
+}
+
+TEST_F(ImageStreamingLexer, ChunkPerStateWithUnbuffered)
+{
   Vector<char> unbufferedVector;
 
   // Test delivering in perfectly-sized chunks, one per state.
   for (unsigned i = 0; i < 3; ++i) {
     Maybe<TerminalState> result =
-      lexer.Lex(data + 3 * i, 3,
-                [&](TestState aState, const char* aData, size_t aLength) {
+      mLexer.Lex(mData + 3 * i, 3,
+                 [&](TestState aState, const char* aData, size_t aLength) {
         return DoLexWithUnbuffered(aState, aData, aLength, unbufferedVector);
     });
 
     if (i == 2) {
       EXPECT_TRUE(result.isSome());
       EXPECT_EQ(Some(TerminalState::SUCCESS), result);
     } else {
       EXPECT_TRUE(result.isNothing());
     }
   }
 }
 
-TEST(ImageStreamingLexer, OneByteChunks)
+TEST_F(ImageStreamingLexer, ChunkPerStateWithUnbufferedFromSourceBuffer)
 {
-  StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3));
-  char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
+  Vector<char> unbufferedVector;
+
+  // Test delivering in perfectly-sized chunks, one per state.
+  for (unsigned i = 0; i < 3; ++i) {
+    mSourceBuffer->Append(mData + 3 * i, 3);
+    Maybe<TerminalState> result =
+      mLexer.Lex(mIterator, mCountResumes,
+                 [&](TestState aState, const char* aData, size_t aLength) {
+        return DoLexWithUnbuffered(aState, aData, aLength, unbufferedVector);
+    });
 
+    if (i == 2) {
+      EXPECT_TRUE(result.isSome());
+      EXPECT_EQ(Some(TerminalState::SUCCESS), result);
+    } else {
+      EXPECT_TRUE(result.isNothing());
+    }
+  }
+
+  EXPECT_EQ(2u, mCountResumes->Count());
+  mSourceBuffer->Complete(NS_OK);
+}
+
+TEST_F(ImageStreamingLexer, OneByteChunks)
+{
   // Test delivering in one byte chunks.
   for (unsigned i = 0; i < 9; ++i) {
-    Maybe<TerminalState> result = lexer.Lex(data + i, 1, DoLex);
+    Maybe<TerminalState> result = mLexer.Lex(mData + i, 1, DoLex);
 
     if (i == 8) {
       EXPECT_TRUE(result.isSome());
       EXPECT_EQ(Some(TerminalState::SUCCESS), result);
     } else {
       EXPECT_TRUE(result.isNothing());
     }
   }
 }
 
-TEST(ImageStreamingLexer, OneByteChunksWithUnbuffered)
+TEST_F(ImageStreamingLexer, OneByteChunksFromSourceBuffer)
 {
-  StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3));
-  char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
+  // Test delivering in one byte chunks.
+  for (unsigned i = 0; i < 9; ++i) {
+    mSourceBuffer->Append(mData + i, 1);
+    Maybe<TerminalState> result = mLexer.Lex(mIterator, mCountResumes, DoLex);
+
+    if (i == 8) {
+      EXPECT_TRUE(result.isSome());
+      EXPECT_EQ(Some(TerminalState::SUCCESS), result);
+    } else {
+      EXPECT_TRUE(result.isNothing());
+    }
+  }
+
+  EXPECT_EQ(8u, mCountResumes->Count());
+  mSourceBuffer->Complete(NS_OK);
+}
+
+TEST_F(ImageStreamingLexer, OneByteChunksWithUnbuffered)
+{
   Vector<char> unbufferedVector;
 
   // Test delivering in one byte chunks.
   for (unsigned i = 0; i < 9; ++i) {
     Maybe<TerminalState> result =
-      lexer.Lex(data + i, 1,
-                [&](TestState aState, const char* aData, size_t aLength) {
+      mLexer.Lex(mData + i, 1,
+                 [&](TestState aState, const char* aData, size_t aLength) {
         return DoLexWithUnbuffered(aState, aData, aLength, unbufferedVector);
     });
 
     if (i == 8) {
       EXPECT_TRUE(result.isSome());
       EXPECT_EQ(Some(TerminalState::SUCCESS), result);
     } else {
       EXPECT_TRUE(result.isNothing());
     }
   }
 }
 
-TEST(ImageStreamingLexer, TerminateSuccess)
+TEST_F(ImageStreamingLexer, OneByteChunksWithUnbufferedFromSourceBuffer)
 {
-  StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3));
-  char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
+  Vector<char> unbufferedVector;
+
+  // Test delivering in one byte chunks.
+  for (unsigned i = 0; i < 9; ++i) {
+    mSourceBuffer->Append(mData + i, 1);
+    Maybe<TerminalState> result =
+      mLexer.Lex(mIterator, mCountResumes,
+                 [&](TestState aState, const char* aData, size_t aLength) {
+        return DoLexWithUnbuffered(aState, aData, aLength, unbufferedVector);
+    });
 
+    if (i == 8) {
+      EXPECT_TRUE(result.isSome());
+      EXPECT_EQ(Some(TerminalState::SUCCESS), result);
+    } else {
+      EXPECT_TRUE(result.isNothing());
+    }
+  }
+
+  EXPECT_EQ(8u, mCountResumes->Count());
+  mSourceBuffer->Complete(NS_OK);
+}
+
+TEST_F(ImageStreamingLexer, TerminateSuccess)
+{
   // Test that Terminate is "sticky".
   Maybe<TerminalState> result =
-    lexer.Lex(data, sizeof(data),
-              [&](TestState aState, const char* aData, size_t aLength) {
+    mLexer.Lex(mData, sizeof(mData),
+               [&](TestState aState, const char* aData, size_t aLength) {
       EXPECT_TRUE(aState == TestState::ONE);
       return Transition::TerminateSuccess();
   });
   EXPECT_TRUE(result.isSome());
   EXPECT_EQ(Some(TerminalState::SUCCESS), result);
 
   result =
-    lexer.Lex(data, sizeof(data),
-              [&](TestState aState, const char* aData, size_t aLength) {
+    mLexer.Lex(mData, sizeof(mData),
+               [&](TestState aState, const char* aData, size_t aLength) {
       EXPECT_TRUE(false);  // Shouldn't get here.
       return Transition::TerminateFailure();
   });
   EXPECT_TRUE(result.isSome());
   EXPECT_EQ(Some(TerminalState::SUCCESS), result);
 }
 
-TEST(ImageStreamingLexer, TerminateFailure)
+TEST_F(ImageStreamingLexer, TerminateSuccessFromSourceBuffer)
 {
-  StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3));
-  char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
+  mSourceBuffer->Append(mData, sizeof(mData));
+  mSourceBuffer->Complete(NS_OK);
 
   // Test that Terminate is "sticky".
+  SourceBufferIterator iterator = mSourceBuffer->Iterator();
   Maybe<TerminalState> result =
-    lexer.Lex(data, sizeof(data),
-              [&](TestState aState, const char* aData, size_t aLength) {
+    mLexer.Lex(iterator, mExpectNoResume,
+               [&](TestState aState, const char* aData, size_t aLength) {
+      EXPECT_TRUE(aState == TestState::ONE);
+      return Transition::TerminateSuccess();
+  });
+  EXPECT_TRUE(result.isSome());
+  EXPECT_EQ(Some(TerminalState::SUCCESS), result);
+
+  SourceBufferIterator iterator2 = mSourceBuffer->Iterator();
+  result =
+    mLexer.Lex(iterator2, mExpectNoResume,
+               [&](TestState aState, const char* aData, size_t aLength) {
+      EXPECT_TRUE(false);  // Shouldn't get here.
+      return Transition::TerminateFailure();
+  });
+  EXPECT_TRUE(result.isSome());
+  EXPECT_EQ(Some(TerminalState::SUCCESS), result);
+}
+
+TEST_F(ImageStreamingLexer, TerminateFailure)
+{
+  // Test that Terminate is "sticky".
+  Maybe<TerminalState> result =
+    mLexer.Lex(mData, sizeof(mData),
+               [&](TestState aState, const char* aData, size_t aLength) {
       EXPECT_TRUE(aState == TestState::ONE);
       return Transition::TerminateFailure();
   });
   EXPECT_TRUE(result.isSome());
   EXPECT_EQ(Some(TerminalState::FAILURE), result);
 
   result =
-    lexer.Lex(data, sizeof(data),
-              [&](TestState aState, const char* aData, size_t aLength) {
+    mLexer.Lex(mData, sizeof(mData),
+               [&](TestState aState, const char* aData, size_t aLength) {
       EXPECT_TRUE(false);  // Shouldn't get here.
       return Transition::TerminateFailure();
   });
   EXPECT_TRUE(result.isSome());
   EXPECT_EQ(Some(TerminalState::FAILURE), result);
 }
 
-TEST(ImageStreamingLexer, TerminateUnbuffered)
+TEST_F(ImageStreamingLexer, TerminateFailureFromSourceBuffer)
 {
-  StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3));
-  char data[9] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
+  mSourceBuffer->Append(mData, sizeof(mData));
+  mSourceBuffer->Complete(NS_OK);
+
+  // Test that Terminate is "sticky".
+  SourceBufferIterator iterator = mSourceBuffer->Iterator();
+  Maybe<TerminalState> result =
+    mLexer.Lex(iterator, mExpectNoResume,
+               [&](TestState aState, const char* aData, size_t aLength) {
+      EXPECT_TRUE(aState == TestState::ONE);
+      return Transition::TerminateFailure();
+  });
+  EXPECT_TRUE(result.isSome());
+  EXPECT_EQ(Some(TerminalState::FAILURE), result);
 
+  SourceBufferIterator iterator2 = mSourceBuffer->Iterator();
+  result =
+    mLexer.Lex(iterator2, mExpectNoResume,
+               [&](TestState aState, const char* aData, size_t aLength) {
+      EXPECT_TRUE(false);  // Shouldn't get here.
+      return Transition::TerminateFailure();
+  });
+  EXPECT_TRUE(result.isSome());
+  EXPECT_EQ(Some(TerminalState::FAILURE), result);
+}
+
+TEST_F(ImageStreamingLexer, TerminateUnbuffered)
+{
   // Test that Terminate works during an unbuffered read.
   for (unsigned i = 0; i < 9; ++i) {
     Maybe<TerminalState> result =
-      lexer.Lex(data + i, 1, DoLexWithUnbufferedTerminate);
+      mLexer.Lex(mData + i, 1, DoLexWithUnbufferedTerminate);
 
     if (i > 2) {
       EXPECT_TRUE(result.isSome());
       EXPECT_EQ(Some(TerminalState::SUCCESS), result);
     } else {
       EXPECT_TRUE(result.isNothing());
     }
   }
 }
+
+TEST_F(ImageStreamingLexer, TerminateUnbufferedFromSourceBuffer)
+{
+  // Test that Terminate works during an unbuffered read.
+  for (unsigned i = 0; i < 9; ++i) {
+    mSourceBuffer->Append(mData + i, 1);
+    Maybe<TerminalState> result =
+      mLexer.Lex(mIterator, mCountResumes, DoLexWithUnbufferedTerminate);
+
+    if (i > 2) {
+      EXPECT_TRUE(result.isSome());
+      EXPECT_EQ(Some(TerminalState::SUCCESS), result);
+    } else {
+      EXPECT_TRUE(result.isNothing());
+    }
+  }
+
+  // We expect 3 resumes because TestState::ONE consumes 3 bytes and then
+  // transitions to TestState::UNBUFFERED, which calls TerminateSuccess() as
+  // soon as it receives a single byte. That's four bytes total,  which are
+  // delivered one at a time, requiring 3 resumes.
+  EXPECT_EQ(3u, mCountResumes->Count());
+
+  mSourceBuffer->Complete(NS_OK);
+}
+
+TEST_F(ImageStreamingLexer, SourceBufferImmediateComplete)
+{
+  // Test calling SourceBuffer::Complete() without appending any data. This
+  // causes the SourceBuffer to automatically have a failing completion status,
+  // no matter what you pass, so we expect TerminalState::FAILURE below.
+  mSourceBuffer->Complete(NS_OK);
+
+  Maybe<TerminalState> result = mLexer.Lex(mIterator, mExpectNoResume, DoLex);
+
+  EXPECT_TRUE(result.isSome());
+  EXPECT_EQ(Some(TerminalState::FAILURE), result);
+}
+
+TEST_F(ImageStreamingLexer, SourceBufferTruncatedSuccess)
+{
+  // Test that calling SourceBuffer::Complete() with a successful status results
+  // in an immediate TerminalState::SUCCESS result.
+  for (unsigned i = 0; i < 9; ++i) {
+    if (i < 2) {
+      mSourceBuffer->Append(mData + i, 1);
+    } else if (i == 2) {
+      mSourceBuffer->Complete(NS_OK);
+    }
+
+    Maybe<TerminalState> result = mLexer.Lex(mIterator, mCountResumes, DoLex);
+
+    if (i >= 2) {
+      EXPECT_TRUE(result.isSome());
+      EXPECT_EQ(Some(TerminalState::SUCCESS), result);
+    } else {
+      EXPECT_TRUE(result.isNothing());
+    }
+  }
+
+  EXPECT_EQ(2u, mCountResumes->Count());
+}
+
+TEST_F(ImageStreamingLexer, SourceBufferTruncatedFailure)
+{
+  // Test that calling SourceBuffer::Complete() with a failing status results in
+  // an immediate TerminalState::FAILURE result.
+  for (unsigned i = 0; i < 9; ++i) {
+    if (i < 2) {
+      mSourceBuffer->Append(mData + i, 1);
+    } else if (i == 2) {
+      mSourceBuffer->Complete(NS_ERROR_FAILURE);
+    }
+
+    Maybe<TerminalState> result = mLexer.Lex(mIterator, mCountResumes, DoLex);
+
+    if (i >= 2) {
+      EXPECT_TRUE(result.isSome());
+      EXPECT_EQ(Some(TerminalState::FAILURE), result);
+    } else {
+      EXPECT_TRUE(result.isNothing());
+    }
+  }
+
+  EXPECT_EQ(2u, mCountResumes->Count());
+}
+
+TEST_F(ImageStreamingLexer, NoSourceBufferResumable)
+{
+  // Test delivering in one byte chunks with no IResumable.
+  for (unsigned i = 0; i < 9; ++i) {
+    mSourceBuffer->Append(mData + i, 1);
+    Maybe<TerminalState> result = mLexer.Lex(mIterator, nullptr, DoLex);
+
+    if (i == 8) {
+      EXPECT_TRUE(result.isSome());
+      EXPECT_EQ(Some(TerminalState::SUCCESS), result);
+    } else {
+      EXPECT_TRUE(result.isNothing());
+    }
+  }
+
+  mSourceBuffer->Complete(NS_OK);
+}