1 /*===========================================================================
2 *
3 * PUBLIC DOMAIN NOTICE
4 * National Center for Biotechnology Information
5 *
6 * This software/database is a "United States Government Work" under the
7 * terms of the United States Copyright Act. It was written as part of
8 * the author's official duties as a United States Government employee and
9 * thus cannot be copyrighted. This software/database is freely available
10 * to the public for use. The National Library of Medicine and the U.S.
11 * Government have not placed any restriction on its use or reproduction.
12 *
13 * Although all reasonable efforts have been taken to ensure the accuracy
14 * and reliability of the software and data, the NLM and the U.S.
15 * Government do not and cannot warrant the performance or results that
16 * may be obtained by using this software or data. The NLM and the U.S.
17 * Government disclaim all warranties, express or implied, including
18 * warranties of performance, merchantability or fitness for any particular
19 * purpose.
20 *
21 * Please cite the author in any work or product based on this material.
22 *
23 * ===========================================================================
24 *
25 */
26
27 /**
28 * Unit tests for cacheteefile2
29 */
30
31 #include <cstring>
32 #include <cstdlib>
33 #include <ctime>
34 #include <algorithm>
35
36 #include <ktst/unit_test.hpp>
37
38 #include <klib/out.h>
39 #include <klib/rc.h>
40
41 #include <kproc/thread.h>
42
43 #include <kfs/defs.h>
44 #include <kfs/directory.h>
45 #include <kfs/file.h>
46 #include <kfs/cachetee2file.h>
47 #include <kfs/recorder.h>
48
49 using namespace std;
50
51 #define DATAFILE "org.dat"
52 #define CACHEFILE "cache.dat"
53 #define CACHEFILE1 "cache.dat.cache"
54 #define DATAFILESIZE ( ( 1024 * 1024 ) + 300 )
55 #define BLOCKSIZE ( 1024 * 16 )
56
57 TEST_SUITE( CacheTeeTests );
58
59 // this is not what we are testing...
60
rand_32(uint32_t min,uint32_t max)61 static uint32_t rand_32( uint32_t min, uint32_t max )
62 {
63 double scaled = ( ( double )rand() / RAND_MAX );
64 return ( ( max - min + 1 ) * scaled ) + min;
65 }
66
fill_file_with_random_data(KFile * file,size_t file_size)67 static rc_t fill_file_with_random_data( KFile * file, size_t file_size )
68 {
69 rc_t rc = KFileSetSize( file, file_size );
70 if ( rc == 0 )
71 {
72 uint64_t pos = 0;
73 size_t total = 0;
74 while ( rc == 0 && total < file_size )
75 {
76 uint32_t data[ 512 ];
77 uint32_t i;
78 size_t to_write, num_writ;
79
80 for ( i = 0; i < 512; ++i ) data[ i ] = rand_32( 0, 0xFFFFFFFF - 1 );
81 to_write = ( file_size - total );
82 if ( to_write > sizeof data ) to_write = sizeof data;
83 rc = KFileWriteAll ( file, pos, data, to_write, &num_writ );
84 if ( rc == 0 )
85 {
86 pos += num_writ;
87 total += num_writ;
88 }
89 }
90 }
91 return rc;
92 }
93
create_random_file(const char * filename,uint64_t file_size)94 static rc_t create_random_file( const char * filename, uint64_t file_size )
95 {
96 KDirectory * dir;
97 rc_t rc = KDirectoryNativeDir( &dir );
98 if ( rc == 0 )
99 {
100 KFile * file;
101 rc = KDirectoryCreateFile ( dir, &file, false, 0664, kcmInit, filename );
102 if ( rc == 0 )
103 {
104 if ( rc == 0 )
105 rc = fill_file_with_random_data( file, file_size );
106 KFileRelease( file );
107 }
108 KDirectoryRelease( dir );
109 }
110 return rc;
111 }
112
113
remove_file(const char * filename)114 static rc_t remove_file( const char * filename )
115 {
116 KDirectory * dir;
117 rc_t rc = KDirectoryNativeDir( &dir );
118 if ( rc == 0 )
119 {
120 rc = KDirectoryRemove ( dir, true, "%s", filename );
121 KDirectoryRelease( dir );
122 }
123 return rc;
124 }
125
126
report_diff(uint8_t * b1,uint8_t * b2,size_t n,uint32_t max_diffs)127 static void report_diff( uint8_t * b1, uint8_t * b2, size_t n, uint32_t max_diffs )
128 {
129 size_t i, d;
130 for ( i = 0, d = 0; i < n && d < max_diffs; ++i )
131 {
132 if ( b1[ i ] != b2[ i ] )
133 {
134 KOutMsg( "[ %.08d ] %.02X %.02X\n", i, b1[ i ], b2[ i ] );
135 d++;
136 }
137 }
138 }
139
compare_file_content(const KFile * file1,const KFile * file2,uint64_t pos,size_t bytes)140 static rc_t compare_file_content( const KFile * file1, const KFile * file2, uint64_t pos, size_t bytes )
141 {
142 rc_t rc = 0;
143 uint8_t * buffer1 = ( uint8_t * )malloc( bytes );
144 if ( buffer1 == NULL )
145 rc = RC ( rcRuntime, rcBuffer, rcConstructing, rcMemory, rcExhausted );
146 else
147 {
148 uint8_t * buffer2 = ( uint8_t * )malloc( bytes );
149 if ( buffer2 == NULL )
150 rc = RC ( rcRuntime, rcBuffer, rcConstructing, rcMemory, rcExhausted );
151 else
152 {
153 size_t num_read1;
154 rc = KFileReadAll ( file1, pos, buffer1, bytes, &num_read1 );
155 if ( rc == 0 )
156 {
157 size_t num_read2;
158 rc = KFileReadAll ( file2, pos, buffer2, bytes, &num_read2 );
159 if ( rc == 0 )
160 {
161 if ( num_read1 != num_read2 )
162 rc = RC ( rcRuntime, rcBuffer, rcReading, rcMemory, rcInvalid );
163 else
164 {
165 int diff = memcmp( buffer1, buffer2, num_read1 );
166 if ( diff != 0 )
167 {
168 report_diff( buffer1, buffer2, num_read1, 20 );
169 rc = RC ( rcRuntime, rcBuffer, rcReading, rcMemory, rcCorrupt );
170 }
171 }
172 }
173 }
174 free( buffer2 );
175 }
176 free( buffer1 );
177 }
178 return rc;
179 }
180
181
read_partial(const KFile * src,size_t block_size,uint64_t to_read)182 static rc_t read_partial( const KFile * src, size_t block_size, uint64_t to_read )
183 {
184 rc_t rc = 0;
185 uint8_t * buffer = ( uint8_t * )malloc( block_size );
186 if ( buffer == NULL )
187 rc = RC ( rcRuntime, rcBuffer, rcConstructing, rcMemory, rcExhausted );
188 else
189 {
190 uint64_t pos = 0;
191 uint64_t total_read = 0;
192 while ( rc == 0 && total_read < to_read )
193 {
194 size_t num_read;
195 if ( block_size > ( to_read - total_read ) )
196 rc = KFileReadAll ( src, pos, buffer, to_read - total_read, &num_read );
197 else
198 rc = KFileReadAll ( src, pos, buffer, block_size, &num_read );
199 if ( rc == 0 )
200 {
201 pos += num_read;
202 total_read += num_read;
203 }
204 }
205 free( buffer );
206 }
207 return rc;
208 }
209
210
read_all(const KFile * src,size_t block_size)211 static rc_t read_all( const KFile * src, size_t block_size )
212 {
213 rc_t rc = 0;
214 uint8_t * buffer = ( uint8_t * )malloc( block_size );
215 if ( buffer == NULL )
216 rc = RC ( rcRuntime, rcBuffer, rcConstructing, rcMemory, rcExhausted );
217 else
218 {
219 uint64_t pos = 0;
220 size_t num_read = 1;
221 while ( rc == 0 && num_read > 0 )
222 {
223 rc = KFileReadAll ( src, pos, buffer, block_size, &num_read );
224 if ( rc == 0 ) pos += num_read;
225 }
226 free( buffer );
227 }
228 return rc;
229 }
230
231
232 // we create a data-file that we want to wrap ....
prepare_cachetee_tests(void)233 static rc_t prepare_cachetee_tests( void )
234 {
235 rc_t rc = create_random_file( DATAFILE, DATAFILESIZE );
236 remove_file( CACHEFILE );
237 remove_file( CACHEFILE1 );
238 return rc;
239 }
240
241 // we remove everything that was created ...
finish_cachetee_tests(void)242 static void finish_cachetee_tests( void )
243 {
244 remove_file( DATAFILE );
245 remove_file( CACHEFILE );
246 remove_file( CACHEFILE1 );
247 }
248
249 //////////////////////////////////////////// Test-cases
250
TEST_CASE(CacheTee2_Basic)251 TEST_CASE( CacheTee2_Basic )
252 {
253 KOutMsg( "Test: CacheTee2_Basic\n" );
254
255 KDirectory * dir;
256 REQUIRE_RC( KDirectoryNativeDir( &dir ) );
257
258 const KFile * org;
259 REQUIRE_RC( KDirectoryOpenFileRead( dir, &org, "%s", DATAFILE ) );
260
261 const KFile * tee;
262 REQUIRE_RC( KDirectoryMakeCacheTee2 ( dir, &tee, org, BLOCKSIZE, "%s", CACHEFILE ) );
263
264 REQUIRE_RC( KFileRelease( tee ) );
265 REQUIRE_RC( KFileRelease( org ) );
266
267 const KFile * cache;
268 REQUIRE_RC( KDirectoryOpenFileRead( dir, &cache, "%s.cache", CACHEFILE ) );
269
270 bool is_complete;
271 REQUIRE_RC( IsCacheTee2FileComplete( cache, &is_complete ) );
272 REQUIRE( !is_complete );
273
274 float percent;
275 uint64_t bytes_in_cache;
276 REQUIRE_RC( GetCacheTee2FileCompleteness( cache, &percent, &bytes_in_cache ) );
277 REQUIRE( ( percent == 0.0 ) );
278 REQUIRE( ( bytes_in_cache == 0 ) );
279
280 REQUIRE_RC( KFileRelease( cache ) );
281 REQUIRE_RC( KDirectoryRelease( dir ) );
282 }
283
284
TEST_CASE(CacheTee2_Read)285 TEST_CASE( CacheTee2_Read )
286 {
287 KOutMsg( "Test: CacheTee2_Read\n" );
288
289 KDirectory * dir;
290 REQUIRE_RC( KDirectoryNativeDir( &dir ) );
291
292 const KFile * org;
293 REQUIRE_RC( KDirectoryOpenFileRead( dir, &org, "%s", DATAFILE ) );
294
295 const KFile * tee;
296 REQUIRE_RC( KDirectoryMakeCacheTee2 ( dir, &tee, org, BLOCKSIZE, "%s", CACHEFILE ) );
297
298
299 REQUIRE_RC( compare_file_content( org, tee, 0, 100 ) ); // small read at pos zero
300 REQUIRE_RC( compare_file_content( org, tee, 10, 100 ) ); // small read at pos 10
301
302 REQUIRE_RC( compare_file_content( org, tee, 0, 100 ) ); // do it again, now from the cache
303 REQUIRE_RC( compare_file_content( org, tee, 10, 100 ) );
304
305 REQUIRE_RC( compare_file_content( org, tee, 1024 * BLOCKSIZE, 100 ) ); // small read at block boundary
306 REQUIRE_RC( compare_file_content( org, tee, 1024 * BLOCKSIZE, 100 ) ); // do it again, now from cache
307
308 REQUIRE_RC( compare_file_content( org, tee, BLOCKSIZE / 2, BLOCKSIZE ) ); // span 2 blocks
309 REQUIRE_RC( compare_file_content( org, tee, BLOCKSIZE / 2, BLOCKSIZE ) ); // do it again, now from cache
310
311 REQUIRE_RC( compare_file_content( org, tee, BLOCKSIZE / 2, BLOCKSIZE * 5 + 100 ) ); // span 5 blocks
312 REQUIRE_RC( compare_file_content( org, tee, BLOCKSIZE / 2, BLOCKSIZE * 5 + 100 ) ); // do it again, now from cache
313
314 REQUIRE_RC( compare_file_content( org, tee, 100, 1024 * BLOCKSIZE + 500 ) ); // large read crossing block boundary
315
316 REQUIRE_RC( compare_file_content( org, tee, 200, 1024 * BLOCKSIZE * 3 ) ); // very large read at pos 200
317 REQUIRE_RC( compare_file_content( org, tee, 1024 * BLOCKSIZE * 2 + 10, 100 ) ); // small read after block boundary
318 REQUIRE_RC( compare_file_content( org, tee, 1024 * BLOCKSIZE * 2 - 10, 100 ) ); // small read crossing block boundary
319
320 REQUIRE_RC( compare_file_content( org, tee, DATAFILESIZE - 100, 300 ) ); // small read crossing EOF
321 REQUIRE_RC( compare_file_content( org, tee, DATAFILESIZE - 100, BLOCKSIZE * 10 ) ); // big read crossing EOF
322 REQUIRE_RC( compare_file_content( org, tee, DATAFILESIZE - 10000, 10000 ) ); // big read right at EOF
323
324 REQUIRE_RC( KFileRelease( tee ) );
325 REQUIRE_RC( KFileRelease( org ) );
326 REQUIRE_RC( KDirectoryRelease( dir ) );
327 }
328
329
TEST_CASE(CacheTee2_Promoting)330 TEST_CASE( CacheTee2_Promoting )
331 {
332 KOutMsg( "Test: CacheTee2_Promoting\n" );
333
334 remove_file( CACHEFILE ); // to start with a clean slate on caching...
335 remove_file( CACHEFILE1 );
336
337 KDirectory * dir;
338 REQUIRE_RC( KDirectoryNativeDir( &dir ) );
339
340 const KFile * org;
341 REQUIRE_RC( KDirectoryOpenFileRead( dir, &org, "%s", DATAFILE ) );
342
343 const KFile * tee;
344 REQUIRE_RC( KDirectoryMakeCacheTee2 ( dir, &tee, org, BLOCKSIZE, "%s", CACHEFILE ) );
345
346 REQUIRE_RC( read_partial( tee, 1024 * 32, DATAFILESIZE / 2 ) );
347 REQUIRE_RC( KFileRelease( tee ) );
348
349 const KFile * cache;
350 REQUIRE_RC( KDirectoryOpenFileRead( dir, &cache, "%s.cache", CACHEFILE ) );
351
352 bool is_complete;
353 REQUIRE_RC( IsCacheTee2FileComplete( cache, &is_complete ) );
354 REQUIRE( !is_complete );
355
356 float percent;
357 REQUIRE_RC( GetCacheTee2FileCompleteness( cache, &percent, NULL ) );
358 REQUIRE( ( percent >= 50.0 ) );
359
360 REQUIRE_RC( KFileRelease( cache ) );
361
362 remove_file( CACHEFILE1 );
363
364 REQUIRE_RC( KDirectoryMakeCacheTee2 ( dir, &tee, org, BLOCKSIZE, "%s", CACHEFILE ) );
365 REQUIRE_RC( read_all( tee, 1024 * 32 ) ); // this should trigger the promotion of the cache file from cache.dat.cache to cache.dat
366 REQUIRE_RC( KFileRelease( tee ) );
367
368 REQUIRE_RC( KDirectoryOpenFileRead( dir, &cache, "%s", CACHEFILE ) ); // if we can open this file then, it was promoted...
369 REQUIRE_RC( KFileRelease( cache ) );
370
371 REQUIRE_RC( KFileRelease( org ) );
372 REQUIRE_RC( KDirectoryRelease( dir ) );
373 }
374
cache_access(int tid,int num_threads,const KFile * origfile,const KFile * cacheteefile)375 static rc_t cache_access( int tid, int num_threads, const KFile * origfile, const KFile * cacheteefile )
376 {
377 rc_t rc;
378 int i;
379 const int num_chunks = 256;
380 int chunk_pos[ num_chunks ];
381 int data_size = ( DATAFILESIZE / num_threads );
382 int data_offset = data_size * ( tid - 1 );
383 int chunk_size;
384 // last thread should read all remaining bytes
385 if ( tid == num_threads )
386 data_size = ( DATAFILESIZE - data_offset );
387 chunk_size = ( data_size / num_chunks );
388
389 for ( i = 0; i < num_chunks; ++i )
390 {
391 chunk_pos[ i ] = i * chunk_size + data_offset;
392 }
393 std::random_shuffle( &chunk_pos[ 0 ], &chunk_pos[ num_chunks ] );
394 for ( i = 0; i < num_chunks; ++i )
395 {
396 rc = compare_file_content( origfile, cacheteefile, chunk_pos[ i ], chunk_size );
397 if ( rc != 0 )
398 break;
399 }
400 if ( rc == 0 )
401 KOutMsg( "Test: Thread #%d OK\n", tid );
402 else
403 KOutMsg( "Test: Thread #%d failed\n", tid );
404
405 return rc;
406 }
407
408 struct ThreadData
409 {
410 int tid;
411 int num_threads;
412 const KFile * origfile; // optional
413 const KFile * cacheteefile; // optional
414 };
415
thread_func(const KThread * self,void * data)416 static rc_t CC thread_func( const KThread *self, void *data )
417 {
418 ThreadData * td = ( ThreadData * ) data;
419 if ( td -> cacheteefile == NULL || td -> origfile == NULL )
420 {
421 KDirectory * dir;
422 rc_t rc = KDirectoryNativeDir( &dir );
423 if ( rc == 0 )
424 {
425 const KFile * org;
426 rc = KDirectoryOpenFileRead( dir, &org, "%s", DATAFILE );
427 if ( rc == 0 )
428 {
429 const KFile * tee;
430 rc = KDirectoryMakeCacheTee2 ( dir, &tee, org, BLOCKSIZE, "%s", CACHEFILE );
431 if ( rc == 0 )
432 {
433 rc = cache_access( td -> tid, td -> num_threads, org, tee );
434 KFileRelease( tee );
435 }
436 KFileRelease( org );
437 }
438 KDirectoryRelease( dir );
439 }
440 return rc;
441 }
442 return cache_access( td -> tid, td -> num_threads, td -> origfile, td -> cacheteefile );
443 }
444
TEST_CASE(CacheTee2_Multiple_Users_Multiple_Inst)445 TEST_CASE( CacheTee2_Multiple_Users_Multiple_Inst )
446 {
447 KOutMsg( "Test: CacheTee2_Multiple_Users_Multiple_Inst\n" );
448 remove_file( CACHEFILE ); // to start with a clean slate on caching...
449 remove_file( CACHEFILE1 );
450
451 const int n = 8;
452 KThread * t [ n ];
453 ThreadData td [ n ];
454 rc_t rc = 0;
455 for ( int i = 0; i < n && rc == 0; ++i )
456 {
457 td[ i ].tid = i + 1;
458 td[ i ].num_threads = n;
459 td[ i ].origfile = NULL;
460 td[ i ].cacheteefile = NULL;
461 rc = KThreadMake ( &( t[ i ] ), thread_func, &( td[ i ] ) );
462 REQUIRE_RC( rc );
463 }
464
465 for ( int i = 0; i < n && rc == 0; ++i )
466 {
467 rc_t rc_thread;
468 rc = KThreadWait ( t[ i ], &rc_thread );
469 REQUIRE_RC( rc );
470 REQUIRE_RC( rc_thread );
471 REQUIRE_RC( KThreadRelease ( t[ i ] ) );
472 }
473 }
474
TEST_CASE(CacheTee2_Multiple_Users_Single_Inst)475 TEST_CASE( CacheTee2_Multiple_Users_Single_Inst )
476 {
477 KOutMsg( "Test: CacheTee2_Multiple_Users_Single_Inst\n" );
478 remove_file( CACHEFILE ); // to start with a clean slate on caching...
479 remove_file( CACHEFILE1 );
480
481 KDirectory * dir;
482 const KFile * org;
483 const KFile * tee;
484 REQUIRE_RC( KDirectoryNativeDir( &dir ) );
485 REQUIRE_RC( KDirectoryOpenFileRead( dir, &org, "%s", DATAFILE ) );
486 REQUIRE_RC( KDirectoryMakeCacheTee2( dir, &tee, org, BLOCKSIZE, "%s", CACHEFILE ) );
487
488 const int n = 8;
489 KThread *t [ n ];
490 ThreadData td [ n ];
491 rc_t rc = 0;
492 for ( int i = 0; i < n && rc == 0; ++i )
493 {
494 td[ i ].tid = i + 1;
495 td[ i ].num_threads = n;
496 td[ i ].origfile = org;
497 td[ i ].cacheteefile = tee;
498 rc = KThreadMake ( &( t[ i ] ), thread_func, &( td[ i ] ) );
499 REQUIRE_RC( rc );
500 }
501
502 for ( int i = 0; i < n && rc == 0; ++i )
503 {
504 rc_t rc_thread;
505 rc = KThreadWait ( t[ i ], &rc_thread );
506 REQUIRE_RC( rc );
507 REQUIRE_RC( rc_thread );
508 REQUIRE_RC( KThreadRelease ( t[ i ] ) );
509 }
510
511 REQUIRE_RC( KFileRelease( tee ) );
512 REQUIRE_RC( KFileRelease( org ) );
513 REQUIRE_RC( KDirectoryRelease( dir ) );
514 }
515
516 // TODO: fix, this does not work on Windows
517 #if !defined(WINDOWS) && !defined(_WIN32) && !defined(MAC)
TEST_CASE(CacheTee2_ReadOnly)518 TEST_CASE( CacheTee2_ReadOnly )
519 {
520 KOutMsg( "Test: CacheTee2_ReadOnly %s\n", CACHEFILE1 );
521 remove_file( CACHEFILE ); // to start with a clean slate on caching...
522 remove_file( CACHEFILE1 );
523
524 KDirectory * dir;
525 REQUIRE_RC( KDirectoryNativeDir( &dir ) );
526
527 const KFile * org;
528 REQUIRE_RC( KDirectoryOpenFileRead( dir, &org, "%s", DATAFILE ) );
529
530 /* make a fresh cache-tee and read 100 bytes from it... */
531 const KFile * tee;
532 REQUIRE_RC( KDirectoryMakeCacheTee2( dir, &tee, org, BLOCKSIZE, "%s", CACHEFILE ) );
533 REQUIRE_RC( read_partial( tee, 100, 100 ) );
534 REQUIRE_RC( KFileRelease( tee ) );
535
536 REQUIRE_RC( KDirectorySetAccess ( dir, false, 0, 0222, "%s", CACHEFILE1 ) );
537
538 /* make a second cache-tee and read all from it... */
539 REQUIRE_RC( KDirectoryMakeCacheTee2( dir, &tee, org, BLOCKSIZE, "%s", CACHEFILE ) );
540 REQUIRE_RC( read_all( tee, 1024 * 32 ) );
541 REQUIRE_RC( KFileRelease( tee ) );
542
543 /* we read all from the tee-file that should have promoted it on Release,
544 but we made it read only before the creation of the 2nd tee-file
545 because of that it should not be promoted and not complete */
546
547 const KFile * cache;
548 REQUIRE_RC_FAIL( KDirectoryOpenFileRead( dir, &cache, "%s", CACHEFILE ) );
549 REQUIRE_RC( KDirectoryOpenFileRead( dir, &cache, "%s", CACHEFILE1 ) );
550
551 /* make a second cache-tee and read all from it... */
552 REQUIRE_RC( KDirectoryMakeCacheTee2 ( dir, &tee, org, BLOCKSIZE, "%s", CACHEFILE ) );
553
554 REQUIRE_RC( read_all( tee, 1024 * 32 ) );
555 REQUIRE_RC( KFileRelease( tee ) );
556
557 /* we read all from the tee-file that should have promoted it on Release,
558 but we made it read only before the creation of the 2nd tee-file
559 because of that it should not be promoted and not complete */
560
561 REQUIRE_RC_FAIL( KDirectoryOpenFileRead( dir, &cache, "%s", CACHEFILE ) );
562 REQUIRE_RC( KDirectoryOpenFileRead( dir, &cache, "%s", CACHEFILE1 ) );
563
564 bool is_complete;
565 REQUIRE_RC( IsCacheTee2FileComplete( cache, &is_complete ) );
566 REQUIRE( !is_complete );
567
568 REQUIRE_RC( KFileRelease( cache ) );
569 REQUIRE_RC( KFileRelease( org ) );
570 REQUIRE_RC( KDirectoryRelease( dir ) );
571
572 }
573 #endif
574
TEST_CASE(CacheTee2_Multiple_Users_with_Promoting)575 TEST_CASE( CacheTee2_Multiple_Users_with_Promoting )
576 {
577 KOutMsg( "Test: CacheTee2_Multiple_Users_with_Promoting\n" );
578 remove_file( CACHEFILE ); // to start with a clean slate on caching...
579 remove_file( CACHEFILE1 );
580
581 KDirectory * dir;
582 REQUIRE_RC( KDirectoryNativeDir( &dir ) );
583
584 const KFile * org;
585 REQUIRE_RC( KDirectoryOpenFileRead( dir, &org, "%s", DATAFILE ) );
586
587 /* make 2 cache-tee's */
588 const KFile * tee1;
589 REQUIRE_RC( KDirectoryMakeCacheTee2 ( dir, &tee1, org, BLOCKSIZE, "%s", CACHEFILE ) );
590 const KFile * tee2;
591 REQUIRE_RC( KDirectoryMakeCacheTee2 ( dir, &tee2, org, BLOCKSIZE, "%s", CACHEFILE ) );
592
593 /* read all from tee1 and release it, that will trigger promotion */
594 REQUIRE_RC( read_all( tee1, 1024 * 32 ) );
595 REQUIRE_RC( KFileRelease( tee1 ) );
596
597 /* read a little bit from tee2 and release it, will it corrupt the cache? */
598 REQUIRE_RC( read_partial( tee2, 100, 100 ) );
599 REQUIRE_RC( KFileRelease( tee2 ) );
600
601 /* the ( newly ) promoted cache file has to be not corrupt */
602 REQUIRE_RC( KDirectoryMakeCacheTee2 ( dir, &tee1, org, BLOCKSIZE, "%s", CACHEFILE ) );
603 REQUIRE_RC( KFileRelease( tee1 ) );
604
605 /* the .cache - file has to be gone */
606 #if !defined(WINDOWS) && !defined(_WIN32)
607 uint32_t pt = KDirectoryPathType ( dir, "%s", CACHEFILE1 );
608 REQUIRE( pt == kptNotFound );
609 #endif
610
611 REQUIRE_RC( KFileRelease( org ) );
612 REQUIRE_RC( KDirectoryRelease( dir ) );
613 }
614
615 //////////////////////////////////////////// Main
616 extern "C"
617 {
618
619 #include <kapp/args.h>
620 #include <kfg/config.h>
621
KAppVersion(void)622 ver_t CC KAppVersion ( void )
623 {
624 return 0x1000000;
625 }
626
UsageSummary(const char * progname)627 rc_t CC UsageSummary ( const char * progname )
628 {
629 return 0;
630 }
631
Usage(const Args * args)632 rc_t CC Usage ( const Args * args )
633 {
634 return 0;
635 }
636
637 const char UsageDefaultName[] = "cachetee2-test";
638
KMain(int argc,char * argv[])639 rc_t CC KMain ( int argc, char *argv [] )
640 {
641 srand( time( NULL ) );
642 KConfigDisableUserSettings();
643 rc_t rc = prepare_cachetee_tests();
644 if ( rc == 0 )
645 {
646 rc = CacheTeeTests( argc, argv );
647 finish_cachetee_tests();
648 }
649 KOutMsg( "and the result is: %R\n", rc );
650 return rc;
651 }
652
653 }
654