From 8d0977145c8739d126e1ec126bbdd081e53f66a5 Mon Sep 17 00:00:00 2001 From: Andrew Cox Date: Tue, 2 Dec 2014 22:21:25 +0000 Subject: [PATCH] Parallelize PlatformAbstraction image loading tests 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 --- .../tct-dali-adaptor-internal-core.cpp | 2 +- .../src/dali-adaptor/tct-dali-adaptor-core.cpp | 2 +- .../src/dali-platform-abstraction/CMakeLists.txt | 5 +- .../resource-collector.cpp | 20 +- .../dali-platform-abstraction/resource-collector.h | 14 + .../tct-dali-platform-abstraction-core.cpp | 120 ++++--- .../utc-image-loading-cancel-all-loads.cpp | 109 +++++++ .../utc-image-loading-cancel-some-loads.cpp | 135 ++++++++ .../utc-image-loading-common.cpp | 99 ++++++ .../utc-image-loading-common.h | 87 ++++++ .../utc-image-loading-load-completion.cpp | 81 +++++ .../utc-image-loading.cpp | 345 --------------------- .../slp/resource-loader/resource-loader.cpp | 1 + .../slp/resource-loader/resource-loader.h | 1 + 14 files changed, 633 insertions(+), 388 deletions(-) create mode 100644 automated-tests/src/dali-platform-abstraction/utc-image-loading-cancel-all-loads.cpp create mode 100644 automated-tests/src/dali-platform-abstraction/utc-image-loading-cancel-some-loads.cpp create mode 100644 automated-tests/src/dali-platform-abstraction/utc-image-loading-common.cpp create mode 100644 automated-tests/src/dali-platform-abstraction/utc-image-loading-common.h create mode 100644 automated-tests/src/dali-platform-abstraction/utc-image-loading-load-completion.cpp delete mode 100644 automated-tests/src/dali-platform-abstraction/utc-image-loading.cpp diff --git a/automated-tests/src/dali-adaptor-internal/tct-dali-adaptor-internal-core.cpp b/automated-tests/src/dali-adaptor-internal/tct-dali-adaptor-internal-core.cpp index f90b8ef..69062a2 100644 --- a/automated-tests/src/dali-adaptor-internal/tct-dali-adaptor-internal-core.cpp +++ b/automated-tests/src/dali-adaptor-internal/tct-dali-adaptor-internal-core.cpp @@ -78,7 +78,7 @@ int RunAllInParallel(const char* processName, bool reRunFailed) { if( nextTestCase < numTestCases ) { - while( numRunningChildren < MAX_NUM_CHILDREN ) + while( numRunningChildren < MAX_NUM_CHILDREN && nextTestCase < numTestCases ) { int pid = fork(); if( pid == 0 ) // Child process diff --git a/automated-tests/src/dali-adaptor/tct-dali-adaptor-core.cpp b/automated-tests/src/dali-adaptor/tct-dali-adaptor-core.cpp index 90b7203..aabf91f 100644 --- a/automated-tests/src/dali-adaptor/tct-dali-adaptor-core.cpp +++ b/automated-tests/src/dali-adaptor/tct-dali-adaptor-core.cpp @@ -78,7 +78,7 @@ int RunAllInParallel(const char* processName, bool reRunFailed) { if( nextTestCase < numTestCases ) { - while( numRunningChildren < MAX_NUM_CHILDREN ) + while( numRunningChildren < MAX_NUM_CHILDREN && nextTestCase < numTestCases ) { int pid = fork(); if( pid == 0 ) // Child process diff --git a/automated-tests/src/dali-platform-abstraction/CMakeLists.txt b/automated-tests/src/dali-platform-abstraction/CMakeLists.txt index be41232..ce1a854 100644 --- a/automated-tests/src/dali-platform-abstraction/CMakeLists.txt +++ b/automated-tests/src/dali-platform-abstraction/CMakeLists.txt @@ -6,13 +6,16 @@ SET(RPM_NAME "core-${PKG_NAME}-tests") 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 diff --git a/automated-tests/src/dali-platform-abstraction/resource-collector.cpp b/automated-tests/src/dali-platform-abstraction/resource-collector.cpp index 7d78fb1..bf95844 100644 --- a/automated-tests/src/dali-platform-abstraction/resource-collector.cpp +++ b/automated-tests/src/dali-platform-abstraction/resource-collector.cpp @@ -16,6 +16,7 @@ */ #include "resource-collector.h" +#include "slp-platform-abstraction.h" #include namespace Dali @@ -27,7 +28,8 @@ namespace Platform using namespace Dali::Integration; ResourceCollector::ResourceCollector() : - mGrandTotalCompletions(0) + mGrandTotalCompletions(0), + mGrandTotalNotifications(0) { } @@ -35,6 +37,7 @@ ResourceCollector::~ResourceCollector() {} 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." ); @@ -52,8 +55,23 @@ void ResourceCollector::LoadFailed( Dali::Integration::ResourceId id, Dali::Inte ++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 */ diff --git a/automated-tests/src/dali-platform-abstraction/resource-collector.h b/automated-tests/src/dali-platform-abstraction/resource-collector.h index 8553e62..b4593ec 100644 --- a/automated-tests/src/dali-platform-abstraction/resource-collector.h +++ b/automated-tests/src/dali-platform-abstraction/resource-collector.h @@ -23,6 +23,12 @@ namespace Dali { + +namespace Integration +{ +class PlatformAbstraction; +} + namespace Internal { namespace Platform @@ -73,9 +79,17 @@ public: 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 */ diff --git a/automated-tests/src/dali-platform-abstraction/tct-dali-platform-abstraction-core.cpp b/automated-tests/src/dali-platform-abstraction/tct-dali-platform-abstraction-core.cpp index 6636584..4bf95fe 100644 --- a/automated-tests/src/dali-platform-abstraction/tct-dali-platform-abstraction-core.cpp +++ b/automated-tests/src/dali-platform-abstraction/tct-dali-platform-abstraction-core.cpp @@ -59,67 +59,110 @@ struct TestCase typedef std::map 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 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; iLoadResource( 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; +} diff --git a/automated-tests/src/dali-platform-abstraction/utc-image-loading-cancel-some-loads.cpp b/automated-tests/src/dali-platform-abstraction/utc-image-loading-cancel-some-loads.cpp new file mode 100644 index 0000000..0a89566 --- /dev/null +++ b/automated-tests/src/dali-platform-abstraction/utc-image-loading-cancel-some-loads.cpp @@ -0,0 +1,135 @@ +/* + * 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 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 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; +} diff --git a/automated-tests/src/dali-platform-abstraction/utc-image-loading-common.cpp b/automated-tests/src/dali-platform-abstraction/utc-image-loading-common.cpp new file mode 100644 index 0000000..181ff67 --- /dev/null +++ b/automated-tests/src/dali-platform-abstraction/utc-image-loading-common.cpp @@ -0,0 +1,99 @@ +/* + * 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 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; +} diff --git a/automated-tests/src/dali-platform-abstraction/utc-image-loading-common.h b/automated-tests/src/dali-platform-abstraction/utc-image-loading-common.h new file mode 100644 index 0000000..7ca44b3 --- /dev/null +++ b/automated-tests/src/dali-platform-abstraction/utc-image-loading-common.h @@ -0,0 +1,87 @@ +/* + * 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 +#include +#include +#include +#include +#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 gCancelAttributes; + + +void utc_dali_loading_startup(void); +void utc_dali_loading_cleanup(void); + +#endif // __DALI_TEST_SUITE_IMAGE_LOADING_COMMON_H__ diff --git a/automated-tests/src/dali-platform-abstraction/utc-image-loading-load-completion.cpp b/automated-tests/src/dali-platform-abstraction/utc-image-loading-load-completion.cpp new file mode 100644 index 0000000..65014b6 --- /dev/null +++ b/automated-tests/src/dali-platform-abstraction/utc-image-loading-load-completion.cpp @@ -0,0 +1,81 @@ +/* + * 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; +} diff --git a/automated-tests/src/dali-platform-abstraction/utc-image-loading.cpp b/automated-tests/src/dali-platform-abstraction/utc-image-loading.cpp deleted file mode 100644 index 4e41883..0000000 --- a/automated-tests/src/dali-platform-abstraction/utc-image-loading.cpp +++ /dev/null @@ -1,345 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#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 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 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; -} diff --git a/platform-abstractions/slp/resource-loader/resource-loader.cpp b/platform-abstractions/slp/resource-loader/resource-loader.cpp index 7312f6c..309328a 100755 --- a/platform-abstractions/slp/resource-loader/resource-loader.cpp +++ b/platform-abstractions/slp/resource-loader/resource-loader.cpp @@ -242,6 +242,7 @@ struct ResourceLoader::ResourceLoaderImpl bool IsLoading() { // TODO - not used - remove? + DALI_ASSERT_DEBUG( 0 == "IsLoading() Is not implemented so don't call it." ); return true; } diff --git a/platform-abstractions/slp/resource-loader/resource-loader.h b/platform-abstractions/slp/resource-loader/resource-loader.h index 9d216f2..dccb366 100644 --- a/platform-abstractions/slp/resource-loader/resource-loader.h +++ b/platform-abstractions/slp/resource-loader/resource-loader.h @@ -252,6 +252,7 @@ public: /** * @copydoc PlatformAbstraction::IsLoading() + * @deprecated This is not implemented: always returns true. */ bool IsLoading(); -- 2.7.4