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 lru_cache in libs/kfs/poolpages.c
29 */
30
31 #include <ktst/unit_test.hpp>
32
33 #include <klib/out.h>
34 #include <klib/rc.h>
35 #include <klib/time.h>
36
37 #include <kfs/directory.h>
38 #include <kfs/file.h>
39 #include <kfs/rrcachedfile.h>
40
rand_32(size_t min,size_t max)41 static size_t rand_32( size_t min, size_t max )
42 {
43 double scaled = ( ( double )rand() / RAND_MAX );
44 return ( ( max - min + 1 ) * scaled ) + min;
45 }
46
fill_file_with_random_data(KFile * file,size_t file_size)47 static rc_t fill_file_with_random_data( KFile * file, size_t file_size )
48 {
49 rc_t rc = KFileSetSize( file, file_size );
50 if ( rc == 0 )
51 {
52 uint64_t pos = 0;
53 size_t total = 0;
54 while ( rc == 0 && total < file_size )
55 {
56 uint32_t data[ 512 ];
57 uint32_t i;
58 size_t to_write, num_writ;
59
60 for ( i = 0; i < 512; ++i ) data[ i ] = rand_32( 0, 0xFFFFFFFF - 1 );
61 to_write = ( file_size - total );
62 if ( to_write > sizeof data ) to_write = sizeof data;
63 rc = KFileWriteAll ( file, pos, data, to_write, &num_writ );
64 if ( rc == 0 )
65 {
66 pos += num_writ;
67 total += num_writ;
68 }
69 }
70 }
71 return rc;
72 }
73
report_diff(uint8_t * b1,uint8_t * b2,size_t n,uint32_t max_diffs)74 static void report_diff( uint8_t * b1, uint8_t * b2, size_t n, uint32_t max_diffs )
75 {
76 size_t i, d;
77 for ( i = 0, d = 0; i < n && d < max_diffs; ++i )
78 {
79 if ( b1[ i ] != b2[ i ] )
80 {
81 KOutMsg( "[ %.08d ] %.02X %.02X\n", i, b1[ i ], b2[ i ] );
82 d++;
83 }
84 }
85 }
86
compare_file_content(const KFile * file1,const KFile * file2,uint64_t pos,size_t bytes)87 static rc_t compare_file_content( const KFile * file1, const KFile * file2, uint64_t pos, size_t bytes )
88 {
89 rc_t rc = 0;
90 uint8_t * buffer1 = ( uint8_t * )malloc( bytes );
91 if ( buffer1 == NULL )
92 rc = RC ( rcRuntime, rcBuffer, rcConstructing, rcMemory, rcExhausted );
93 else
94 {
95 uint8_t * buffer2 = ( uint8_t * )malloc( bytes );
96 if ( buffer2 == NULL )
97 rc = RC ( rcRuntime, rcBuffer, rcConstructing, rcMemory, rcExhausted );
98 else
99 {
100 size_t num_read1;
101 rc = KFileReadAll ( file1, pos, buffer1, bytes, &num_read1 );
102 if ( rc == 0 )
103 {
104 size_t num_read2;
105 rc = KFileReadAll ( file2, pos, buffer2, bytes, &num_read2 );
106 if ( rc == 0 )
107 {
108 if ( num_read1 != num_read2 )
109 {
110 rc = RC ( rcRuntime, rcBuffer, rcReading, rcMemory, rcInvalid );
111 KOutMsg( "compare_file_content() requested:%lu, read 1:%lu, 2:%lu", bytes, num_read1, num_read2 );
112 }
113 else
114 {
115 int diff = memcmp( buffer1, buffer2, num_read1 );
116 if ( diff != 0 )
117 {
118 report_diff( buffer1, buffer2, num_read1, 20 );
119 rc = RC ( rcRuntime, rcBuffer, rcReading, rcMemory, rcCorrupt );
120 }
121 }
122 }
123 }
124 free( buffer2 );
125 }
126 free( buffer1 );
127 }
128 return rc;
129 }
130
seed_random_number_generator(void)131 void seed_random_number_generator( void )
132 {
133 KTime_t t = KTimeStamp (); /* klib/time.h */
134 srand( t );
135 }
136
137 TEST_SUITE( LRU_Cache_Test );
138
139 #define PAGE_SIZE 128 * 1024
140 #define PAGE_COUNT 1024
141
TEST_CASE(LRU_Cache_Test_Basic)142 TEST_CASE( LRU_Cache_Test_Basic )
143 {
144 KOutMsg( "Test: LRU-Cache-Test-Basic\n" );
145
146 REQUIRE_RC_FAIL( MakeRRCached ( NULL, NULL, 0, 0 ) );
147 REQUIRE_RC_FAIL( MakeRRCached ( NULL, NULL, PAGE_SIZE, PAGE_COUNT ) );
148 REQUIRE_RC_FAIL( MakeRRCached ( NULL, NULL, 0, PAGE_COUNT ) );
149 REQUIRE_RC_FAIL( MakeRRCached ( NULL, NULL, PAGE_SIZE, 0 ) );
150
151 KDirectory * dir;
152 REQUIRE_RC( KDirectoryNativeDir( &dir ) );
153
154 const char * filename = "./LRU_Cache_Test_Basic.txt";
155 KFile * file;
156
157 REQUIRE_RC( KDirectoryCreateFile ( dir, &file, false, 0664, kcmInit, filename ) );
158 REQUIRE_RC( fill_file_with_random_data( file, PAGE_SIZE * 2 ) );
159 KFileRelease( file );
160
161 const KFile * org;
162 REQUIRE_RC( KDirectoryOpenFileRead( dir, &org, "%s", filename ) );
163
164 REQUIRE_RC_FAIL( MakeRRCached ( NULL, org, 0, 0L ) );
165 REQUIRE_RC_FAIL( MakeRRCached ( NULL, org, PAGE_SIZE, PAGE_COUNT ) );
166 REQUIRE_RC_FAIL( MakeRRCached ( NULL, org, 0, PAGE_COUNT ) );
167 REQUIRE_RC_FAIL( MakeRRCached ( NULL, org, PAGE_SIZE, 0 ) );
168
169 const KFile * cache;
170 REQUIRE_RC_FAIL( MakeRRCached ( &cache, org, 0, 0 ) );
171 REQUIRE_RC_FAIL( MakeRRCached ( &cache, org, 0, PAGE_COUNT ) );
172 REQUIRE_RC_FAIL( MakeRRCached ( &cache, org, PAGE_SIZE, 0 ) );
173
174 REQUIRE_RC( MakeRRCached ( &cache, org, PAGE_SIZE, PAGE_COUNT ) );
175
176 KFileRelease( org );
177 KFileRelease( cache );
178 KDirectoryRemove ( dir, true, "%s", filename );
179
180 REQUIRE_RC( KDirectoryRelease( dir ) );
181 }
182
183 typedef struct events
184 {
185 uint32_t requests, found, enter, discard, failed;
186 } events;
187
on_event(void * data,enum cache_event event,uint64_t pos,size_t len,uint32_t block_nr)188 void on_event( void * data, enum cache_event event, uint64_t pos, size_t len, uint32_t block_nr )
189 {
190 events * ev = ( events * )data;
191 if ( ev != NULL )
192 {
193 switch ( event )
194 {
195 case CE_REQUEST : ev -> requests++;
196 //KOutMsg( "R %lx.%lx (%u)\n", pos, len, block_nr );
197 break;
198
199 case CE_FOUND : ev -> found++;
200 //KOutMsg( "F %lx.%lx (%u)\n", pos, len, block_nr );
201 break;
202
203 case CE_ENTER : ev -> enter++;
204 //KOutMsg( "E %lx.%lx (%u)\n", pos, len, block_nr );
205 break;
206
207 case CE_DISCARD : ev -> discard++;
208 //KOutMsg( "D %lx.%lx (%u)\n", pos, len, block_nr );
209 break;
210
211 case CE_FAILED : ev -> failed++; break;
212 }
213 }
214 }
215
print_events(events * events)216 void print_events( events * events )
217 {
218 KOutMsg( "req = %u\n", events -> requests );
219 KOutMsg( "found = %u\n", events -> found );
220 KOutMsg( "enter = %u\n", events -> enter );
221 KOutMsg( "discard = %u\n", events -> discard );
222 KOutMsg( "failed = %u\n", events -> failed );
223 }
224
TEST_CASE(LRU_Cache_Test_Linear_Reading)225 TEST_CASE( LRU_Cache_Test_Linear_Reading )
226 {
227 KOutMsg( "Test: LRU-Cache-Test-Linear-Reading\n" );
228
229 KDirectory * dir;
230 REQUIRE_RC( KDirectoryNativeDir( &dir ) );
231
232 const char * filename = "./LRU_Cache_Test_Linear_Reading.txt";
233 KFile * file;
234
235 REQUIRE_RC( KDirectoryCreateFile ( dir, &file, false, 0664, kcmInit, filename ) );
236 REQUIRE_RC( fill_file_with_random_data( file, PAGE_SIZE * 10 ) );
237 REQUIRE_RC( KFileRelease( file ) );
238
239 const KFile * org;
240 REQUIRE_RC( KDirectoryOpenFileRead( dir, &org, "%s", filename ) );
241
242 const KFile * cache;
243 REQUIRE_RC( MakeRRCached ( &cache, org, 64 * 1024, 22 ) );
244
245 uint64_t file_size;
246 REQUIRE_RC( KFileSize ( cache, &file_size ) );
247 REQUIRE( ( file_size == ( PAGE_SIZE * 10 ) ) );
248
249 events events;
250 memset( &events, 0, sizeof events );
251 REQUIRE_RC( SetRRCachedEventHandler( cache, &events, on_event ) );
252
253 REQUIRE_RC( compare_file_content( org, cache, 0, file_size ) );
254 REQUIRE_RC( compare_file_content( org, cache, 0, file_size ) );
255
256 REQUIRE_RC( KFileRelease( cache ) );
257 REQUIRE_RC( KFileRelease( org ) );
258
259 #if _DEBUGGING
260 // print_events( &events );
261 REQUIRE( events . requests == 21 );
262 REQUIRE( events . found == 20 );
263 REQUIRE( events . enter == 20 );
264 REQUIRE( events . discard == 0 );
265 REQUIRE( events . failed == 0 );
266 #endif
267
268 REQUIRE_RC( KDirectoryOpenFileRead( dir, &org, "%s", filename ) );
269 REQUIRE_RC( MakeRRCached ( &cache, org, 64 * 1024, 20 ) );
270 memset( &events, 0, sizeof events );
271 REQUIRE_RC( SetRRCachedEventHandler( cache, &events, on_event ) );
272
273 REQUIRE_RC( compare_file_content( org, cache, 0, file_size ) );
274 REQUIRE_RC( compare_file_content( org, cache, 0, file_size ) );
275
276 #if _DEBUGGING
277 //print_events( &events );
278 REQUIRE( events . requests == 21 );
279 REQUIRE( events . found == 20 );
280 REQUIRE( events . enter == 20 );
281 REQUIRE( events . discard == 0 );
282 REQUIRE( events . failed == 0 );
283 #endif
284
285 REQUIRE_RC( KFileRelease( cache ) );
286 REQUIRE_RC( KFileRelease( org ) );
287 REQUIRE_RC( KDirectoryRemove ( dir, true, "%s", filename ) );
288 REQUIRE_RC( KDirectoryRelease( dir ) );
289 }
290
TEST_CASE(LRU_Cache_Test_Random_Reading)291 TEST_CASE( LRU_Cache_Test_Random_Reading )
292 {
293 KOutMsg( "Test: LRU-Cache-Test-Random-Reading\n" );
294
295 KDirectory * dir;
296 REQUIRE_RC( KDirectoryNativeDir( &dir ) );
297
298 const char * filename = "./LRU_Cache_Test_Random_Reading.txt";
299 KFile * file;
300
301 size_t file_size = PAGE_SIZE * 10;
302 REQUIRE_RC( KDirectoryCreateFile ( dir, &file, false, 0664, kcmInit, filename ) );
303 REQUIRE_RC( fill_file_with_random_data( file, file_size ) );
304 REQUIRE_RC( KFileRelease( file ) );
305
306 const KFile * org;
307 REQUIRE_RC( KDirectoryOpenFileRead( dir, &org, "%s", filename ) );
308
309 uint32_t page_count = 20;
310 const KFile * cache;
311 REQUIRE_RC( MakeRRCached ( &cache, org, 64 * 1024, page_count ) );
312
313 events events;
314 memset( &events, 0, sizeof events );
315 REQUIRE_RC( SetRRCachedEventHandler( cache, &events, on_event ) );
316
317 uint32_t loops = 100000;
318 KOutMsg( "---testing %u loops\n", loops );
319 for ( uint32_t i = 0; i < loops; ++i )
320 {
321 size_t bsize = rand_32( 10, 50000 );
322 uint64_t pos = rand_32( 0, file_size - ( bsize + 1 ) );
323 rc_t rc = compare_file_content( org, cache, pos, bsize );
324 if ( rc != 0 )
325 KOutMsg( "Test: LRU-Cache-Test-Random-Reading in loop #%u : %lu.%lu = %R\n", i, pos, bsize, rc );
326 REQUIRE_RC( rc );
327 }
328
329 #if _DEBUGGING
330 //print_events( &events );
331 REQUIRE( events . requests >= loops );
332 REQUIRE( events . found >= loops );
333 REQUIRE( events . enter = page_count );
334 REQUIRE( events . failed == 0 );
335 #endif
336
337 REQUIRE_RC( KFileRelease( cache ) );
338 REQUIRE_RC( KFileRelease( org ) );
339 REQUIRE_RC( KDirectoryRemove ( dir, true, "%s", filename ) );
340 REQUIRE_RC( KDirectoryRelease( dir ) );
341 }
342
343 //////////////////////////////////////////// Main
344 extern "C"
345 {
346
347 #include <kapp/args.h>
348 #include <kfg/config.h>
349
KAppVersion(void)350 ver_t CC KAppVersion ( void )
351 {
352 return 0x1000000;
353 }
354
UsageSummary(const char * progname)355 rc_t CC UsageSummary ( const char * progname )
356 {
357 return 0;
358 }
359
Usage(const Args * args)360 rc_t CC Usage ( const Args * args )
361 {
362 return 0;
363 }
364
365 const char UsageDefaultName[] = "lru-cache-test";
366
KMain(int argc,char * argv[])367 rc_t CC KMain ( int argc, char *argv [] )
368 {
369 rc_t rc;
370 seed_random_number_generator();
371 rc = LRU_Cache_Test( argc, argv );
372 KOutMsg( "lru-cache-test : %R\n", rc );
373 return rc;
374 }
375
376 }
377