Skip to content

Commit d02ac05

Browse files
committed
tests: add zero-length file coverage in integration and unit tests
Integration (generate_test_files.py): - Add 9 zero-length files with varied names covering all special-character categories (spaces, dashes, underscores, dots, parens, brackets, @, and a moderately long name) to exercise the fi->fh=0 FUSE bypass path across the full filename surface. Integration (run_integration_test.sh): - Add a dedicated 'Zero-length files (varied names)' test group in the short test suite that derives the file list from the manifest and checks existence, size == 0, content is empty, and SHA-256 matches the well-known empty-file digest for every zero-byte file. - Add a matching cache-mode zero-length file test group (gated to the first block-size iteration) to verify the bypass path works when the cache system is active. Unit (test_cache.c): - Add test_Cache_read_negative_len: verifies the new len < 0 guard returns -EINVAL rather than wrapping the value to a huge size_t. - Add test_Cache_read_null_cf: verifies NULL cf returns -EINVAL. - Add test_Cache_read_null_link: verifies NULL cf->link returns -EINVAL.
1 parent a491ade commit d02ac05

3 files changed

Lines changed: 168 additions & 0 deletions

File tree

tests/integration/generate_test_files.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,22 @@ def main():
138138
test_files.append(("empty_file.txt", 0))
139139
test_files.append(("tiny.txt", 1))
140140

141+
# Zero-length files with varied names to exercise the fi->fh=0 bypass
142+
# path through every special-character category.
143+
zero_length_names = [
144+
"zero_basic.txt",
145+
"zero file with spaces.txt",
146+
"zero-dashes.bin",
147+
"zero_underscores.bin",
148+
"zero.multiple.dots.bin",
149+
"zero(parens).txt",
150+
"zero[brackets].txt",
151+
"zero@at.txt",
152+
"zero" + "a" * 50 + ".txt", # moderately long name
153+
]
154+
for name in zero_length_names:
155+
test_files.append((name, 0))
156+
141157
# Add the 1 GB file for cache system testing when is_large is given
142158
if is_large:
143159
test_files.append(("large_1g.bin", 1024 * 1024 * 1024))

tests/integration/run_integration_test.sh

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,63 @@ else
347347
skip "Tiny file not found"
348348
fi
349349

350+
# 4f-extra. Test: Zero-length files with varied special-character names
351+
log_info "Test group: Zero-length files (varied names)"
352+
353+
# Build the list from the manifest: all files whose size is 0
354+
ZERO_FILES_LIST=$(python3 -c "
355+
import json
356+
with open('${MANIFEST}') as f:
357+
m = json.load(f)
358+
for name, info in sorted(m.items()):
359+
if info['size'] == 0:
360+
print(name)
361+
")
362+
363+
ZERO_FILE_COUNT=0
364+
while IFS= read -r zf_name; do
365+
[[ -z "${zf_name}" ]] && continue
366+
ZERO_FILE_COUNT=$((ZERO_FILE_COUNT + 1))
367+
target="${MOUNT_DIR}/${zf_name}"
368+
369+
# Existence
370+
if [[ ! -e "${target}" ]]; then
371+
fail "Zero-length file missing: ${zf_name}"
372+
continue
373+
fi
374+
375+
# Size must be 0
376+
zf_size=$(stat -c%s "${target}" 2>/dev/null || echo "-1")
377+
if [[ "${zf_size}" == "0" ]]; then
378+
pass "Zero-length size correct: ${zf_name}"
379+
else
380+
fail "Zero-length size wrong (got ${zf_size}): ${zf_name}"
381+
fi
382+
383+
# Reading must yield empty content (no hang, no error)
384+
zf_content=$(cat "${target}" 2>/dev/null)
385+
if [[ -z "${zf_content}" ]]; then
386+
pass "Zero-length content empty: ${zf_name}"
387+
else
388+
fail "Zero-length file has unexpected content: ${zf_name}"
389+
fi
390+
391+
# SHA-256 of an empty file is the well-known constant
392+
EMPTY_SHA256="e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
393+
zf_sha=$(sha256sum "${target}" 2>/dev/null | awk '{print $1}')
394+
if [[ "${zf_sha}" == "${EMPTY_SHA256}" ]]; then
395+
pass "Zero-length checksum OK: ${zf_name}"
396+
else
397+
fail "Zero-length checksum mismatch: ${zf_name} (got ${zf_sha})"
398+
fi
399+
done <<< "${ZERO_FILES_LIST}"
400+
401+
if [[ "${ZERO_FILE_COUNT}" -eq 0 ]]; then
402+
skip "No zero-length files found in manifest"
403+
else
404+
log_info "Tested ${ZERO_FILE_COUNT} zero-length file(s)."
405+
fi
406+
350407
# 4f. Test: Subdirectory is readable
351408
SUBDIR="${MOUNT_DIR}/subdir with spaces"
352409
if [[ -d "${SUBDIR}" ]]; then
@@ -465,10 +522,64 @@ else
465522
log_error " actual: ${actual_sha256}"
466523
fi
467524

525+
# Test: Zero-length files in cache mode (run once — not block-size
526+
# dependent, but checked here to confirm the fi->fh=0 bypass works
527+
# when the cache system is active).
528+
if [[ "${BLKSZ}" == "8" ]]; then
529+
log_info "Test group: Zero-length files in cache mode"
530+
CACHE_ZERO_FILES_LIST=$(python3 -c "
531+
import json
532+
with open('${MANIFEST}') as f:
533+
m = json.load(f)
534+
for name, info in sorted(m.items()):
535+
if info['size'] == 0:
536+
print(name)
537+
")
538+
CACHE_ZERO_COUNT=0
539+
EMPTY_SHA256="e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
540+
while IFS= read -r czf_name; do
541+
[[ -z "${czf_name}" ]] && continue
542+
CACHE_ZERO_COUNT=$((CACHE_ZERO_COUNT + 1))
543+
czf_target="${CACHE_MOUNT_DIR}/${czf_name}"
544+
545+
if [[ ! -e "${czf_target}" ]]; then
546+
fail "Cache: zero-length file missing: ${czf_name}"
547+
continue
548+
fi
549+
550+
czf_size=$(stat -c%s "${czf_target}" 2>/dev/null || echo "-1")
551+
if [[ "${czf_size}" == "0" ]]; then
552+
pass "Cache: zero-length size correct: ${czf_name}"
553+
else
554+
fail "Cache: zero-length size wrong (${czf_size}): ${czf_name}"
555+
fi
556+
557+
czf_content=$(cat "${czf_target}" 2>/dev/null)
558+
if [[ -z "${czf_content}" ]]; then
559+
pass "Cache: zero-length content empty: ${czf_name}"
560+
else
561+
fail "Cache: zero-length has unexpected content: ${czf_name}"
562+
fi
563+
564+
czf_sha=$(sha256sum "${czf_target}" 2>/dev/null | awk '{print $1}')
565+
if [[ "${czf_sha}" == "${EMPTY_SHA256}" ]]; then
566+
pass "Cache: zero-length checksum OK: ${czf_name}"
567+
else
568+
fail "Cache: zero-length checksum mismatch: ${czf_name}"
569+
fi
570+
done <<< "${CACHE_ZERO_FILES_LIST}"
571+
if [[ "${CACHE_ZERO_COUNT}" -eq 0 ]]; then
572+
skip "Cache: no zero-length files in manifest"
573+
else
574+
log_info "Cache: tested ${CACHE_ZERO_COUNT} zero-length file(s)."
575+
fi
576+
fi
577+
468578
# Unmount
469579
do_unmount "${CACHE_MOUNT_DIR}"
470580
wait "${CACHE_HTTPDIRFS_PID}" 2>/dev/null || true
471581
log_info "Cache mount (blksz=${BLKSZ}M) unmounted."
582+
472583
done
473584
fi
474585
fi

tests/test_cache.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include <unity.h>
2+
#include <errno.h>
23
#include <stdlib.h>
34
#include <string.h>
45
#include <sys/stat.h>
@@ -109,6 +110,43 @@ void test_Cache_read_past_eof(void)
109110
TEST_ASSERT_EQUAL_INT(0, res);
110111
}
111112

113+
void test_Cache_read_negative_len(void)
114+
{
115+
/* The new len < 0 guard must reject negative lengths with -EINVAL
116+
* rather than wrapping the value to a huge size_t. */
117+
Cache cf = {0};
118+
Link link = {0};
119+
link.content_length = 1024;
120+
cf.link = &link;
121+
cf.blksz = 4096;
122+
123+
char buf[64];
124+
long res = Cache_read(&cf, buf, (off_t)-1, 0);
125+
TEST_ASSERT_EQUAL_INT(-EINVAL, res);
126+
127+
res = Cache_read(&cf, buf, (off_t)-1024, 0);
128+
TEST_ASSERT_EQUAL_INT(-EINVAL, res);
129+
}
130+
131+
void test_Cache_read_null_cf(void)
132+
{
133+
/* Cache_read must return -EINVAL when passed a NULL cf pointer. */
134+
char buf[16];
135+
long res = Cache_read(NULL, buf, sizeof(buf), 0);
136+
TEST_ASSERT_EQUAL_INT(-EINVAL, res);
137+
}
138+
139+
void test_Cache_read_null_link(void)
140+
{
141+
/* Cache_read must return -EINVAL when cf->link is NULL. */
142+
Cache cf = {0};
143+
cf.link = NULL;
144+
145+
char buf[16];
146+
long res = Cache_read(&cf, buf, sizeof(buf), 0);
147+
TEST_ASSERT_EQUAL_INT(-EINVAL, res);
148+
}
149+
112150
static void cleanup_temp_dir(const char *tmp_cache_dir)
113151
{
114152
char filepath[512];
@@ -246,6 +284,9 @@ int main(void)
246284
RUN_TEST(test_ActiveDownload_find);
247285
RUN_TEST(test_Cache_read_zero_length);
248286
RUN_TEST(test_Cache_read_past_eof);
287+
RUN_TEST(test_Cache_read_negative_len);
288+
RUN_TEST(test_Cache_read_null_cf);
289+
RUN_TEST(test_Cache_read_null_link);
249290
RUN_TEST(test_Cache_invalid_zero_length_disk_files);
250291
return UNITY_END();
251292
}

0 commit comments

Comments
 (0)