Remove CircularFileStream / replace it with CallSessionFileRotatingStream.

BUG=4838, 4839

Review URL: https://codereview.webrtc.org/1245143005

Cr-Commit-Position: refs/heads/master@{#9628}
This commit is contained in:
tkchin 2015-07-23 12:27:02 -07:00 committed by Commit bot
parent 3ab2f14d56
commit 28bae02bd3
10 changed files with 105 additions and 439 deletions

View File

@ -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<CircularFileStream> _stream;
};
} // namespace rtc
@implementation RTCFileLogger {
BOOL _hasStarted;
NSString *_filePath;
NSString *_dirPath;
NSUInteger _maxFileSize;
rtc::scoped_ptr<rtc::CircularFileStreamLogSink> _logSink;
rtc::scoped_ptr<rtc::CallSessionFileRotatingLogSink> _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<rtc::CircularFileStream> 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<rtc::CallSessionFileRotatingStream> 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<uint8_t[]> buffer(static_cast<uint8_t*>(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<uint8_t[]> buffer(static_cast<uint8_t*>(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:

View File

@ -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

View File

@ -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();
}

View File

@ -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.

View File

@ -60,11 +60,15 @@ class FileRotatingStreamTest : public ::testing::Test {
scoped_ptr<FileRotatingStream> 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<uint8_t[]> 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<CallSessionFileRotatingStream> 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<uint8_t[]> 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<CallSessionFileRotatingStream> stream_;

View File

@ -10,8 +10,11 @@
#include "webrtc/base/logsinks.h"
#include <iostream>
#include <string>
#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)

View File

@ -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<CallSessionFileRotatingStream> stream_;
DISALLOW_COPY_AND_ASSIGN(CallSessionFileRotatingLogSink);
};

View File

@ -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
///////////////////////////////////////////////////////////////////////////////

View File

@ -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

View File

@ -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<FileStream> 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<uint8_t[]> 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<FileStream> 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<uint8_t[]> 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<FileStream> 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<uint8_t[]> 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