--[[----------------------------------------------------------------------------

PixelPathProviderStateMachineTest.lua

--------------------------------------------------------------------------------

ADOBE CONFIDENTIAL
------------------
Copyright 2015 Adobe Systems Incorporated
All Rights Reserved.

NOTICE: All information contained herein is, and remain the property of
Adobe Systems Incorporated and its suppliers, if any. The intellectual and
technical concepts contained herein are proprietary to Adobe Systems
Incorporated and its suppliers and may be covered by U.S. and Foreign
Patents, patents in process, and are protected by trade secret or copyright
law. Dissemination of this information or reproduction of this material is
strictly forbidden unless prior written permission is obtained from
Adobe Systems Incorporated.

------------------------------------------------------------------------------]]

	-- If ACR is not supported, disable test before we attempt to
	-- require WFAcr. (That will fail.)
if not require 'WFPlatformInfo'.hasACR then return {} end

	-- wf.core
local WFRxTestCase = require 'WFRxTestCase'
local WFCatMockCatalog = require 'WFCatMockCatalog'
local WFFiniteStateMachine = require 'WFFiniteStateMachine'

local PixelPathProviderScheduler = require "PixelPathProviderScheduler"
local PixelPathProviderRequestTypes = require "PixelPathProviderRequestTypes"
local PixelPathProviderStateMachine = require "PixelPathProviderStateMachine"
local PixelPathProviderSchedulerPrivateMethods =
		require "PixelPathProviderSchedulerPrivateMethods"

--------------------------------------------------------------------------------

local PixelPathProviderRequestTypeNames = PixelPathProviderRequestTypes.names

--------------------------------------------------------------------------------

local getPriority = PixelPathProviderRequestTypes.getPriority

--------------------------------------------------------------------------------

local function makeSchedulerForTest( rxTest )

	local catalog = WFCatMockCatalog.createFakeWFCatalog( rxTest )

	local scheduler = PixelPathProviderScheduler( catalog )

	rxTest:finally( function() scheduler:close() end )

	return scheduler

end

--------------------------------------------------------------------------------

local function makeNoRunLoopSchedulerForTest( rxTest )

	local scheduler = makeSchedulerForTest( rxTest )

	local SchedulerTestAPI = scheduler:_expose_test_API()

	return SchedulerTestAPI.suspendRunLoop( scheduler )
	
end

--------------------------------------------------------------------------------

local function rxTestAddRequest( rxTest )

	local scheduler = makeNoRunLoopSchedulerForTest( rxTest )

	-- no requests in new scheduler
	rxTest:expectEqual( {}, { PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState( scheduler,  PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender ) } )

	-- test bogus requests
	rxTest:expectError( scheduler.addRequest, scheduler )

	local pixStream1 = "pixStream1"
	local asset1 = 1
	local longSide1 = 1

	rxTest:expectError( scheduler.addRequest, scheduler )

	rxTest:expectError( scheduler.addRequest, scheduler, 
							pixStream1, asset1, "bogus", longSide1 )

	-- no requests after bogus addRequest
	rxTest:expectEqual( {}, { PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState( scheduler,  PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender ) } )

	scheduler:addRequest( pixStream1, asset1, PixelPathProviderRequestTypeNames.highest, 
								longSide1 )

	-- successfully added a request
	rxTest:expectEqual( { pixStream1, asset1, 
								longSide1,
								getPriority( PixelPathProviderRequestTypeNames.highest ),
								PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender },
							{ PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState( scheduler,  PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender ) }	)

end

--------------------------------------------------------------------------------

local function rxTestRemoveRequest( rxTest )

	local scheduler = makeNoRunLoopSchedulerForTest( rxTest )

	local pixStream1 = "pixStream1"
	local asset1 = 1
	local longSide1 = 1

	scheduler:addRequest( pixStream1, asset1, PixelPathProviderRequestTypeNames.highest, 
								longSide1 )

	-- doesn't remove request from other pixstream
	scheduler:removeRequest( 'nonesuch', asset1 )

	rxTest:expectEqual( { pixStream1, asset1, 
								longSide1,
								getPriority( PixelPathProviderRequestTypeNames.highest ),
								PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender },
							{ PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState( scheduler,  PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender ) }	)

	-- does remove request
	scheduler:removeRequest( pixStream1, asset1 )

	rxTest:expectEqual( {}, { PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState( scheduler,  PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender ) } )


end

--------------------------------------------------------------------------------

local function rxTestSetRequestPriorityToNewPriority( rxTest )

	local scheduler = makeNoRunLoopSchedulerForTest( rxTest )

	local pixStream1 = "pixStream1"
	local asset1 = 1
	local longSide1 = 1

	scheduler:addRequest( pixStream1, asset1, PixelPathProviderRequestTypeNames.highest, 
								longSide1 )

	-- throws with bogus priorities
	rxTest:expectError( scheduler.setRequestPriorityToNewPriority, scheduler,
							pixStream1, asset1, 'nonesuch' )

	rxTest:expectError( scheduler.setRequestPriorityToNewPriority, 
							scheduler, pixStream1, asset1,
							PixelPathProviderRequestTypeNames.highest,
							'nonesuch' )

	-- doesn't change priority
	scheduler:setRequestPriorityToNewPriority( pixStream1, asset1, 
											PixelPathProviderRequestTypeNames.idle,
											PixelPathProviderRequestTypeNames.highest )

	rxTest:expectEqual( { pixStream1, asset1, 
								longSide1,
								getPriority( PixelPathProviderRequestTypeNames.highest ),
								PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender },
							{ PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState( scheduler,  PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender ) }	)

	-- does change priority
	scheduler:setRequestPriorityToNewPriority( pixStream1, asset1, 
											PixelPathProviderRequestTypeNames.highest,
											PixelPathProviderRequestTypeNames.idle )

	rxTest:expectEqual( { pixStream1, asset1, 
								longSide1,
								getPriority( PixelPathProviderRequestTypeNames.idle ),
								PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender },
							{ PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState( scheduler,  PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender ) }	)

end

--------------------------------------------------------------------------------

local function localAssetId( i )

	return i

end

--------------------------------------------------------------------------------

local function compareArrays( a1, a2 )

	if #a1 ~= #a2 then

		return #a1 > #a2

	end

	for i = 1, math.min( #a1, #a2 ) do

		if a1[ i ] ~= a2[ i ] then

			return a1[ i ] > a2 [i ]

		end

	end

	return false

end

--------------------------------------------------------------------------------

local function tableSort( t, elementSorter )

	table.sort( t, elementSorter )

	return t

end

--------------------------------------------------------------------------------

local function rxTestBatchAddRequests( rxTest )

	local scheduler = makeNoRunLoopSchedulerForTest( rxTest )

	local SchedulerTestAPI = scheduler:_expose_test_API()

	-- no requests in new scheduler
	rxTest:expectEqual( {}, { PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState( scheduler,  PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender ) } )

	local pixStream1 = "pixStream1"
	local longSide1 = 1

	local localAssetIds = {}

	local expectedRequests = {}

	local gridActivePriority = getPriority( PixelPathProviderRequestTypeNames.idle )

	for i = 1, 10 do

		localAssetIds[ i ] = localAssetId( i )

		expectedRequests[ i ] = 
			{ pixStream1, localAssetId( i ), longSide1, gridActivePriority, PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender }

	end

	-- test bogus invocations

	rxTest:expectError( scheduler.batchAddRequests, scheduler, nil )

	rxTest:expectError( scheduler.batchAddRequests, scheduler, 
							pixStream1, "string" )

	rxTest:expectError( scheduler.batchAddRequests, scheduler, 
							pixStream1, localAssetId( 1 ), "bogus", longSide1 )

	rxTest:expectError( scheduler.batchAddRequests, scheduler, 
							pixStream1, localAssetIds, "bogus", longSide1 )

	-- no requests after bogus addRequest
	rxTest:expectEqual( {}, { PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState( scheduler,  PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender ) } )

	scheduler:batchAddRequests( pixStream1, localAssetIds,
									PixelPathProviderRequestTypeNames.idle, 
									longSide1 )

	rxTest:expectEqual( 
		tableSort( expectedRequests, compareArrays ),
		tableSort( 
			SchedulerTestAPI.getRequestsWithStatesInPriorityOrder( scheduler,
							PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender ),
			compareArrays ) )

end

--------------------------------------------------------------------------------

local function rxTestBatchRemoveRequests( rxTest )

	local scheduler = makeNoRunLoopSchedulerForTest( rxTest )

	local SchedulerTestAPI = scheduler:_expose_test_API()

	-- no requests in new scheduler
	rxTest:expectEqual( {}, { PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState( scheduler,  PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender ) } )

	local pixStream1 = "pixStream1"

	local localAssetIds = {}

	local expectedRequests = {}

	local localAssetIdsToRemove = {}

	local removeOffset = 5

	local longSide1 = 52

	local gridActivePriority = getPriority( PixelPathProviderRequestTypeNames.idle )

	for i = 1, 10 do

		localAssetIds[ i ] = localAssetId( i )

		if i > removeOffset then

			expectedRequests[ i - removeOffset ] = 
				{ pixStream1, localAssetId( i ), longSide1, gridActivePriority, PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender }

		else

			localAssetIdsToRemove[ i ] = localAssetIds[ i ]

		end

	end

	rxTest:expectError( scheduler.batchRemoveRequests, scheduler )

	rxTest:expectError( scheduler.batchRemoveRequests, scheduler, 
							pixStream1, "string" )

	scheduler:batchAddRequests( pixStream1, localAssetIds,
									PixelPathProviderRequestTypeNames.idle, 
									longSide1 )

	scheduler:batchRemoveRequests( pixStream1, localAssetIdsToRemove )

	rxTest:expectEqual( 
		tableSort( expectedRequests, compareArrays ),
		tableSort( 
			SchedulerTestAPI.getRequestsWithStatesInPriorityOrder( scheduler,
							PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender ),
			compareArrays ) )

end

--------------------------------------------------------------------------------

local function pixStreamId( i )

	return string.format( "pixStreamId%d", i )

end

--------------------------------------------------------------------------------

local function rxTestGetNextRequest( rxTest )

	local scheduler = makeNoRunLoopSchedulerForTest( rxTest )
	
	local gridVisiblePriority = getPriority( PixelPathProviderRequestTypeNames.grid_visible )
	local loupeVisiblePriority = getPriority( PixelPathProviderRequestTypeNames.highest )
	
	-- add two grid_visible requests for different localAssetIds on same stream
	scheduler:addRequest( pixStreamId( 1 ), localAssetId( 1 ), 
							PixelPathProviderRequestTypeNames.grid_visible, 1 )

	scheduler:addRequest( pixStreamId( 1 ), localAssetId( 2 ), 
							PixelPathProviderRequestTypeNames.grid_visible, 2 )

	-- make sure the identical priorities are sorted FIFO
	rxTest:expectEqual( { pixStreamId( 1 ), localAssetId( 1 ), 
								1,
								gridVisiblePriority,
								PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender,},
							{ PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState( scheduler,  PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender ) }	)

	scheduler:abandonAllPixStreamRequests( pixStreamId( 1 ) )

	-- no more requests
	rxTest:expectEqual( {}, { PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState( scheduler,  PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender ) } )

	-- add two grid_visible requests for different localAssetIds on same stream
	scheduler:addRequest( pixStreamId( 1 ), localAssetId( 1 ), 
							PixelPathProviderRequestTypeNames.grid_visible, 1 )

	scheduler:addRequest( pixStreamId( 1 ), localAssetId( 2 ), 
							PixelPathProviderRequestTypeNames.grid_visible, 1 )

	-- update the size of each
	scheduler:addRequest( pixStreamId( 1 ), localAssetId( 1 ), 
							PixelPathProviderRequestTypeNames.grid_visible, 2 )

	scheduler:addRequest( pixStreamId( 1 ), localAssetId( 2 ), 
							PixelPathProviderRequestTypeNames.grid_visible, 2 )

	-- make sure first request is still FIFO
	rxTest:expectEqual( { pixStreamId( 1 ), localAssetId( 1 ), 
								2,
								gridVisiblePriority,
								PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender },
							{ PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState( scheduler,  PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender ) }	)

	scheduler:abandonAllPixStreamRequests( pixStreamId( 1 ) )

	-- no more requests
	rxTest:expectEqual( {}, { PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState( scheduler,  PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender ) } )

	-- add two grid_visible requests for different localAssetIds on same stream
	scheduler:addRequest( pixStreamId( 1 ), localAssetId( 1 ), 
							PixelPathProviderRequestTypeNames.grid_visible, 1 )

	scheduler:addRequest( pixStreamId( 1 ), localAssetId( 2 ), 
							PixelPathProviderRequestTypeNames.grid_visible, 2 )

	-- now decrease the priority of the first
	scheduler:addRequest( pixStreamId( 1 ), localAssetId( 1 ), 
							PixelPathProviderRequestTypeNames.idle, 1 )

	-- make sure second request is more urgent
	rxTest:expectEqual( { pixStreamId( 1 ), localAssetId( 2 ), 
								2,
								gridVisiblePriority,
								PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender }, 
							{ PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState( scheduler,  PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender ) }	)

	scheduler:abandonAllPixStreamRequests( pixStreamId( 1 ) )

	-- no more requests
	rxTest:expectEqual( {}, { PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState( scheduler,  PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender ) } )

	-- add two requests for same localAssetId on different streams, one of them loupe
	scheduler:addRequest( pixStreamId( 1 ), localAssetId( 1 ), 
							PixelPathProviderRequestTypeNames.grid_visible, 1 )

	scheduler:addRequest( pixStreamId( 2 ), localAssetId( 1 ), 
							PixelPathProviderRequestTypeNames.highest, 2 )

	-- make sure loupe comes first
	rxTest:expectEqual( { pixStreamId( 2 ), localAssetId( 1 ), 
								2,
								loupeVisiblePriority,
								PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender },
							{ PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState( scheduler,  PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender ) }	)

	scheduler:abandonAllLocalAssetIdRequests( localAssetId( 1 ) )

	-- no more requests
	rxTest:expectEqual( {}, { PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState( scheduler,  PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender ) } )

	-- add two requests for same localAssetId on different streams, one of them loupe
	scheduler:addRequest( pixStreamId( 1 ), localAssetId( 1 ), 
							PixelPathProviderRequestTypeNames.grid_visible, 1 )

	scheduler:addRequest( pixStreamId( 2 ), localAssetId( 1 ), 
							PixelPathProviderRequestTypeNames.highest, 2 )

	-- add another request, different localAssetId
	scheduler:addRequest( pixStreamId( 1 ), localAssetId( 2 ), 
							PixelPathProviderRequestTypeNames.grid_visible, 1 )

	scheduler:abandonAllLocalAssetIdRequests( localAssetId( 1 ) )

	-- make sure second localAssetId still scheduled
	rxTest:expectEqual( { pixStreamId( 1 ), localAssetId( 2 ), 
								1, 
								gridVisiblePriority,
								PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender },
							{ PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState( scheduler,  PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender ) }	)

	scheduler:abandonAllPixStreamRequests( pixStreamId( 1 ) )

	-- no more requests
	rxTest:expectEqual( {}, { PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState( scheduler,  PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender ) } )

	-- add two requests for same localAssetId on different streams
	scheduler:addRequest( pixStreamId( 1 ), localAssetId( 1 ), 
							PixelPathProviderRequestTypeNames.grid_visible, 1 )

	scheduler:addRequest( pixStreamId( 2 ), localAssetId( 1 ), 
							PixelPathProviderRequestTypeNames.grid_visible, 1 )

	scheduler:abandonAllPixStreamRequests( pixStreamId( 1 ) )

	-- make sure second pixstream still scheduled
	rxTest:expectEqual( { pixStreamId( 2 ), localAssetId( 1 ), 
								1,
								gridVisiblePriority,
								PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender },
							{ PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState( scheduler,  PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender ) }	)

end

--------------------------------------------------------------------------------

local function rxTestGetNextRequestWithState( rxTest )

	local scheduler = makeNoRunLoopSchedulerForTest( rxTest )
	
	local gridVisiblePriority = getPriority( PixelPathProviderRequestTypeNames.grid_visible )
	
	-- test bogus invocation
	rxTest:expectError( PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState, scheduler )

	-- add two grid requests for different localAssetIds on same stream
	scheduler:addRequest( pixStreamId( 1 ), localAssetId( 1 ), 
							PixelPathProviderRequestTypeNames.grid_visible, 1 )

	scheduler:addRequest( pixStreamId( 1 ), localAssetId( 2 ), 
							PixelPathProviderRequestTypeNames.grid_visible, 2 )

	-- tweak the states

	scheduler:_maybeSetNextState( pixStreamId( 1 ), localAssetId( 2 ), "must_fetch" )

	scheduler:_maybeSetNextState( pixStreamId( 1 ), localAssetId( 1 ), "must_fetch" )

	-- make sure it's the second request when we filter by state
	rxTest:expectEqual( 
		{ pixStreamId( 1 ), localAssetId( 1 ), 1,
			gridVisiblePriority,
			PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRenderFromDocstore },
		{ PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState( scheduler, 
			PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRenderFromDocstore ) } )

end

--------------------------------------------------------------------------------

local function rxTestCountRequestsWithState( rxTest )

	local scheduler = makeSchedulerForTest( rxTest )

	local SchedulerTestAPI = scheduler:_expose_test_API()

	local needSaveRenderStates =
			{ WFFiniteStateMachine.makeRetryableStates(
						PixelPathProviderStateMachine.SaveRenderRetryCount, 
							PixelPathProviderStateMachine.SaveRenderStates.NeedToSaveRender ) }

	for i = 1, #needSaveRenderStates do

		scheduler:addRequest( pixStreamId( i ), localAssetId( i ),
									PixelPathProviderRequestTypeNames.grid_visible, 1 )

		SchedulerTestAPI.setState( scheduler, pixStreamId( i ), localAssetId( i ), 
									needSaveRenderStates[ i ] )

	end

	rxTest:expectEqual( 0, 
						PixelPathProviderSchedulerPrivateMethods.countRequestsWithState( scheduler,  
							PixelPathProviderStateMachine.DocstoreInfoStates.FetchingAssetInfoForRender ) )

	rxTest:expectEqual( #needSaveRenderStates, 
						PixelPathProviderSchedulerPrivateMethods.countRequestsWithState( scheduler,  unpack( needSaveRenderStates ) ) )

end

--------------------------------------------------------------------------------

local function rxTestGetLocalAssetIdRequestsWithState( rxTest )

	local scheduler = makeSchedulerForTest( rxTest )

	local SchedulerTestAPI = scheduler:_expose_test_API()

	local nRetries = 5

	local needSaveRenderStates =
			{ WFFiniteStateMachine.makeRetryableStates(
						nRetries, 
							PixelPathProviderStateMachine.SaveRenderStates.NeedToSaveRender ) }

	for i = 1, #needSaveRenderStates do

		scheduler:addRequest( pixStreamId( i ), localAssetId( 1 ),
									PixelPathProviderRequestTypeNames.grid_visible, 1 )

		SchedulerTestAPI.setState( scheduler, pixStreamId( i ), localAssetId( 1 ), 
									needSaveRenderStates[ i ] )

	end

	for i = 1, #needSaveRenderStates do

		local pixStreamId1 = pixStreamId( i + #needSaveRenderStates )

		local pixStreamId2 = pixStreamId( i + 2*#needSaveRenderStates )

		scheduler:addRequest( pixStreamId1, localAssetId( 1 ),
									PixelPathProviderRequestTypeNames.grid_visible, 1 )

		scheduler:addRequest( pixStreamId2, localAssetId( 2 ),
									PixelPathProviderRequestTypeNames.grid_visible, 1 )

		SchedulerTestAPI.setState( scheduler, pixStreamId1, localAssetId( 1 ), 
									PixelPathProviderStateMachine.DocstoreInfoStates.FetchingAssetInfoForRender ) 

		SchedulerTestAPI.setState( scheduler, pixStreamId2, localAssetId( 2 ), 
									PixelPathProviderStateMachine.DocstoreInfoStates.FetchingAssetInfoForRender ) 

	end

	rxTest:expectEqual( 2 * #needSaveRenderStates, 
						PixelPathProviderSchedulerPrivateMethods.countRequestsWithState( scheduler,  
							PixelPathProviderStateMachine.DocstoreInfoStates.FetchingAssetInfoForRender ) )

	rxTest:expectEqual( #needSaveRenderStates, 
						PixelPathProviderSchedulerPrivateMethods.countRequestsWithState( scheduler,  unpack( needSaveRenderStates ) ) )

	local requests = scheduler:_getLocalAssetIdRequestsWithState( localAssetId( 1 ),
																	unpack( needSaveRenderStates ) )

	rxTest:expectEqual( #needSaveRenderStates, nRetries ) 

	rxTest:expectEqual( #needSaveRenderStates, #requests )

	for i, request in ipairs( requests ) do

		rxTest:expectEqual( request[ 1 ], pixStreamId( i ) )

	end

	requests = scheduler:_getLocalAssetIdRequestsWithState( localAssetId( 2 ),
																	unpack( needSaveRenderStates ) )

	rxTest:expectEqual( 0, #requests )

	requests = scheduler:_getLocalAssetIdRequestsWithState( localAssetId( 2 ),
																PixelPathProviderStateMachine.DocstoreInfoStates.FetchingAssetInfoForRender )

	for i, request in ipairs( requests ) do

		rxTest:expectEqual( request[ 1 ], pixStreamId( i + 2 * #needSaveRenderStates) )

	end

end


--------------------------------------------------------------------------------

local function rxTestSetNextState( rxTest )

	local scheduler = makeNoRunLoopSchedulerForTest( rxTest )
	
	local gridVisiblePriority = getPriority( PixelPathProviderRequestTypeNames.grid_visible )
	
	-- test bogusly updating the state of something not in the queue
	scheduler:_maybeSetNextState( pixStreamId( 1 ), localAssetId( 1 ), "bogus" )

	-- no more requests
	rxTest:expectEqual( {}, { PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState( scheduler,  PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender ) } )

	-- add a request
	scheduler:addRequest( pixStreamId( 1 ), localAssetId( 1 ), 
							PixelPathProviderRequestTypeNames.grid_visible, 1 )

	-- update the state
	scheduler:_maybeSetNextState( pixStreamId( 1 ), localAssetId( 1 ), "must_fetch" )

	-- make sure request state is correct
	rxTest:expectEqual( { pixStreamId( 1 ), localAssetId( 1 ), 
								1,
								gridVisiblePriority,
								PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRenderFromDocstore }, 
						{ PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState( scheduler,  PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRenderFromDocstore ) }	)

	-- update the state bogusly - _maybeSetNextState should not change state if there is no transition with the given name 
	scheduler:_maybeSetNextState( pixStreamId( 1 ), localAssetId( 1 ), "bogus" )

	-- make sure request state is correct
	rxTest:expectEqual( { pixStreamId( 1 ), localAssetId( 1 ), 
								1,
								gridVisiblePriority,
								PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRenderFromDocstore },
							{ PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState( scheduler,  PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRenderFromDocstore ) }	)

end

--------------------------------------------------------------------------------

local function rxTestMaybeApplyTransitionForLocalAssetId( rxTest )

	local scheduler = makeNoRunLoopSchedulerForTest( rxTest )

	local SchedulerTestAPI = scheduler:_expose_test_API()
	
	local gridVisiblePriority = getPriority( PixelPathProviderRequestTypeNames.grid_visible )
	
	-- add requests
	scheduler:addRequest( pixStreamId( 1 ), localAssetId( 1 ), 
							PixelPathProviderRequestTypeNames.grid_visible, 1 )

	SchedulerTestAPI.setState( scheduler, pixStreamId( 1 ), localAssetId( 1 ), 
								PixelPathProviderStateMachine.RenderStates.NeedsRender )

	scheduler:addRequest( pixStreamId( 2 ), localAssetId( 1 ), 
							PixelPathProviderRequestTypeNames.grid_visible, 2 )

	SchedulerTestAPI.setState( scheduler, pixStreamId( 2 ), localAssetId( 1 ), 
								PixelPathProviderStateMachine.DownloadStates.NeedOriginalDownload )

	rxTest:log( "before transitions applied" )

	-- make sure relevant requests updated
	rxTest:expectEqual( { pixStreamId( 1 ), localAssetId( 1 ), 1,
							gridVisiblePriority,
							PixelPathProviderStateMachine.RenderStates.NeedsRender },
						{ PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState( scheduler, 
										PixelPathProviderStateMachine.RenderStates.NeedsRender ) } )

	rxTest:expectEqual( { pixStreamId( 2 ), localAssetId( 1 ), 2,
								gridVisiblePriority,
								PixelPathProviderStateMachine.DownloadStates.NeedOriginalDownload },
						{ PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState( scheduler, 
										PixelPathProviderStateMachine.DownloadStates.NeedOriginalDownload ) } )

	rxTest:log( "test apply new_docstore_render_info" )

	PixelPathProviderSchedulerPrivateMethods.maybeApplyTransitionForLocalAssetId( scheduler, localAssetId( 1 ), 'new_docstore_render_info' )

	-- make sure relevant requests updated
	rxTest:expectEqual( { pixStreamId( 1 ), localAssetId( 1 ), 1,
							gridVisiblePriority,
							PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender },
						{ PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState( scheduler, 
										PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender ) } )

	rxTest:expectEqual( { pixStreamId( 2 ), localAssetId( 1 ), 2,
								gridVisiblePriority,
								PixelPathProviderStateMachine.DownloadStates.NeedOriginalDownload },
						{ PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState( scheduler, 
										PixelPathProviderStateMachine.DownloadStates.NeedOriginalDownload ) } )

	rxTest:log( "test apply new_docstore_binary_path_info" )

	-- make sure relevant requests updated
	PixelPathProviderSchedulerPrivateMethods.maybeApplyTransitionForLocalAssetId( scheduler, localAssetId( 1 ), 'new_docstore_binary_path_info' )

	rxTest:expectEqual( { pixStreamId( 2 ), localAssetId( 1 ), 2,
								gridVisiblePriority,
								PixelPathProviderStateMachine.DownloadStates.NeedOriginalDownload },
						{ PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState( scheduler, 
										PixelPathProviderStateMachine.DownloadStates.NeedOriginalDownload ) } )

	-- make sure request state is correct
	rxTest:expectEqual( { pixStreamId( 1 ), localAssetId( 1 ), 1,
							gridVisiblePriority,
							PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender },
						{ PixelPathProviderSchedulerPrivateMethods.getNextRequestWithState( scheduler, 
										PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender ) } )

end

--------------------------------------------------------------------------------

local function rxTestApplyTransitionToAllWithState( rxTest )

	local scheduler = makeNoRunLoopSchedulerForTest( rxTest )

	local SchedulerTestAPI = scheduler:_expose_test_API()

	-- add requests
	scheduler:addRequest( pixStreamId( 1 ), localAssetId( 1 ), 
							PixelPathProviderRequestTypeNames.grid_visible, 1 )

	scheduler:addRequest( pixStreamId( 1 ), localAssetId( 2 ), 
							PixelPathProviderRequestTypeNames.grid_visible, 1 )

	scheduler:addRequest( pixStreamId( 2 ), localAssetId( 2 ), 
							PixelPathProviderRequestTypeNames.grid_visible, 2 )

	scheduler:addRequest( pixStreamId( 3 ), localAssetId( 1 ), 
							PixelPathProviderRequestTypeNames.grid_visible, 3 )

	SchedulerTestAPI.setState( scheduler, pixStreamId( 3 ), localAssetId( 1 ), 
								PixelPathProviderStateMachine.DownloadStates.DownloadingRender )


	local gridVisiblePriority = 
			getPriority( PixelPathProviderRequestTypeNames.grid_visible )

	rxTest:expectEqual( 
		tableSort( 
			{ { pixStreamId( 1 ), localAssetId( 1 ), 1, gridVisiblePriority, PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender },
			  { pixStreamId( 1 ), localAssetId( 2 ), 1, gridVisiblePriority, PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender },
			  { pixStreamId( 2 ), localAssetId( 2 ), 2, gridVisiblePriority, PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender } }, 
			compareArrays ),
		tableSort( 
			SchedulerTestAPI.getRequestsWithStatesInPriorityOrder( scheduler,
							PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender ),
			compareArrays ) )

	-- one request with DownloadingRender
	rxTest:expectEqual( 
		tableSort( 
			{ { pixStreamId( 3 ), localAssetId( 1 ), 3, gridVisiblePriority, PixelPathProviderStateMachine.DownloadStates.DownloadingRender } },
			compareArrays ),
		tableSort( 
			SchedulerTestAPI.getRequestsWithStatesInPriorityOrder( scheduler,
							PixelPathProviderStateMachine.DownloadStates.DownloadingRender ),
			compareArrays ) )

	-- no requests with FetchingAssetInfoForRender
	rxTest:expectEqual( 
			{},
			SchedulerTestAPI.getRequestsWithStatesInPriorityOrder( scheduler,
							PixelPathProviderStateMachine.DocstoreInfoStates.FetchingAssetInfoForRender ) )
					
	-- apply the transitition
	SchedulerTestAPI.applyTransitionToAllWithState( scheduler,
					PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender,
					'must_fetch' )

	-- make sure request states correct

	-- 3 requests with FetchingAssetInfoForRender
	rxTest:expectEqual( 
		tableSort( 
			{ { pixStreamId( 1 ), localAssetId( 1 ), 1, gridVisiblePriority, PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRenderFromDocstore },
			  { pixStreamId( 1 ), localAssetId( 2 ), 1, gridVisiblePriority, PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRenderFromDocstore },
			  { pixStreamId( 2 ), localAssetId( 2 ), 2, gridVisiblePriority, PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRenderFromDocstore } }, 
			compareArrays ),
		tableSort( 
			SchedulerTestAPI.getRequestsWithStatesInPriorityOrder( scheduler,
							PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRenderFromDocstore ),
			compareArrays ) )

	-- one request with DownloadingRender
	rxTest:expectEqual( 
		tableSort( 
			{ { pixStreamId( 3 ), localAssetId( 1 ), 3, gridVisiblePriority, PixelPathProviderStateMachine.DownloadStates.DownloadingRender } },
			compareArrays ),
		tableSort( 
			SchedulerTestAPI.getRequestsWithStatesInPriorityOrder( scheduler,
							PixelPathProviderStateMachine.DownloadStates.DownloadingRender ),
			compareArrays ) )

	-- no requests with NeedAssetInfoForRenderFromDocstore
	rxTest:expectEqual( 
			{},
			SchedulerTestAPI.getRequestsWithStatesInPriorityOrder( scheduler,
							PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender ) )

end

--------------------------------------------------------------------------------

local function rxTestRunOnAllRequestsWithState( rxTest )

	local scheduler = makeNoRunLoopSchedulerForTest( rxTest )

	local SchedulerTestAPI = scheduler:_expose_test_API()

	-- add requests
	scheduler:addRequest( pixStreamId( 1 ), localAssetId( 1 ), 
							PixelPathProviderRequestTypeNames.grid_visible, 1 )

	scheduler:addRequest( pixStreamId( 1 ), localAssetId( 2 ), 
							PixelPathProviderRequestTypeNames.grid_visible, 1 )

	scheduler:addRequest( pixStreamId( 2 ), localAssetId( 2 ), 
							PixelPathProviderRequestTypeNames.grid_visible, 2 )

	scheduler:addRequest( pixStreamId( 3 ), localAssetId( 1 ), 
							PixelPathProviderRequestTypeNames.grid_visible, 3 )

	SchedulerTestAPI.setState( scheduler, pixStreamId( 3 ), localAssetId( 1 ), 
								PixelPathProviderStateMachine.DownloadStates.DownloadingRender )

	------------------------------------------------------------

	local function mungeRequest( request, newThird )

		return { request[ 1 ], request[ 2 ], newThird }

	end

	------------------------------------------------------------

	-- 3 requests with NeedAssetInfoForRender
	rxTest:expectEqual( 
		tableSort( 
			{ { pixStreamId( 1 ), localAssetId( 1 ), "mange" },
			  { pixStreamId( 1 ), localAssetId( 2 ), "mange" },
			  { pixStreamId( 2 ), localAssetId( 2 ), "mange" } },
			compareArrays ),
		tableSort( 
			scheduler:_mapAllRequestsWithState( 
						PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender,
						mungeRequest,
							"mange" ),
			compareArrays ) )

	-- one request with DownloadingRender
	rxTest:expectEqual( 
		tableSort( 
			{ { pixStreamId( 3 ), localAssetId( 1 ), "minge" } },
			compareArrays ),
		tableSort( 
			scheduler:_mapAllRequestsWithState( 
						PixelPathProviderStateMachine.DownloadStates.DownloadingRender,
						mungeRequest,
							"minge" ),
			compareArrays ) )

	-- no requests with FetchingAssetInfoForRender
	rxTest:expectEqual( 
			{},
			scheduler:_mapAllRequestsWithState( 
						PixelPathProviderStateMachine.DocstoreInfoStates.FetchingAssetInfoForRender,
						mungeRequest,
							"munge" ) )


end

--------------------------------------------------------------------------------

local function rxTestChangeCounter( rxTest )

	local scheduler = makeNoRunLoopSchedulerForTest( rxTest )

	rxTest:expectEqual( nil, PixelPathProviderSchedulerPrivateMethods.getChangeCounter( scheduler ) )

	-- add a request
	scheduler:addRequest( pixStreamId( 1 ), localAssetId( 1 ), 
							PixelPathProviderRequestTypeNames.grid_visible, 1 )

	local currChangeCounter1 = PixelPathProviderSchedulerPrivateMethods.getChangeCounter( scheduler )

	rxTest:expectEqual( true, currChangeCounter1 ~= nil )

	-- update the state
	scheduler:_maybeSetNextState( pixStreamId( 1 ), localAssetId( 1 ), "must_fetch" )

	local currChangeCounter2 = PixelPathProviderSchedulerPrivateMethods.getChangeCounter( scheduler )

	rxTest:expectEqual( true, currChangeCounter1 == currChangeCounter2 )

	-- close remaining pixstreams
	scheduler:abandonAllPixStreamRequests( pixStreamId( 1 ) )

	local currChangeCounter3 = PixelPathProviderSchedulerPrivateMethods.getChangeCounter( scheduler )

	rxTest:expectEqual( true, currChangeCounter3 ~= currChangeCounter2 )

	rxTest:expectEqual( true, currChangeCounter3 ~= currChangeCounter1 )

end

--------------------------------------------------------------------------------

local function rxTestGetLocalAssetIds( rxTest )

	local scheduler = makeNoRunLoopSchedulerForTest( rxTest )

	rxTest:expectEqual( {}, PixelPathProviderSchedulerPrivateMethods.getLocalAssetIds( scheduler ) )

	-- add requests
	scheduler:addRequest( pixStreamId( 1 ), localAssetId( 1 ), 
							PixelPathProviderRequestTypeNames.grid_visible, 1 )

	scheduler:addRequest( pixStreamId( 2 ), localAssetId( 1 ), 
							PixelPathProviderRequestTypeNames.grid_visible, 1 )

	scheduler:addRequest( pixStreamId( 1 ), localAssetId( 2 ), 
							PixelPathProviderRequestTypeNames.grid_visible, 1 )

	local SchedulerTestAPI = scheduler:_expose_test_API()

	rxTest:expectEqual( true, SchedulerTestAPI.hasRequestForLocalAssetId( scheduler, localAssetId( 1 ) ) )

	rxTest:expectEqual( true, scheduler:_hasPixstreamRequestForLocalAssetId( 
											pixStreamId( 1 ), localAssetId( 1 ) ) )

	rxTest:expectEqual( true, scheduler:_hasPixstreamRequestForLocalAssetId( 
											pixStreamId( 1 ), localAssetId( 2 ) ) )

	rxTest:expectEqual( false, scheduler:_hasPixstreamRequestForLocalAssetId( 
											pixStreamId( 1 ), localAssetId( 3 ) ) )

	rxTest:expectEqual( true, scheduler:_hasPixstreamRequestForLocalAssetId( 
											pixStreamId( 2 ), localAssetId( 1 ) ) )

	rxTest:expectEqual( false, scheduler:_hasPixstreamRequestForLocalAssetId( 
											pixStreamId( 2 ), localAssetId( 2 ) ) )

	rxTest:expectEqual( true, SchedulerTestAPI.hasRequestForLocalAssetId( scheduler, localAssetId( 2 ) ) )

	rxTest:expectEqual( false , SchedulerTestAPI.hasRequestForLocalAssetId( scheduler, localAssetId( 3 ) ) )

	-- close pixstream
	scheduler:abandonAllPixStreamRequests( pixStreamId( 1 ) )

	rxTest:expectEqual( true, SchedulerTestAPI.hasRequestForLocalAssetId( scheduler, localAssetId( 1 ) ) )

	rxTest:expectEqual( false, SchedulerTestAPI.hasRequestForLocalAssetId( scheduler, localAssetId( 2 ) ) )

	rxTest:expectEqual( tableSort( { localAssetId( 1 ) } ), 
							tableSort( PixelPathProviderSchedulerPrivateMethods.getLocalAssetIds( scheduler ) ) )

end

--------------------------------------------------------------------------------

local function rxTestGetNormalizedRenderSize( rxTest )

	-- test with a multip cell size. This like the dynamic grid
	local cellSizes = { 1280, 1280, 640, 640 }

	rxTest:log( "Testing cell 1 in dynamic grid, expecting 1280 since we have cell size for cell 1" )

	rxTest
		:expectEqual(
			PixelPathProviderScheduler.privateUnitTestExports.getNormalizedRenderSize(
				cellSizes, 1, 2048 ),
			1280 )

	rxTest:log( "Testing cell 2 in dynamic grid, expecting 640 we have cell size for cell 2" )

	rxTest
		:expectEqual(
			PixelPathProviderScheduler.privateUnitTestExports.getNormalizedRenderSize(
				cellSizes, 2, 2048 ),
			640 )

	-- test with a single cell size. This is like the static grid
	cellSizes = { 320 }

	rxTest:log( "Testing cell 3 in static grid, expecting 640, which is the specified default to use when cell size is not given for a cell" )

	rxTest
		:expectEqual(
			PixelPathProviderScheduler.privateUnitTestExports.getNormalizedRenderSize(
				cellSizes, 3, 640 ),
			640 )

end

--==============================================================================

return WFRxTestCase.wrapSuite( {

	rxTestAddRequest = rxTestAddRequest,

	rxTestRemoveRequest = rxTestRemoveRequest,

	rxTestSetRequestPriorityToNewPriority = 
		rxTestSetRequestPriorityToNewPriority,

	rxTestBatchAddRequests = rxTestBatchAddRequests,

	rxTestBatchRemoveRequests = rxTestBatchRemoveRequests,

	rxTestGetNextRequest = rxTestGetNextRequest,

	rxTestGetNextRequestWithState = rxTestGetNextRequestWithState,

	rxTestCountRequestsWithState = rxTestCountRequestsWithState,

	rxTestGetLocalAssetIdRequestsWithState =
		rxTestGetLocalAssetIdRequestsWithState,

	rxTestSetNextState = rxTestSetNextState,

	rxTestMaybeApplyTransitionForLocalAssetId = 
		rxTestMaybeApplyTransitionForLocalAssetId,

	rxTestApplyTransitionToAllWithState = rxTestApplyTransitionToAllWithState,

	rxTestRunOnAllRequestsWithState = rxTestRunOnAllRequestsWithState,

	rxTestChangeCounter = rxTestChangeCounter,

	rxTestGetLocalAssetIds = rxTestGetLocalAssetIds,

	rxTestGetNormalizedRenderSize = rxTestGetNormalizedRenderSize,

} )
