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

WFRxSchedulerTest.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.

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

	-- WF.core.os
local KSDateImp = require 'KSDateImp'

	-- WF.core.rxlua
local WFRxScheduler = require 'WFRxScheduler'


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

local WFRxSchedulerTest = {}

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

local scheduler = assert( WFRxScheduler.sharedScheduler() )

local timeNow = assert( KSDateImp.timeNowForAnimation )

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

function WFRxSchedulerTest.testNow()

	local currentTime = timeNow()

	assert( scheduler:now() >= currentTime,
			"time isn't tied to KSDate.timeNowForAnimation")

end

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

function WFRxSchedulerTest.testSchedule( test )

	scheduler:schedule( function()

		test:notifyTestDidSucceed()

	end )

	test:waitForTestToFinish( 1 )

end

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

function WFRxSchedulerTest.testErrorInScheduledFunction( test )

	local gotToFirstFunction = false

	scheduler:schedule( function()

		gotToFirstFunction = true
		error "expect this error"

	end )

	scheduler:schedule( function()

		if not gotToFirstFunction then
			test:log( "Did not reach first function first" )
			test:notifyTestDidFail()
		else
			test:notifyTestDidSucceed()
		end

	end )

	test:waitForTestToFinish( 1 )

end

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

function WFRxSchedulerTest.testScheduleFuture( test )

	local ran = false

	local dueTime = timeNow() + .25

	scheduler:scheduleFuture( .25, function()

		if ran then
			test:log( "scheduled function invoked twice" )
			test:notifyTestDidFail()
			return
		end

		if timeNow() < dueTime then
			test:log( "scheduled function ran too soon" )
			test:notifyTestDidFail()
			return
		end

		ran = true

		test:notifyTestDidSucceed()

	end )

	assert( not ran, "scheduled function ran immediately" )

	test:waitForTestToFinish( 1 )

end

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

function WFRxSchedulerTest.testScheduleFuture_multiple( test )

	local ran1 = false
	local ran2 = false

	local dueTime1 = timeNow() + .25
	local dueTime2 = timeNow() + .50

	scheduler:scheduleFuture( .25, function()

		if ran1 then
			test:log( "scheduled function 1 invoked twice" )
			test:notifyTestDidFail()
			return
		end

		if ran2 then
			test:log( "scheduled function 2 invoked before func 1" )
			test:notifyTestDidFail()
			return
		end

		if timeNow() < dueTime1 then
			test:log( "scheduled function 1 ran too soon" )
			test:notifyTestDidFail()
			return
		end

		ran1 = true

	end )

	scheduler:scheduleFuture( .50, function()

		if not ran1 then
			test:log( "scheduled function 2 before func 1" )
			test:notifyTestDidFail()
			return
		end

		if ran2 then
			test:log( "scheduled function 2 invoked twice" )
			test:notifyTestDidFail()
			return
		end

		if timeNow() < dueTime2 then
			test:log( "scheduled function 2 ran too soon" )
			test:notifyTestDidFail()
			return
		end

		ran2 = true

		test:notifyTestDidSucceed()

	end )

	assert( not ran1, "scheduled function 1 ran immediately" )
	assert( not ran2, "scheduled function 2 ran immediately" )

	test:waitForTestToFinish( 1 )

end

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

function WFRxSchedulerTest.testScheduleFuture_cancel( test )

	local ran2 = false

	local d1 = scheduler:scheduleFuture( .25, function()

		test:log( "task 1 ran after it was disposed" )
		test:notifyTestDidFail()

	end)

	scheduler:scheduleFuture( .50, function()

		if ran2 then
			test:log( "task 2 ran twice" )
			test:notifyTestDidFail()
			return
		else
			ran2 = true
			test:notifyTestDidSucceed()
		end

	end )

	assert( not ran2, "task 2 ran immediately" )

	d1:dispose()

	test:waitForTestToFinish( 1 )

end

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

function WFRxSchedulerTest.testCatchUp( test )

	local ran1 = false
	local ran2 = false

	scheduler:scheduleFuture( .25, function()

		if ran1 then
			test:log( "scheduled function 1 invoked twice" )
			test:notifyTestDidFail()
			return
		end

		if ran2 then
			test:log( "scheduled function 2 invoked before func 1" )
			test:notifyTestDidFail()
			return
		end

		ran1 = true

	end )

	scheduler:scheduleFuture( .50, function()

		if not ran1 then
			test:log( "scheduled function 2 before func 1" )
			test:notifyTestDidFail()
			return
		end

		if ran2 then
			test:log( "scheduled function 2 invoked twice" )
			test:notifyTestDidFail()
			return
		end

		ran2 = true

		test:notifyTestDidSucceed()

	end )

	assert( not ran1, "scheduled function 1 ran immediately" )
	assert( not ran2, "scheduled function 2 ran immediately" )

	local pastDueTime2 = scheduler:now() + .65

	while scheduler:now() < pastDueTime2 do
		-- Blocking = evil, evil, evil. Kids, don't try this at home, please.
	end

	test:waitForTestToFinish( 1 )

end

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

local timeInterval = .35

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

function WFRxSchedulerTest.testSchedulePeriodic( test )

	local i = 0
	local previousTime = timeNow()

	local d
	d = scheduler:schedulePeriodic( timeInterval, function()

		i = i + 1

		-- Verify that we didn't get here too soon.

		if timeNow() < previousTime + timeInterval then
			test:log( "periodic task fired too soon" )
			test:notifyDidFail()
			d:dispose()
		end

		previousTime = timeNow()

		-- Stop after three times.

		if i == 3 then
			d:dispose()
			test:notifyTestDidSucceed()
		elseif i > 3 then
			test:log( "periodic task didn't stop after :dispose" )
			test:notifyTestDidFail()
		end

	end )

	assert( d ~= nil, ":schedulePeriodic didn't return a disposable" )

	test:waitForTestToFinish( timeInterval * 5 + 1 )

end

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

function WFRxSchedulerTest.testSchedulePeriodic_cancel( test )

	local i = 0

	local d
	d = scheduler:schedulePeriodic( timeInterval, function()

		i = i + 1

		-- Fail after three times.

		if i > 2 then
			error "expected failure"
		end

	end )

	-- Schedule another task to verify that we didn't continue
	-- to invoke the periodic task after it failed.

	scheduler:scheduleFuture( timeInterval * 5, function()

		if not d:isDisposed() then
			test:log( "task should have been disposed" )
			test:notifyTestDidFail()
			return
		end

		if i == 3 then
			test:notifyTestDidSucceed()
		else
			test:log( "task should run 3 times, actually ran " .. i .. " times" )
			test:notifyTestDidFail()
		end

	end )

	test:waitForTestToFinish( timeInterval * 7 + 1 )

end

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

function WFRxSchedulerTest.testSchedulePeriodicWithState( test )

	local i = 0
	local previousTime = timeNow()

	local state = { hello = true }

	local d
	d = scheduler:schedulePeriodic( state, timeInterval, function( x )

		if state ~= x then
			test:log( "state wasn't passed to task function properly" )
			test:notifyTestDidFail()
			return
		end

		i = i + 1

		-- Verify that we didn't get here too soon.

		if timeNow() < previousTime + timeInterval then
			test:log( "periodic task fired too soon" )
			test:notifyTestDidFail()
			d:dispose()
		end

		previousTime = timeNow()

		-- Stop after three times.

		if i == 3 then
			d:dispose()
			test:notifyTestDidSucceed()
		elseif i > 3 then
			test:log( "periodic task didn't stop after :dispose" )
			test:notifyTestDidFail()
		end

		return state

	end )

	assert( d ~= nil, ":schedulePeriodic didn't return a disposable" )

	test:waitForTestToFinish( timeInterval * 5 + 1 )

end

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

return WFRxSchedulerTest
