blob: c32f9858029b7d69537856599db39587fd8c7b16 [file] [log] [blame]
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define C_TESTLUCY_TESTINSTREAM
#define C_LUCY_INSTREAM
#define C_LUCY_FILEWINDOW
#define TESTLUCY_USE_SHORT_NAMES
#include "Lucy/Util/ToolSet.h"
#include "Clownfish/TestHarness/TestBatchRunner.h"
#include "Lucy/Test.h"
#include "Lucy/Test/TestUtils.h"
#include "Lucy/Test/Store/TestInStream.h"
#include "Lucy/Test/Store/MockFileHandle.h"
#include "Lucy/Store/FileWindow.h"
#include "Lucy/Store/InStream.h"
#include "Lucy/Store/OutStream.h"
#include "Lucy/Store/RAMFile.h"
#include "Lucy/Store/RAMFileHandle.h"
TestInStream*
TestInStream_new() {
return (TestInStream*)Class_Make_Obj(TESTINSTREAM);
}
static void
test_refill(TestBatchRunner *runner) {
RAMFile *file = RAMFile_new(NULL, false);
OutStream *outstream = OutStream_open((Obj*)file);
InStream *instream;
char scratch[5];
InStreamIVARS *ivars;
for (int32_t i = 0; i < 1023; i++) {
OutStream_Write_U8(outstream, 'x');
}
OutStream_Write_U8(outstream, 'y');
OutStream_Write_U8(outstream, 'z');
OutStream_Close(outstream);
instream = InStream_open((Obj*)file);
ivars = InStream_IVARS(instream);
InStream_Refill(instream);
TEST_INT_EQ(runner, ivars->limit - ivars->buf, IO_STREAM_BUF_SIZE,
"Refill");
TEST_INT_EQ(runner, (long)InStream_Tell(instream), 0,
"Correct file pos after standing-start Refill()");
DECREF(instream);
instream = InStream_open((Obj*)file);
ivars = InStream_IVARS(instream);
InStream_Fill(instream, 30);
TEST_INT_EQ(runner, ivars->limit - ivars->buf, 30, "Fill()");
TEST_INT_EQ(runner, (long)InStream_Tell(instream), 0,
"Correct file pos after standing-start Fill()");
DECREF(instream);
instream = InStream_open((Obj*)file);
ivars = InStream_IVARS(instream);
InStream_Read_Bytes(instream, scratch, 5);
TEST_INT_EQ(runner, ivars->limit - ivars->buf,
IO_STREAM_BUF_SIZE - 5, "small read triggers refill");
DECREF(instream);
instream = InStream_open((Obj*)file);
ivars = InStream_IVARS(instream);
TEST_INT_EQ(runner, InStream_Read_U8(instream), 'x', "Read_U8");
InStream_Seek(instream, 1023);
TEST_INT_EQ(runner, (long)FileWindow_IVARS(ivars->window)->offset, 0,
"no unnecessary refill on Seek");
TEST_INT_EQ(runner, (long)InStream_Tell(instream), 1023, "Seek/Tell");
TEST_INT_EQ(runner, InStream_Read_U8(instream), 'y',
"correct data after in-buffer Seek()");
TEST_INT_EQ(runner, InStream_Read_U8(instream), 'z', "automatic Refill");
TEST_TRUE(runner, (FileWindow_IVARS(ivars->window)->offset != 0),
"refilled");
DECREF(instream);
DECREF(outstream);
DECREF(file);
}
static void
test_Clone_and_Reopen(TestBatchRunner *runner) {
String *foo = SSTR_WRAP_C("foo");
String *bar = SSTR_WRAP_C("bar");
RAMFile *file = RAMFile_new(NULL, false);
OutStream *outstream = OutStream_open((Obj*)file);
RAMFileHandle *fh;
InStream *instream;
InStream *clone;
InStream *reopened;
for (uint8_t i = 0; i < 26; i++) {
OutStream_Write_U8(outstream, 'a' + i);
}
OutStream_Close(outstream);
fh = RAMFH_open(foo, FH_READ_ONLY, file);
instream = InStream_open((Obj*)fh);
InStream_Seek(instream, 1);
TEST_TRUE(runner, Str_Equals(InStream_Get_Filename(instream), (Obj*)foo),
"Get_Filename");
clone = InStream_Clone(instream);
TEST_TRUE(runner, Str_Equals(InStream_Get_Filename(clone), (Obj*)foo),
"Clones have same filename");
TEST_TRUE(runner, InStream_Length(instream) == InStream_Length(clone),
"Clones have same length");
TEST_TRUE(runner, InStream_Read_U8(instream) == InStream_Read_U8(clone),
"Clones start at same file position");
reopened = InStream_Reopen(instream, bar, 25, 1);
TEST_TRUE(runner, Str_Equals(InStream_Get_Filename(reopened), (Obj*)bar),
"Reopened InStreams take new filename");
TEST_TRUE(runner, InStream_Read_U8(reopened) == 'z',
"Reopened stream starts at supplied offset");
TEST_TRUE(runner, InStream_Length(reopened) == 1,
"Reopened stream uses supplied length");
TEST_TRUE(runner, InStream_Tell(reopened) == 1,
"Tell() uses supplied offset for reopened stream");
InStream_Seek(reopened, 0);
TEST_TRUE(runner, InStream_Read_U8(reopened) == 'z',
"Seek() uses supplied offset for reopened stream");
DECREF(reopened);
DECREF(clone);
DECREF(instream);
DECREF(outstream);
DECREF(fh);
DECREF(file);
}
static void
test_Close(TestBatchRunner *runner) {
RAMFile *file = RAMFile_new(NULL, false);
InStream *instream = InStream_open((Obj*)file);
InStream_Close(instream);
TEST_TRUE(runner, InStream_IVARS(instream)->file_handle == NULL,
"Close decrements FileHandle's refcount");
DECREF(instream);
DECREF(file);
}
static void
test_Seek_and_Tell(TestBatchRunner *runner) {
int64_t gb1 = INT64_C(0x40000000);
int64_t gb3 = gb1 * 3;
int64_t gb6 = gb1 * 6;
int64_t gb12 = gb1 * 12;
FileHandle *fh = (FileHandle*)MockFileHandle_new(NULL, gb12);
InStream *instream = InStream_open((Obj*)fh);
InStreamIVARS *const ivars = InStream_IVARS(instream);
InStream_Buf(instream, 10000);
TEST_TRUE(runner, ivars->limit == ((char*)NULL) + 10000,
"InStream_Buf sets limit");
InStream_Seek(instream, gb6);
TEST_TRUE(runner, InStream_Tell(instream) == gb6,
"Tell after seek forwards outside buffer");
TEST_TRUE(runner, ivars->buf == NULL,
"Seek forwards outside buffer sets buf to NULL");
TEST_TRUE(runner, ivars->limit == NULL,
"Seek forwards outside buffer sets limit to NULL");
TEST_TRUE(runner, FileWindow_IVARS(ivars->window)->offset == gb6,
"Seek forwards outside buffer tracks pos in window offset");
InStream_Buf(instream, (size_t)gb1);
TEST_TRUE(runner, ivars->limit == ((char*)NULL) + gb1,
"InStream_Buf sets limit");
InStream_Seek(instream, gb6 + 10);
TEST_TRUE(runner, InStream_Tell(instream) == gb6 + 10,
"Tell after seek forwards within buffer");
TEST_TRUE(runner, ivars->buf == ((char*)NULL) + 10,
"Seek within buffer sets buf");
TEST_TRUE(runner, ivars->limit == ((char*)NULL) + gb1,
"Seek within buffer leaves limit alone");
InStream_Seek(instream, gb6 + 1);
TEST_TRUE(runner, InStream_Tell(instream) == gb6 + 1,
"Tell after seek backwards within buffer");
TEST_TRUE(runner, ivars->buf == ((char*)NULL) + 1,
"Seek backwards within buffer sets buf");
TEST_TRUE(runner, ivars->limit == ((char*)NULL) + gb1,
"Seek backwards within buffer leaves limit alone");
InStream_Seek(instream, gb3);
TEST_TRUE(runner, InStream_Tell(instream) == gb3,
"Tell after seek backwards outside buffer");
TEST_TRUE(runner, ivars->buf == NULL,
"Seek backwards outside buffer sets buf to NULL");
TEST_TRUE(runner, ivars->limit == NULL,
"Seek backwards outside buffer sets limit to NULL");
TEST_TRUE(runner, FileWindow_IVARS(ivars->window)->offset == gb3,
"Seek backwards outside buffer tracks pos in window offset");
DECREF(instream);
DECREF(fh);
}
void
TestInStream_Run_IMP(TestInStream *self, TestBatchRunner *runner) {
TestBatchRunner_Plan(runner, (TestBatch*)self, 37);
test_refill(runner);
test_Clone_and_Reopen(runner);
test_Close(runner);
test_Seek_and_Tell(runner);
}