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