4848#include <chunkio/cio_stream.h>
4949#include <chunkio/cio_chunk.h>
5050
51+ /* Note: We still need to include cio_file_native.h for testing native functions
52+ * directly to verify bug fixes, but we use public APIs for state checks */
53+
5154#include "cio_tests_internal.h"
5255
5356#define CIO_ENV "tmp"
@@ -66,8 +69,8 @@ static int log_cb(struct cio_ctx *ctx, int level, const char *file, int line,
6669/*
6770 * ISSUE #1: Test deleting a file that is open/mapped
6871 *
69- * Expected behavior: Should fail with CIO_ERROR (as Unix does)
70- * Current behavior: Succeeds and may cause resource leaks
72+ * Expected behavior: Delete should succeed after automatically releasing
73+ * any outstanding mappings and handles.
7174 */
7275static void test_win32_delete_while_open ()
7376{
@@ -101,20 +104,16 @@ static void test_win32_delete_while_open()
101104 cf = (struct cio_file * ) chunk -> backend ;
102105 TEST_CHECK (cf != NULL );
103106
104- /* Verify file is open */
105- TEST_CHECK (cio_file_native_is_open ( cf ) == 1 );
107+ /* Verify file is open (using public API) */
108+ TEST_CHECK (cio_chunk_is_up ( chunk ) == CIO_TRUE );
106109
107- /* Try to delete while open - THIS SHOULD FAIL but currently doesn't on Windows */
110+ /* Delete while open - should succeed and close resources automatically */
108111 ret = cio_file_native_delete (cf );
109- printf ("Result of delete while open: %d (expected: CIO_ERROR=%d)\n" ,
110- ret , CIO_ERROR );
111-
112- /* On Unix this returns CIO_ERROR, on Windows it currently succeeds */
113- /* This TEST_CHECK will FAIL in CI, highlighting the inconsistency */
114- TEST_CHECK (ret == CIO_ERROR );
115- if (ret != CIO_ERROR ) {
116- printf ("ISSUE DETECTED: Delete succeeded while file is open (inconsistent with Unix)\n" );
117- }
112+ printf ("Result of delete while open: %d (expected: CIO_OK=%d)\n" ,
113+ ret , CIO_OK );
114+ TEST_CHECK (ret == CIO_OK );
115+ TEST_CHECK (cio_file_native_is_open (cf ) == CIO_FALSE );
116+ TEST_CHECK (cio_file_native_is_mapped (cf ) == CIO_FALSE );
118117
119118 cio_chunk_close (chunk , CIO_FALSE );
120119 cio_stream_delete (stream );
@@ -124,8 +123,8 @@ static void test_win32_delete_while_open()
124123/*
125124 * ISSUE #2: Test deleting a file that is mapped
126125 *
127- * Expected behavior: Should fail with CIO_ERROR (as Unix does)
128- * Current behavior: Succeeds and may cause crashes
126+ * Expected behavior: Delete should succeed after the implementation releases
127+ * the mapping safely.
129128 */
130129static void test_win32_delete_while_mapped ()
131130{
@@ -163,21 +162,16 @@ static void test_win32_delete_while_mapped()
163162 ret = cio_chunk_write (chunk , "test data" , 9 );
164163 TEST_CHECK (ret == 0 );
165164
166- /* Verify file is mapped */
167- TEST_CHECK (cio_file_native_is_mapped ( cf ) == 1 );
165+ /* Verify file is mapped (using public API) */
166+ TEST_CHECK (cio_chunk_is_up ( chunk ) == CIO_TRUE );
168167
169- /* Try to delete while mapped - THIS SHOULD FAIL but currently doesn't on Windows */
168+ /* Delete while mapped - should succeed and release mapping */
170169 ret = cio_file_native_delete (cf );
171- printf ("Result of delete while mapped: %d (expected: CIO_ERROR=%d)\n" ,
172- ret , CIO_ERROR );
173-
174- /* On Unix this returns CIO_ERROR, on Windows it currently succeeds */
175- /* This TEST_CHECK will FAIL in CI, highlighting the inconsistency */
176- TEST_CHECK (ret == CIO_ERROR );
177- if (ret != CIO_ERROR ) {
178- printf ("ISSUE DETECTED: Delete succeeded while file is mapped (inconsistent with Unix)\n" );
179- printf ("WARNING: This can cause crashes when accessing the mapped memory\n" );
180- }
170+ printf ("Result of delete while mapped: %d (expected: CIO_OK=%d)\n" ,
171+ ret , CIO_OK );
172+ TEST_CHECK (ret == CIO_OK );
173+ TEST_CHECK (cio_file_native_is_open (cf ) == CIO_FALSE );
174+ TEST_CHECK (cio_file_native_is_mapped (cf ) == CIO_FALSE );
181175
182176 cio_chunk_close (chunk , CIO_FALSE );
183177 cio_stream_delete (stream );
@@ -222,41 +216,30 @@ static void test_win32_sync_without_map()
222216 cf = (struct cio_file * ) chunk -> backend ;
223217 TEST_CHECK (cf != NULL );
224218
225- /* Manually unmap if it was auto-mapped */
226- if (cio_file_native_is_mapped ( cf ) ) {
227- ret = cio_file_native_unmap ( cf );
228- TEST_CHECK (ret == CIO_OK );
219+ /* Manually unmap if it was auto-mapped (using public API) */
220+ if (cio_chunk_is_up ( chunk ) == CIO_TRUE ) {
221+ ret = cio_file_down ( chunk );
222+ TEST_CHECK (ret == 0 );
229223 }
230224
231- /* Verify file is not mapped */
232- TEST_CHECK (cio_file_native_is_mapped (cf ) == 0 );
233-
234- /* Verify cf->map is actually NULL (this is the issue) */
235- TEST_CHECK (cf -> map == NULL );
236- printf ("Verified: cf->map is NULL\n" );
237-
238- /* Try to sync without mapping - THIS SHOULD CHECK FIRST */
239- /* On Windows, cio_file_native_sync accesses cf->map directly without checking */
240- /* This will likely cause a crash or access violation because FlushViewOfFile
241- * is called with a NULL pointer */
242- printf ("Attempting sync on unmapped file (cf->map is NULL)...\n" );
243- printf ("WARNING: cio_file_native_sync will call FlushViewOfFile(cf->map, ...)\n" );
244- printf (" which will fail or crash if cf->map is NULL\n" );
245-
246- /* This test is designed to highlight the issue - on Windows it may crash */
247- /* On Windows, cio_file_native_sync accesses cf->map without checking if NULL */
248- /* This TEST_CHECK will FAIL in CI if sync succeeds or crashes, highlighting the issue */
249- /* Note: If it crashes, the test will fail anyway */
250- ret = cio_file_native_sync (cf , 0 );
251- printf ("Result of sync without map: %d (expected: CIO_ERROR=%d)\n" , ret , CIO_ERROR );
252-
253- /* Expected: CIO_ERROR due to NULL map pointer */
254- /* This TEST_CHECK will FAIL in CI, highlighting the inconsistency */
255- TEST_CHECK (ret == CIO_ERROR );
256- if (ret != CIO_ERROR ) {
257- printf ("ISSUE DETECTED: Sync succeeded with NULL map pointer (inconsistent behavior)\n" );
258- printf ("Expected: CIO_ERROR due to NULL map pointer\n" );
259- }
225+ /* Verify file is not mapped (using public API) */
226+ TEST_CHECK (cio_chunk_is_up (chunk ) == CIO_FALSE );
227+ printf ("Verified: chunk is down (not mapped)\n" );
228+
229+ /* Set synced flag to FALSE to force sync path (since cio_file_down syncs before unmapping) */
230+ cf -> synced = CIO_FALSE ;
231+
232+ /* Try to sync without mapping using public API */
233+ /* cio_file_sync should auto-remap and emit a warning */
234+ printf ("Attempting sync on unmapped file using cio_file_sync()...\n" );
235+ printf ("cio_file_sync() should remap and warn instead of failing\n" );
236+
237+ ret = cio_file_sync (chunk );
238+ printf ("Result of sync without map: %d (expected: 0 for success with warning)\n" , ret );
239+
240+ TEST_CHECK (ret == 0 );
241+ TEST_CHECK (cio_chunk_is_up (chunk ) == CIO_FALSE );
242+ TEST_CHECK (cio_file_native_is_open (cf ) == CIO_FALSE );
260243
261244 cio_chunk_close (chunk , CIO_FALSE );
262245 cio_stream_delete (stream );
@@ -326,40 +309,58 @@ static void test_win32_map_size_mismatch()
326309 cf = (struct cio_file * ) chunk -> backend ;
327310 TEST_CHECK (cf != NULL );
328311
312+ /* Unmap if cio_chunk_open auto-mapped the file (using public API) */
313+ if (cio_chunk_is_up (chunk ) == CIO_TRUE ) {
314+ ret = cio_file_down (chunk );
315+ TEST_CHECK (ret == 0 );
316+ }
317+
318+ /* Ensure file is still open */
319+ if (!cio_file_native_is_open (cf )) {
320+ ret = cio_file_native_open (cf );
321+ TEST_CHECK (ret == CIO_OK );
322+ }
323+
329324 /* Try to map with a size larger than the file */
330325 map_size = file_size + 4096 ; /* Request 4KB more than file size */
331326 printf ("Attempting to map %zu bytes (file is %zu bytes)\n" , map_size , file_size );
332327
333- /* Open file */
334- ret = cio_file_native_open (cf );
335- TEST_CHECK (ret == CIO_OK );
336-
337328 /* This is where the issue occurs: CreateFileMapping uses current file size (0,0),
338329 * but MapViewOfFile tries to map a larger size */
339330 ret = cio_file_native_map (cf , map_size );
340331 printf ("Result of mapping %zu bytes to %zu byte file: %d\n" ,
341332 map_size , file_size , ret );
342333
343- /* The issue: CreateFileMapping is called with (0, 0) which uses file size,
344- * but MapViewOfFile requests map_size. This mismatch can cause issues.
345- * If mapping succeeds, alloc_size should match map_size, not file_size */
334+ /* For read-only files, mapping beyond file size is not possible on Windows.
335+ * The mapping should be limited to file_size, and alloc_size should reflect
336+ * the actual mapped size (file_size), not the requested size (map_size).
337+ * This ensures consistency between CreateFileMappingA and MapViewOfFile sizes. */
346338 if (ret == CIO_OK ) {
347- printf ("WARNING: Mapping succeeded with size mismatch \n" );
348- printf ("Expected alloc_size : %zu (map_size) , file_size: %zu\n" , map_size , file_size );
339+ printf ("Mapping succeeded\n" );
340+ printf ("Requested map_size : %zu, file_size: %zu\n" , map_size , file_size );
349341
350- /* Verify what was actually mapped - this may expose the issue */
351- if (cio_file_native_is_mapped ( cf ) ) {
342+ /* Verify what was actually mapped (using public API) */
343+ if (cio_chunk_is_up ( chunk ) == CIO_TRUE ) {
352344 printf ("File is mapped, alloc_size: %zu\n" , cf -> alloc_size );
353- /* This TEST_CHECK will highlight if alloc_size doesn't match requested map_size */
354- /* Due to the bug, it may match file_size instead of map_size */
355- TEST_CHECK (cf -> alloc_size == map_size );
345+
346+ /* For read-only files, alloc_size should match file_size (the actual mapped size),
347+ * not the requested map_size (which exceeds file size) */
348+ /* This ensures consistency: CreateFileMappingA and MapViewOfFile both use actual_map_size */
349+ TEST_CHECK (cf -> alloc_size == file_size );
350+
351+ if (cf -> alloc_size != file_size ) {
352+ printf ("ISSUE DETECTED: alloc_size (%zu) doesn't match file_size (%zu)\n" ,
353+ cf -> alloc_size , file_size );
354+ printf ("For read-only files, mapping is limited to file_size\n" );
355+ }
356356 }
357357
358358 ret = cio_file_native_unmap (cf );
359359 TEST_CHECK (ret == CIO_OK );
360360 }
361361 else {
362- printf ("Mapping failed when size mismatch occurs (may be correct behavior)\n" );
362+ printf ("Mapping failed when size mismatch occurs\n" );
363+ /* For read-only files, this might be expected if map_size > file_size */
363364 }
364365
365366 cio_file_native_close (cf );
@@ -405,26 +406,15 @@ static void test_win32_fd_check_inconsistency()
405406 cf = (struct cio_file * ) chunk -> backend ;
406407 TEST_CHECK (cf != NULL );
407408
408- /* Verify file is open using the proper macro */
409- ret = cio_file_native_is_open (cf );
410- TEST_CHECK (ret == 1 );
411- printf ("cio_file_native_is_open(cf): %d\n" , ret );
412-
413- /* Check cf->fd value on Windows (should be -1 or not set properly) */
414- printf ("cf->fd value: %d\n" , cf -> fd );
415- printf ("cf->fd > 0 check: %d\n" , (cf -> fd > 0 ));
416- printf ("cio_file_native_is_open(cf): %d\n" , cio_file_native_is_open (cf ));
409+ /* Verify file is open (using public API) */
410+ ret = cio_chunk_is_up (chunk );
411+ TEST_CHECK (ret == CIO_TRUE );
412+ printf ("cio_chunk_is_up(chunk): %d\n" , ret );
417413
418- /* This highlights that cf->fd > 0 doesn't work on Windows */
414+ /* Check cf->fd value on Windows (internal check for documentation) */
419415 /* On Windows, cf->fd is typically -1, but the file is still open via backing_file */
420- /* This TEST_CHECK will FAIL in CI, highlighting the inconsistency */
421- /* cio_file.c line 804 uses cf->fd > 0 which is Unix-specific and doesn't work on Windows */
422- TEST_CHECK ((cf -> fd > 0 ) == cio_file_native_is_open (cf ));
423- if ((cf -> fd > 0 ) != cio_file_native_is_open (cf )) {
424- printf ("ISSUE DETECTED: cf->fd check (%d) doesn't match cio_file_native_is_open (%d)\n" ,
425- (cf -> fd > 0 ), cio_file_native_is_open (cf ));
426- printf ("WARNING: cio_file.c line 804 uses cf->fd > 0 which is Unix-specific\n" );
427- }
416+ printf ("cf->fd value: %d (internal, not used on Windows)\n" , cf -> fd );
417+ printf ("Note: cio_file.c now uses cio_file_native_is_open() instead of cf->fd > 0\n" );
428418
429419 cio_chunk_close (chunk , CIO_FALSE );
430420 cio_stream_delete (stream );
0 commit comments