a413465fb4130a880ab5db49570568e302f5de2a
[platform/core/uifw/dali-adaptor.git] / platform-abstractions / slp / data-cache / tests / data-cache-debug.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include "data-cache-debug.h"
20
21 #ifdef DATA_CACHE_DEBUG
22
23 // INTERNAL INCLUDES
24 #include "../data-cache-io.h"
25 #include "../data-compression.h"
26 #include <dali/integration-api/debug.h>
27
28 // EXTERNAL INCLUDES
29 #include <boost/thread.hpp>
30 #include <set>
31
32 namespace Dali
33 {
34
35 namespace SlpPlatform
36 {
37
38 namespace DataCacheIo
39 {
40
41 namespace
42 {
43 const char * const DALI_USER_FONT_CACHE_PATH( DALI_USER_FONT_CACHE_DIR );
44 const std::string TEST_FILE("test-file");
45
46 const unsigned int NUMBER_TEST_ITERATIONS(25000);               ///< number of test iterations to perform per thread
47 const bool RANDOM_DATA_SIZE(true);                              ///< whether to use random data sizes (up to DATA_SIZE)
48 const unsigned int MAX_KEY_VALUE(200000);                       ////< number key index value
49 const unsigned int DATA_SIZE(64);                               ///< maximum data size
50 const unsigned int MAX_NUMBER_ENTRIES( 200000 );                ///< Maximum number of entries size
51 const unsigned int ENTRIES_TO_READ_WRITE(1);                    ///< maximum number of entries to read/write in one API call
52 const Platform::DataCache::CompressionMode COMPRESSION_MODE(  Platform::DataCache::RUN_LENGTH_ENCODING); //or COMPRESSION_OFF );
53
54 /**
55  * Fills the key vector with random keys between 0 and MAX_KEY_VALUE
56  */
57 void FillVectorWithRandomKeys( Platform::DataCache::KeyVector& keyVector)
58 {
59   // make sure if we are setup to write 20 key/data pairs in a single API call
60   // the we have at least 20  unique keys
61   DALI_ASSERT_ALWAYS( MAX_KEY_VALUE > ENTRIES_TO_READ_WRITE);
62
63   // create a set of unique keys, then insert them in to the key vector
64   typedef std::set< Platform::DataCache::DataKey > KeySet;
65   KeySet uniqueKeys;
66   while( uniqueKeys.size() != keyVector.size() )
67   {
68     uniqueKeys.insert(  rand() % MAX_KEY_VALUE );
69   }
70   int i(0);
71   for( KeySet::const_iterator iter =  uniqueKeys.begin(), endIter = uniqueKeys.end(); iter != endIter; ++iter )
72   {
73     keyVector[i] =  (*iter);
74     i++;
75   }
76 }
77
78 /**
79  * Fill the data vector with random data
80  */
81 void FillVectorWithRandomData( Platform::DataCache::DataVector& dataVector)
82 {
83   for( unsigned int i = 0; i < dataVector.size(); i++ )
84   {
85     unsigned int length( DATA_SIZE );
86     if( RANDOM_DATA_SIZE )
87     {
88      length =  1 + rand() % DATA_SIZE;
89     }
90     unsigned char* data =  new unsigned char[length];
91     dataVector[i].SetData( data, length );
92   }
93 }
94
95 /**
96  * delete the data vector, and check the exists flag is correct
97  */
98 void DeleteData(Platform::DataCache::DataVector& dataVector )
99 {
100   for( unsigned int i = 0; i < dataVector.size(); i++ )
101   {
102     Platform::DataCache::Data& data( dataVector[i] );
103     // check the exists flag
104     if (data.data && !data.exists)
105     {
106       DALI_ASSERT_ALWAYS(0 && "data exist flag wrong #1 ");
107     }
108     if (!data.data && data.exists)
109     {
110       DALI_ASSERT_ALWAYS(0 && "data exist flag wrong #2 ");
111     }
112     if (data.data)
113     {
114       delete []data.data;
115     }
116   }
117 }
118
119 /**
120  * read the number of entries, and get the entry list
121  */
122 unsigned int CheckNumberEntries(  FILE* indexFile, KeyMeta** keyMeta  )
123 {
124   unsigned int numberEntries;
125
126   bool ok = ReadNumberEntries( indexFile, numberEntries );
127   if( !ok )
128   {
129     // ReadEntries will log an error
130     DALI_ASSERT_ALWAYS(0);
131   }
132   // Check there is no data after the header, if there are no entries in the index file
133   if( numberEntries == 0 )
134   {
135     if( fgetc( indexFile ) != EOF)
136     {
137       // data index is corrupt, it contains more data than are recorded in numberEntries field
138       DALI_LOG_ERROR("index file has zero entries, but contains data after header\n");
139       DALI_ASSERT_ALWAYS(0);
140     }
141     return 0;
142   }
143
144   // allocate an array of  ( key | offset ) structures
145   *keyMeta = new  DataCacheIo::KeyMeta[ numberEntries ];
146
147   // read the index table in to memory
148   ok = ReadEntries(indexFile, *keyMeta, 0, numberEntries);
149   if( !ok )
150   {
151     // ReadEntries will log an error
152     DALI_ASSERT_ALWAYS(0);
153   }
154   // check there's no data at the end of the file
155   if( fgetc( indexFile ) != EOF)
156   {
157     // data index is corrupt, it contains more entries than are recorded in numberEntries field
158     DALI_LOG_ERROR("index has  more entries than recorded\n");
159     DALI_ASSERT_ALWAYS(0);
160   }
161   return numberEntries;
162 }
163
164 unsigned int GetMaxDataSizeOnFile( unsigned int dataSize )
165 {
166   if( COMPRESSION_MODE == Platform::DataCache::RUN_LENGTH_ENCODING )
167   {
168     return DataCompression::GetMaximumRleCompressedSize(  dataSize );
169   }
170   return dataSize;
171 }
172
173 /**
174  * Checks every single entry in the data file.
175  * This is slow, it should not be performed unless debugging.
176  */
177 void FullFileCheck(  FILE* indexFile,
178                      FILE* dataFile,
179                      unsigned int maxDataSize)
180 {
181   //  Read entries from index file and make sure the file size is correct
182   DataCacheIo::KeyMeta* keyMeta(NULL);
183   unsigned int numberEntries = CheckNumberEntries( indexFile, &keyMeta );
184   unsigned int maxDataSizeOnFile = GetMaxDataSizeOnFile( maxDataSize );
185
186   // Allocate the file buffer for reading the data, and a decode buffer if the data is compressed
187   unsigned char* fileDataBuffer = new unsigned char[ maxDataSizeOnFile ];
188   unsigned char* decodeBuffer = new unsigned char[ maxDataSize ];
189
190   // For each entry, check the data is valid in the
191   unsigned int previousOffset = GetHeaderSize();
192
193   std::set< Dali::Platform::DataCache::DataKey  > KeyLookup;
194
195   for(unsigned int i = 0; i < numberEntries; ++i )
196   {
197     Dali::Platform::DataCache::DataKey key( keyMeta[i].mKey );
198     Dali::Platform::DataCache::Data data;
199
200     // check for duplicate keys
201     if( KeyLookup.find(key) != KeyLookup.end() )
202     {
203       printf(" Duplicate Key Found %d \n",key);
204       DALI_ASSERT_ALWAYS(0 );
205     }
206     KeyLookup.insert(key);
207
208     unsigned int offset( keyMeta[i].mOffset);
209
210     // ensure the offset of each entry is correct
211     DALI_ASSERT_ALWAYS( previousOffset == offset);
212
213     bool ok = ReadData( dataFile, offset, key, data, fileDataBuffer, maxDataSizeOnFile );
214     if( !ok )
215     {
216       DALI_ASSERT_ALWAYS(0 && "DataCacheIO::ReadData failed");
217     }
218
219     unsigned int dataLengthOnFile = data.length;
220
221     if( COMPRESSION_MODE == Platform::DataCache::RUN_LENGTH_ENCODING )
222     {
223       // try and de-compress it
224       std::size_t decodedSize;
225       bool ok = DataCompression::DecodeRle( fileDataBuffer, data.length, decodeBuffer, maxDataSize, decodedSize);
226       DALI_ASSERT_ALWAYS( ok && " DataCompression::DecodeRle failed");
227       data.length = decodedSize;
228     }
229
230    // un-comment for list of key / data entries
231    // printf(" key = %d, length = %d \n",key,data.length);
232     previousOffset = offset + dataLengthOnFile + 8; /* 8 = key + length field */
233   }
234   delete []keyMeta;
235   delete []decodeBuffer;
236   delete []fileDataBuffer;
237 }
238
239 void ClearTestFiles( const std::string& indexFileName, const std::string& dataFileName )
240 {
241   FILE* dataFile = DataCacheIo::OpenFile( dataFileName, DataCacheIo::DATA_FILE, DataCacheIo::NO_LOCK, DataCacheIo::READ_WRITE, DataCacheIo::CREATE_IF_MISSING );
242   FILE* indexFile = DataCacheIo::OpenFile( indexFileName, DataCacheIo::INDEX_FILE, DataCacheIo::NO_LOCK, DataCacheIo::READ_WRITE, DataCacheIo::CREATE_IF_MISSING);
243
244   ReCreateFiles( indexFile, dataFile, COMPRESSION_MODE);
245
246   fclose( dataFile );
247   fclose( indexFile );
248 }
249
250 } // unnamed name space
251
252 void DataCacheStressTest(void)
253 {
254   printf("thread started \n");
255
256   static unsigned int apiCalls(0);
257   std::string path(DALI_USER_FONT_CACHE_PATH);
258   std::string file = path + TEST_FILE ;
259
260   Platform::DataCache* cache =  Platform::DataCache::New(   Platform::DataCache::READ_WRITE,
261                                           COMPRESSION_MODE,
262                                           file,
263                                           DATA_SIZE,
264                                           MAX_NUMBER_ENTRIES);
265
266
267   for(unsigned int i = 0; i < NUMBER_TEST_ITERATIONS; ++i )
268   {
269     apiCalls+=2;
270     if( i%50 == 0)
271     {
272       printf("DataCache Add() & Find() calls: %d \r",apiCalls);
273     }
274
275     // Read a random array of key/data pairs
276     Platform::DataCache::KeyVector keyVector;
277     Platform::DataCache::DataVector dataVector;
278
279     unsigned int arraySize  =  ENTRIES_TO_READ_WRITE;
280     keyVector.resize( arraySize );
281
282     // read a random entry
283     FillVectorWithRandomKeys( keyVector );
284     cache->Find( keyVector, dataVector);
285     DeleteData( dataVector);
286
287     // Write a random entry
288     FillVectorWithRandomKeys( keyVector );
289     FillVectorWithRandomData( dataVector );
290     cache->Add( keyVector, dataVector );
291     DeleteData(dataVector);
292   }
293   delete cache;
294 }
295
296 void ThreadedStressTest()
297 {
298   //  only allow test to run once
299   static int done(false);
300   if( done )
301   {
302     return;
303   }
304   done = true;
305
306   std::string path(DALI_USER_FONT_CACHE_PATH);
307   std::string dataFileName = path + TEST_FILE + ".data";
308   std::string indexFileName = path + TEST_FILE + ".index";
309
310   // Make sure the data files are empty to start with
311   ClearTestFiles( indexFileName  , dataFileName);
312
313
314   printf("____ DataCache Multi Thread Test Starting ____ \n");
315
316   boost::thread t1(DataCacheStressTest);
317   boost::thread t2(DataCacheStressTest);
318   boost::thread t3(DataCacheStressTest);
319   boost::thread t4(DataCacheStressTest);
320   boost::thread t5(DataCacheStressTest);
321   boost::thread t6(DataCacheStressTest);
322   boost::thread t7(DataCacheStressTest);
323   boost::thread t8(DataCacheStressTest);
324
325   t1.join();
326   t2.join();
327   t3.join();
328   t4.join();
329   t5.join();
330   t6.join();
331   t7.join();
332   t8.join();
333
334   // Check the data that was written is not corrupt
335
336   FILE* dataFile = OpenFile( dataFileName, DataCacheIo::DATA_FILE, DataCacheIo::NO_LOCK, DataCacheIo::READ_ONLY );
337   FILE* indexFile = OpenFile( indexFileName, DataCacheIo::INDEX_FILE, DataCacheIo::NO_LOCK, DataCacheIo::READ_ONLY );
338
339   FullFileCheck( indexFile, dataFile, DATA_SIZE );
340
341   fclose( dataFile );
342   fclose( indexFile );
343
344   printf("____ DataCache Multi Thread Test PASSED ____ \n");
345 }
346
347 } // namespace DataCacheIO
348
349 } // namespace SlpPlatform
350
351 } // namespace Dali
352 #endif // #ifdef DATA_CACHE_DEBUG