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:
parent
3ab2f14d56
commit
28bae02bd3
@ -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:
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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_;
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
|
||||
@ -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
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user