mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-07 08:18:41 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
973
image/test/gtest/TestStreamingLexer.cpp
Normal file
973
image/test/gtest/TestStreamingLexer.cpp
Normal file
|
|
@ -0,0 +1,973 @@
|
|||
/* 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/. */
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "mozilla/Vector.h"
|
||||
#include "StreamingLexer.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::image;
|
||||
|
||||
enum class TestState
|
||||
{
|
||||
ONE,
|
||||
TWO,
|
||||
THREE,
|
||||
UNBUFFERED,
|
||||
TRUNCATED_SUCCESS,
|
||||
TRUNCATED_FAILURE
|
||||
};
|
||||
|
||||
void
|
||||
CheckLexedData(const char* aData,
|
||||
size_t aLength,
|
||||
size_t aOffset,
|
||||
size_t aExpectedLength)
|
||||
{
|
||||
EXPECT_TRUE(aLength == aExpectedLength);
|
||||
|
||||
for (size_t i = 0; i < aLength; ++i) {
|
||||
EXPECT_EQ(aData[i], char(aOffset + i + 1));
|
||||
}
|
||||
}
|
||||
|
||||
LexerTransition<TestState>
|
||||
DoLex(TestState aState, const char* aData, size_t aLength)
|
||||
{
|
||||
switch (aState) {
|
||||
case TestState::ONE:
|
||||
CheckLexedData(aData, aLength, 0, 3);
|
||||
return Transition::To(TestState::TWO, 3);
|
||||
case TestState::TWO:
|
||||
CheckLexedData(aData, aLength, 3, 3);
|
||||
return Transition::To(TestState::THREE, 3);
|
||||
case TestState::THREE:
|
||||
CheckLexedData(aData, aLength, 6, 3);
|
||||
return Transition::TerminateSuccess();
|
||||
case TestState::TRUNCATED_SUCCESS:
|
||||
return Transition::TerminateSuccess();
|
||||
case TestState::TRUNCATED_FAILURE:
|
||||
return Transition::TerminateFailure();
|
||||
default:
|
||||
MOZ_CRASH("Unexpected or unhandled TestState");
|
||||
}
|
||||
}
|
||||
|
||||
LexerTransition<TestState>
|
||||
DoLexWithUnbuffered(TestState aState, const char* aData, size_t aLength,
|
||||
Vector<char>& aUnbufferedVector)
|
||||
{
|
||||
switch (aState) {
|
||||
case TestState::ONE:
|
||||
CheckLexedData(aData, aLength, 0, 3);
|
||||
return Transition::ToUnbuffered(TestState::TWO, TestState::UNBUFFERED, 3);
|
||||
case TestState::TWO:
|
||||
CheckLexedData(aUnbufferedVector.begin(), aUnbufferedVector.length(), 3, 3);
|
||||
return Transition::To(TestState::THREE, 3);
|
||||
case TestState::THREE:
|
||||
CheckLexedData(aData, aLength, 6, 3);
|
||||
return Transition::TerminateSuccess();
|
||||
case TestState::UNBUFFERED:
|
||||
EXPECT_TRUE(aLength <= 3);
|
||||
EXPECT_TRUE(aUnbufferedVector.append(aData, aLength));
|
||||
return Transition::ContinueUnbuffered(TestState::UNBUFFERED);
|
||||
default:
|
||||
MOZ_CRASH("Unexpected or unhandled TestState");
|
||||
}
|
||||
}
|
||||
|
||||
LexerTransition<TestState>
|
||||
DoLexWithUnbufferedTerminate(TestState aState, const char* aData, size_t aLength)
|
||||
{
|
||||
switch (aState) {
|
||||
case TestState::ONE:
|
||||
CheckLexedData(aData, aLength, 0, 3);
|
||||
return Transition::ToUnbuffered(TestState::TWO, TestState::UNBUFFERED, 3);
|
||||
case TestState::UNBUFFERED:
|
||||
return Transition::TerminateSuccess();
|
||||
default:
|
||||
MOZ_CRASH("Unexpected or unhandled TestState");
|
||||
}
|
||||
}
|
||||
|
||||
LexerTransition<TestState>
|
||||
DoLexWithYield(TestState aState, const char* aData, size_t aLength)
|
||||
{
|
||||
switch (aState) {
|
||||
case TestState::ONE:
|
||||
CheckLexedData(aData, aLength, 0, 3);
|
||||
return Transition::ToAfterYield(TestState::TWO);
|
||||
case TestState::TWO:
|
||||
CheckLexedData(aData, aLength, 0, 3);
|
||||
return Transition::To(TestState::THREE, 6);
|
||||
case TestState::THREE:
|
||||
CheckLexedData(aData, aLength, 3, 6);
|
||||
return Transition::TerminateSuccess();
|
||||
default:
|
||||
MOZ_CRASH("Unexpected or unhandled TestState");
|
||||
}
|
||||
}
|
||||
|
||||
LexerTransition<TestState>
|
||||
DoLexWithTerminateAfterYield(TestState aState, const char* aData, size_t aLength)
|
||||
{
|
||||
switch (aState) {
|
||||
case TestState::ONE:
|
||||
CheckLexedData(aData, aLength, 0, 3);
|
||||
return Transition::ToAfterYield(TestState::TWO);
|
||||
case TestState::TWO:
|
||||
return Transition::TerminateSuccess();
|
||||
default:
|
||||
MOZ_CRASH("Unexpected or unhandled TestState");
|
||||
}
|
||||
}
|
||||
|
||||
LexerTransition<TestState>
|
||||
DoLexWithZeroLengthStates(TestState aState, const char* aData, size_t aLength)
|
||||
{
|
||||
switch (aState) {
|
||||
case TestState::ONE:
|
||||
EXPECT_TRUE(aLength == 0);
|
||||
return Transition::To(TestState::TWO, 0);
|
||||
case TestState::TWO:
|
||||
EXPECT_TRUE(aLength == 0);
|
||||
return Transition::To(TestState::THREE, 9);
|
||||
case TestState::THREE:
|
||||
CheckLexedData(aData, aLength, 0, 9);
|
||||
return Transition::TerminateSuccess();
|
||||
default:
|
||||
MOZ_CRASH("Unexpected or unhandled TestState");
|
||||
}
|
||||
}
|
||||
|
||||
LexerTransition<TestState>
|
||||
DoLexWithZeroLengthStatesAtEnd(TestState aState, const char* aData, size_t aLength)
|
||||
{
|
||||
switch (aState) {
|
||||
case TestState::ONE:
|
||||
CheckLexedData(aData, aLength, 0, 9);
|
||||
return Transition::To(TestState::TWO, 0);
|
||||
case TestState::TWO:
|
||||
EXPECT_TRUE(aLength == 0);
|
||||
return Transition::To(TestState::THREE, 0);
|
||||
case TestState::THREE:
|
||||
EXPECT_TRUE(aLength == 0);
|
||||
return Transition::TerminateSuccess();
|
||||
default:
|
||||
MOZ_CRASH("Unexpected or unhandled TestState");
|
||||
}
|
||||
}
|
||||
|
||||
LexerTransition<TestState>
|
||||
DoLexWithZeroLengthYield(TestState aState, const char* aData, size_t aLength)
|
||||
{
|
||||
switch (aState) {
|
||||
case TestState::ONE:
|
||||
EXPECT_EQ(0u, aLength);
|
||||
return Transition::ToAfterYield(TestState::TWO);
|
||||
case TestState::TWO:
|
||||
EXPECT_EQ(0u, aLength);
|
||||
return Transition::To(TestState::THREE, 9);
|
||||
case TestState::THREE:
|
||||
CheckLexedData(aData, aLength, 0, 9);
|
||||
return Transition::TerminateSuccess();
|
||||
default:
|
||||
MOZ_CRASH("Unexpected or unhandled TestState");
|
||||
}
|
||||
}
|
||||
|
||||
LexerTransition<TestState>
|
||||
DoLexWithZeroLengthStatesUnbuffered(TestState aState,
|
||||
const char* aData,
|
||||
size_t aLength)
|
||||
{
|
||||
switch (aState) {
|
||||
case TestState::ONE:
|
||||
EXPECT_TRUE(aLength == 0);
|
||||
return Transition::ToUnbuffered(TestState::TWO, TestState::UNBUFFERED, 0);
|
||||
case TestState::TWO:
|
||||
EXPECT_TRUE(aLength == 0);
|
||||
return Transition::To(TestState::THREE, 9);
|
||||
case TestState::THREE:
|
||||
CheckLexedData(aData, aLength, 0, 9);
|
||||
return Transition::TerminateSuccess();
|
||||
case TestState::UNBUFFERED:
|
||||
ADD_FAILURE() << "Should not enter zero-length unbuffered state";
|
||||
return Transition::TerminateFailure();
|
||||
default:
|
||||
MOZ_CRASH("Unexpected or unhandled TestState");
|
||||
}
|
||||
}
|
||||
|
||||
LexerTransition<TestState>
|
||||
DoLexWithZeroLengthStatesAfterUnbuffered(TestState aState,
|
||||
const char* aData,
|
||||
size_t aLength)
|
||||
{
|
||||
switch (aState) {
|
||||
case TestState::ONE:
|
||||
EXPECT_TRUE(aLength == 0);
|
||||
return Transition::ToUnbuffered(TestState::TWO, TestState::UNBUFFERED, 9);
|
||||
case TestState::TWO:
|
||||
EXPECT_TRUE(aLength == 0);
|
||||
return Transition::To(TestState::THREE, 0);
|
||||
case TestState::THREE:
|
||||
EXPECT_TRUE(aLength == 0);
|
||||
return Transition::TerminateSuccess();
|
||||
case TestState::UNBUFFERED:
|
||||
CheckLexedData(aData, aLength, 0, 9);
|
||||
return Transition::ContinueUnbuffered(TestState::UNBUFFERED);
|
||||
default:
|
||||
MOZ_CRASH("Unexpected or unhandled TestState");
|
||||
}
|
||||
}
|
||||
|
||||
class ImageStreamingLexer : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
// Note that mLexer is configured to enter TerminalState::FAILURE immediately
|
||||
// if the input data is truncated. We don't expect that to happen in most
|
||||
// tests, so we want to detect that issue. If a test needs a different
|
||||
// behavior, we create a special StreamingLexer just for that test.
|
||||
ImageStreamingLexer()
|
||||
: mLexer(Transition::To(TestState::ONE, 3), Transition::TerminateFailure())
|
||||
, mSourceBuffer(new SourceBuffer)
|
||||
, mIterator(mSourceBuffer->Iterator())
|
||||
, mExpectNoResume(new ExpectNoResume)
|
||||
, mCountResumes(new CountResumes)
|
||||
{ }
|
||||
|
||||
protected:
|
||||
void CheckTruncatedState(StreamingLexer<TestState>& aLexer,
|
||||
TerminalState aExpectedTerminalState,
|
||||
nsresult aCompletionStatus = NS_OK)
|
||||
{
|
||||
for (unsigned i = 0; i < 9; ++i) {
|
||||
if (i < 2) {
|
||||
mSourceBuffer->Append(mData + i, 1);
|
||||
} else if (i == 2) {
|
||||
mSourceBuffer->Complete(aCompletionStatus);
|
||||
}
|
||||
|
||||
LexerResult result = aLexer.Lex(mIterator, mCountResumes, DoLex);
|
||||
|
||||
if (i >= 2) {
|
||||
EXPECT_TRUE(result.is<TerminalState>());
|
||||
EXPECT_EQ(aExpectedTerminalState, result.as<TerminalState>());
|
||||
} else {
|
||||
EXPECT_TRUE(result.is<Yield>());
|
||||
EXPECT_EQ(Yield::NEED_MORE_DATA, result.as<Yield>());
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_EQ(2u, mCountResumes->Count());
|
||||
}
|
||||
|
||||
AutoInitializeImageLib mInit;
|
||||
const char mData[9] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
||||
StreamingLexer<TestState> mLexer;
|
||||
RefPtr<SourceBuffer> mSourceBuffer;
|
||||
SourceBufferIterator mIterator;
|
||||
RefPtr<ExpectNoResume> mExpectNoResume;
|
||||
RefPtr<CountResumes> mCountResumes;
|
||||
};
|
||||
|
||||
TEST_F(ImageStreamingLexer, ZeroLengthData)
|
||||
{
|
||||
// Test a zero-length input.
|
||||
mSourceBuffer->Complete(NS_OK);
|
||||
|
||||
LexerResult result = mLexer.Lex(mIterator, mExpectNoResume, DoLex);
|
||||
|
||||
EXPECT_TRUE(result.is<TerminalState>());
|
||||
EXPECT_EQ(TerminalState::FAILURE, result.as<TerminalState>());
|
||||
}
|
||||
|
||||
TEST_F(ImageStreamingLexer, ZeroLengthDataUnbuffered)
|
||||
{
|
||||
// Test a zero-length input.
|
||||
mSourceBuffer->Complete(NS_OK);
|
||||
|
||||
// Create a special StreamingLexer for this test because we want the first
|
||||
// state to be unbuffered.
|
||||
StreamingLexer<TestState> lexer(Transition::ToUnbuffered(TestState::ONE,
|
||||
TestState::UNBUFFERED,
|
||||
sizeof(mData)),
|
||||
Transition::TerminateFailure());
|
||||
|
||||
LexerResult result = lexer.Lex(mIterator, mExpectNoResume, DoLex);
|
||||
EXPECT_TRUE(result.is<TerminalState>());
|
||||
EXPECT_EQ(TerminalState::FAILURE, result.as<TerminalState>());
|
||||
}
|
||||
|
||||
TEST_F(ImageStreamingLexer, StartWithTerminal)
|
||||
{
|
||||
// Create a special StreamingLexer for this test because we want the first
|
||||
// state to be a terminal state. This doesn't really make sense, but we should
|
||||
// handle it.
|
||||
StreamingLexer<TestState> lexer(Transition::TerminateSuccess(),
|
||||
Transition::TerminateFailure());
|
||||
LexerResult result = lexer.Lex(mIterator, mExpectNoResume, DoLex);
|
||||
EXPECT_TRUE(result.is<TerminalState>());
|
||||
EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>());
|
||||
|
||||
mSourceBuffer->Complete(NS_OK);
|
||||
}
|
||||
|
||||
TEST_F(ImageStreamingLexer, SingleChunk)
|
||||
{
|
||||
// Test delivering all the data at once.
|
||||
mSourceBuffer->Append(mData, sizeof(mData));
|
||||
mSourceBuffer->Complete(NS_OK);
|
||||
|
||||
LexerResult result = mLexer.Lex(mIterator, mExpectNoResume, DoLex);
|
||||
|
||||
EXPECT_TRUE(result.is<TerminalState>());
|
||||
EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>());
|
||||
}
|
||||
|
||||
TEST_F(ImageStreamingLexer, SingleChunkWithUnbuffered)
|
||||
{
|
||||
Vector<char> unbufferedVector;
|
||||
|
||||
// Test delivering all the data at once.
|
||||
mSourceBuffer->Append(mData, sizeof(mData));
|
||||
mSourceBuffer->Complete(NS_OK);
|
||||
|
||||
LexerResult result =
|
||||
mLexer.Lex(mIterator, mExpectNoResume,
|
||||
[&](TestState aState, const char* aData, size_t aLength) {
|
||||
return DoLexWithUnbuffered(aState, aData, aLength, unbufferedVector);
|
||||
});
|
||||
|
||||
EXPECT_TRUE(result.is<TerminalState>());
|
||||
EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>());
|
||||
}
|
||||
|
||||
TEST_F(ImageStreamingLexer, SingleChunkWithYield)
|
||||
{
|
||||
// Test delivering all the data at once.
|
||||
mSourceBuffer->Append(mData, sizeof(mData));
|
||||
mSourceBuffer->Complete(NS_OK);
|
||||
|
||||
LexerResult result = mLexer.Lex(mIterator, mExpectNoResume, DoLexWithYield);
|
||||
ASSERT_TRUE(result.is<Yield>());
|
||||
EXPECT_EQ(Yield::OUTPUT_AVAILABLE, result.as<Yield>());
|
||||
|
||||
result = mLexer.Lex(mIterator, mExpectNoResume, DoLexWithYield);
|
||||
ASSERT_TRUE(result.is<TerminalState>());
|
||||
EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>());
|
||||
}
|
||||
|
||||
TEST_F(ImageStreamingLexer, ChunkPerState)
|
||||
{
|
||||
// Test delivering in perfectly-sized chunks, one per state.
|
||||
for (unsigned i = 0; i < 3; ++i) {
|
||||
mSourceBuffer->Append(mData + 3 * i, 3);
|
||||
LexerResult result = mLexer.Lex(mIterator, mCountResumes, DoLex);
|
||||
|
||||
if (i == 2) {
|
||||
EXPECT_TRUE(result.is<TerminalState>());
|
||||
EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>());
|
||||
} else {
|
||||
EXPECT_TRUE(result.is<Yield>());
|
||||
EXPECT_EQ(Yield::NEED_MORE_DATA, result.as<Yield>());
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
mSourceBuffer->Append(mData + 3 * i, 3);
|
||||
LexerResult 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.is<TerminalState>());
|
||||
EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>());
|
||||
} else {
|
||||
EXPECT_TRUE(result.is<Yield>());
|
||||
EXPECT_EQ(Yield::NEED_MORE_DATA, result.as<Yield>());
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_EQ(2u, mCountResumes->Count());
|
||||
mSourceBuffer->Complete(NS_OK);
|
||||
}
|
||||
|
||||
TEST_F(ImageStreamingLexer, ChunkPerStateWithYield)
|
||||
{
|
||||
// Test delivering in perfectly-sized chunks, one per state.
|
||||
mSourceBuffer->Append(mData, 3);
|
||||
LexerResult result = mLexer.Lex(mIterator, mCountResumes, DoLexWithYield);
|
||||
EXPECT_TRUE(result.is<Yield>());
|
||||
EXPECT_EQ(Yield::OUTPUT_AVAILABLE, result.as<Yield>());
|
||||
|
||||
result = mLexer.Lex(mIterator, mCountResumes, DoLexWithYield);
|
||||
EXPECT_TRUE(result.is<Yield>());
|
||||
EXPECT_EQ(Yield::NEED_MORE_DATA, result.as<Yield>());
|
||||
|
||||
mSourceBuffer->Append(mData + 3, 6);
|
||||
result = mLexer.Lex(mIterator, mCountResumes, DoLexWithYield);
|
||||
EXPECT_TRUE(result.is<TerminalState>());
|
||||
EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>());
|
||||
|
||||
EXPECT_EQ(1u, mCountResumes->Count());
|
||||
mSourceBuffer->Complete(NS_OK);
|
||||
}
|
||||
|
||||
TEST_F(ImageStreamingLexer, ChunkPerStateWithUnbufferedYield)
|
||||
{
|
||||
size_t unbufferedCallCount = 0;
|
||||
Vector<char> unbufferedVector;
|
||||
auto lexerFunc = [&](TestState aState, const char* aData, size_t aLength)
|
||||
-> LexerTransition<TestState> {
|
||||
switch (aState) {
|
||||
case TestState::ONE:
|
||||
CheckLexedData(aData, aLength, 0, 3);
|
||||
return Transition::ToUnbuffered(TestState::TWO, TestState::UNBUFFERED, 3);
|
||||
case TestState::TWO:
|
||||
CheckLexedData(unbufferedVector.begin(), unbufferedVector.length(), 3, 3);
|
||||
return Transition::To(TestState::THREE, 3);
|
||||
case TestState::THREE:
|
||||
CheckLexedData(aData, aLength, 6, 3);
|
||||
return Transition::TerminateSuccess();
|
||||
case TestState::UNBUFFERED:
|
||||
switch (unbufferedCallCount) {
|
||||
case 0:
|
||||
CheckLexedData(aData, aLength, 3, 3);
|
||||
EXPECT_TRUE(unbufferedVector.append(aData, 2));
|
||||
unbufferedCallCount++;
|
||||
|
||||
// Continue after yield, telling StreamingLexer we consumed 2 bytes.
|
||||
return Transition::ContinueUnbufferedAfterYield(TestState::UNBUFFERED, 2);
|
||||
|
||||
case 1:
|
||||
CheckLexedData(aData, aLength, 5, 1);
|
||||
EXPECT_TRUE(unbufferedVector.append(aData, 1));
|
||||
unbufferedCallCount++;
|
||||
|
||||
// Continue after yield, telling StreamingLexer we consumed 1 byte.
|
||||
// We should end up in the TWO state.
|
||||
return Transition::ContinueUnbuffered(TestState::UNBUFFERED);
|
||||
}
|
||||
ADD_FAILURE() << "Too many invocations of TestState::UNBUFFERED";
|
||||
return Transition::TerminateFailure();
|
||||
default:
|
||||
MOZ_CRASH("Unexpected or unhandled TestState");
|
||||
}
|
||||
};
|
||||
|
||||
// Test delivering in perfectly-sized chunks, one per state.
|
||||
for (unsigned i = 0; i < 3; ++i) {
|
||||
mSourceBuffer->Append(mData + 3 * i, 3);
|
||||
LexerResult result = mLexer.Lex(mIterator, mCountResumes, lexerFunc);
|
||||
|
||||
switch (i) {
|
||||
case 0:
|
||||
EXPECT_TRUE(result.is<Yield>());
|
||||
EXPECT_EQ(Yield::NEED_MORE_DATA, result.as<Yield>());
|
||||
EXPECT_EQ(0u, unbufferedCallCount);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
EXPECT_TRUE(result.is<Yield>());
|
||||
EXPECT_EQ(Yield::OUTPUT_AVAILABLE, result.as<Yield>());
|
||||
EXPECT_EQ(1u, unbufferedCallCount);
|
||||
|
||||
result = mLexer.Lex(mIterator, mCountResumes, lexerFunc);
|
||||
EXPECT_TRUE(result.is<Yield>());
|
||||
EXPECT_EQ(Yield::NEED_MORE_DATA, result.as<Yield>());
|
||||
EXPECT_EQ(2u, unbufferedCallCount);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
EXPECT_TRUE(result.is<TerminalState>());
|
||||
EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_EQ(2u, mCountResumes->Count());
|
||||
mSourceBuffer->Complete(NS_OK);
|
||||
|
||||
LexerResult result = mLexer.Lex(mIterator, mCountResumes, lexerFunc);
|
||||
EXPECT_TRUE(result.is<TerminalState>());
|
||||
EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>());
|
||||
}
|
||||
|
||||
TEST_F(ImageStreamingLexer, OneByteChunks)
|
||||
{
|
||||
// Test delivering in one byte chunks.
|
||||
for (unsigned i = 0; i < 9; ++i) {
|
||||
mSourceBuffer->Append(mData + i, 1);
|
||||
LexerResult result = mLexer.Lex(mIterator, mCountResumes, DoLex);
|
||||
|
||||
if (i == 8) {
|
||||
EXPECT_TRUE(result.is<TerminalState>());
|
||||
EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>());
|
||||
} else {
|
||||
EXPECT_TRUE(result.is<Yield>());
|
||||
EXPECT_EQ(Yield::NEED_MORE_DATA, result.as<Yield>());
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
mSourceBuffer->Append(mData + i, 1);
|
||||
LexerResult 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.is<TerminalState>());
|
||||
EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>());
|
||||
} else {
|
||||
EXPECT_TRUE(result.is<Yield>());
|
||||
EXPECT_EQ(Yield::NEED_MORE_DATA, result.as<Yield>());
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_EQ(8u, mCountResumes->Count());
|
||||
mSourceBuffer->Complete(NS_OK);
|
||||
}
|
||||
|
||||
TEST_F(ImageStreamingLexer, OneByteChunksWithYield)
|
||||
{
|
||||
// Test delivering in one byte chunks.
|
||||
for (unsigned i = 0; i < 9; ++i) {
|
||||
mSourceBuffer->Append(mData + i, 1);
|
||||
LexerResult result = mLexer.Lex(mIterator, mCountResumes, DoLexWithYield);
|
||||
|
||||
switch (i) {
|
||||
case 2:
|
||||
EXPECT_TRUE(result.is<Yield>());
|
||||
EXPECT_EQ(Yield::OUTPUT_AVAILABLE, result.as<Yield>());
|
||||
|
||||
result = mLexer.Lex(mIterator, mCountResumes, DoLexWithYield);
|
||||
EXPECT_TRUE(result.is<Yield>());
|
||||
EXPECT_EQ(Yield::NEED_MORE_DATA, result.as<Yield>());
|
||||
break;
|
||||
|
||||
case 8:
|
||||
EXPECT_TRUE(result.is<TerminalState>());
|
||||
EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>());
|
||||
break;
|
||||
|
||||
default:
|
||||
EXPECT_TRUE(i < 9);
|
||||
EXPECT_TRUE(result.is<Yield>());
|
||||
EXPECT_EQ(Yield::NEED_MORE_DATA, result.as<Yield>());
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_EQ(8u, mCountResumes->Count());
|
||||
mSourceBuffer->Complete(NS_OK);
|
||||
}
|
||||
|
||||
TEST_F(ImageStreamingLexer, ZeroLengthState)
|
||||
{
|
||||
mSourceBuffer->Append(mData, sizeof(mData));
|
||||
mSourceBuffer->Complete(NS_OK);
|
||||
|
||||
// Create a special StreamingLexer for this test because we want the first
|
||||
// state to be zero length.
|
||||
StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 0),
|
||||
Transition::TerminateFailure());
|
||||
|
||||
LexerResult result =
|
||||
lexer.Lex(mIterator, mExpectNoResume, DoLexWithZeroLengthStates);
|
||||
|
||||
EXPECT_TRUE(result.is<TerminalState>());
|
||||
EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>());
|
||||
}
|
||||
|
||||
TEST_F(ImageStreamingLexer, ZeroLengthStatesAtEnd)
|
||||
{
|
||||
mSourceBuffer->Append(mData, sizeof(mData));
|
||||
mSourceBuffer->Complete(NS_OK);
|
||||
|
||||
// Create a special StreamingLexer for this test because we want the first
|
||||
// state to consume the full input.
|
||||
StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 9),
|
||||
Transition::TerminateFailure());
|
||||
|
||||
LexerResult result =
|
||||
lexer.Lex(mIterator, mExpectNoResume, DoLexWithZeroLengthStatesAtEnd);
|
||||
|
||||
EXPECT_TRUE(result.is<TerminalState>());
|
||||
EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>());
|
||||
}
|
||||
|
||||
TEST_F(ImageStreamingLexer, ZeroLengthStateWithYield)
|
||||
{
|
||||
// Create a special StreamingLexer for this test because we want the first
|
||||
// state to be zero length.
|
||||
StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 0),
|
||||
Transition::TerminateFailure());
|
||||
|
||||
mSourceBuffer->Append(mData, 3);
|
||||
LexerResult result =
|
||||
lexer.Lex(mIterator, mExpectNoResume, DoLexWithZeroLengthYield);
|
||||
ASSERT_TRUE(result.is<Yield>());
|
||||
EXPECT_EQ(Yield::OUTPUT_AVAILABLE, result.as<Yield>());
|
||||
|
||||
result = lexer.Lex(mIterator, mCountResumes, DoLexWithZeroLengthYield);
|
||||
ASSERT_TRUE(result.is<Yield>());
|
||||
EXPECT_EQ(Yield::NEED_MORE_DATA, result.as<Yield>());
|
||||
|
||||
mSourceBuffer->Append(mData + 3, sizeof(mData) - 3);
|
||||
mSourceBuffer->Complete(NS_OK);
|
||||
result = lexer.Lex(mIterator, mExpectNoResume, DoLexWithZeroLengthYield);
|
||||
ASSERT_TRUE(result.is<TerminalState>());
|
||||
EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>());
|
||||
EXPECT_EQ(1u, mCountResumes->Count());
|
||||
}
|
||||
|
||||
TEST_F(ImageStreamingLexer, ZeroLengthStateWithUnbuffered)
|
||||
{
|
||||
mSourceBuffer->Append(mData, sizeof(mData));
|
||||
mSourceBuffer->Complete(NS_OK);
|
||||
|
||||
// Create a special StreamingLexer for this test because we want the first
|
||||
// state to be both zero length and unbuffered.
|
||||
StreamingLexer<TestState> lexer(Transition::ToUnbuffered(TestState::ONE,
|
||||
TestState::UNBUFFERED,
|
||||
0),
|
||||
Transition::TerminateFailure());
|
||||
|
||||
LexerResult result =
|
||||
lexer.Lex(mIterator, mExpectNoResume, DoLexWithZeroLengthStatesUnbuffered);
|
||||
|
||||
EXPECT_TRUE(result.is<TerminalState>());
|
||||
EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>());
|
||||
}
|
||||
|
||||
TEST_F(ImageStreamingLexer, ZeroLengthStateAfterUnbuffered)
|
||||
{
|
||||
mSourceBuffer->Append(mData, sizeof(mData));
|
||||
mSourceBuffer->Complete(NS_OK);
|
||||
|
||||
// Create a special StreamingLexer for this test because we want the first
|
||||
// state to be zero length.
|
||||
StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 0),
|
||||
Transition::TerminateFailure());
|
||||
|
||||
LexerResult result =
|
||||
lexer.Lex(mIterator, mExpectNoResume, DoLexWithZeroLengthStatesAfterUnbuffered);
|
||||
|
||||
EXPECT_TRUE(result.is<TerminalState>());
|
||||
EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>());
|
||||
}
|
||||
|
||||
TEST_F(ImageStreamingLexer, ZeroLengthStateWithUnbufferedYield)
|
||||
{
|
||||
size_t unbufferedCallCount = 0;
|
||||
auto lexerFunc = [&](TestState aState, const char* aData, size_t aLength)
|
||||
-> LexerTransition<TestState> {
|
||||
switch (aState) {
|
||||
case TestState::ONE:
|
||||
EXPECT_EQ(0u, aLength);
|
||||
return Transition::TerminateSuccess();
|
||||
|
||||
case TestState::UNBUFFERED:
|
||||
switch (unbufferedCallCount) {
|
||||
case 0:
|
||||
CheckLexedData(aData, aLength, 0, 3);
|
||||
unbufferedCallCount++;
|
||||
|
||||
// Continue after yield, telling StreamingLexer we consumed 0 bytes.
|
||||
return Transition::ContinueUnbufferedAfterYield(TestState::UNBUFFERED, 0);
|
||||
|
||||
case 1:
|
||||
CheckLexedData(aData, aLength, 0, 3);
|
||||
unbufferedCallCount++;
|
||||
|
||||
// Continue after yield, telling StreamingLexer we consumed 2 bytes.
|
||||
return Transition::ContinueUnbufferedAfterYield(TestState::UNBUFFERED, 2);
|
||||
|
||||
case 2:
|
||||
EXPECT_EQ(1u, aLength);
|
||||
CheckLexedData(aData, aLength, 2, 1);
|
||||
unbufferedCallCount++;
|
||||
|
||||
// Continue after yield, telling StreamingLexer we consumed 1 bytes.
|
||||
return Transition::ContinueUnbufferedAfterYield(TestState::UNBUFFERED, 1);
|
||||
|
||||
case 3:
|
||||
CheckLexedData(aData, aLength, 3, 6);
|
||||
unbufferedCallCount++;
|
||||
|
||||
// Continue after yield, telling StreamingLexer we consumed 6 bytes.
|
||||
// We should transition to TestState::ONE when we return from the
|
||||
// yield.
|
||||
return Transition::ContinueUnbufferedAfterYield(TestState::UNBUFFERED, 6);
|
||||
}
|
||||
|
||||
ADD_FAILURE() << "Too many invocations of TestState::UNBUFFERED";
|
||||
return Transition::TerminateFailure();
|
||||
|
||||
default:
|
||||
MOZ_CRASH("Unexpected or unhandled TestState");
|
||||
}
|
||||
};
|
||||
|
||||
// Create a special StreamingLexer for this test because we want the first
|
||||
// state to be unbuffered.
|
||||
StreamingLexer<TestState> lexer(Transition::ToUnbuffered(TestState::ONE,
|
||||
TestState::UNBUFFERED,
|
||||
sizeof(mData)),
|
||||
Transition::TerminateFailure());
|
||||
|
||||
mSourceBuffer->Append(mData, 3);
|
||||
LexerResult result = lexer.Lex(mIterator, mExpectNoResume, lexerFunc);
|
||||
ASSERT_TRUE(result.is<Yield>());
|
||||
EXPECT_EQ(Yield::OUTPUT_AVAILABLE, result.as<Yield>());
|
||||
EXPECT_EQ(1u, unbufferedCallCount);
|
||||
|
||||
result = lexer.Lex(mIterator, mExpectNoResume, lexerFunc);
|
||||
ASSERT_TRUE(result.is<Yield>());
|
||||
EXPECT_EQ(Yield::OUTPUT_AVAILABLE, result.as<Yield>());
|
||||
EXPECT_EQ(2u, unbufferedCallCount);
|
||||
|
||||
result = lexer.Lex(mIterator, mExpectNoResume, lexerFunc);
|
||||
ASSERT_TRUE(result.is<Yield>());
|
||||
EXPECT_EQ(Yield::OUTPUT_AVAILABLE, result.as<Yield>());
|
||||
EXPECT_EQ(3u, unbufferedCallCount);
|
||||
|
||||
result = lexer.Lex(mIterator, mCountResumes, lexerFunc);
|
||||
ASSERT_TRUE(result.is<Yield>());
|
||||
EXPECT_EQ(Yield::NEED_MORE_DATA, result.as<Yield>());
|
||||
EXPECT_EQ(3u, unbufferedCallCount);
|
||||
|
||||
mSourceBuffer->Append(mData + 3, 6);
|
||||
mSourceBuffer->Complete(NS_OK);
|
||||
EXPECT_EQ(1u, mCountResumes->Count());
|
||||
result = lexer.Lex(mIterator, mExpectNoResume, lexerFunc);
|
||||
ASSERT_TRUE(result.is<Yield>());
|
||||
EXPECT_EQ(Yield::OUTPUT_AVAILABLE, result.as<Yield>());
|
||||
EXPECT_EQ(4u, unbufferedCallCount);
|
||||
|
||||
result = lexer.Lex(mIterator, mExpectNoResume, lexerFunc);
|
||||
ASSERT_TRUE(result.is<TerminalState>());
|
||||
EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>());
|
||||
}
|
||||
|
||||
TEST_F(ImageStreamingLexer, TerminateSuccess)
|
||||
{
|
||||
mSourceBuffer->Append(mData, sizeof(mData));
|
||||
mSourceBuffer->Complete(NS_OK);
|
||||
|
||||
// Test that Terminate is "sticky".
|
||||
SourceBufferIterator iterator = mSourceBuffer->Iterator();
|
||||
LexerResult result =
|
||||
mLexer.Lex(iterator, mExpectNoResume,
|
||||
[&](TestState aState, const char* aData, size_t aLength) {
|
||||
EXPECT_TRUE(aState == TestState::ONE);
|
||||
return Transition::TerminateSuccess();
|
||||
});
|
||||
EXPECT_TRUE(result.is<TerminalState>());
|
||||
EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>());
|
||||
|
||||
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.is<TerminalState>());
|
||||
EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>());
|
||||
}
|
||||
|
||||
TEST_F(ImageStreamingLexer, TerminateFailure)
|
||||
{
|
||||
mSourceBuffer->Append(mData, sizeof(mData));
|
||||
mSourceBuffer->Complete(NS_OK);
|
||||
|
||||
// Test that Terminate is "sticky".
|
||||
SourceBufferIterator iterator = mSourceBuffer->Iterator();
|
||||
LexerResult result =
|
||||
mLexer.Lex(iterator, mExpectNoResume,
|
||||
[&](TestState aState, const char* aData, size_t aLength) {
|
||||
EXPECT_TRUE(aState == TestState::ONE);
|
||||
return Transition::TerminateFailure();
|
||||
});
|
||||
EXPECT_TRUE(result.is<TerminalState>());
|
||||
EXPECT_EQ(TerminalState::FAILURE, result.as<TerminalState>());
|
||||
|
||||
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.is<TerminalState>());
|
||||
EXPECT_EQ(TerminalState::FAILURE, result.as<TerminalState>());
|
||||
}
|
||||
|
||||
TEST_F(ImageStreamingLexer, TerminateUnbuffered)
|
||||
{
|
||||
// Test that Terminate works during an unbuffered read.
|
||||
for (unsigned i = 0; i < 9; ++i) {
|
||||
mSourceBuffer->Append(mData + i, 1);
|
||||
LexerResult result =
|
||||
mLexer.Lex(mIterator, mCountResumes, DoLexWithUnbufferedTerminate);
|
||||
|
||||
if (i > 2) {
|
||||
EXPECT_TRUE(result.is<TerminalState>());
|
||||
EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>());
|
||||
} else {
|
||||
EXPECT_TRUE(result.is<Yield>());
|
||||
EXPECT_EQ(Yield::NEED_MORE_DATA, result.as<Yield>());
|
||||
}
|
||||
}
|
||||
|
||||
// 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, TerminateAfterYield)
|
||||
{
|
||||
// Test that Terminate works after yielding.
|
||||
for (unsigned i = 0; i < 9; ++i) {
|
||||
mSourceBuffer->Append(mData + i, 1);
|
||||
LexerResult result =
|
||||
mLexer.Lex(mIterator, mCountResumes, DoLexWithTerminateAfterYield);
|
||||
|
||||
if (i > 2) {
|
||||
EXPECT_TRUE(result.is<TerminalState>());
|
||||
EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>());
|
||||
} else if (i == 2) {
|
||||
EXPECT_TRUE(result.is<Yield>());
|
||||
EXPECT_EQ(Yield::OUTPUT_AVAILABLE, result.as<Yield>());
|
||||
} else {
|
||||
EXPECT_TRUE(result.is<Yield>());
|
||||
EXPECT_EQ(Yield::NEED_MORE_DATA, result.as<Yield>());
|
||||
}
|
||||
}
|
||||
|
||||
// We expect 2 resumes because TestState::ONE consumes 3 bytes and then
|
||||
// yields. When the lexer resumes at TestState::TWO, which receives the same 3
|
||||
// bytes, TerminateSuccess() gets called immediately. That's three bytes
|
||||
// total, which are delivered one at a time, requiring 2 resumes.
|
||||
EXPECT_EQ(2u, 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);
|
||||
|
||||
LexerResult result = mLexer.Lex(mIterator, mExpectNoResume, DoLex);
|
||||
|
||||
EXPECT_TRUE(result.is<TerminalState>());
|
||||
EXPECT_EQ(TerminalState::FAILURE, result.as<TerminalState>());
|
||||
}
|
||||
|
||||
TEST_F(ImageStreamingLexer, SourceBufferTruncatedTerminalStateSuccess)
|
||||
{
|
||||
// Test that using a terminal state (in this case TerminalState::SUCCESS) as a
|
||||
// truncated state works.
|
||||
StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3),
|
||||
Transition::TerminateSuccess());
|
||||
|
||||
CheckTruncatedState(lexer, TerminalState::SUCCESS);
|
||||
}
|
||||
|
||||
TEST_F(ImageStreamingLexer, SourceBufferTruncatedTerminalStateFailure)
|
||||
{
|
||||
// Test that using a terminal state (in this case TerminalState::FAILURE) as a
|
||||
// truncated state works.
|
||||
StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3),
|
||||
Transition::TerminateFailure());
|
||||
|
||||
CheckTruncatedState(lexer, TerminalState::FAILURE);
|
||||
}
|
||||
|
||||
TEST_F(ImageStreamingLexer, SourceBufferTruncatedStateReturningSuccess)
|
||||
{
|
||||
// Test that a truncated state that returns TerminalState::SUCCESS works. When
|
||||
// |lexer| discovers that the data is truncated, it invokes the
|
||||
// TRUNCATED_SUCCESS state, which returns TerminalState::SUCCESS.
|
||||
// CheckTruncatedState() verifies that this happens.
|
||||
StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3),
|
||||
Transition::To(TestState::TRUNCATED_SUCCESS, 0));
|
||||
|
||||
CheckTruncatedState(lexer, TerminalState::SUCCESS);
|
||||
}
|
||||
|
||||
TEST_F(ImageStreamingLexer, SourceBufferTruncatedStateReturningFailure)
|
||||
{
|
||||
// Test that a truncated state that returns TerminalState::FAILURE works. When
|
||||
// |lexer| discovers that the data is truncated, it invokes the
|
||||
// TRUNCATED_FAILURE state, which returns TerminalState::FAILURE.
|
||||
// CheckTruncatedState() verifies that this happens.
|
||||
StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3),
|
||||
Transition::To(TestState::TRUNCATED_FAILURE, 0));
|
||||
|
||||
CheckTruncatedState(lexer, TerminalState::FAILURE);
|
||||
}
|
||||
|
||||
TEST_F(ImageStreamingLexer, SourceBufferTruncatedFailingCompleteStatus)
|
||||
{
|
||||
// Test that calling SourceBuffer::Complete() with a failing status results in
|
||||
// an immediate TerminalState::FAILURE result. (Note that |lexer|'s truncated
|
||||
// state is TerminalState::SUCCESS, so if we ignore the failing status, the
|
||||
// test will fail.)
|
||||
StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3),
|
||||
Transition::TerminateSuccess());
|
||||
|
||||
CheckTruncatedState(lexer, TerminalState::FAILURE, NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
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);
|
||||
LexerResult result = mLexer.Lex(mIterator, nullptr, DoLex);
|
||||
|
||||
if (i == 8) {
|
||||
EXPECT_TRUE(result.is<TerminalState>());
|
||||
EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>());
|
||||
} else {
|
||||
EXPECT_TRUE(result.is<Yield>());
|
||||
EXPECT_EQ(Yield::NEED_MORE_DATA, result.as<Yield>());
|
||||
}
|
||||
}
|
||||
|
||||
mSourceBuffer->Complete(NS_OK);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue