diff --git a/talk/app/webrtc/objc/RTCFileLogger.mm b/talk/app/webrtc/objc/RTCFileLogger.mm index b474d7a5c6..3080ebc080 100644 --- a/talk/app/webrtc/objc/RTCFileLogger.mm +++ b/talk/app/webrtc/objc/RTCFileLogger.mm @@ -28,45 +28,19 @@ #import "RTCFileLogger.h" #include "webrtc/base/checks.h" +#include "webrtc/base/filerotatingstream.h" #include "webrtc/base/logging.h" +#include "webrtc/base/logsinks.h" #include "webrtc/base/scoped_ptr.h" -#include "webrtc/base/stream.h" -NSString *const kDefaultLogFileName = @"webrtc.log"; +NSString *const kDefaultLogDirName = @"webrtc_logs"; NSUInteger const kDefaultMaxFileSize = 10 * 1024 * 1024; // 10MB. -namespace rtc { - -class CircularFileStreamLogSink : public LogSink { - public: - // Creates a log sink that writes to the given stream. This log sink takes - // ownership of |stream|. - CircularFileStreamLogSink(CircularFileStream *stream) { - DCHECK(stream); - _stream.reset(stream); - } - - ~CircularFileStreamLogSink() override {} - - void OnLogMessage(const std::string &message) override { - if (_stream) { - _stream->WriteAll(message.data(), message.size(), nullptr, nullptr); - } - } - - CircularFileStream *GetStream() { return _stream.get(); } - - private: - scoped_ptr _stream; -}; - -} // namespace rtc - @implementation RTCFileLogger { BOOL _hasStarted; - NSString *_filePath; + NSString *_dirPath; NSUInteger _maxFileSize; - rtc::scoped_ptr _logSink; + rtc::scoped_ptr _logSink; } @synthesize severity = _severity; @@ -75,18 +49,34 @@ class CircularFileStreamLogSink : public LogSink { NSArray *paths = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirPath = [paths firstObject]; - NSString *defaultFilePath = - [documentsDirPath stringByAppendingPathComponent:kDefaultLogFileName]; - return [self initWithFilePath:defaultFilePath - maxFileSize:kDefaultMaxFileSize]; + NSString *defaultDirPath = + [documentsDirPath stringByAppendingPathComponent:kDefaultLogDirName]; + return [self initWithDirPath:defaultDirPath + maxFileSize:kDefaultMaxFileSize]; } -- (instancetype)initWithFilePath:(NSString *)filePath - maxFileSize:(NSUInteger)maxFileSize { - NSParameterAssert(filePath.length); +- (instancetype)initWithDirPath:(NSString *)dirPath + maxFileSize:(NSUInteger)maxFileSize { + NSParameterAssert(dirPath.length); NSParameterAssert(maxFileSize); if (self = [super init]) { - _filePath = filePath; + BOOL isDir = NO; + NSFileManager *fileManager = [NSFileManager defaultManager]; + if ([fileManager fileExistsAtPath:dirPath isDirectory:&isDir]) { + if (!isDir) { + // Bail if something already exists there. + return nil; + } + } else { + if (![fileManager createDirectoryAtPath:dirPath + withIntermediateDirectories:NO + attributes:nil + error:nil]) { + // Bail if we failed to create a directory. + return nil; + } + } + _dirPath = dirPath; _maxFileSize = maxFileSize; _severity = kRTCFileLoggerSeverityInfo; } @@ -101,19 +91,14 @@ class CircularFileStreamLogSink : public LogSink { if (_hasStarted) { return; } - rtc::scoped_ptr stream; - stream.reset(new rtc::CircularFileStream(_maxFileSize)); - _logSink.reset(new rtc::CircularFileStreamLogSink(stream.release())); - int error = 0; - if (!_logSink->GetStream()->Open(_filePath.UTF8String, "wb", &error)) { - LOG(LS_ERROR) << "Failed to open log file at path: " - << _filePath.UTF8String - << " Error: " - << error; + _logSink.reset(new rtc::CallSessionFileRotatingLogSink(_dirPath.UTF8String, + _maxFileSize)); + if (!_logSink->Init()) { + LOG(LS_ERROR) << "Failed to open log files at path: " + << _dirPath.UTF8String; _logSink.reset(); return; } - // TODO(tkchin): Log thead info on iOS, currently this doesn't do anything. rtc::LogMessage::LogThreads(true); rtc::LogMessage::LogTimestamps(true); rtc::LogMessage::AddLogToStream(_logSink.get(), [self rtcSeverity]); @@ -127,93 +112,35 @@ class CircularFileStreamLogSink : public LogSink { DCHECK(_logSink); rtc::LogMessage::RemoveLogToStream(_logSink.get()); _hasStarted = NO; - - // Read the ordered version of the log. - NSData *logData = [self reorderedLogData]; - NSError *error = nil; - // Write the ordered version back to disk. - if (![logData writeToFile:_filePath - options:NSDataWritingAtomic - error:&error]) { - LOG(LS_ERROR) << "Failed to rewrite log to disk at path: " - << _filePath.UTF8String; - if (error) { - LOG(LS_ERROR) << "Error: " << error.localizedDescription.UTF8String; - } - } else { - // If we succeeded in writing to disk we don't need to hold on to the - // stream anymore. - _logSink.reset(); - } + _logSink.reset(); } - (NSData *)logData { if (_hasStarted) { return nil; } - if (!_logSink.get()) { - // If there isn't a previously used stream just return contents of file. - return [[self class] contentsOfFileAtPath:_filePath]; + NSMutableData* logData = [NSMutableData data]; + rtc::scoped_ptr stream( + new rtc::CallSessionFileRotatingStream(_dirPath.UTF8String)); + if (!stream->Open()) { + return logData; } - return [self reorderedLogData]; + size_t bufferSize = 0; + if (!stream->GetSize(&bufferSize) || bufferSize == 0) { + return logData; + } + size_t read = 0; + // Allocate memory using malloc so we can pass it direcly to NSData without + // copying. + rtc::scoped_ptr buffer(static_cast(malloc(bufferSize))); + stream->ReadAll(buffer.get(), bufferSize, &read, nullptr); + logData = [[NSMutableData alloc] initWithBytesNoCopy:buffer.release() + length:read]; + return logData; } #pragma mark - Private -+ (NSData *)contentsOfFileAtPath:(NSString *)path { - NSError *error = nil; - NSData *contents = [NSData dataWithContentsOfFile:path - options:0 - error:&error]; - if (error) { - LOG(LS_ERROR) << "Failed to read contents of file at path: " - << path.UTF8String - << " Error: " - << error.localizedDescription.UTF8String; - return nil; - } - return contents; -} - -- (NSData *)reorderedLogData { - if (_hasStarted || !_logSink.get()) { - return nil; - } - // We have a stream we used for writing in memory and we're not writing. The - // stream has a pointer to where the log boundary is so it can reorder the - // log correctly. We just need to reopen the file in read mode. - int error = 0; - rtc::CircularFileStream *stream = _logSink->GetStream(); - if (!stream->Open(_filePath.UTF8String, "r", &error)) { - LOG(LS_ERROR) << "Failed to open log file at path: " - << _filePath.UTF8String - << " Error: " - << error; - return nil; - } - size_t logSize = 0; - size_t bytesRead = 0; - error = 0; - if (!stream->GetSize(&logSize)) { - LOG(LS_ERROR) << "Failed to get log file size."; - return nil; - } - // Allocate memory using malloc so we can pass it direcly to NSData without - // copying. - rtc::scoped_ptr buffer(static_cast(malloc(logSize))); - if (stream->ReadAll(buffer.get(), logSize, &bytesRead, &error) - != rtc::SR_SUCCESS) { - LOG(LS_ERROR) << "Failed to read log file at path: " - << _filePath.UTF8String - << " Error: " - << error; - } - DCHECK_LE(bytesRead, logSize); - // NSData takes ownership of the bytes and frees it on dealloc. - return [NSData dataWithBytesNoCopy:buffer.release() - length:bytesRead]; -} - - (rtc::LoggingSeverity)rtcSeverity { switch (_severity) { case kRTCFileLoggerSeverityVerbose: diff --git a/talk/app/webrtc/objc/public/RTCFileLogger.h b/talk/app/webrtc/objc/public/RTCFileLogger.h index 5c311b711a..3900cb6fbe 100644 --- a/talk/app/webrtc/objc/public/RTCFileLogger.h +++ b/talk/app/webrtc/objc/public/RTCFileLogger.h @@ -49,24 +49,22 @@ typedef NS_ENUM(NSUInteger, RTCFileLoggerSeverity) { // The severity level to capture. The default is kRTCFileLoggerSeverityInfo. @property(nonatomic, assign) RTCFileLoggerSeverity severity; -// Default constructor provides default settings for file path and file size. +// Default constructor provides default settings for dir path and file size. - (instancetype)init; -- (instancetype)initWithFilePath:(NSString *)filePath - maxFileSize:(NSUInteger)maxFileSize +- (instancetype)initWithDirPath:(NSString *)dirPath + maxFileSize:(NSUInteger)maxFileSize NS_DESIGNATED_INITIALIZER; -// Starts writing WebRTC logs to file if not already started. Overwrites any -// existing file. +// Starts writing WebRTC logs to disk if not already started. Overwrites any +// existing file(s). - (void)start; -// Stops writing WebRTC logs to file. Rewrites the log file as required to -// reorder logs because logs may be disordered due to use of -// rtc::CircularFileStream. This method is also called on dealloc. +// Stops writing WebRTC logs to disk. This method is also called on dealloc. - (void)stop; -// Returns the current contents of the log file. Returns nil if start has been -// called without a stop, or if there is no data. +// Returns the current contents of the logs, or nil if start has been called +// without a stop. - (NSData *)logData; @end diff --git a/webrtc/base/filerotatingstream.cc b/webrtc/base/filerotatingstream.cc index 3fd60ac823..f2a6def013 100644 --- a/webrtc/base/filerotatingstream.cc +++ b/webrtc/base/filerotatingstream.cc @@ -177,6 +177,26 @@ bool FileRotatingStream::Flush() { return file_stream_->Flush(); } +bool FileRotatingStream::GetSize(size_t* size) const { + if (mode_ != kRead) { + // Not possible to get accurate size on disk when writing because of + // potential buffering. + return false; + } + DCHECK(size); + *size = 0; + size_t total_size = 0; + for (auto file_name : file_names_) { + Pathname pathname(file_name); + size_t file_size = 0; + if (Filesystem::GetFileSize(file_name, &file_size)) { + total_size += file_size; + } + } + *size = total_size; + return true; +} + void FileRotatingStream::Close() { CloseCurrentFile(); } diff --git a/webrtc/base/filerotatingstream.h b/webrtc/base/filerotatingstream.h index 7f6bc50fa2..f3de14ede4 100644 --- a/webrtc/base/filerotatingstream.h +++ b/webrtc/base/filerotatingstream.h @@ -50,6 +50,8 @@ class FileRotatingStream : public StreamInterface { size_t* written, int* error) override; bool Flush() override; + // Returns the total file size currently used on disk. + bool GetSize(size_t* size) const override; void Close() override; // Opens the appropriate file(s). Call this before using the stream. diff --git a/webrtc/base/filerotatingstream_unittest.cc b/webrtc/base/filerotatingstream_unittest.cc index 084c61244c..09438f870e 100644 --- a/webrtc/base/filerotatingstream_unittest.cc +++ b/webrtc/base/filerotatingstream_unittest.cc @@ -60,11 +60,15 @@ class FileRotatingStreamTest : public ::testing::Test { scoped_ptr stream; stream.reset(new FileRotatingStream(dir_path, file_prefix)); ASSERT_TRUE(stream->Open()); + size_t read = 0; + size_t stream_size = 0; + EXPECT_TRUE(stream->GetSize(&stream_size)); scoped_ptr buffer(new uint8_t[expected_length]); EXPECT_EQ(SR_SUCCESS, - stream->ReadAll(buffer.get(), expected_length, nullptr, nullptr)); + stream->ReadAll(buffer.get(), expected_length, &read, nullptr)); EXPECT_EQ(0, memcmp(expected_contents, buffer.get(), expected_length)); EXPECT_EQ(SR_EOS, stream->ReadAll(buffer.get(), 1, nullptr, nullptr)); + EXPECT_EQ(stream_size, read); } void VerifyFileContents(const char* expected_contents, @@ -214,11 +218,15 @@ class CallSessionFileRotatingStreamTest : public ::testing::Test { scoped_ptr stream( new CallSessionFileRotatingStream(dir_path)); ASSERT_TRUE(stream->Open()); + size_t read = 0; + size_t stream_size = 0; + EXPECT_TRUE(stream->GetSize(&stream_size)); scoped_ptr buffer(new uint8_t[expected_length]); EXPECT_EQ(SR_SUCCESS, - stream->ReadAll(buffer.get(), expected_length, nullptr, nullptr)); + stream->ReadAll(buffer.get(), expected_length, &read, nullptr)); EXPECT_EQ(0, memcmp(expected_contents, buffer.get(), expected_length)); EXPECT_EQ(SR_EOS, stream->ReadAll(buffer.get(), 1, nullptr, nullptr)); + EXPECT_EQ(stream_size, read); } scoped_ptr stream_; diff --git a/webrtc/base/logsinks.cc b/webrtc/base/logsinks.cc index e202a81f1e..4968339156 100644 --- a/webrtc/base/logsinks.cc +++ b/webrtc/base/logsinks.cc @@ -10,8 +10,11 @@ #include "webrtc/base/logsinks.h" +#include #include +#include "webrtc/base/checks.h" + namespace rtc { FileRotatingLogSink::FileRotatingLogSink(const std::string& log_dir_path, @@ -26,14 +29,15 @@ FileRotatingLogSink::FileRotatingLogSink(const std::string& log_dir_path, FileRotatingLogSink::FileRotatingLogSink(FileRotatingStream* stream) : stream_(stream) { + DCHECK(stream); } FileRotatingLogSink::~FileRotatingLogSink() { } void FileRotatingLogSink::OnLogMessage(const std::string& message) { - if (!stream_ || stream_->GetState() != SS_OPEN) { - LOG(LS_WARNING) << "Init() must be called before adding this sink."; + if (stream_->GetState() != SS_OPEN) { + std::cerr << "Init() must be called before adding this sink." << std::endl; return; } stream_->WriteAll(message.c_str(), message.size(), nullptr, nullptr); @@ -43,6 +47,10 @@ bool FileRotatingLogSink::Init() { return stream_->Open(); } +bool FileRotatingLogSink::DisableBuffering() { + return stream_->DisableBuffering(); +} + CallSessionFileRotatingLogSink::CallSessionFileRotatingLogSink( const std::string& log_dir_path, size_t max_total_log_size) diff --git a/webrtc/base/logsinks.h b/webrtc/base/logsinks.h index f86bdd0a2d..849e1dcf16 100644 --- a/webrtc/base/logsinks.h +++ b/webrtc/base/logsinks.h @@ -39,6 +39,9 @@ class FileRotatingLogSink : public LogSink { // Deletes any existing files in the directory and creates a new log file. virtual bool Init(); + // Disables buffering on the underlying stream. + bool DisableBuffering(); + protected: explicit FileRotatingLogSink(FileRotatingStream* stream); @@ -57,8 +60,6 @@ class CallSessionFileRotatingLogSink : public FileRotatingLogSink { ~CallSessionFileRotatingLogSink() override; private: - scoped_ptr stream_; - DISALLOW_COPY_AND_ASSIGN(CallSessionFileRotatingLogSink); }; diff --git a/webrtc/base/stream.cc b/webrtc/base/stream.cc index 3e3afa040d..e22c3d8aa4 100644 --- a/webrtc/base/stream.cc +++ b/webrtc/base/stream.cc @@ -517,113 +517,6 @@ void FileStream::DoClose() { fclose(file_); } -CircularFileStream::CircularFileStream(size_t max_size) - : max_write_size_(max_size), - position_(0), - marked_position_(max_size / 2), - last_write_position_(0), - read_segment_(READ_LATEST), - read_segment_available_(0) { -} - -bool CircularFileStream::Open(const std::string& filename, - const char* mode, - int* error) { - if (!FileStream::Open(filename.c_str(), mode, error)) - return false; - - if (strchr(mode, "r") != NULL) { // Opened in read mode. - // Check if the buffer has been overwritten and determine how to read the - // log in time sequence. - size_t file_size; - GetSize(&file_size); - if (file_size == position_) { - // The buffer has not been overwritten yet. Read 0 .. file_size - read_segment_ = READ_LATEST; - read_segment_available_ = file_size; - } else { - // The buffer has been over written. There are three segments: The first - // one is 0 .. marked_position_, which is the marked earliest log. The - // second one is position_ .. file_size, which is the middle log. The - // last one is marked_position_ .. position_, which is the latest log. - read_segment_ = READ_MARKED; - read_segment_available_ = marked_position_; - last_write_position_ = position_; - } - - // Read from the beginning. - position_ = 0; - SetPosition(position_); - } - - return true; -} - -StreamResult CircularFileStream::Read(void* buffer, - size_t buffer_len, - size_t* read, - int* error) { - if (read_segment_available_ == 0) { - size_t file_size; - switch (read_segment_) { - case READ_MARKED: // Finished READ_MARKED and start READ_MIDDLE. - read_segment_ = READ_MIDDLE; - position_ = last_write_position_; - SetPosition(position_); - GetSize(&file_size); - read_segment_available_ = file_size - position_; - break; - - case READ_MIDDLE: // Finished READ_MIDDLE and start READ_LATEST. - read_segment_ = READ_LATEST; - position_ = marked_position_; - SetPosition(position_); - read_segment_available_ = last_write_position_ - position_; - break; - - default: // Finished READ_LATEST and return EOS. - return rtc::SR_EOS; - } - } - - size_t local_read; - if (!read) - read = &local_read; - - size_t to_read = std::min(buffer_len, read_segment_available_); - rtc::StreamResult result = - rtc::FileStream::Read(buffer, to_read, read, error); - if (result == rtc::SR_SUCCESS) { - read_segment_available_ -= *read; - position_ += *read; - } - return result; -} - -StreamResult CircularFileStream::Write(const void* data, - size_t data_len, - size_t* written, - int* error) { - if (position_ >= max_write_size_) { - ASSERT(position_ == max_write_size_); - position_ = marked_position_; - SetPosition(position_); - } - - size_t local_written; - if (!written) - written = &local_written; - - size_t to_eof = max_write_size_ - position_; - size_t to_write = std::min(data_len, to_eof); - rtc::StreamResult result = - rtc::FileStream::Write(data, to_write, written, error); - if (result == rtc::SR_SUCCESS) { - position_ += *written; - } - return result; -} - /////////////////////////////////////////////////////////////////////////////// // MemoryStream /////////////////////////////////////////////////////////////////////////////// diff --git a/webrtc/base/stream.h b/webrtc/base/stream.h index f67c704aa5..b3317663b5 100644 --- a/webrtc/base/stream.h +++ b/webrtc/base/stream.h @@ -418,39 +418,6 @@ class FileStream : public StreamInterface { DISALLOW_COPY_AND_ASSIGN(FileStream); }; -// A stream that caps the output at a certain size, dropping content from the -// middle of the logical stream and maintaining equal parts of the start/end of -// the logical stream. -class CircularFileStream : public FileStream { - public: - explicit CircularFileStream(size_t max_size); - - bool Open(const std::string& filename, const char* mode, int* error) override; - StreamResult Read(void* buffer, - size_t buffer_len, - size_t* read, - int* error) override; - StreamResult Write(const void* data, - size_t data_len, - size_t* written, - int* error) override; - - private: - enum ReadSegment { - READ_MARKED, // Read 0 .. marked_position_ - READ_MIDDLE, // Read position_ .. file_size - READ_LATEST, // Read marked_position_ .. position_ if the buffer was - // overwritten or 0 .. position_ otherwise. - }; - - size_t max_write_size_; - size_t position_; - size_t marked_position_; - size_t last_write_position_; - ReadSegment read_segment_; - size_t read_segment_available_; -}; - /////////////////////////////////////////////////////////////////////////////// // MemoryStream is a simple implementation of a StreamInterface over in-memory // data. Data is read and written at the current seek position. Reads return diff --git a/webrtc/base/stream_unittest.cc b/webrtc/base/stream_unittest.cc index 4d5066ab55..4172a9726c 100644 --- a/webrtc/base/stream_unittest.cc +++ b/webrtc/base/stream_unittest.cc @@ -372,162 +372,4 @@ TEST(FifoBufferTest, WriteOffsetAndReadOffset) { EXPECT_EQ(SR_BLOCK, buf.ReadOffset(out, 10, 16, NULL)); } -class CircularFileStreamTest : public ::testing::Test { - protected: - static size_t const kMaxSize = 12; - - CircularFileStreamTest() : is_open_(false), stream_(kMaxSize) { - Pathname temp_dir; - if (Filesystem::GetAppTempFolder(&temp_dir)) { - logfile_name_ = - Filesystem::TempFilename(temp_dir, "CircularFileStreamTest"); - } - } - - virtual void SetUp() { - int error = -1; - is_open_ = stream_.Open(logfile_name_, "wb", &error); - } - - virtual void TearDown() { - if (!Filesystem::IsAbsent(logfile_name_)) { - Filesystem::DeleteFile(logfile_name_); - } - } - - bool is_open_; - CircularFileStream stream_; - std::string logfile_name_; -}; - -TEST_F(CircularFileStreamTest, ReadWriteWithinCapacity) { - EXPECT_TRUE(is_open_); - // Write contents. - const uint8_t bytes[] = {1, 2, 3, 4, 5, 6}; - size_t written = 0; - int error = 0; - EXPECT_EQ(SR_SUCCESS, stream_.Write(bytes, sizeof(bytes), &written, &error)); - EXPECT_EQ(0, error); - EXPECT_EQ(written, sizeof(bytes)); - stream_.Close(); - - // Check file contents. - uint8_t content_bytes[sizeof(bytes)] = {}; - scoped_ptr content_stream( - Filesystem::OpenFile(logfile_name_, "r")); - size_t num_content_bytes_read = 0; - EXPECT_TRUE(content_stream); - error = 0; - EXPECT_EQ(SR_SUCCESS, - content_stream->Read(content_bytes, sizeof(content_bytes), - &num_content_bytes_read, &error)); - EXPECT_EQ(sizeof(content_bytes), num_content_bytes_read); - ASSERT_EQ(sizeof(content_bytes), sizeof(bytes)); - EXPECT_EQ(0, memcmp(content_bytes, bytes, sizeof(content_bytes))); - - // Check read result. - error = 0; - size_t file_size = 0; - EXPECT_TRUE(stream_.Open(logfile_name_, "r", &error)); - EXPECT_TRUE(stream_.GetSize(&file_size)); - EXPECT_EQ(0, error); - EXPECT_EQ(sizeof(bytes), file_size); - scoped_ptr read_bytes(new uint8_t[file_size]); - size_t num_read_bytes = 0; - error = 0; - EXPECT_EQ(SR_SUCCESS, stream_.ReadAll(read_bytes.get(), file_size, - &num_read_bytes, &error)); - EXPECT_EQ(sizeof(bytes), num_read_bytes); - EXPECT_EQ(0, memcmp(bytes, read_bytes.get(), file_size)); -} - -TEST_F(CircularFileStreamTest, ReadWriteAtCapacity) { - EXPECT_TRUE(is_open_); - // Write contents. - const uint8_t bytes[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; - size_t written = 0; - int error = 0; - EXPECT_EQ(SR_SUCCESS, stream_.Write(bytes, sizeof(bytes), &written, &error)); - EXPECT_EQ(0, error); - EXPECT_EQ(written, sizeof(bytes)); - stream_.Close(); - - // Check file contents. - uint8_t content_bytes[sizeof(bytes)] = {}; - scoped_ptr content_stream( - Filesystem::OpenFile(logfile_name_, "r")); - size_t num_content_bytes_read = 0; - EXPECT_TRUE(content_stream); - error = 0; - EXPECT_EQ(SR_SUCCESS, - content_stream->Read(content_bytes, sizeof(content_bytes), - &num_content_bytes_read, &error)); - EXPECT_EQ(sizeof(content_bytes), num_content_bytes_read); - ASSERT_EQ(sizeof(content_bytes), sizeof(bytes)); - EXPECT_EQ(0, memcmp(content_bytes, bytes, sizeof(content_bytes))); - - // Check read result. - error = 0; - size_t file_size = 0; - EXPECT_TRUE(stream_.Open(logfile_name_, "r", &error)); - EXPECT_TRUE(stream_.GetSize(&file_size)); - EXPECT_EQ(0, error); - EXPECT_EQ(sizeof(bytes), file_size); - scoped_ptr read_bytes(new uint8_t[file_size]); - size_t num_read_bytes = 0; - error = 0; - EXPECT_EQ(SR_SUCCESS, stream_.ReadAll(read_bytes.get(), file_size, - &num_read_bytes, &error)); - EXPECT_EQ(sizeof(bytes), num_read_bytes); - EXPECT_EQ(0, memcmp(bytes, read_bytes.get(), file_size)); -} - -TEST_F(CircularFileStreamTest, ReadWriteOverCapacity) { - EXPECT_TRUE(is_open_); - // Write contents. - const uint8_t bytes[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; - size_t written = 0; - int error = 0; - EXPECT_EQ(SR_SUCCESS, - stream_.WriteAll(bytes, sizeof(bytes), &written, &error)); - EXPECT_EQ(0, error); - EXPECT_EQ(written, sizeof(bytes)); - stream_.Close(); - - // Check file contents. - uint8_t content_bytes[kMaxSize] = {}; - scoped_ptr content_stream( - Filesystem::OpenFile(logfile_name_, "r")); - size_t num_content_bytes_read = 0; - EXPECT_TRUE(content_stream); - error = 0; - EXPECT_EQ(SR_SUCCESS, - content_stream->Read(content_bytes, sizeof(content_bytes), - &num_content_bytes_read, &error)); - EXPECT_EQ(sizeof(content_bytes), num_content_bytes_read); - const uint8_t expected_content_bytes[] = { - 1, 2, 3, 4, 5, 6, 13, 14, 15, 10, 11, 12}; - ASSERT_EQ(sizeof(content_bytes), sizeof(expected_content_bytes)); - EXPECT_EQ( - 0, memcmp(expected_content_bytes, content_bytes, sizeof(content_bytes))); - - // Check read result. - error = 0; - size_t file_size = 0; - EXPECT_TRUE(stream_.Open(logfile_name_, "r", &error)); - EXPECT_TRUE(stream_.GetSize(&file_size)); - EXPECT_EQ(0, error); - EXPECT_EQ(sizeof(content_bytes), file_size); - scoped_ptr read_bytes(new uint8_t[file_size]); - size_t num_read_bytes = 0; - error = 0; - EXPECT_EQ(SR_SUCCESS, stream_.ReadAll(read_bytes.get(), file_size, - &num_read_bytes, &error)); - - const uint8_t expected_read_bytes[] = { - 1, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14, 15}; - EXPECT_EQ(sizeof(expected_read_bytes), num_read_bytes); - EXPECT_EQ(0, memcmp(expected_read_bytes, read_bytes.get(), file_size)); -} - } // namespace rtc