File size: 10,160 Bytes
78d2150
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
#include "../../unity/unity.h"
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
#include <limits.h>
#include <getopt.h>

/* The test file is included inside dd.c after all internal definitions,
   so we can access the static variables and functions declared there. */

extern int optind; /* ensure we can set optind */

/* Forward declarations of static functions from dd.c we need to compare. */
static ssize_t iread (int fd, char *buf, idx_t size);
static ssize_t iread_fullblock (int fd, char *buf, idx_t size);

/* Helper to reset the dd global state between tests. */
static void reset_dd_state(void)
{
    /* Filenames */
    input_file = NULL;
    output_file = NULL;

    /* Block sizes */
    input_blocksize = 0;
    output_blocksize = 0;
    conversion_blocksize = 0;

    /* Skips/Seeks/Counts */
    skip_records = 0;
    skip_bytes = 0;
    seek_records = 0;
    seek_bytes = 0;
    max_records = INTMAX_MAX;
    max_bytes = 0;

    /* Flags and conversions */
    conversions_mask = 0;
    input_flags = 0;
    output_flags = 0;
    status_level = STATUS_DEFAULT;

    /* Read path */
    warn_partial_read = false;
    i_nocache = false;
    o_nocache = false;
    i_nocache_eof = false;
    o_nocache_eof = false;
    iread_fnc = NULL;

    /* Misc counters/output settings (not strictly needed by scanargs but reset anyway) */
    w_partial = 0;
    w_full = 0;
    r_partial = 0;
    r_full = 0;
    w_bytes = 0;
    reported_w_bytes = -1;
    progress_len = 0;
    r_truncate = 0;

    translation_needed = false;
    newline_character = '\n';
    space_character = ' ';
}

void setUp(void) {
    /* Setup before each test */
}

void tearDown(void) {
    /* Cleanup after each test */
}

/* Tests */

void test_scanargs_defaults_sets_twobufs_and_defaults(void)
{
    reset_dd_state();
    optind = 1;
    char arg0[] = "dd";
    char *argv[] = { arg0 };
    int argc = 1;

    scanargs(argc, argv);

    TEST_ASSERT_TRUE_MESSAGE((conversions_mask & C_TWOBUFS) != 0, "Expected C_TWOBUFS when bs is not specified");
    TEST_ASSERT_EQUAL_INT(DEFAULT_BLOCKSIZE, (int)input_blocksize);
    TEST_ASSERT_EQUAL_INT(DEFAULT_BLOCKSIZE, (int)output_blocksize);
    TEST_ASSERT_EQUAL_INT(STATUS_DEFAULT, status_level);
    TEST_ASSERT_EQUAL_PTR_MESSAGE((void*)iread, (void*)iread_fnc, "Default iread_fnc should be iread");
}

void test_scanargs_bs_sets_both_block_sizes_and_no_twobufs(void)
{
    reset_dd_state();
    optind = 1;
    char arg0[] = "dd";
    char arg1[] = "bs=1024";
    char *argv[] = { arg0, arg1 };
    int argc = 2;

    scanargs(argc, argv);

    TEST_ASSERT_EQUAL_INT(1024, (int)input_blocksize);
    TEST_ASSERT_EQUAL_INT(1024, (int)output_blocksize);
    TEST_ASSERT_TRUE_MESSAGE((conversions_mask & C_TWOBUFS) == 0, "bs should suppress implicit C_TWOBUFS");
}

void test_scanargs_ibs_and_obs_set_independently_and_twobufs_added(void)
{
    reset_dd_state();
    optind = 1;
    char arg0[] = "dd";
    char arg1[] = "ibs=4096";
    char arg2[] = "obs=8192";
    char *argv[] = { arg0, arg1, arg2 };
    int argc = 3;

    scanargs(argc, argv);

    TEST_ASSERT_EQUAL_INT(4096, (int)input_blocksize);
    TEST_ASSERT_EQUAL_INT(8192, (int)output_blocksize);
    TEST_ASSERT_TRUE_MESSAGE((conversions_mask & C_TWOBUFS) != 0, "ibs/obs without bs should add C_TWOBUFS");
}

void test_scanargs_iflag_fullblock_sets_fullblock_reader(void)
{
    reset_dd_state();
    optind = 1;
    char arg0[] = "dd";
    char arg1[] = "iflag=fullblock";
    char *argv[] = { arg0, arg1 };
    int argc = 2;

    scanargs(argc, argv);

    TEST_ASSERT_EQUAL_PTR((void*)iread_fullblock, (void*)iread_fnc);
    TEST_ASSERT_TRUE_MESSAGE((input_flags & O_FULLBLOCK) == 0, "O_FULLBLOCK should be cleared after setting iread_fnc");
}

void test_scanargs_skip_with_B_suffix_splits_into_records_and_bytes(void)
{
    reset_dd_state();
    optind = 1;
    char arg0[] = "dd";
    char arg1[] = "ibs=4";
    char arg2[] = "skip=10B";
    char *argv[] = { arg0, arg1, arg2 };
    int argc = 3;

    scanargs(argc, argv);

    TEST_ASSERT_TRUE_MESSAGE((input_flags & O_SKIP_BYTES) != 0, "O_SKIP_BYTES should be set when skip uses B suffix");
    TEST_ASSERT_EQUAL_INT(2, (int)skip_records);
    TEST_ASSERT_EQUAL_INT(2, (int)skip_bytes);
}

void test_scanargs_skip_without_B_suffix_sets_records_only(void)
{
    reset_dd_state();
    optind = 1;
    char arg0[] = "dd";
    char arg1[] = "skip=3";
    char *argv[] = { arg0, arg1 };
    int argc = 2;

    scanargs(argc, argv);

    TEST_ASSERT_TRUE_MESSAGE((input_flags & O_SKIP_BYTES) == 0, "O_SKIP_BYTES should not be set without B suffix");
    TEST_ASSERT_EQUAL_INT(3, (int)skip_records);
    TEST_ASSERT_EQUAL_INT(0, (int)skip_bytes);
}

void test_scanargs_count_with_B_suffix_splits_into_records_and_bytes(void)
{
    reset_dd_state();
    optind = 1;
    char arg0[] = "dd";
    char arg1[] = "ibs=10";
    char arg2[] = "count=25B";
    char *argv[] = { arg0, arg1, arg2 };
    int argc = 3;

    scanargs(argc, argv);

    TEST_ASSERT_TRUE_MESSAGE((input_flags & O_COUNT_BYTES) != 0, "O_COUNT_BYTES should be set when count uses B suffix");
    TEST_ASSERT_EQUAL_INT(2, (int)max_records);
    TEST_ASSERT_EQUAL_INT(5, (int)max_bytes);
}

void test_scanargs_count_without_B_suffix_sets_records_only(void)
{
    reset_dd_state();
    optind = 1;
    char arg0[] = "dd";
    char arg1[] = "count=7";
    char *argv[] = { arg0, arg1 };
    int argc = 2;

    scanargs(argc, argv);

    TEST_ASSERT_TRUE_MESSAGE((input_flags & O_COUNT_BYTES) == 0, "O_COUNT_BYTES should not be set without B suffix");
    TEST_ASSERT_EQUAL_INT(7, (int)max_records);
    TEST_ASSERT_EQUAL_INT(0, (int)max_bytes);
}

void test_scanargs_seek_with_B_suffix_splits_into_records_and_bytes(void)
{
    reset_dd_state();
    optind = 1;
    char arg0[] = "dd";
    char arg1[] = "obs=8";
    char arg2[] = "seek=17B";
    char *argv[] = { arg0, arg1, arg2 };
    int argc = 3;

    scanargs(argc, argv);

    TEST_ASSERT_TRUE_MESSAGE((output_flags & O_SEEK_BYTES) != 0, "O_SEEK_BYTES should be set when seek uses B suffix");
    TEST_ASSERT_EQUAL_INT(2, (int)seek_records);
    TEST_ASSERT_EQUAL_INT(1, (int)seek_bytes);
}

void test_scanargs_status_progress_and_exclusive_behavior(void)
{
    reset_dd_state();
    optind = 1;
    char arg0[] = "dd";
    char arg1[] = "status=none,progress"; /* last wins (exclusive) */
    char *argv[] = { arg0, arg1 };
    int argc = 2;

    scanargs(argc, argv);

    TEST_ASSERT_EQUAL_INT(STATUS_PROGRESS, status_level);
}

void test_scanargs_conv_ucase_and_swab_sets_bits_and_twobufs(void)
{
    reset_dd_state();
    optind = 1;
    char arg0[] = "dd";
    char arg1[] = "conv=ucase,swab";
    char *argv[] = { arg0, arg1 };
    int argc = 2;

    scanargs(argc, argv);

    TEST_ASSERT_TRUE_MESSAGE((conversions_mask & C_UCASE) != 0, "C_UCASE should be set");
    TEST_ASSERT_TRUE_MESSAGE((conversions_mask & C_SWAB) != 0, "C_SWAB should be set");
    TEST_ASSERT_TRUE_MESSAGE((conversions_mask & C_TWOBUFS) != 0, "C_TWOBUFS should be set (swab implies, and bs not set adds too)");
}

void test_scanargs_iflag_nocache_sets_internal_flags_and_clears_input_flag(void)
{
    reset_dd_state();
    optind = 1;
    char arg0[] = "dd";
    char arg1[] = "iflag=nocache";
    char *argv[] = { arg0, arg1 };
    int argc = 2;

    scanargs(argc, argv);

    TEST_ASSERT_TRUE(i_nocache);
    TEST_ASSERT_FALSE(i_nocache_eof); /* default max_records!=0 */
    TEST_ASSERT_TRUE_MESSAGE((input_flags & O_NOCACHE) == 0, "O_NOCACHE should be cleared from input_flags");
}

void test_scanargs_oflag_nocache_sets_internal_flags_and_clears_output_flag(void)
{
    reset_dd_state();
    optind = 1;
    char arg0[] = "dd";
    char arg1[] = "oflag=nocache";
    char *argv[] = { arg0, arg1 };
    int argc = 2;

    scanargs(argc, argv);

    TEST_ASSERT_TRUE(o_nocache);
    TEST_ASSERT_FALSE(o_nocache_eof); /* default max_records!=0 */
    TEST_ASSERT_TRUE_MESSAGE((output_flags & O_NOCACHE) == 0, "O_NOCACHE should be cleared from output_flags");
}

void test_scanargs_warn_partial_read_set_with_bs_and_small_count(void)
{
    reset_dd_state();
    optind = 1;
    char arg0[] = "dd";
    char arg1[] = "bs=100";
    char arg2[] = "count=1";
    char *argv[] = { arg0, arg1, arg2 };
    int argc = 3;

    scanargs(argc, argv);

    TEST_ASSERT_TRUE_MESSAGE(warn_partial_read, "warn_partial_read should be true when bs is set and counting records");
}

void test_scanargs_warn_partial_read_suppressed_by_iflag_fullblock(void)
{
    reset_dd_state();
    optind = 1;
    char arg0[] = "dd";
    char arg1[] = "bs=100";
    char arg2[] = "count=1";
    char arg3[] = "iflag=fullblock";
    char *argv[] = { arg0, arg1, arg2, arg3 };
    int argc = 4;

    scanargs(argc, argv);

    TEST_ASSERT_FALSE_MESSAGE(warn_partial_read, "warn_partial_read should be false when iflag=fullblock is specified");
    TEST_ASSERT_EQUAL_PTR((void*)iread_fullblock, (void*)iread_fnc);
}

int main(void)
{
    UNITY_BEGIN();

    RUN_TEST(test_scanargs_defaults_sets_twobufs_and_defaults);
    RUN_TEST(test_scanargs_bs_sets_both_block_sizes_and_no_twobufs);
    RUN_TEST(test_scanargs_ibs_and_obs_set_independently_and_twobufs_added);
    RUN_TEST(test_scanargs_iflag_fullblock_sets_fullblock_reader);
    RUN_TEST(test_scanargs_skip_with_B_suffix_splits_into_records_and_bytes);
    RUN_TEST(test_scanargs_skip_without_B_suffix_sets_records_only);
    RUN_TEST(test_scanargs_count_with_B_suffix_splits_into_records_and_bytes);
    RUN_TEST(test_scanargs_count_without_B_suffix_sets_records_only);
    RUN_TEST(test_scanargs_seek_with_B_suffix_splits_into_records_and_bytes);
    RUN_TEST(test_scanargs_status_progress_and_exclusive_behavior);
    RUN_TEST(test_scanargs_conv_ucase_and_swab_sets_bits_and_twobufs);
    RUN_TEST(test_scanargs_iflag_nocache_sets_internal_flags_and_clears_input_flag);
    RUN_TEST(test_scanargs_oflag_nocache_sets_internal_flags_and_clears_output_flag);
    RUN_TEST(test_scanargs_warn_partial_read_set_with_bs_and_small_count);
    RUN_TEST(test_scanargs_warn_partial_read_suppressed_by_iflag_fullblock);

    return UNITY_END();
}