EXPORT_C void RMemSpySession::GetThreadInfoItemsL( RArray<CMemSpyApiThreadInfoItem*> &aInfoItems, TThreadId aId, TMemSpyThreadInfoItemType aType )
	{
	TPckgBuf<TThreadId> id( aId );	
	TPckgBuf<TMemSpyThreadInfoItemType> type( aType );
	TPckgBuf<TInt> count;	
	
	TInt error = SendReceive( EMemSpyClientServerOpGetThreadInfoItemsCount, TIpcArgs( &id, &type, &count ) );
	TInt itemCount = count();
	
	if( error == KErrNone )
		{		
		if( itemCount == 0 )
			{
			aInfoItems.Reset();
			}
		else
			{
			HBufC8* buffer = HBufC8::NewLC( itemCount * sizeof(TMemSpyThreadInfoItemData) );
			TPtr8 bufferPtr(buffer->Des());
			
			TPckgBuf<TInt> requestedCount( itemCount );
			
			TIpcArgs args( &requestedCount, &id, &type, &bufferPtr );
			TInt error = SendReceive( EMemSpyClientServerOpGetThreadInfoItems, args ); // TODO check error
			
			aInfoItems.Reset();
		
			for(TInt i=0, offset = 0; i < itemCount; i++, offset+=sizeof(TMemSpyThreadInfoItemData))
				{
				TPckgBuf<TMemSpyThreadInfoItemData> data;
				data.Copy(bufferPtr.Ptr()+offset, sizeof(TMemSpyThreadInfoItemData));
				aInfoItems.AppendL(CMemSpyApiThreadInfoItem::NewLC(data()));
				}
			
			CleanupStack::Pop(aInfoItems.Count());
			CleanupStack::PopAndDestroy(buffer);
			}
		}
	
	User::LeaveIfError(error);
	}
namespace Magnum { namespace Test {

struct ResourceManagerTest: TestSuite::Tester {
    explicit ResourceManagerTest();

    void state();
    void stateFallback();
    void stateDisallowed();
    void basic();
    void residentPolicy();
    void referenceCountedPolicy();
    void manualPolicy();
    void defaults();
    void clear();
    void clearWhileReferenced();
    void loader();
};

struct Data {
    static std::size_t count;

    explicit Data() { ++count; }
    ~Data() { --count; }
};

typedef Magnum::ResourceManager<Int, Data> ResourceManager;

size_t Data::count = 0;

ResourceManagerTest::ResourceManagerTest() {
    addTests({&ResourceManagerTest::state,
              &ResourceManagerTest::stateFallback,
              &ResourceManagerTest::stateDisallowed,
              &ResourceManagerTest::basic,
              &ResourceManagerTest::residentPolicy,
              &ResourceManagerTest::referenceCountedPolicy,
              &ResourceManagerTest::manualPolicy,
              &ResourceManagerTest::defaults,
              &ResourceManagerTest::clear,
              &ResourceManagerTest::clearWhileReferenced,
              &ResourceManagerTest::loader});
}

void ResourceManagerTest::state() {
    ResourceManager rm;

    Resource<Data> data = rm.get<Data>("data");
    CORRADE_VERIFY(!data);
    CORRADE_COMPARE(data.state(), ResourceState::NotLoaded);
    CORRADE_COMPARE(rm.state<Data>("data"), ResourceState::NotLoaded);

    rm.set<Data>("data", nullptr, ResourceDataState::Loading, ResourcePolicy::Resident);
    CORRADE_VERIFY(!data);
    CORRADE_COMPARE(data.state(), ResourceState::Loading);
    CORRADE_COMPARE(rm.state<Data>("data"), ResourceState::Loading);

    rm.set<Data>("data", nullptr, ResourceDataState::NotFound, ResourcePolicy::Resident);
    CORRADE_VERIFY(!data);
    CORRADE_COMPARE(data.state(), ResourceState::NotFound);
    CORRADE_COMPARE(rm.state<Data>("data"), ResourceState::NotFound);

    /* Nothing happened at all */
    CORRADE_COMPARE(Data::count, 0);
}

void ResourceManagerTest::stateFallback() {
    {
        ResourceManager rm;
        rm.setFallback(new Data);

        Resource<Data> data = rm.get<Data>("data");
        CORRADE_VERIFY(data);
        CORRADE_COMPARE(data.state(), ResourceState::NotLoadedFallback);
        CORRADE_COMPARE(rm.state<Data>("data"), ResourceState::NotLoadedFallback);

        rm.set<Data>("data", nullptr, ResourceDataState::Loading, ResourcePolicy::Resident);
        CORRADE_VERIFY(data);
        CORRADE_COMPARE(data.state(), ResourceState::LoadingFallback);
        CORRADE_COMPARE(rm.state<Data>("data"), ResourceState::LoadingFallback);

        rm.set<Data>("data", nullptr, ResourceDataState::NotFound, ResourcePolicy::Resident);
        CORRADE_VERIFY(data);
        CORRADE_COMPARE(data.state(), ResourceState::NotFoundFallback);
        CORRADE_COMPARE(rm.state<Data>("data"), ResourceState::NotFoundFallback);

        /* Only fallback is here */
        CORRADE_COMPARE(Data::count, 1);
    }

    /* Fallback gets destroyed */
    CORRADE_COMPARE(Data::count, 0);
}

void ResourceManagerTest::stateDisallowed() {
    ResourceManager rm;

    std::ostringstream out;
    Error::setOutput(&out);

    rm.set("data", Data(), ResourceDataState::Loading, ResourcePolicy::Resident);
    CORRADE_COMPARE(out.str(), "ResourceManager::set(): data should be null if and only if state is NotFound or Loading\n");

    out.str({});
    rm.set<Data>("data", nullptr, ResourceDataState::Final, ResourcePolicy::Resident);
    CORRADE_COMPARE(out.str(), "ResourceManager::set(): data should be null if and only if state is NotFound or Loading\n");
}

void ResourceManagerTest::basic() {
    ResourceManager rm;

    /* One mutable, one final */
    ResourceKey questionKey("the-question");
    ResourceKey answerKey("the-answer");
    rm.set(questionKey, 10, ResourceDataState::Mutable, ResourcePolicy::Resident);
    rm.set(answerKey, 42, ResourceDataState::Final, ResourcePolicy::Resident);
    Resource<Int> theQuestion = rm.get<Int>(questionKey);
    Resource<Int> theAnswer = rm.get<Int>(answerKey);

    /* Check basic functionality */
    CORRADE_COMPARE(theQuestion.state(), ResourceState::Mutable);
    CORRADE_COMPARE(theAnswer.state(), ResourceState::Final);
    CORRADE_COMPARE(*theQuestion, 10);
    CORRADE_COMPARE(*theAnswer, 42);
    CORRADE_COMPARE(rm.count<Int>(), 2);

    /* Cannot change already final resource */
    std::ostringstream out;
    Error::setOutput(&out);
    rm.set(answerKey, 43, ResourceDataState::Mutable, ResourcePolicy::Resident);
    CORRADE_COMPARE(*theAnswer, 42);
    CORRADE_COMPARE(out.str(), "ResourceManager::set(): cannot change already final resource " + answerKey.hexString() + '\n');

    /* But non-final can be changed */
    rm.set(questionKey, 20, ResourceDataState::Final, ResourcePolicy::Resident);
    CORRADE_COMPARE(theQuestion.state(), ResourceState::Final);
    CORRADE_COMPARE(*theQuestion, 20);
}

void ResourceManagerTest::residentPolicy() {
    ResourceManager* rm = new ResourceManager;

    rm->set("blah", new Data, ResourceDataState::Mutable, ResourcePolicy::Resident);
    CORRADE_COMPARE(Data::count, 1);

    rm->free();
    CORRADE_COMPARE(Data::count, 1);

    delete rm;
    CORRADE_COMPARE(Data::count, 0);
}

void ResourceManagerTest::referenceCountedPolicy() {
    ResourceManager rm;

    ResourceKey dataRefCountKey("dataRefCount");

    /* Resource is deleted after all references are removed */
    rm.set(dataRefCountKey, new Data, ResourceDataState::Final, ResourcePolicy::ReferenceCounted);
    CORRADE_COMPARE(rm.count<Data>(), 1);
    {
        Resource<Data> data = rm.get<Data>(dataRefCountKey);
        CORRADE_COMPARE(data.state(), ResourceState::Final);
        CORRADE_COMPARE(Data::count, 1);
    }

    CORRADE_COMPARE(rm.count<Data>(), 0);
    CORRADE_COMPARE(Data::count, 0);

    /* Reference counted resources which were not used once will stay loaded
       until free() is called */
    rm.set(dataRefCountKey, new Data, ResourceDataState::Final, ResourcePolicy::ReferenceCounted);
    CORRADE_COMPARE(rm.count<Data>(), 1);
    CORRADE_COMPARE(rm.state<Data>(dataRefCountKey), ResourceState::Final);
    CORRADE_COMPARE(rm.referenceCount<Data>(dataRefCountKey), 0);

    rm.free<Data>();
    CORRADE_COMPARE(rm.count<Data>(), 0);
    CORRADE_COMPARE(rm.state<Data>(dataRefCountKey), ResourceState::NotLoaded);
    CORRADE_COMPARE(rm.referenceCount<Data>(dataRefCountKey), 0);
}

void ResourceManagerTest::manualPolicy() {
    ResourceManager rm;

    ResourceKey dataKey("data");

    /* Manual free */
    {
        rm.set(dataKey, new Data, ResourceDataState::Mutable, ResourcePolicy::Manual);
        Resource<Data> data = rm.get<Data>(dataKey);
        rm.free();
    }

    CORRADE_COMPARE(rm.count<Data>(), 1);
    CORRADE_COMPARE(Data::count, 1);
    rm.free();
    CORRADE_COMPARE(rm.count<Data>(), 0);
    CORRADE_COMPARE(Data::count, 0);

    rm.set(dataKey, new Data, ResourceDataState::Mutable, ResourcePolicy::Manual);
    CORRADE_COMPARE(rm.count<Data>(), 1);
    CORRADE_COMPARE(Data::count, 1);
}

void ResourceManagerTest::defaults() {
    ResourceManager rm;
    rm.set("data", new Data);
    CORRADE_COMPARE(rm.state<Data>("data"), ResourceState::Final);
}

void ResourceManagerTest::clear() {
    ResourceManager rm;

    rm.set("blah", new Data);
    CORRADE_COMPARE(Data::count, 1);

    rm.free();
    CORRADE_COMPARE(Data::count, 1);

    rm.clear();
    CORRADE_COMPARE(Data::count, 0);
}

void ResourceManagerTest::clearWhileReferenced() {
    /* Should cover also the destruction case */

    std::ostringstream out;
    Error::setOutput(&out);

    ResourceManager rm;
    rm.set("blah", Int());
    /** @todo this will leak, is there any better solution without hitting
        assertion in decrementReferenceCount()? */
    new Resource<Int>(rm.get<Int>("blah"));

    rm.clear();
    CORRADE_COMPARE(out.str(), "ResourceManager: cleared/destroyed while data are still referenced\n");
}

void ResourceManagerTest::loader() {
    class IntResourceLoader: public AbstractResourceLoader<Int> {
        public:
            IntResourceLoader(): resource(ResourceManager::instance().get<Data>("data")) {}

            void load() {
                set("hello", 773, ResourceDataState::Final, ResourcePolicy::Resident);
                setNotFound("world");
            }

        private:
            void doLoad(ResourceKey) override {}

            std::string doName(ResourceKey key) const override {
                if(key == ResourceKey("hello")) return "hello";
                return "";
            }

            /* To verify that the loader is destroyed before unloading
               _all types of_ resources */
            Resource<Data> resource;
    };

    auto rm = new ResourceManager;
    auto loader = new IntResourceLoader;
    rm->setLoader(loader);

    {
        Resource<Data> data = rm->get<Data>("data");
        Resource<Int> hello = rm->get<Int>("hello");
        Resource<Int> world = rm->get<Int>("world");
        CORRADE_COMPARE(data.state(), ResourceState::NotLoaded);
        CORRADE_COMPARE(hello.state(), ResourceState::Loading);
        CORRADE_COMPARE(world.state(), ResourceState::Loading);

        CORRADE_COMPARE(loader->requestedCount(), 2);
        CORRADE_COMPARE(loader->loadedCount(), 0);
        CORRADE_COMPARE(loader->notFoundCount(), 0);
        CORRADE_COMPARE(loader->name(ResourceKey("hello")), "hello");

        loader->load();
        CORRADE_COMPARE(hello.state(), ResourceState::Final);
        CORRADE_COMPARE(*hello, 773);
        CORRADE_COMPARE(world.state(), ResourceState::NotFound);

        CORRADE_COMPARE(loader->requestedCount(), 2);
        CORRADE_COMPARE(loader->loadedCount(), 1);
        CORRADE_COMPARE(loader->notFoundCount(), 1);

        /* Verify that the loader is deleted at proper time */
        rm->set("data", new Data);
        CORRADE_COMPARE(Data::count, 1);
    }

    delete rm;
    CORRADE_COMPARE(Data::count, 0);
}

}}