Merge "DALi Version 1.0.20" into tizen
[platform/core/uifw/dali-adaptor.git] / automated-tests / src / dali-platform-abstraction / utc-image-loading.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 #include <unistd.h>
19 #include <iostream>
20 #include <stdlib.h>
21 #include <dali/dali.h>
22 #include <dali-test-suite-utils.h>
23 #include "slp-platform-abstraction.h"
24 #include "resource-collector.h"
25
26 using namespace Dali;
27 using namespace Dali::Integration;
28 using namespace Dali::Internal::Platform;
29
30 namespace
31 {
32 /**
33  * The number of loads issued in test cases is a multiple of this. The higher it
34  * is, the more the tests stress the system but the longer they take to run.
35  * A value of 1000 is enough to make load tests take tens of seconds each
36  * on desktop. */
37 const unsigned NUM_LOAD_GROUPS_TO_ISSUE = 200;
38
39 /**
40  * The number of loads to issue when they will be cancelled.
41  * Cancelled loads are cheap so we do a lot.
42  */
43 const unsigned NUM_CANCELLED_LOAD_GROUPS_TO_ISSUE = NUM_LOAD_GROUPS_TO_ISSUE * 10;
44
45 /** The number of times to ask for resource load status. */
46 const unsigned MAX_NUM_RESOURCE_TRIES = 5;
47
48 /** Images that should load without issue. */
49 const char* const VALID_IMAGES[] = {
50   TEST_IMAGE_DIR "/frac.jpg",
51   TEST_IMAGE_DIR "/frac.24.bmp",
52   TEST_IMAGE_DIR "/frac.png",
53   TEST_IMAGE_DIR "/interlaced.gif",
54   TEST_IMAGE_DIR "/pattern.gif"
55 };
56 const unsigned NUM_VALID_IMAGES = sizeof(VALID_IMAGES) / sizeof(VALID_IMAGES[0]);
57
58 ///@ToDo: Add valid ktx, ico, and wbmp image examples.
59
60 /** Live platform abstraction recreated for each test case. */
61 Integration::PlatformAbstraction * gAbstraction = 0;
62
63 /** A variety of ImageAttributes to reach different code paths that have embedded code paths. */
64 std::vector<ImageAttributes> gCancelAttributes;
65
66 } // anon namespace
67
68 void utc_dali_loading_startup(void)
69 {
70   test_return_value = TET_UNDEF;
71   gAbstraction = CreatePlatformAbstraction();
72
73   // Setup some ImageAttributes to engage post-processing stages:
74
75   ImageAttributes scaleToFillAttributes;
76   scaleToFillAttributes.SetScalingMode( ImageAttributes::ScaleToFill );
77   scaleToFillAttributes.SetSize( 160, 120 );
78   gCancelAttributes.push_back( scaleToFillAttributes );
79
80   // Hit the derived dimensions code:
81   ImageAttributes scaleToFillAttributesDeriveWidth = scaleToFillAttributes;
82   scaleToFillAttributesDeriveWidth.SetSize( 0, 120 );
83   gCancelAttributes.push_back( scaleToFillAttributesDeriveWidth );
84
85   ImageAttributes scaleToFillAttributesDeriveHeight = scaleToFillAttributes;
86   scaleToFillAttributesDeriveHeight.SetSize( 160, 0 );
87   gCancelAttributes.push_back( scaleToFillAttributesDeriveHeight );
88
89   // Try to push a tall crop:
90   ImageAttributes scaleToFillAttributesTall = scaleToFillAttributes;
91   scaleToFillAttributesTall.SetSize( 160, 480 );
92   ImageAttributes scaleToFillAttributesTall2 = scaleToFillAttributes;
93   scaleToFillAttributesTall2.SetSize( 160, 509 );
94   ImageAttributes scaleToFillAttributesTall3 = scaleToFillAttributes;
95   scaleToFillAttributesTall3.SetSize( 37, 251 );
96   gCancelAttributes.push_back( scaleToFillAttributesTall );
97   gCancelAttributes.push_back( scaleToFillAttributesTall2 );
98   gCancelAttributes.push_back( scaleToFillAttributesTall3 );
99
100   // Try to push a wide crop:
101   ImageAttributes scaleToFillAttributesWide = scaleToFillAttributes;
102   scaleToFillAttributesWide.SetSize( 320, 60 );
103   ImageAttributes scaleToFillAttributesWide2 = scaleToFillAttributes;
104   scaleToFillAttributesWide2.SetSize( 317, 60 );
105   ImageAttributes scaleToFillAttributesWide3 = scaleToFillAttributes;
106   scaleToFillAttributesWide3.SetSize( 317, 53 );
107   gCancelAttributes.push_back( scaleToFillAttributesWide );
108   gCancelAttributes.push_back( scaleToFillAttributesWide2 );
109   gCancelAttributes.push_back( scaleToFillAttributesWide3 );
110
111   ImageAttributes shrinkToFitAttributes = scaleToFillAttributes;
112   shrinkToFitAttributes.SetScalingMode( ImageAttributes::ShrinkToFit );
113   gCancelAttributes.push_back( shrinkToFitAttributes );
114
115   ImageAttributes fitWidthAttributes = scaleToFillAttributes;
116   fitWidthAttributes.SetScalingMode( ImageAttributes::FitWidth );
117   gCancelAttributes.push_back( fitWidthAttributes );
118
119   ImageAttributes fitHeightAttributes = scaleToFillAttributes;
120   fitHeightAttributes.SetScalingMode( ImageAttributes::FitHeight );
121   gCancelAttributes.push_back( fitHeightAttributes );
122
123   ///@ToDo: Add attribute variants for all scale modes.
124
125   // Pad the array to a prime number to mitigate any accidental periodic
126   // patterns in which image file has which attributes applied to its load:
127   srand48( 104729 );
128   const float lastUniques = gCancelAttributes.size() - 0.001f;
129   while( gCancelAttributes.size() < 61u )
130   {
131     gCancelAttributes.push_back( gCancelAttributes[unsigned(drand48() * lastUniques)] );
132   }
133 }
134
135 void utc_dali_loading_cleanup(void)
136 {
137   delete gAbstraction;
138   gAbstraction = 0;
139
140   test_return_value = TET_PASS;
141 }
142
143 // Positive test case for loading. Load lots and be sure it has succeeded.
144 int UtcDaliLoadCompletion(void)
145 {
146   tet_printf("Running load completion test \n");
147
148   DALI_ASSERT_ALWAYS( gAbstraction != 0 );
149
150   // Start a bunch of loads that should work:
151
152   const Dali::ImageAttributes attributes;
153   const Dali::Integration::BitmapResourceType bitmapResourceType( attributes );
154   Dali::Integration::LoadResourcePriority priority = Dali::Integration::LoadPriorityNormal;
155   unsigned loadsLaunched = 0;
156
157   for( unsigned loadGroup = 0; loadGroup < NUM_LOAD_GROUPS_TO_ISSUE; ++loadGroup )
158   {
159     for( unsigned validImage = 0; validImage < NUM_VALID_IMAGES; ++validImage )
160     {
161       gAbstraction->LoadResource( ResourceRequest( loadGroup * NUM_VALID_IMAGES + validImage + 1, bitmapResourceType, VALID_IMAGES[validImage], priority ) );
162     }
163     loadsLaunched += NUM_VALID_IMAGES;
164   }
165
166   // Drain the completed loads:
167
168   Dali::Internal::Platform::ResourceCollector resourceSink;
169
170   for( unsigned i = 0; i < MAX_NUM_RESOURCE_TRIES && resourceSink.mGrandTotalCompletions < loadsLaunched; ++i )
171   {
172     tet_printf( "Draining sleep %u, at total completion count %u of %u.\n", i, resourceSink.mGrandTotalCompletions, loadsLaunched );
173     usleep( 1200 * 1000 );
174     gAbstraction->GetResources( resourceSink );
175   }
176
177   // Check the loads completed as expected:
178
179   tet_printf( "Issued Loads: %u, Completed Loads: %u, Successful Loads: %u, Failed Loads: %u \n", loadsLaunched, resourceSink.mGrandTotalCompletions, unsigned(resourceSink.mSuccessCounts.size()), unsigned(resourceSink.mFailureCounts.size()) );
180   DALI_TEST_CHECK( loadsLaunched == resourceSink.mGrandTotalCompletions );
181   DALI_TEST_CHECK( loadsLaunched == resourceSink.mSuccessCounts.size() );
182   DALI_TEST_CHECK( 0 == resourceSink.mFailureCounts.size() );
183
184   // Check that each success was reported exactly once:
185   for( ResourceCounterMap::const_iterator it = resourceSink.mSuccessCounts.begin(), end = resourceSink.mSuccessCounts.end(); it != end; ++it )
186   {
187     DALI_TEST_CHECK( it->second == 1u );
188   }
189
190   END_TEST;
191 }
192
193 /**
194  * @brief Test case for load cancellation.
195  *
196  * Load lots of images in batches, cancelling all in a batch after a small delay to
197  * allow the first of a batch to be launched before cancellation starts.
198  * Assert that all loads issued are either completed or cancelled.
199  */
200 int UtcDaliCancelAllLoads(void)
201 {
202   tet_printf( "Running load cancel-all test.\n" );
203
204   DALI_ASSERT_ALWAYS( gAbstraction != 0 );
205
206   // Start a bunch of loads that should work:
207
208   Dali::Integration::LoadResourcePriority priority = LoadPriorityNormal;
209   unsigned loadsLaunched = 0;
210
211   for( unsigned loadGroup = 0; loadGroup < NUM_CANCELLED_LOAD_GROUPS_TO_ISSUE; ++loadGroup )
212   {
213     // Issue load requests for a batch of images:
214     for( unsigned validImage = 0; validImage < NUM_VALID_IMAGES; ++validImage )
215     {
216       const BitmapResourceType bitmapResourceType( gCancelAttributes[ loadsLaunched % gCancelAttributes.size() ] );
217       const ResourceId resourceId = loadGroup * NUM_VALID_IMAGES + validImage + 1;
218       gAbstraction->LoadResource( ResourceRequest( resourceId, bitmapResourceType, VALID_IMAGES[validImage], priority ) );
219       loadsLaunched += 1;
220     }
221
222     // Let the first image in the batch start to load:
223     usleep( 5000 ); // This number is tuned. Turn it up too much and all loads will complete and the test will take so long it seems to hang.
224
225     // Cancel all the launched loads from oldest to newest:
226     for( unsigned validImage = 0; validImage < NUM_VALID_IMAGES; ++validImage )
227     {
228       const ResourceId resourceId = loadGroup * NUM_VALID_IMAGES + validImage + 1;
229       gAbstraction->CancelLoad( resourceId, ResourceBitmap );
230     }
231   }
232
233   // Drain the completed loads:
234   Dali::Internal::Platform::ResourceCollector resourceSink;
235
236   unsigned lastCompletions = -1;
237   for( unsigned i = 0; i < MAX_NUM_RESOURCE_TRIES && resourceSink.mGrandTotalCompletions < loadsLaunched && resourceSink.mGrandTotalCompletions != lastCompletions; ++i )
238   {
239     lastCompletions = resourceSink.mGrandTotalCompletions;
240     gAbstraction->GetResources( resourceSink );
241     tet_printf( "Draining sleep %u, at total completion count %u of %u.\n", i, resourceSink.mGrandTotalCompletions, loadsLaunched );
242     usleep( 100 * 1000 );
243   }
244
245   // Check the loads completed as expected:
246
247   tet_printf( "Issued Loads: %u, Completed Loads: %u, Successful Loads: %u, Failed Loads: %u \n", loadsLaunched, resourceSink.mGrandTotalCompletions, unsigned(resourceSink.mSuccessCounts.size()), unsigned(resourceSink.mFailureCounts.size()) );
248   DALI_TEST_CHECK( loadsLaunched > resourceSink.mGrandTotalCompletions );
249   DALI_TEST_CHECK( loadsLaunched > resourceSink.mSuccessCounts.size() );
250   DALI_TEST_CHECK( 0 == resourceSink.mFailureCounts.size() );
251
252   // Check that each success was reported exactly once:
253   for( ResourceCounterMap::const_iterator it = resourceSink.mSuccessCounts.begin(), end = resourceSink.mSuccessCounts.end(); it != end; ++it )
254   {
255     DALI_TEST_CHECK( it->second == 1u );
256   }
257
258   END_TEST;
259 }
260
261 /**
262  * @brief Test case for load cancellation.
263  *
264  * Load lots, cancel a subset and be sure the wrong loads are never cancelled
265  * and that all loads issued are either completed or cancelled.
266  */
267 int UtcDaliCancelSomeLoads(void)
268 {
269   tet_printf( "Running load cancel load subset test.\n" );
270
271   DALI_ASSERT_ALWAYS( gAbstraction != 0 );
272
273   // Start a bunch of loads that should work:
274
275   Dali::Integration::LoadResourcePriority priority = LoadPriorityNormal;
276   unsigned loadsLaunched = 0;
277
278   std::set<Integration::ResourceId> cancelledLoadSet;
279
280   for( unsigned loadGroup = 0; loadGroup < NUM_LOAD_GROUPS_TO_ISSUE; ++loadGroup )
281   {
282     // Issue load requests for a batch of images:
283     for( unsigned validImage = 0; validImage < NUM_VALID_IMAGES; ++validImage )
284     {
285       const BitmapResourceType bitmapResourceType( gCancelAttributes[ loadsLaunched % gCancelAttributes.size() ] );
286       const ResourceId resourceId = loadGroup * NUM_VALID_IMAGES + validImage + 1;
287       gAbstraction->LoadResource( ResourceRequest( resourceId, bitmapResourceType, VALID_IMAGES[validImage], priority ) );
288       loadsLaunched += 1;
289     }
290
291     // Let the first image in the batch start to load so we can try to cancel it in-flight:
292     usleep( 17000 );
293     ///@Note: The log should show cancellations of many in-flight loads in desktop builds with info-level logging enabled (e.g., "INFO: DALI: : CheckForCancellation: Cancelled in-flight resource (21)."). If it doesn't, the above delay may need to be adjusted.
294
295     // Cancel just two loads (hopefully one in-flight and one queued):
296
297     // Cancel first load, hopefully while it is in-flight:
298     const ResourceId cancelledInFlight = loadGroup * NUM_VALID_IMAGES + 1;
299     gAbstraction->CancelLoad( cancelledInFlight, ResourceBitmap );
300     cancelledLoadSet.insert( cancelledInFlight );
301
302     // Cancel second load, that is still queued:
303     const ResourceId cancelledFromQueue = loadGroup * NUM_VALID_IMAGES + NUM_VALID_IMAGES;
304     gAbstraction->CancelLoad( cancelledFromQueue, ResourceBitmap );
305     cancelledLoadSet.insert( cancelledFromQueue );
306   }
307
308   // Drain the completed loads:
309
310   Dali::Internal::Platform::ResourceCollector resourceSink;
311
312   unsigned lastCompletions = -1;
313   for( unsigned i = 0; i < MAX_NUM_RESOURCE_TRIES && resourceSink.mGrandTotalCompletions < loadsLaunched && resourceSink.mGrandTotalCompletions != lastCompletions; ++i )
314   {
315     lastCompletions = resourceSink.mGrandTotalCompletions;
316     gAbstraction->GetResources( resourceSink );
317     tet_printf( "Draining sleep %u, at total completion count %u of %u.\n", i, resourceSink.mGrandTotalCompletions, loadsLaunched );
318     usleep( 100 * 1000 );
319   }
320
321   // Check the loads completed as expected:
322
323   tet_printf( "Issued Loads: %u, Completed Loads: %u, Successful Loads: %u, Failed Loads: %u \n", loadsLaunched, resourceSink.mGrandTotalCompletions, unsigned(resourceSink.mSuccessCounts.size()), unsigned(resourceSink.mFailureCounts.size()) );
324   DALI_TEST_CHECK( loadsLaunched >= resourceSink.mGrandTotalCompletions );
325   DALI_TEST_CHECK( loadsLaunched >= resourceSink.mSuccessCounts.size() );
326   DALI_TEST_CHECK( 0 == resourceSink.mFailureCounts.size() );
327
328   // Check that if an image was not loaded, it is one of the ones that was cancelled:
329   // This is the main point of this test case.
330   for( unsigned resourceId = 1; resourceId <= NUM_LOAD_GROUPS_TO_ISSUE * NUM_VALID_IMAGES; ++resourceId )
331   {
332     if( resourceSink.mCompletionStatuses.find( resourceId ) == resourceSink.mCompletionStatuses.end() )
333     {
334       DALI_TEST_CHECK( cancelledLoadSet.find( resourceId ) != cancelledLoadSet.end() );
335     }
336   }
337
338   // Check that each success was reported exactly once:
339   for(ResourceCounterMap::const_iterator it = resourceSink.mSuccessCounts.begin(), end = resourceSink.mSuccessCounts.end(); it != end; ++it )
340   {
341     DALI_TEST_CHECK( it->second == 1u );
342   }
343
344   END_TEST;
345 }