The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/* 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_LUCY_CHARBUF
#include "Lucy/Util/ToolSet.h"

#include "Lucy/Test.h"
#include "Lucy/Test/Store/TestCompoundFileWriter.h"
#include "Lucy/Store/CompoundFileWriter.h"
#include "Lucy/Store/FileHandle.h"
#include "Lucy/Store/OutStream.h"
#include "Lucy/Store/RAMFolder.h"
#include "Lucy/Util/Json.h"

static CharBuf cfmeta_file = ZCB_LITERAL("cfmeta.json");
static CharBuf cfmeta_temp = ZCB_LITERAL("cfmeta.json.temp");
static CharBuf cf_file     = ZCB_LITERAL("cf.dat");
static CharBuf foo         = ZCB_LITERAL("foo");
static CharBuf bar         = ZCB_LITERAL("bar");
static CharBuf seg_1       = ZCB_LITERAL("seg_1");

static Folder*
S_folder_with_contents() {
    RAMFolder *folder  = RAMFolder_new(&seg_1);
    OutStream *foo_out = RAMFolder_Open_Out(folder, &foo);
    OutStream *bar_out = RAMFolder_Open_Out(folder, &bar);
    OutStream_Write_Bytes(foo_out, "foo", 3);
    OutStream_Write_Bytes(bar_out, "bar", 3);
    OutStream_Close(foo_out);
    OutStream_Close(bar_out);
    DECREF(foo_out);
    DECREF(bar_out);
    return (Folder*)folder;
}

static void
test_Consolidate(TestBatch *batch) {
    Folder *folder = S_folder_with_contents();
    FileHandle *fh;

    // Fake up detritus from failed consolidation.
    fh = Folder_Open_FileHandle(folder, &cf_file,
                                FH_CREATE | FH_WRITE_ONLY | FH_EXCLUSIVE);
    DECREF(fh);
    fh = Folder_Open_FileHandle(folder, &cfmeta_temp,
                                FH_CREATE | FH_WRITE_ONLY | FH_EXCLUSIVE);
    DECREF(fh);

    CompoundFileWriter *cf_writer = CFWriter_new(folder);
    CFWriter_Consolidate(cf_writer);
    PASS(batch, "Consolidate completes despite leftover files");
    DECREF(cf_writer);

    TEST_TRUE(batch, Folder_Exists(folder, &cf_file),
              "cf.dat file written");
    TEST_TRUE(batch, Folder_Exists(folder, &cfmeta_file),
              "cfmeta.json file written");
    TEST_FALSE(batch, Folder_Exists(folder, &foo),
               "original file zapped");
    TEST_FALSE(batch, Folder_Exists(folder, &cfmeta_temp),
               "detritus from failed consolidation zapped");

    DECREF(folder);
}

static void
test_offsets(TestBatch *batch) {
    Folder *folder = S_folder_with_contents();
    CompoundFileWriter *cf_writer = CFWriter_new(folder);
    Hash    *cf_metadata;
    Hash    *files;

    CFWriter_Consolidate(cf_writer);

    cf_metadata = (Hash*)CERTIFY(
                      Json_slurp_json(folder, &cfmeta_file), HASH);
    files = (Hash*)CERTIFY(
                Hash_Fetch_Str(cf_metadata, "files", 5), HASH);

    CharBuf *file;
    Obj     *filestats;
    bool_t   offsets_ok = true;

    TEST_TRUE(batch, Hash_Get_Size(files) > 0, "Multiple files");

    Hash_Iterate(files);
    while (Hash_Next(files, (Obj**)&file, &filestats)) {
        Hash *stats = (Hash*)CERTIFY(filestats, HASH);
        Obj *offset = CERTIFY(Hash_Fetch_Str(stats, "offset", 6), OBJ);
        int64_t offs = Obj_To_I64(offset);
        if (offs % 8 != 0) {
            offsets_ok = false;
            FAIL(batch, "Offset %" I64P " for %s not a multiple of 8",
                 offset, CB_Get_Ptr8(file));
            break;
        }
    }
    if (offsets_ok) {
        PASS(batch, "All offsets are multiples of 8");
    }

    DECREF(cf_metadata);
    DECREF(cf_writer);
    DECREF(folder);
}

void
TestCFWriter_run_tests() {
    TestBatch *batch = TestBatch_new(7);

    TestBatch_Plan(batch);
    test_Consolidate(batch);
    test_offsets(batch);

    DECREF(batch);
}