1/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
3/**
4 * @copyright 2013 Couchbase, Inc.
5 *
6 * @author Filipe Manana  <filipe@couchbase.com>
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
9 * use this file except in compliance with the License. You may obtain a copy of
10 * the License at
11 *
12 *  http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17 * License for the specific language governing permissions and limitations under
18 * the License.
19 **/
20
21#include "config.h"
22#include <stdio.h>
23#include <stdlib.h>
24#include "macros.h"
25#include "../src/file_merger.h"
26#include "file_tests.h"
27
28#define N_FILES 4
29#define MAX_RECORDS_PER_FILE 100
30
31
32static int read_record(FILE *f, void **buffer, void *ctx)
33{
34    int *rec = (int *) malloc(sizeof(int));
35    (void) ctx;
36
37    if (rec == NULL) {
38        return FILE_MERGER_ERROR_ALLOC;
39    }
40
41    if (fread(rec, sizeof(int), 1, f) != 1) {
42        free(rec);
43        if (feof(f)) {
44            return 0;
45        } else {
46            return FILE_MERGER_ERROR_FILE_READ;
47        }
48    }
49
50    *buffer = rec;
51
52    return sizeof(int);
53}
54
55static file_merger_error_t write_record(FILE *f, void *buffer, void *ctx)
56{
57    (void) ctx;
58
59    if (fwrite(buffer, sizeof(int), 1, f) != 1) {
60        return FILE_MERGER_ERROR_FILE_WRITE;
61    }
62
63    return FILE_MERGER_SUCCESS;
64}
65
66static int compare_records(const void *rec1, const void *rec2, void *ctx)
67{
68    (void) ctx;
69
70    return *((const int *) rec1) - *((const int *) rec2);
71}
72
73static void free_record(void *rec, void *ctx)
74{
75   (void) ctx;
76
77   free(rec);
78}
79
80static unsigned long check_file_sorted(const char *file_path)
81{
82    FILE *f;
83    void *a = NULL, *b;
84    int record_size;
85    unsigned long num_records = 0;
86
87    f = fopen(file_path, "rb");
88    cb_assert(f != NULL);
89
90    record_size = read_record(f, &a, NULL);
91    cb_assert(record_size > 0);
92    num_records += 1;
93
94    while (record_size > 0) {
95        record_size = read_record(f, &b, NULL);
96        cb_assert(record_size >= 0);
97
98        if (record_size > 0) {
99            num_records += 1;
100            cb_assert(compare_records(a, b, NULL) < 0);
101            free_record(a, NULL);
102            a = b;
103        }
104    }
105
106    free_record(a, NULL);
107    fclose(f);
108
109    return num_records;
110}
111
112
113void file_merger_tests(void)
114{
115    const char *source_files[N_FILES] = {
116        "sorted_file_1.tmp",
117        "sorted_file_2.tmp",
118        "sorted_file_3.tmp",
119        "sorted_file_4.tmp"
120    };
121    const char *dest_file = "merged_file.tmp";
122    const int batches[N_FILES][MAX_RECORDS_PER_FILE] = {
123        {3, 5, 6, 14, 18, 19, 29, 30, 35, 38, 44, 45, 46, 51, 54, 57, 62, 65,
124         75, 76, 81, 83, 91, 92, 95, 104, 105, 107},
125        {1, 2, 4, 9, 17, 23, 25, 32, 33, 37, 41, 49, 55, 58, 61, 68, 70, 71, 72,
126         77, 80, 87, 89, 94, 98, 100, 111, 112, 113, 114, 115, 116, 117, 119},
127        {10, 12, 15, 20, 21, 22, 27, 34, 36, 39, 42, 47, 52, 53, 56, 63, 64, 74,
128         78, 79, 86, 88, 93, 99, 103, 106, 108, 109, 121, 122, 123},
129        {7, 8, 11, 13, 16, 24, 26, 28, 31, 40, 43, 48, 50, 59, 60, 66, 69, 73,
130         82, 84, 85, 90, 96, 97, 101, 102, 110, 118, 120}
131    };
132    unsigned i, j;
133    unsigned num_records = 0;
134    file_merger_error_t ret;
135
136    fprintf(stderr, "\nRunning file merger tests...\n");
137
138    for (i = 0; i < N_FILES; ++i) {
139        FILE *f;
140
141        remove(source_files[i]);
142        f = fopen(source_files[i], "ab");
143        cb_assert(f != NULL);
144
145        for (j = 0; j < MAX_RECORDS_PER_FILE; ++j) {
146            if (batches[i][j] == 0) {
147                break;
148            }
149            if (j > 0) {
150                cb_assert(batches[i][j] > batches[i][j - 1]);
151            }
152            cb_assert(fwrite(&batches[i][j], sizeof(batches[i][j]), 1, f) == 1);
153            num_records += 1;
154        }
155
156        fclose(f);
157    }
158
159    remove(dest_file);
160    ret = merge_files(source_files, N_FILES,
161                      dest_file,
162                      read_record, write_record, NULL, compare_records,
163                      NULL, free_record, 0, NULL);
164
165    cb_assert(ret == FILE_MERGER_SUCCESS);
166    cb_assert(check_file_sorted(dest_file) == num_records);
167
168    for (i = 0; i < N_FILES; ++i) {
169        remove(source_files[i]);
170    }
171    remove(dest_file);
172
173    fprintf(stderr, "Running file merger tests passed\n\n");
174}
175