Broke them into three test cases so they can run in parallel.
Sped them up a bit individually.
Made all three test harness apps work with a number of test cases < the
parallel batch size.
Change-Id: Iafdb26772f35154e4ade092650b3fc23cc168cd6
Signed-off-by: Andrew Cox <andrew.cox@partner.samsung.com>
{
if( nextTestCase < numTestCases )
{
- while( numRunningChildren < MAX_NUM_CHILDREN )
+ while( numRunningChildren < MAX_NUM_CHILDREN && nextTestCase < numTestCases )
{
int pid = fork();
if( pid == 0 ) // Child process
{
if( nextTestCase < numTestCases )
{
- while( numRunningChildren < MAX_NUM_CHILDREN )
+ while( numRunningChildren < MAX_NUM_CHILDREN && nextTestCase < numTestCases )
{
int pid = fork();
if( pid == 0 ) // Child process
SET(CAPI_LIB "dali-platform-abstraction")
SET(TC_SOURCES
- utc-image-loading.cpp
+ utc-image-loading-load-completion.cpp
+ utc-image-loading-cancel-all-loads.cpp
+ utc-image-loading-cancel-some-loads.cpp
)
LIST(APPEND TC_SOURCES
resource-collector.cpp
../dali-adaptor/dali-test-suite-utils/dali-test-suite-utils.cpp
tct-dali-platform-abstraction-core.cpp
+ utc-image-loading-common.cpp
)
PKG_CHECK_MODULES(${CAPI_LIB} REQUIRED
*/
#include "resource-collector.h"
+#include "slp-platform-abstraction.h"
#include <dali/integration-api/debug.h>
namespace Dali
using namespace Dali::Integration;
ResourceCollector::ResourceCollector() :
- mGrandTotalCompletions(0)
+ mGrandTotalCompletions(0),
+ mGrandTotalNotifications(0)
{
}
void ResourceCollector::LoadResponse( Dali::Integration::ResourceId id, Dali::Integration::ResourceTypeId type, Dali::Integration::ResourcePointer resource, Dali::Integration::LoadStatus status )
{
+ ++mGrandTotalNotifications;
if( status == RESOURCE_COMPLETELY_LOADED )
{
DALI_ASSERT_DEBUG( mCompletionCounts.find(id) == mCompletionCounts.end() && "A resource can only complete once." );
++mFailureCounts[id];
mCompletionSequence.push_back( id );
++mGrandTotalCompletions;
+ ++mGrandTotalNotifications;
}
+void PollForNotification( ResourceCollector& collector, Integration::PlatformAbstraction& abstraction, const unsigned maxPolls )
+{
+ // Poll for at least one completed or partially completed load:
+ const unsigned outstandingNotifications = collector.mGrandTotalNotifications;
+ for( unsigned poll = 0; poll < maxPolls; ++poll )
+ {
+ abstraction.GetResources( collector );
+ if( collector.mGrandTotalNotifications > outstandingNotifications )
+ {
+ break;
+ }
+ usleep( 3 ); //< Wait 3 microseconds each time around.
+ }
+}
} /* namespace Platform */
} /* namespace Internal */
namespace Dali
{
+
+namespace Integration
+{
+class PlatformAbstraction;
+}
+
namespace Internal
{
namespace Platform
ResourceSequence mCompletionSequence;
/** Count of all successes and failures.*/
unsigned mGrandTotalCompletions;
+ /** Count of all successes, failures, loading notifications and partially loaded notifications.*/
+ unsigned mGrandTotalNotifications;
};
+/**
+ * Helper to poll the abstraction for notifications assuming loads have been
+ * issued to it previously and are in-flight.
+ */
+void PollForNotification( ResourceCollector& collector, Integration::PlatformAbstraction& abstraction, const unsigned maxPolls = 100 );
+
} /* namespace Platform */
} /* namespace Internal */
} /* namespace Dali */
typedef std::map<int, TestCase> RunningTestCases;
-int RunAll(const char* processName, bool reRunFailed)
+// Constantly runs up to MAX_NUM_CHILDREN processes
+int RunAllInParallel(const char* processName, bool reRunFailed)
{
int numFailures = 0;
int numPasses = 0;
- unsigned int numTestCases = sizeof(tc_array)/sizeof(struct testcase_s) - 1;
+ int numTestCases = sizeof(tc_array)/sizeof(struct testcase_s) - 1;
- // Run test cases in child process( to kill output ), but run serially.
- for( unsigned int i=0; i<numTestCases; i++)
+ RunningTestCases children;
+ std::vector<int> failedTestCases;
+
+ // Fork up to MAX_NUM_CHILDREN processes, then
+ // wait. As soon as a proc completes, fork the next.
+
+ int nextTestCase = 0;
+ int numRunningChildren = 0;
+ while( nextTestCase < numTestCases || numRunningChildren > 0)
{
- int pid = fork();
- if( pid == 0 ) // Child process
+ // Spawn more children if there any left to start and a slot to run one in:
+ if( nextTestCase < numTestCases )
{
- close(STDOUT_FILENO);
- close(STDERR_FILENO);
- exit( RunTestCase( tc_array[i] ) );
+ while( numRunningChildren < MAX_NUM_CHILDREN && nextTestCase < numTestCases )
+ {
+ int pid = fork();
+ if( pid == 0 ) // Child process
+ {
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+ exit( RunTestCase( tc_array[nextTestCase] ) );
+ }
+ else if(pid == -1)
+ {
+ perror("fork");
+ exit(2);
+ }
+ else // Parent process
+ {
+ TestCase tc(nextTestCase, tc_array[nextTestCase].name);
+ children[pid] = tc;
+ nextTestCase++;
+ numRunningChildren++;
+ }
+ }
}
- else if(pid == -1)
+
+ int status=0;
+ int childPid = waitpid(-1, &status, 0);
+ if( childPid == -1 )
{
- perror("fork");
+ perror("waitpid");
exit(2);
}
- else // Parent process
+
+ if( WIFEXITED(status) )
{
- int status = 0;
- int childPid = waitpid(-1, &status, 0);
- if( childPid == -1 )
+ if( childPid > 0 )
{
- perror("waitpid");
- exit(2);
- }
- if( WIFEXITED(status) )
- {
- if( childPid > 0 )
+ int testResult = WEXITSTATUS(status);
+ if( testResult )
+ {
+ printf("Test case %s failed: %d\n", children[childPid].testCaseName, testResult);
+ failedTestCases.push_back(children[childPid].testCase);
+ numFailures++;
+ }
+ else
{
- int testResult = WEXITSTATUS(status);
- if( testResult )
- {
- printf("Test case %s failed: %d\n", tc_array[i].name, testResult);
- numFailures++;
- }
- else
- {
- numPasses++;
- }
+ numPasses++;
}
+ numRunningChildren--;
}
- else if(WIFSIGNALED(status) )
+ }
+
+ else if( WIFSIGNALED(status) )
+ {
+ if( childPid > 0 )
{
- if( childPid > 0 )
+ RunningTestCases::iterator iter = children.find(childPid);
+ if( iter != children.end() )
{
- printf("Test case %s exited with signal %d\n", tc_array[i].name, WTERMSIG(status));
- numFailures++;
+ printf("Test case %s exited with signal %d\n", iter->second.testCaseName, WTERMSIG(status));
+ failedTestCases.push_back(iter->second.testCase);
+ }
+ else
+ {
+ printf("Unknown child process: %d signaled %d\n", childPid, WTERMSIG(status));
}
+
+ numFailures++;
+ numRunningChildren--;
}
}
}
-
- printf("\rNumber of test passes: %d\n", numPasses);
+ printf("\rNumber of test passes: %d \n", numPasses);
printf("Number of test failures: %d\n", numFailures);
+ if( reRunFailed )
+ {
+ for( unsigned int i=0; i<failedTestCases.size(); i++)
+ {
+ printf("Running test case %s:\n", tc_array[failedTestCases[i]].name );
+ RunTestCase( tc_array[failedTestCases[i] ] );
+ }
+ }
+
return numFailures;
}
if( optParallel )
{
- // For this test harness, run tests only in serial ( but without output )
- result = RunAll(argv[0], optRerunFailed);
+ result = RunAllInParallel(argv[0], optRerunFailed);
}
else
{
--- /dev/null
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "utc-image-loading-common.h"
+
+void utc_image_loading_cancel_all_loads_startup(void)
+{
+ utc_dali_loading_startup();
+}
+
+void utc_image_loading_cancel_all_loads_cleanup(void)
+{
+ utc_dali_loading_cleanup();
+}
+
+/**
+ * @brief Test case for load cancellation.
+ *
+ * Load lots of images in batches, cancelling all in a batch after a small delay to
+ * allow the first of a batch to be launched before cancellation starts.
+ * Assert that all loads issued are either completed or cancelled.
+ *
+ * @note Many loads will succeed despite our cancellations due to the coarse
+ * granularity of the waits we introduce after loading each batch. That is
+ * expected.
+ */
+int UtcDaliCancelAllLoads(void)
+{
+ tet_printf( "Running load cancel-all test.\n" );
+
+ DALI_ASSERT_ALWAYS( gAbstraction != 0 );
+
+ // Start a bunch of loads that should work:
+
+ Dali::Integration::LoadResourcePriority priority = LoadPriorityNormal;
+ unsigned loadsLaunched = 0;
+ Dali::Internal::Platform::ResourceCollector resourceSink;
+
+ for( unsigned loadGroup = 0; loadGroup < NUM_CANCELLED_LOAD_GROUPS_TO_ISSUE; ++loadGroup )
+ {
+ // Issue load requests for a batch of images:
+ for( unsigned validImage = 0; validImage < NUM_VALID_IMAGES; ++validImage )
+ {
+ const BitmapResourceType bitmapResourceType( gCancelAttributes[ loadsLaunched % gCancelAttributes.size() ] );
+ const ResourceId resourceId = loadGroup * NUM_VALID_IMAGES + validImage + 1;
+ gAbstraction->LoadResource( ResourceRequest( resourceId, bitmapResourceType, VALID_IMAGES[validImage], priority ) );
+ loadsLaunched += 1;
+ }
+
+ // Poll for at least one completed load so we have a good chance of catching an
+ // in-flight load as we run through the cancellations further below:
+ PollForNotification( resourceSink, *gAbstraction, 100 );
+
+ // Cancel all the launched loads in the batch from oldest to newest:
+ for( unsigned validImage = 0; validImage < NUM_VALID_IMAGES; ++validImage )
+ {
+ const ResourceId resourceId = loadGroup * NUM_VALID_IMAGES + validImage + 1;
+ gAbstraction->CancelLoad( resourceId, ResourceBitmap );
+ }
+ }
+
+ // Drain the completed loads:
+
+ unsigned lastNotifications = -1;
+ for( unsigned i = 0; resourceSink.mGrandTotalCompletions < loadsLaunched && resourceSink.mGrandTotalNotifications != lastNotifications; )
+ {
+ lastNotifications = resourceSink.mGrandTotalNotifications;
+ gAbstraction->GetResources( resourceSink );
+
+ ++i;
+ if( i < MAX_NUM_RESOURCE_TRIES && resourceSink.mGrandTotalCompletions < loadsLaunched )
+ {
+ usleep( 1000 * 10 );
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ // Check the loads completed as expected:
+
+ 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()) );
+ DALI_TEST_CHECK( loadsLaunched > resourceSink.mGrandTotalCompletions );
+ DALI_TEST_CHECK( loadsLaunched > resourceSink.mSuccessCounts.size() );
+ DALI_TEST_CHECK( 0 == resourceSink.mFailureCounts.size() );
+
+ // Check that each success was reported exactly once:
+ for( ResourceCounterMap::const_iterator it = resourceSink.mSuccessCounts.begin(), end = resourceSink.mSuccessCounts.end(); it != end; ++it )
+ {
+ DALI_TEST_CHECK( it->second == 1u );
+ }
+
+ END_TEST;
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "utc-image-loading-common.h"
+
+void utc_image_loading_cancel_some_loads_startup(void)
+{
+ utc_dali_loading_startup();
+}
+
+void utc_image_loading_cancel_some_loads_cleanup(void)
+{
+ utc_dali_loading_cleanup();
+}
+
+/**
+ * @brief Test case for load cancellation.
+ *
+ * Load lots, cancel a subset and be sure the wrong loads are never cancelled
+ * and that all loads issued are either completed or cancelled.
+ */
+int UtcDaliCancelSomeLoads(void)
+{
+ tet_printf( "Running load cancel load subset test.\n" );
+
+ DALI_ASSERT_ALWAYS( gAbstraction != 0 );
+
+ // Start a bunch of loads that should work:
+
+ Dali::Integration::LoadResourcePriority priority = LoadPriorityNormal;
+ unsigned loadsLaunched = 0;
+
+ std::set<Integration::ResourceId> cancelledLoadSet;
+ Dali::Internal::Platform::ResourceCollector resourceSink;
+
+ for( unsigned loadGroup = 0; loadGroup < NUM_LOAD_GROUPS_TO_ISSUE; ++loadGroup )
+ {
+ const unsigned preIterationCompletions = resourceSink.mGrandTotalCompletions;
+
+ // Issue load requests for a batch of images:
+ for( unsigned validImage = 0; validImage < NUM_VALID_IMAGES; ++validImage )
+ {
+ const BitmapResourceType bitmapResourceType( gCancelAttributes[ loadsLaunched % gCancelAttributes.size() ] );
+ const ResourceId resourceId = loadGroup * NUM_VALID_IMAGES + validImage + 1;
+ gAbstraction->LoadResource( ResourceRequest( resourceId, bitmapResourceType, VALID_IMAGES[validImage], priority ) );
+ loadsLaunched += 1;
+ }
+
+ // Let the first image in the batch start to load so we can try to cancel it in-flight:
+ usleep( 1 * 1000 ); //< 1 ms is enough to let an image start to load.
+ ///@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.
+
+ // Cancel just two loads (hopefully one in-flight and one queued):
+
+ // Cancel first load, hopefully while it is in-flight:
+ const ResourceId cancelledInFlight = loadGroup * NUM_VALID_IMAGES + 1;
+ gAbstraction->CancelLoad( cancelledInFlight, ResourceBitmap );
+ cancelledLoadSet.insert( cancelledInFlight );
+
+ // Cancel second load, that is still queued:
+ const ResourceId cancelledFromQueue = loadGroup * NUM_VALID_IMAGES + NUM_VALID_IMAGES;
+ gAbstraction->CancelLoad( cancelledFromQueue, ResourceBitmap );
+ cancelledLoadSet.insert( cancelledFromQueue );
+
+ // Drain a group worth of images so the cancellations hit in-flight loads on the next iteration:
+ for( unsigned i = 0; i < NUM_VALID_IMAGES * 1000 * 1000 * 10 / (5 * 1000) && resourceSink.mGrandTotalCompletions < preIterationCompletions + NUM_VALID_IMAGES - 2; ++i )
+ {
+ gAbstraction->GetResources( resourceSink );
+ usleep( 5 * 1000 );
+ }
+ }
+
+ // Drain any spare completed loads until no new loads complete on an iteration:
+ unsigned lastNotifications = -1;
+ for( unsigned i = 0; i < MAX_NUM_RESOURCE_TRIES && resourceSink.mGrandTotalCompletions < loadsLaunched && resourceSink.mGrandTotalNotifications != lastNotifications; ++i )
+ {
+ lastNotifications = resourceSink.mGrandTotalNotifications;
+ gAbstraction->GetResources( resourceSink );
+ usleep( 70 * 1000 ); //< 70 ms should allow at least one medium image to load. You might to increase this to run on a slow device.
+ gAbstraction->GetResources( resourceSink );
+ usleep( 70 * 1000 );
+ gAbstraction->GetResources( resourceSink );
+ usleep( 70 * 1000 );
+ gAbstraction->GetResources( resourceSink );
+ }
+
+ // Check the loads completed as expected:
+
+ 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()) );
+ DALI_TEST_CHECK( loadsLaunched >= resourceSink.mGrandTotalCompletions );
+ DALI_TEST_CHECK( loadsLaunched >= resourceSink.mSuccessCounts.size() );
+ DALI_TEST_CHECK( 0 == resourceSink.mFailureCounts.size() );
+
+ // Check that if an image was not loaded, it is one of the ones that was cancelled:
+ // This is the main point of this test case.
+ std::vector<Integration::ResourceId> missingLoads;
+ for( unsigned resourceId = 1; resourceId <= NUM_LOAD_GROUPS_TO_ISSUE * NUM_VALID_IMAGES; ++resourceId )
+ {
+ // Was the load (not) completed?
+ if( resourceSink.mCompletionStatuses.find( resourceId ) == resourceSink.mCompletionStatuses.end() )
+ {
+ // Was the load (not) cancelled?
+ if( cancelledLoadSet.find( resourceId ) == cancelledLoadSet.end() )
+ {
+ // Whoa, the load was not completed and not cancelled either... so where did it go then?
+ missingLoads.push_back( resourceId );
+ tet_printf( "Missing load. ResourceId %u was not completed but was also not cancelled.\n", resourceId );
+ ///@note If this fires, you are probably not waiting long enough in the draining loop above (usleep( 70 * 1000 );).
+ }
+ }
+ }
+ DALI_TEST_CHECK( missingLoads.size() == 0U );
+
+ // Check that each success was reported exactly once:
+ for(ResourceCounterMap::const_iterator it = resourceSink.mSuccessCounts.begin(), end = resourceSink.mSuccessCounts.end(); it != end; ++it )
+ {
+ DALI_TEST_CHECK( it->second == 1u );
+ }
+
+ END_TEST;
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "utc-image-loading-common.h"
+
+/** Live platform abstraction recreated for each test case. */
+Integration::PlatformAbstraction * gAbstraction = 0;
+
+/** A variety of ImageAttributes to reach different code paths that have embedded code paths. */
+std::vector<ImageAttributes> gCancelAttributes;
+
+void utc_dali_loading_startup(void)
+{
+ test_return_value = TET_UNDEF;
+ gAbstraction = CreatePlatformAbstraction();
+
+ // Setup some ImageAttributes to engage post-processing stages:
+
+ ImageAttributes scaleToFillAttributes;
+ scaleToFillAttributes.SetScalingMode( ImageAttributes::ScaleToFill );
+ scaleToFillAttributes.SetSize( 160, 120 );
+ gCancelAttributes.push_back( scaleToFillAttributes );
+
+ // Hit the derived dimensions code:
+ ImageAttributes scaleToFillAttributesDeriveWidth = scaleToFillAttributes;
+ scaleToFillAttributesDeriveWidth.SetSize( 0, 120 );
+ gCancelAttributes.push_back( scaleToFillAttributesDeriveWidth );
+
+ ImageAttributes scaleToFillAttributesDeriveHeight = scaleToFillAttributes;
+ scaleToFillAttributesDeriveHeight.SetSize( 160, 0 );
+ gCancelAttributes.push_back( scaleToFillAttributesDeriveHeight );
+
+ // Try to push a tall crop:
+ ImageAttributes scaleToFillAttributesTall = scaleToFillAttributes;
+ scaleToFillAttributesTall.SetSize( 160, 480 );
+ ImageAttributes scaleToFillAttributesTall2 = scaleToFillAttributes;
+ scaleToFillAttributesTall2.SetSize( 160, 509 );
+ ImageAttributes scaleToFillAttributesTall3 = scaleToFillAttributes;
+ scaleToFillAttributesTall3.SetSize( 37, 251 );
+ gCancelAttributes.push_back( scaleToFillAttributesTall );
+ gCancelAttributes.push_back( scaleToFillAttributesTall2 );
+ gCancelAttributes.push_back( scaleToFillAttributesTall3 );
+
+ // Try to push a wide crop:
+ ImageAttributes scaleToFillAttributesWide = scaleToFillAttributes;
+ scaleToFillAttributesWide.SetSize( 320, 60 );
+ ImageAttributes scaleToFillAttributesWide2 = scaleToFillAttributes;
+ scaleToFillAttributesWide2.SetSize( 317, 60 );
+ ImageAttributes scaleToFillAttributesWide3 = scaleToFillAttributes;
+ scaleToFillAttributesWide3.SetSize( 317, 53 );
+ gCancelAttributes.push_back( scaleToFillAttributesWide );
+ gCancelAttributes.push_back( scaleToFillAttributesWide2 );
+ gCancelAttributes.push_back( scaleToFillAttributesWide3 );
+
+ ImageAttributes shrinkToFitAttributes = scaleToFillAttributes;
+ shrinkToFitAttributes.SetScalingMode( ImageAttributes::ShrinkToFit );
+ gCancelAttributes.push_back( shrinkToFitAttributes );
+
+ ImageAttributes fitWidthAttributes = scaleToFillAttributes;
+ fitWidthAttributes.SetScalingMode( ImageAttributes::FitWidth );
+ gCancelAttributes.push_back( fitWidthAttributes );
+
+ ImageAttributes fitHeightAttributes = scaleToFillAttributes;
+ fitHeightAttributes.SetScalingMode( ImageAttributes::FitHeight );
+ gCancelAttributes.push_back( fitHeightAttributes );
+
+ ///@ToDo: Add attribute variants for all scale modes.
+
+ // Pad the array to a prime number to mitigate any accidental periodic
+ // patterns in which image file has which attributes applied to its load:
+ srand48( 104729 );
+ const float lastUniques = gCancelAttributes.size() - 0.001f;
+ while( gCancelAttributes.size() < 61u )
+ {
+ gCancelAttributes.push_back( gCancelAttributes[unsigned(drand48() * lastUniques)] );
+ }
+}
+
+void utc_dali_loading_cleanup(void)
+{
+ delete gAbstraction;
+ gAbstraction = 0;
+
+ test_return_value = TET_PASS;
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef __DALI_TEST_SUITE_IMAGE_LOADING_COMMON_H__
+#define __DALI_TEST_SUITE_IMAGE_LOADING_COMMON_H__
+#include <unistd.h>
+#include <iostream>
+#include <stdlib.h>
+#include <dali/dali.h>
+#include <dali-test-suite-utils.h>
+#include "slp-platform-abstraction.h"
+#include "resource-collector.h"
+
+using namespace Dali;
+using namespace Dali::Integration;
+using namespace Dali::Internal::Platform;
+
+namespace
+{
+/**
+ * The number of loads issued in test cases is a multiple of this. The higher it
+ * is, the more the tests stress the system but the longer they take to run.
+ * A value of 1000 is enough to make load tests take tens of seconds each
+ * on desktop. */
+const unsigned NUM_LOAD_GROUPS_TO_ISSUE = 158;
+
+/**
+ * The number of loads to issue when they will be cancelled.
+ * Cancelled loads are cheap so we do a lot.
+ */
+const unsigned NUM_CANCELLED_LOAD_GROUPS_TO_ISSUE = NUM_LOAD_GROUPS_TO_ISSUE * 10;
+
+/** The number of times to ask for resource load status. */
+const unsigned MAX_NUM_RESOURCE_TRIES = 10;
+
+/** The maximum time to wait for loads to complete when the number of expected loads is known. */
+const unsigned MAX_MILLIS_TO_WAIT_FOR_KNOWN_LOADS = 1000 * 60;
+
+/** Images that should load without issue. */
+const char* const VALID_IMAGES[] = {
+ TEST_IMAGE_DIR "/frac.jpg",
+ TEST_IMAGE_DIR "/frac.24.bmp",
+ TEST_IMAGE_DIR "/frac.png",
+ TEST_IMAGE_DIR "/interlaced.gif",
+ TEST_IMAGE_DIR "/pattern.gif"
+};
+const unsigned NUM_VALID_IMAGES = sizeof(VALID_IMAGES) / sizeof(VALID_IMAGES[0]);
+
+///@ToDo: Add valid ktx, ico, and wbmp image examples.
+
+/** Returns elapsed milliseconds. */
+double GetTimeMilliseconds( Integration::PlatformAbstraction& abstraction )
+{
+ unsigned int seconds;
+ unsigned int microseconds;
+ abstraction.GetTimeMicroseconds( seconds, microseconds );
+ double milliseconds = seconds * 1000.0 + microseconds / 1000.0;
+ return milliseconds;
+}
+
+} // anon namespace
+
+/** Live platform abstraction recreated for each test case. */
+extern Integration::PlatformAbstraction * gAbstraction;
+
+/** A variety of ImageAttributes to reach different code paths that have embedded code paths. */
+extern std::vector<ImageAttributes> gCancelAttributes;
+
+
+void utc_dali_loading_startup(void);
+void utc_dali_loading_cleanup(void);
+
+#endif // __DALI_TEST_SUITE_IMAGE_LOADING_COMMON_H__
--- /dev/null
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "utc-image-loading-common.h"
+
+void utc_image_loading_load_completion_startup(void)
+{
+ utc_dali_loading_startup();
+}
+
+void utc_image_loading_load_completion_cleanup(void)
+{
+ utc_dali_loading_cleanup();
+}
+
+// Positive test case for loading. Load lots and be sure it has succeeded.
+int UtcDaliLoadCompletion(void)
+{
+ tet_printf("Running load completion test \n");
+
+ DALI_ASSERT_ALWAYS( gAbstraction != 0 );
+
+ // Start a bunch of loads that should work:
+
+ const Dali::ImageAttributes attributes;
+ const Dali::Integration::BitmapResourceType bitmapResourceType( attributes );
+ Dali::Integration::LoadResourcePriority priority = Dali::Integration::LoadPriorityNormal;
+ unsigned loadsLaunched = 0;
+
+ for( unsigned loadGroup = 0; loadGroup < NUM_LOAD_GROUPS_TO_ISSUE; ++loadGroup )
+ {
+ for( unsigned validImage = 0; validImage < NUM_VALID_IMAGES; ++validImage )
+ {
+ gAbstraction->LoadResource( ResourceRequest( loadGroup * NUM_VALID_IMAGES + validImage + 1, bitmapResourceType, VALID_IMAGES[validImage], priority ) );
+ }
+ loadsLaunched += NUM_VALID_IMAGES;
+ }
+
+ // Drain the completed loads:
+ Dali::Internal::Platform::ResourceCollector resourceSink;
+ usleep( 1000 * 1000 );
+ gAbstraction->GetResources( resourceSink );
+ usleep( 500 * 1000 );
+ gAbstraction->GetResources( resourceSink );
+
+ const double startDrainTime = GetTimeMilliseconds( *gAbstraction );
+ while( resourceSink.mGrandTotalCompletions < loadsLaunched && GetTimeMilliseconds( *gAbstraction ) - startDrainTime < MAX_MILLIS_TO_WAIT_FOR_KNOWN_LOADS )
+ {
+ usleep( 100 * 40 );
+ gAbstraction->GetResources( resourceSink );
+ }
+
+ // Check the loads completed as expected:
+
+ 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()) );
+ DALI_TEST_CHECK( loadsLaunched == resourceSink.mGrandTotalCompletions );
+ DALI_TEST_CHECK( loadsLaunched == resourceSink.mSuccessCounts.size() );
+ DALI_TEST_CHECK( 0 == resourceSink.mFailureCounts.size() );
+
+ // Check that each success was reported exactly once:
+ for( ResourceCounterMap::const_iterator it = resourceSink.mSuccessCounts.begin(), end = resourceSink.mSuccessCounts.end(); it != end; ++it )
+ {
+ DALI_TEST_CHECK( it->second == 1u );
+ }
+
+ END_TEST;
+}
+++ /dev/null
-/*
- * Copyright (c) 2014 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-#include <unistd.h>
-#include <iostream>
-#include <stdlib.h>
-#include <dali/dali.h>
-#include <dali-test-suite-utils.h>
-#include "slp-platform-abstraction.h"
-#include "resource-collector.h"
-
-using namespace Dali;
-using namespace Dali::Integration;
-using namespace Dali::Internal::Platform;
-
-namespace
-{
-/**
- * The number of loads issued in test cases is a multiple of this. The higher it
- * is, the more the tests stress the system but the longer they take to run.
- * A value of 1000 is enough to make load tests take tens of seconds each
- * on desktop. */
-const unsigned NUM_LOAD_GROUPS_TO_ISSUE = 200;
-
-/**
- * The number of loads to issue when they will be cancelled.
- * Cancelled loads are cheap so we do a lot.
- */
-const unsigned NUM_CANCELLED_LOAD_GROUPS_TO_ISSUE = NUM_LOAD_GROUPS_TO_ISSUE * 10;
-
-/** The number of times to ask for resource load status. */
-const unsigned MAX_NUM_RESOURCE_TRIES = 5;
-
-/** Images that should load without issue. */
-const char* const VALID_IMAGES[] = {
- TEST_IMAGE_DIR "/frac.jpg",
- TEST_IMAGE_DIR "/frac.24.bmp",
- TEST_IMAGE_DIR "/frac.png",
- TEST_IMAGE_DIR "/interlaced.gif",
- TEST_IMAGE_DIR "/pattern.gif"
-};
-const unsigned NUM_VALID_IMAGES = sizeof(VALID_IMAGES) / sizeof(VALID_IMAGES[0]);
-
-///@ToDo: Add valid ktx, ico, and wbmp image examples.
-
-/** Live platform abstraction recreated for each test case. */
-Integration::PlatformAbstraction * gAbstraction = 0;
-
-/** A variety of ImageAttributes to reach different code paths that have embedded code paths. */
-std::vector<ImageAttributes> gCancelAttributes;
-
-} // anon namespace
-
-void utc_dali_loading_startup(void)
-{
- test_return_value = TET_UNDEF;
- gAbstraction = CreatePlatformAbstraction();
-
- // Setup some ImageAttributes to engage post-processing stages:
-
- ImageAttributes scaleToFillAttributes;
- scaleToFillAttributes.SetScalingMode( ImageAttributes::ScaleToFill );
- scaleToFillAttributes.SetSize( 160, 120 );
- gCancelAttributes.push_back( scaleToFillAttributes );
-
- // Hit the derived dimensions code:
- ImageAttributes scaleToFillAttributesDeriveWidth = scaleToFillAttributes;
- scaleToFillAttributesDeriveWidth.SetSize( 0, 120 );
- gCancelAttributes.push_back( scaleToFillAttributesDeriveWidth );
-
- ImageAttributes scaleToFillAttributesDeriveHeight = scaleToFillAttributes;
- scaleToFillAttributesDeriveHeight.SetSize( 160, 0 );
- gCancelAttributes.push_back( scaleToFillAttributesDeriveHeight );
-
- // Try to push a tall crop:
- ImageAttributes scaleToFillAttributesTall = scaleToFillAttributes;
- scaleToFillAttributesTall.SetSize( 160, 480 );
- ImageAttributes scaleToFillAttributesTall2 = scaleToFillAttributes;
- scaleToFillAttributesTall2.SetSize( 160, 509 );
- ImageAttributes scaleToFillAttributesTall3 = scaleToFillAttributes;
- scaleToFillAttributesTall3.SetSize( 37, 251 );
- gCancelAttributes.push_back( scaleToFillAttributesTall );
- gCancelAttributes.push_back( scaleToFillAttributesTall2 );
- gCancelAttributes.push_back( scaleToFillAttributesTall3 );
-
- // Try to push a wide crop:
- ImageAttributes scaleToFillAttributesWide = scaleToFillAttributes;
- scaleToFillAttributesWide.SetSize( 320, 60 );
- ImageAttributes scaleToFillAttributesWide2 = scaleToFillAttributes;
- scaleToFillAttributesWide2.SetSize( 317, 60 );
- ImageAttributes scaleToFillAttributesWide3 = scaleToFillAttributes;
- scaleToFillAttributesWide3.SetSize( 317, 53 );
- gCancelAttributes.push_back( scaleToFillAttributesWide );
- gCancelAttributes.push_back( scaleToFillAttributesWide2 );
- gCancelAttributes.push_back( scaleToFillAttributesWide3 );
-
- ImageAttributes shrinkToFitAttributes = scaleToFillAttributes;
- shrinkToFitAttributes.SetScalingMode( ImageAttributes::ShrinkToFit );
- gCancelAttributes.push_back( shrinkToFitAttributes );
-
- ImageAttributes fitWidthAttributes = scaleToFillAttributes;
- fitWidthAttributes.SetScalingMode( ImageAttributes::FitWidth );
- gCancelAttributes.push_back( fitWidthAttributes );
-
- ImageAttributes fitHeightAttributes = scaleToFillAttributes;
- fitHeightAttributes.SetScalingMode( ImageAttributes::FitHeight );
- gCancelAttributes.push_back( fitHeightAttributes );
-
- ///@ToDo: Add attribute variants for all scale modes.
-
- // Pad the array to a prime number to mitigate any accidental periodic
- // patterns in which image file has which attributes applied to its load:
- srand48( 104729 );
- const float lastUniques = gCancelAttributes.size() - 0.001f;
- while( gCancelAttributes.size() < 61u )
- {
- gCancelAttributes.push_back( gCancelAttributes[unsigned(drand48() * lastUniques)] );
- }
-}
-
-void utc_dali_loading_cleanup(void)
-{
- delete gAbstraction;
- gAbstraction = 0;
-
- test_return_value = TET_PASS;
-}
-
-// Positive test case for loading. Load lots and be sure it has succeeded.
-int UtcDaliLoadCompletion(void)
-{
- tet_printf("Running load completion test \n");
-
- DALI_ASSERT_ALWAYS( gAbstraction != 0 );
-
- // Start a bunch of loads that should work:
-
- const Dali::ImageAttributes attributes;
- const Dali::Integration::BitmapResourceType bitmapResourceType( attributes );
- Dali::Integration::LoadResourcePriority priority = Dali::Integration::LoadPriorityNormal;
- unsigned loadsLaunched = 0;
-
- for( unsigned loadGroup = 0; loadGroup < NUM_LOAD_GROUPS_TO_ISSUE; ++loadGroup )
- {
- for( unsigned validImage = 0; validImage < NUM_VALID_IMAGES; ++validImage )
- {
- gAbstraction->LoadResource( ResourceRequest( loadGroup * NUM_VALID_IMAGES + validImage + 1, bitmapResourceType, VALID_IMAGES[validImage], priority ) );
- }
- loadsLaunched += NUM_VALID_IMAGES;
- }
-
- // Drain the completed loads:
-
- Dali::Internal::Platform::ResourceCollector resourceSink;
-
- for( unsigned i = 0; i < MAX_NUM_RESOURCE_TRIES && resourceSink.mGrandTotalCompletions < loadsLaunched; ++i )
- {
- tet_printf( "Draining sleep %u, at total completion count %u of %u.\n", i, resourceSink.mGrandTotalCompletions, loadsLaunched );
- usleep( 1200 * 1000 );
- gAbstraction->GetResources( resourceSink );
- }
-
- // Check the loads completed as expected:
-
- 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()) );
- DALI_TEST_CHECK( loadsLaunched == resourceSink.mGrandTotalCompletions );
- DALI_TEST_CHECK( loadsLaunched == resourceSink.mSuccessCounts.size() );
- DALI_TEST_CHECK( 0 == resourceSink.mFailureCounts.size() );
-
- // Check that each success was reported exactly once:
- for( ResourceCounterMap::const_iterator it = resourceSink.mSuccessCounts.begin(), end = resourceSink.mSuccessCounts.end(); it != end; ++it )
- {
- DALI_TEST_CHECK( it->second == 1u );
- }
-
- END_TEST;
-}
-
-/**
- * @brief Test case for load cancellation.
- *
- * Load lots of images in batches, cancelling all in a batch after a small delay to
- * allow the first of a batch to be launched before cancellation starts.
- * Assert that all loads issued are either completed or cancelled.
- */
-int UtcDaliCancelAllLoads(void)
-{
- tet_printf( "Running load cancel-all test.\n" );
-
- DALI_ASSERT_ALWAYS( gAbstraction != 0 );
-
- // Start a bunch of loads that should work:
-
- Dali::Integration::LoadResourcePriority priority = LoadPriorityNormal;
- unsigned loadsLaunched = 0;
-
- for( unsigned loadGroup = 0; loadGroup < NUM_CANCELLED_LOAD_GROUPS_TO_ISSUE; ++loadGroup )
- {
- // Issue load requests for a batch of images:
- for( unsigned validImage = 0; validImage < NUM_VALID_IMAGES; ++validImage )
- {
- const BitmapResourceType bitmapResourceType( gCancelAttributes[ loadsLaunched % gCancelAttributes.size() ] );
- const ResourceId resourceId = loadGroup * NUM_VALID_IMAGES + validImage + 1;
- gAbstraction->LoadResource( ResourceRequest( resourceId, bitmapResourceType, VALID_IMAGES[validImage], priority ) );
- loadsLaunched += 1;
- }
-
- // Let the first image in the batch start to load:
- 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.
-
- // Cancel all the launched loads from oldest to newest:
- for( unsigned validImage = 0; validImage < NUM_VALID_IMAGES; ++validImage )
- {
- const ResourceId resourceId = loadGroup * NUM_VALID_IMAGES + validImage + 1;
- gAbstraction->CancelLoad( resourceId, ResourceBitmap );
- }
- }
-
- // Drain the completed loads:
- Dali::Internal::Platform::ResourceCollector resourceSink;
-
- unsigned lastCompletions = -1;
- for( unsigned i = 0; i < MAX_NUM_RESOURCE_TRIES && resourceSink.mGrandTotalCompletions < loadsLaunched && resourceSink.mGrandTotalCompletions != lastCompletions; ++i )
- {
- lastCompletions = resourceSink.mGrandTotalCompletions;
- gAbstraction->GetResources( resourceSink );
- tet_printf( "Draining sleep %u, at total completion count %u of %u.\n", i, resourceSink.mGrandTotalCompletions, loadsLaunched );
- usleep( 100 * 1000 );
- }
-
- // Check the loads completed as expected:
-
- 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()) );
- DALI_TEST_CHECK( loadsLaunched > resourceSink.mGrandTotalCompletions );
- DALI_TEST_CHECK( loadsLaunched > resourceSink.mSuccessCounts.size() );
- DALI_TEST_CHECK( 0 == resourceSink.mFailureCounts.size() );
-
- // Check that each success was reported exactly once:
- for( ResourceCounterMap::const_iterator it = resourceSink.mSuccessCounts.begin(), end = resourceSink.mSuccessCounts.end(); it != end; ++it )
- {
- DALI_TEST_CHECK( it->second == 1u );
- }
-
- END_TEST;
-}
-
-/**
- * @brief Test case for load cancellation.
- *
- * Load lots, cancel a subset and be sure the wrong loads are never cancelled
- * and that all loads issued are either completed or cancelled.
- */
-int UtcDaliCancelSomeLoads(void)
-{
- tet_printf( "Running load cancel load subset test.\n" );
-
- DALI_ASSERT_ALWAYS( gAbstraction != 0 );
-
- // Start a bunch of loads that should work:
-
- Dali::Integration::LoadResourcePriority priority = LoadPriorityNormal;
- unsigned loadsLaunched = 0;
-
- std::set<Integration::ResourceId> cancelledLoadSet;
-
- for( unsigned loadGroup = 0; loadGroup < NUM_LOAD_GROUPS_TO_ISSUE; ++loadGroup )
- {
- // Issue load requests for a batch of images:
- for( unsigned validImage = 0; validImage < NUM_VALID_IMAGES; ++validImage )
- {
- const BitmapResourceType bitmapResourceType( gCancelAttributes[ loadsLaunched % gCancelAttributes.size() ] );
- const ResourceId resourceId = loadGroup * NUM_VALID_IMAGES + validImage + 1;
- gAbstraction->LoadResource( ResourceRequest( resourceId, bitmapResourceType, VALID_IMAGES[validImage], priority ) );
- loadsLaunched += 1;
- }
-
- // Let the first image in the batch start to load so we can try to cancel it in-flight:
- usleep( 17000 );
- ///@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.
-
- // Cancel just two loads (hopefully one in-flight and one queued):
-
- // Cancel first load, hopefully while it is in-flight:
- const ResourceId cancelledInFlight = loadGroup * NUM_VALID_IMAGES + 1;
- gAbstraction->CancelLoad( cancelledInFlight, ResourceBitmap );
- cancelledLoadSet.insert( cancelledInFlight );
-
- // Cancel second load, that is still queued:
- const ResourceId cancelledFromQueue = loadGroup * NUM_VALID_IMAGES + NUM_VALID_IMAGES;
- gAbstraction->CancelLoad( cancelledFromQueue, ResourceBitmap );
- cancelledLoadSet.insert( cancelledFromQueue );
- }
-
- // Drain the completed loads:
-
- Dali::Internal::Platform::ResourceCollector resourceSink;
-
- unsigned lastCompletions = -1;
- for( unsigned i = 0; i < MAX_NUM_RESOURCE_TRIES && resourceSink.mGrandTotalCompletions < loadsLaunched && resourceSink.mGrandTotalCompletions != lastCompletions; ++i )
- {
- lastCompletions = resourceSink.mGrandTotalCompletions;
- gAbstraction->GetResources( resourceSink );
- tet_printf( "Draining sleep %u, at total completion count %u of %u.\n", i, resourceSink.mGrandTotalCompletions, loadsLaunched );
- usleep( 100 * 1000 );
- }
-
- // Check the loads completed as expected:
-
- 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()) );
- DALI_TEST_CHECK( loadsLaunched >= resourceSink.mGrandTotalCompletions );
- DALI_TEST_CHECK( loadsLaunched >= resourceSink.mSuccessCounts.size() );
- DALI_TEST_CHECK( 0 == resourceSink.mFailureCounts.size() );
-
- // Check that if an image was not loaded, it is one of the ones that was cancelled:
- // This is the main point of this test case.
- for( unsigned resourceId = 1; resourceId <= NUM_LOAD_GROUPS_TO_ISSUE * NUM_VALID_IMAGES; ++resourceId )
- {
- if( resourceSink.mCompletionStatuses.find( resourceId ) == resourceSink.mCompletionStatuses.end() )
- {
- DALI_TEST_CHECK( cancelledLoadSet.find( resourceId ) != cancelledLoadSet.end() );
- }
- }
-
- // Check that each success was reported exactly once:
- for(ResourceCounterMap::const_iterator it = resourceSink.mSuccessCounts.begin(), end = resourceSink.mSuccessCounts.end(); it != end; ++it )
- {
- DALI_TEST_CHECK( it->second == 1u );
- }
-
- END_TEST;
-}
bool IsLoading()
{
// TODO - not used - remove?
+ DALI_ASSERT_DEBUG( 0 == "IsLoading() Is not implemented so don't call it." );
return true;
}
/**
* @copydoc PlatformAbstraction::IsLoading()
+ * @deprecated This is not implemented: always returns true.
*/
bool IsLoading();