/* * libjingle * Copyright 2004--2011, Google Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. 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. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. */ #include "talk/base/gunit.h" #include "talk/base/stream.h" namespace talk_base { /////////////////////////////////////////////////////////////////////////////// // TestStream /////////////////////////////////////////////////////////////////////////////// class TestStream : public StreamInterface { public: TestStream() : pos_(0) { } virtual StreamState GetState() const { return SS_OPEN; } virtual StreamResult Read(void* buffer, size_t buffer_len, size_t* read, int* error) { unsigned char* uc_buffer = static_cast(buffer); for (size_t i = 0; i < buffer_len; ++i) { uc_buffer[i] = static_cast(pos_++); } if (read) *read = buffer_len; return SR_SUCCESS; } virtual StreamResult Write(const void* data, size_t data_len, size_t* written, int* error) { if (error) *error = -1; return SR_ERROR; } virtual void Close() { } virtual bool SetPosition(size_t position) { pos_ = position; return true; } virtual bool GetPosition(size_t* position) const { if (position) *position = pos_; return true; } virtual bool GetSize(size_t* size) const { return false; } virtual bool GetAvailable(size_t* size) const { return false; } private: size_t pos_; }; bool VerifyTestBuffer(unsigned char* buffer, size_t len, unsigned char value) { bool passed = true; for (size_t i = 0; i < len; ++i) { if (buffer[i] != value++) { passed = false; break; } } // Ensure that we don't pass again without re-writing memset(buffer, 0, len); return passed; } void SeekTest(StreamInterface* stream, const unsigned char value) { size_t bytes; unsigned char buffer[13] = { 0 }; const size_t kBufSize = sizeof(buffer); EXPECT_EQ(stream->Read(buffer, kBufSize, &bytes, NULL), SR_SUCCESS); EXPECT_EQ(bytes, kBufSize); EXPECT_TRUE(VerifyTestBuffer(buffer, kBufSize, value)); EXPECT_TRUE(stream->GetPosition(&bytes)); EXPECT_EQ(13U, bytes); EXPECT_TRUE(stream->SetPosition(7)); EXPECT_EQ(stream->Read(buffer, kBufSize, &bytes, NULL), SR_SUCCESS); EXPECT_EQ(bytes, kBufSize); EXPECT_TRUE(VerifyTestBuffer(buffer, kBufSize, value + 7)); EXPECT_TRUE(stream->GetPosition(&bytes)); EXPECT_EQ(20U, bytes); } TEST(StreamSegment, TranslatesPosition) { TestStream* test = new TestStream; // Verify behavior of original stream SeekTest(test, 0); StreamSegment* segment = new StreamSegment(test); // Verify behavior of adapted stream (all values offset by 20) SeekTest(segment, 20); delete segment; } TEST(StreamSegment, SupportsArtificialTermination) { TestStream* test = new TestStream; size_t bytes; unsigned char buffer[5000] = { 0 }; const size_t kBufSize = sizeof(buffer); { StreamInterface* stream = test; // Read a lot of bytes EXPECT_EQ(stream->Read(buffer, kBufSize, &bytes, NULL), SR_SUCCESS); EXPECT_EQ(bytes, kBufSize); EXPECT_TRUE(VerifyTestBuffer(buffer, kBufSize, 0)); // Test seeking far ahead EXPECT_TRUE(stream->SetPosition(12345)); // Read a bunch more bytes EXPECT_EQ(stream->Read(buffer, kBufSize, &bytes, NULL), SR_SUCCESS); EXPECT_EQ(bytes, kBufSize); EXPECT_TRUE(VerifyTestBuffer(buffer, kBufSize, 12345 % 256)); } // Create a segment of test stream in range [100,600) EXPECT_TRUE(test->SetPosition(100)); StreamSegment* segment = new StreamSegment(test, 500); { StreamInterface* stream = segment; EXPECT_EQ(stream->Read(buffer, kBufSize, &bytes, NULL), SR_SUCCESS); EXPECT_EQ(500U, bytes); EXPECT_TRUE(VerifyTestBuffer(buffer, 500, 100)); EXPECT_EQ(stream->Read(buffer, kBufSize, &bytes, NULL), SR_EOS); // Test seeking past "end" of stream EXPECT_FALSE(stream->SetPosition(12345)); EXPECT_FALSE(stream->SetPosition(501)); // Test seeking to end (edge case) EXPECT_TRUE(stream->SetPosition(500)); EXPECT_EQ(stream->Read(buffer, kBufSize, &bytes, NULL), SR_EOS); // Test seeking to start EXPECT_TRUE(stream->SetPosition(0)); EXPECT_EQ(stream->Read(buffer, kBufSize, &bytes, NULL), SR_SUCCESS); EXPECT_EQ(500U, bytes); EXPECT_TRUE(VerifyTestBuffer(buffer, 500, 100)); EXPECT_EQ(stream->Read(buffer, kBufSize, &bytes, NULL), SR_EOS); } delete segment; } TEST(FifoBufferTest, TestAll) { const size_t kSize = 16; const char in[kSize * 2 + 1] = "0123456789ABCDEFGHIJKLMNOPQRSTUV"; char out[kSize * 2]; void* p; const void* q; size_t bytes; FifoBuffer buf(kSize); StreamInterface* stream = &buf; // Test assumptions about base state EXPECT_EQ(SS_OPEN, stream->GetState()); EXPECT_EQ(SR_BLOCK, stream->Read(out, kSize, &bytes, NULL)); EXPECT_TRUE(NULL != stream->GetReadData(&bytes)); EXPECT_EQ((size_t)0, bytes); stream->ConsumeReadData(0); EXPECT_TRUE(NULL != stream->GetWriteBuffer(&bytes)); EXPECT_EQ(kSize, bytes); stream->ConsumeWriteBuffer(0); // Try a full write EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize, &bytes, NULL)); EXPECT_EQ(kSize, bytes); // Try a write that should block EXPECT_EQ(SR_BLOCK, stream->Write(in, kSize, &bytes, NULL)); // Try a full read EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize, &bytes, NULL)); EXPECT_EQ(kSize, bytes); EXPECT_EQ(0, memcmp(in, out, kSize)); // Try a read that should block EXPECT_EQ(SR_BLOCK, stream->Read(out, kSize, &bytes, NULL)); // Try a too-big write EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize * 2, &bytes, NULL)); EXPECT_EQ(bytes, kSize); // Try a too-big read EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize * 2, &bytes, NULL)); EXPECT_EQ(kSize, bytes); EXPECT_EQ(0, memcmp(in, out, kSize)); // Try some small writes and reads EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize / 2, &bytes, NULL)); EXPECT_EQ(kSize / 2, bytes); EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize / 2, &bytes, NULL)); EXPECT_EQ(kSize / 2, bytes); EXPECT_EQ(0, memcmp(in, out, kSize / 2)); EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize / 2, &bytes, NULL)); EXPECT_EQ(kSize / 2, bytes); EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize / 2, &bytes, NULL)); EXPECT_EQ(kSize / 2, bytes); EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize / 2, &bytes, NULL)); EXPECT_EQ(kSize / 2, bytes); EXPECT_EQ(0, memcmp(in, out, kSize / 2)); EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize / 2, &bytes, NULL)); EXPECT_EQ(kSize / 2, bytes); EXPECT_EQ(0, memcmp(in, out, kSize / 2)); // Try wraparound reads and writes in the following pattern // WWWWWWWWWWWW.... 0123456789AB.... // RRRRRRRRXXXX.... ........89AB.... // WWWW....XXXXWWWW 4567....89AB0123 // XXXX....RRRRXXXX 4567........0123 // XXXXWWWWWWWWXXXX 4567012345670123 // RRRRXXXXXXXXRRRR ....01234567.... // ....RRRRRRRR.... ................ EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize * 3 / 4, &bytes, NULL)); EXPECT_EQ(kSize * 3 / 4, bytes); EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize / 2, &bytes, NULL)); EXPECT_EQ(kSize / 2, bytes); EXPECT_EQ(0, memcmp(in, out, kSize / 2)); EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize / 2, &bytes, NULL)); EXPECT_EQ(kSize / 2, bytes); EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize / 4, &bytes, NULL)); EXPECT_EQ(kSize / 4 , bytes); EXPECT_EQ(0, memcmp(in + kSize / 2, out, kSize / 4)); EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize / 2, &bytes, NULL)); EXPECT_EQ(kSize / 2, bytes); EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize / 2, &bytes, NULL)); EXPECT_EQ(kSize / 2 , bytes); EXPECT_EQ(0, memcmp(in, out, kSize / 2)); EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize / 2, &bytes, NULL)); EXPECT_EQ(kSize / 2 , bytes); EXPECT_EQ(0, memcmp(in, out, kSize / 2)); // Use GetWriteBuffer to reset the read_position for the next tests stream->GetWriteBuffer(&bytes); stream->ConsumeWriteBuffer(0); // Try using GetReadData to do a full read EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize, &bytes, NULL)); q = stream->GetReadData(&bytes); EXPECT_TRUE(NULL != q); EXPECT_EQ(kSize, bytes); EXPECT_EQ(0, memcmp(q, in, kSize)); stream->ConsumeReadData(kSize); EXPECT_EQ(SR_BLOCK, stream->Read(out, kSize, &bytes, NULL)); // Try using GetReadData to do some small reads EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize, &bytes, NULL)); q = stream->GetReadData(&bytes); EXPECT_TRUE(NULL != q); EXPECT_EQ(kSize, bytes); EXPECT_EQ(0, memcmp(q, in, kSize / 2)); stream->ConsumeReadData(kSize / 2); q = stream->GetReadData(&bytes); EXPECT_TRUE(NULL != q); EXPECT_EQ(kSize / 2, bytes); EXPECT_EQ(0, memcmp(q, in + kSize / 2, kSize / 2)); stream->ConsumeReadData(kSize / 2); EXPECT_EQ(SR_BLOCK, stream->Read(out, kSize, &bytes, NULL)); // Try using GetReadData in a wraparound case // WWWWWWWWWWWWWWWW 0123456789ABCDEF // RRRRRRRRRRRRXXXX ............CDEF // WWWWWWWW....XXXX 01234567....CDEF // ............RRRR 01234567........ // RRRRRRRR........ ................ EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize, &bytes, NULL)); EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize * 3 / 4, &bytes, NULL)); EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize / 2, &bytes, NULL)); q = stream->GetReadData(&bytes); EXPECT_TRUE(NULL != q); EXPECT_EQ(kSize / 4, bytes); EXPECT_EQ(0, memcmp(q, in + kSize * 3 / 4, kSize / 4)); stream->ConsumeReadData(kSize / 4); q = stream->GetReadData(&bytes); EXPECT_TRUE(NULL != q); EXPECT_EQ(kSize / 2, bytes); EXPECT_EQ(0, memcmp(q, in, kSize / 2)); stream->ConsumeReadData(kSize / 2); // Use GetWriteBuffer to reset the read_position for the next tests stream->GetWriteBuffer(&bytes); stream->ConsumeWriteBuffer(0); // Try using GetWriteBuffer to do a full write p = stream->GetWriteBuffer(&bytes); EXPECT_TRUE(NULL != p); EXPECT_EQ(kSize, bytes); memcpy(p, in, kSize); stream->ConsumeWriteBuffer(kSize); EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize, &bytes, NULL)); EXPECT_EQ(kSize, bytes); EXPECT_EQ(0, memcmp(in, out, kSize)); // Try using GetWriteBuffer to do some small writes p = stream->GetWriteBuffer(&bytes); EXPECT_TRUE(NULL != p); EXPECT_EQ(kSize, bytes); memcpy(p, in, kSize / 2); stream->ConsumeWriteBuffer(kSize / 2); p = stream->GetWriteBuffer(&bytes); EXPECT_TRUE(NULL != p); EXPECT_EQ(kSize / 2, bytes); memcpy(p, in + kSize / 2, kSize / 2); stream->ConsumeWriteBuffer(kSize / 2); EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize, &bytes, NULL)); EXPECT_EQ(kSize, bytes); EXPECT_EQ(0, memcmp(in, out, kSize)); // Try using GetWriteBuffer in a wraparound case // WWWWWWWWWWWW.... 0123456789AB.... // RRRRRRRRXXXX.... ........89AB.... // ........XXXXWWWW ........89AB0123 // WWWW....XXXXXXXX 4567....89AB0123 // RRRR....RRRRRRRR ................ EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize * 3 / 4, &bytes, NULL)); EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize / 2, &bytes, NULL)); p = stream->GetWriteBuffer(&bytes); EXPECT_TRUE(NULL != p); EXPECT_EQ(kSize / 4, bytes); memcpy(p, in, kSize / 4); stream->ConsumeWriteBuffer(kSize / 4); p = stream->GetWriteBuffer(&bytes); EXPECT_TRUE(NULL != p); EXPECT_EQ(kSize / 2, bytes); memcpy(p, in + kSize / 4, kSize / 4); stream->ConsumeWriteBuffer(kSize / 4); EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize * 3 / 4, &bytes, NULL)); EXPECT_EQ(kSize * 3 / 4, bytes); EXPECT_EQ(0, memcmp(in + kSize / 2, out, kSize / 4)); EXPECT_EQ(0, memcmp(in, out + kSize / 4, kSize / 4)); // Check that the stream is now empty EXPECT_EQ(SR_BLOCK, stream->Read(out, kSize, &bytes, NULL)); // Try growing the buffer EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize, &bytes, NULL)); EXPECT_EQ(kSize, bytes); EXPECT_TRUE(buf.SetCapacity(kSize * 2)); EXPECT_EQ(SR_SUCCESS, stream->Write(in + kSize, kSize, &bytes, NULL)); EXPECT_EQ(kSize, bytes); EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize * 2, &bytes, NULL)); EXPECT_EQ(kSize * 2, bytes); EXPECT_EQ(0, memcmp(in, out, kSize * 2)); // Try shrinking the buffer EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize, &bytes, NULL)); EXPECT_EQ(kSize, bytes); EXPECT_TRUE(buf.SetCapacity(kSize)); EXPECT_EQ(SR_BLOCK, stream->Write(in, kSize, &bytes, NULL)); EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize, &bytes, NULL)); EXPECT_EQ(kSize, bytes); EXPECT_EQ(0, memcmp(in, out, kSize)); // Write to the stream, close it, read the remaining bytes EXPECT_EQ(SR_SUCCESS, stream->Write(in, kSize / 2, &bytes, NULL)); stream->Close(); EXPECT_EQ(SS_CLOSED, stream->GetState()); EXPECT_EQ(SR_EOS, stream->Write(in, kSize / 2, &bytes, NULL)); EXPECT_EQ(SR_SUCCESS, stream->Read(out, kSize / 2, &bytes, NULL)); EXPECT_EQ(0, memcmp(in, out, kSize / 2)); EXPECT_EQ(SR_EOS, stream->Read(out, kSize / 2, &bytes, NULL)); } TEST(FifoBufferTest, FullBufferCheck) { FifoBuffer buff(10); buff.ConsumeWriteBuffer(10); size_t free; EXPECT_TRUE(buff.GetWriteBuffer(&free) != NULL); EXPECT_EQ(0U, free); } TEST(FifoBufferTest, WriteOffsetAndReadOffset) { const size_t kSize = 16; const char in[kSize * 2 + 1] = "0123456789ABCDEFGHIJKLMNOPQRSTUV"; char out[kSize * 2]; FifoBuffer buf(kSize); // Write 14 bytes. EXPECT_EQ(SR_SUCCESS, buf.Write(in, 14, NULL, NULL)); // Make sure data is in |buf|. size_t buffered; EXPECT_TRUE(buf.GetBuffered(&buffered)); EXPECT_EQ(14u, buffered); // Read 10 bytes. buf.ConsumeReadData(10); // There should be now 12 bytes of available space. size_t remaining; EXPECT_TRUE(buf.GetWriteRemaining(&remaining)); EXPECT_EQ(12u, remaining); // Write at offset 12, this should fail. EXPECT_EQ(SR_BLOCK, buf.WriteOffset(in, 10, 12, NULL)); // Write 8 bytes at offset 4, this wraps around the buffer. EXPECT_EQ(SR_SUCCESS, buf.WriteOffset(in, 8, 4, NULL)); // Number of available space remains the same until we call // ConsumeWriteBuffer(). EXPECT_TRUE(buf.GetWriteRemaining(&remaining)); EXPECT_EQ(12u, remaining); buf.ConsumeWriteBuffer(12); // There's 4 bytes bypassed and 4 bytes no read so skip them and verify the // 8 bytes written. size_t read; EXPECT_EQ(SR_SUCCESS, buf.ReadOffset(out, 8, 8, &read)); EXPECT_EQ(8u, read); EXPECT_EQ(0, memcmp(out, in, 8)); // There should still be 16 bytes available for reading. EXPECT_TRUE(buf.GetBuffered(&buffered)); EXPECT_EQ(16u, buffered); // Read at offset 16, this should fail since we don't have that much data. EXPECT_EQ(SR_BLOCK, buf.ReadOffset(out, 10, 16, NULL)); } TEST(AsyncWriteTest, TestWrite) { FifoBuffer* buf = new FifoBuffer(100); AsyncWriteStream stream(buf, Thread::Current()); EXPECT_EQ(SS_OPEN, stream.GetState()); // Write "abc". Will go to the logging thread, which is the current // thread. stream.Write("abc", 3, NULL, NULL); char bytes[100]; size_t count; // Messages on the thread's queue haven't been processed, so "abc" // hasn't been written yet. EXPECT_NE(SR_SUCCESS, buf->ReadOffset(&bytes, 3, 0, &count)); // Now we process the messages on the thread's queue, so "abc" has // been written. EXPECT_TRUE_WAIT(SR_SUCCESS == buf->ReadOffset(&bytes, 3, 0, &count), 10); EXPECT_EQ(3u, count); EXPECT_EQ(0, memcmp(bytes, "abc", 3)); // Write "def". Will go to the logging thread, which is the current // thread. stream.Write("d", 1, &count, NULL); stream.Write("e", 1, &count, NULL); stream.Write("f", 1, &count, NULL); EXPECT_EQ(1u, count); // Messages on the thread's queue haven't been processed, so "def" // hasn't been written yet. EXPECT_NE(SR_SUCCESS, buf->ReadOffset(&bytes, 3, 3, &count)); // Flush() causes the message to be processed, so "def" has now been // written. stream.Flush(); EXPECT_EQ(SR_SUCCESS, buf->ReadOffset(&bytes, 3, 3, &count)); EXPECT_EQ(3u, count); EXPECT_EQ(0, memcmp(bytes, "def", 3)); // Write "xyz". Will go to the logging thread, which is the current // thread. stream.Write("xyz", 3, &count, NULL); EXPECT_EQ(3u, count); // Messages on the thread's queue haven't been processed, so "xyz" // hasn't been written yet. EXPECT_NE(SR_SUCCESS, buf->ReadOffset(&bytes, 3, 6, &count)); // Close() causes the message to be processed, so "xyz" has now been // written. stream.Close(); EXPECT_EQ(SR_SUCCESS, buf->ReadOffset(&bytes, 3, 6, &count)); EXPECT_EQ(3u, count); EXPECT_EQ(0, memcmp(bytes, "xyz", 3)); EXPECT_EQ(SS_CLOSED, stream.GetState()); // Is't closed, so the writes should fail. EXPECT_EQ(SR_ERROR, stream.Write("000", 3, NULL, NULL)); } } // namespace talk_base