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

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

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

	-- RxLua
local Rx = require 'Rx'
local TestScheduler = Rx.testing.TestScheduler

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

	-- WF.core.self_test
local WFRxTestCase = require 'WFRxTestCase'


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

local onNext = Rx.testing.ReactiveTest.onNext
local onError = Rx.testing.ReactiveTest.onError
local onCompleted = Rx.testing.ReactiveTest.onCompleted


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

local observableFactorySpy = Rx.Class 'observableFactorySpy' {

	_rxTest = false,
		-- WFRxTestCaseRunner

	_observables = false,
		-- array of Observables to send back

	_nCallsToFactory = 0,
		-- how many times did factory get called?

	__init = function( self, rxTest, ... )
		self._rxTest = rxTest
		self._observables = { ... }
	end,

	makeFactory = function( self )

		return function( count )

			self._nCallsToFactory = self._nCallsToFactory + 1
			self._rxTest:expectEqual( self._nCallsToFactory, count )

			return self._observables[ count ]

		end

	end

}


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

local WFRxRetryObservableTest = {}

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

function WFRxRetryObservableTest.rxTestHappyPath( rxTest )

	local scheduler = TestScheduler()

	local xs = scheduler:createHotObservable(
		onNext( 210, 1 ),
		onNext( 220, 2 ),
		onNext( 230, 3 ),
		onNext( 240, 4 ),
		onCompleted( 250 )
	)

	local spy = observableFactorySpy( rxTest, xs )

	local res = scheduler:startScheduler(
		function()

			return WFRxRetryObservable {
				maxAttempts = 3,
				retryDelay = 2,
				factory = spy:makeFactory (),
			}

		end )

	rxTest:expectEqual( {
		onNext( 210, 1 ),
		onNext( 220, 2 ),
		onNext( 230, 3 ),
		onNext( 240, 4 ),
		onCompleted( 250 )
	}, res.messages )

	rxTest:expectEqual( 1, spy._nCallsToFactory )

end

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

function WFRxRetryObservableTest.rxTestFail_maxAttempts1( rxTest )

	local scheduler = TestScheduler()

	local xs = scheduler:createHotObservable(
		onNext( 210, 1 ),
		onNext( 220, 2 ),
		onNext( 230, 3 ),
		onNext( 240, 4 ),
		onError( 250, "expected error" )
	)

	local spy = observableFactorySpy( rxTest, xs )

	local res = scheduler:startScheduler(
		function()

			return WFRxRetryObservable {
				maxAttempts = 1,
				retryDelay = 2,
				factory = spy:makeFactory(),
			}

		end )

	rxTest:expectEqual( {
		onNext( 210, 1 ),
		onNext( 220, 2 ),
		onNext( 230, 3 ),
		onNext( 240, 4 ),
		onError( 250, "expected error" )
	}, res.messages )

	rxTest:expectEqual( 1, spy._nCallsToFactory )

end

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

function WFRxRetryObservableTest.rxTestFailThenSucceed( rxTest )

	local scheduler = TestScheduler()

	local xs1 = scheduler:createHotObservable(
		onNext( 210, 1 ),
		onNext( 220, 2 ),
		onNext( 230, 3 ),
		onNext( 240, 4 ),
		onError( 250, "expected error" )
	)

	local xs2 = scheduler:createHotObservable(
		onNext( 260, 21 ),
		onNext( 270, 22 ),
		onNext( 280, 23 ),
		onNext( 290, 24 ),
		onCompleted( 300 )
	)

	local spy = observableFactorySpy( rxTest, xs1, xs2 )

	local res = scheduler:startScheduler(
		function()

			return WFRxRetryObservable {
				maxAttempts = 2,
				retryDelay = 2,
				factory = spy:makeFactory(),
				scheduler = scheduler,
			}

		end )

	rxTest:expectEqual( {
		onNext( 210, 1 ),
		onNext( 220, 2 ),
		onNext( 230, 3 ),
		onNext( 240, 4 ),
		onNext( 260, 21 ),
		onNext( 270, 22 ),
		onNext( 280, 23 ),
		onNext( 290, 24 ),
		onCompleted( 300 )
	}, res.messages )

	rxTest:expectEqual( 2, spy._nCallsToFactory )

end

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

function WFRxRetryObservableTest.rxTestFail_maxAttempts3( rxTest )

	local scheduler = TestScheduler()

	local xs1 = scheduler:createHotObservable(
		onNext( 210, 1 ),
		onNext( 220, 2 ),
		onError( 250, "expected error" )
	)

	local xs2 = scheduler:createHotObservable(
		onNext( 260, 3 ),
		onNext( 270, 4 ),
		onError( 280, "expected error 2" )
	)

	local xs3 = scheduler:createHotObservable(
		onNext( 300, 5 ),
		onNext( 310, 6 ),
		onError( 320, "expected error 3" )
	)

	local xs4 = scheduler:createHotObservable(
		onNext( 330, 3 ),
		onNext( 340, 4 ),
		onCompleted( 350 ) -- shouldn't reach this
	)

	local spy = observableFactorySpy( rxTest, xs1, xs2, xs3, xs4 )

	local res = scheduler:startScheduler(
		function()

			return WFRxRetryObservable {
				maxAttempts = 3,
				retryDelay = 2,
				factory = spy:makeFactory(),
				scheduler = scheduler,
			}

		end )

	rxTest:expectEqual( {
		onNext( 210, 1 ),
		onNext( 220, 2 ),
		onNext( 260, 3 ),
		onNext( 270, 4 ),
		onNext( 300, 5 ),
		onNext( 310, 6 ),
		onError( 320, "expected error 3" )
	}, res.messages )

	rxTest:expectEqual( 3, spy._nCallsToFactory )

end

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

function WFRxRetryObservableTest.rxTestNoRetryDelay( rxTest )

	local scheduler = TestScheduler()

	local xs1 = scheduler:createHotObservable(
		onNext( 210, 1 ),
		onNext( 220, 2 ),
		onNext( 230, 3 ),
		onNext( 240, 4 ),
		onError( 250, "expected error" )
	)

	local xs2 = scheduler:createHotObservable(
		onNext( 251, 20 ),
		onNext( 260, 21 ),
		onNext( 270, 22 ),
		onNext( 280, 23 ),
		onNext( 290, 24 ),
		onCompleted( 300 )
	)

	local spy = observableFactorySpy( rxTest, xs1, xs2 )

	local res = scheduler:startScheduler(
		function()

			return WFRxRetryObservable {
				maxAttempts = 2,
				--retryDelay = nil,
				factory = spy:makeFactory(),
				scheduler = scheduler,
			}

		end )

	rxTest:expectEqual( {
		onNext( 210, 1 ),
		onNext( 220, 2 ),
		onNext( 230, 3 ),
		onNext( 240, 4 ),
		onNext( 251, 20 ),
		onNext( 260, 21 ),
		onNext( 270, 22 ),
		onNext( 280, 23 ),
		onNext( 290, 24 ),
		onCompleted( 300 )
	}, res.messages )

	rxTest:expectEqual( 2, spy._nCallsToFactory )

end

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

function WFRxRetryObservableTest.rxTestRetryDelay( rxTest )

	local scheduler = TestScheduler()

	local xs1 = scheduler:createHotObservable(
		onNext( 210, 1 ),
		onNext( 220, 2 ),
		onNext( 230, 3 ),
		onNext( 240, 4 ),
		onError( 250, "expected error" )
	)

	local xs2 = scheduler:createHotObservable(
		onNext( 251, 20 ),
		onNext( 260, 21 ),
		onNext( 270, 22 ),
		onNext( 280, 23 ),
		onNext( 290, 24 ),
		onCompleted( 300 )
	)

	local spy = observableFactorySpy( rxTest, xs1, xs2 )

	local res = scheduler:startScheduler(
		function()

			return WFRxRetryObservable {
				maxAttempts = 2,
				retryDelay = 15,
				factory = spy:makeFactory(),
				scheduler = scheduler,
			}

		end )

	rxTest:expectEqual( {
		onNext( 210, 1 ),
		onNext( 220, 2 ),
		onNext( 230, 3 ),
		onNext( 240, 4 ),
		onNext( 270, 22 ),
		onNext( 280, 23 ),
		onNext( 290, 24 ),
		onCompleted( 300 )
	}, res.messages )

	rxTest:expectEqual( 2, spy._nCallsToFactory )

end

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

function WFRxRetryObservableTest.rxTestProgressiveBackoffDelayIncrease( rxTest )

	local scheduler = TestScheduler()

	local xs1 = scheduler:createHotObservable(
		onNext( 210, 1 ),
		onNext( 220, 2 ),
		onNext( 230, 3 ),
		onNext( 240, 4 ),
		onError( 250, "expected error" )
	)

	local xs2 = scheduler:createHotObservable(
		onNext( 251, 20 ),
		onNext( 260, 21 ),
		onNext( 270, 22 ),
		onNext( 280, 23 ),
		onError( 290, "expected error" )
	)

	local xs3 = scheduler:createHotObservable(
		onNext( 291, 30 ),
		onNext( 300, 31 ),
		onNext( 310, 32 ),
		onNext( 320, 33 ),
		onNext( 330, 34 ),
		onError( 340, "expected error" )
	)

	local xs4 = scheduler:createHotObservable(
		onNext( 341, 40 ),
		onNext( 350, 41 ),
		onNext( 360, 42 ),
		onNext( 370, 43 ),
		onNext( 380, 44 ),
		onCompleted( 390 )
	)

	local spy = observableFactorySpy( rxTest, xs1, xs2, xs3, xs4 )

	local res = scheduler:startScheduler(
		function()

			return WFRxRetryObservable {
				maxAttempts = 4,
				progressiveBackoffDelay = 20,
				factory = spy:makeFactory(),
				scheduler = scheduler,
			}

		end )

	rxTest:expectEqual( {
		onNext( 210, 1 ),
		onNext( 220, 2 ),
		onNext( 230, 3 ),
		onNext( 240, 4 ),
		onNext( 260, 21 ),
		onNext( 270, 22 ),
		onNext( 280, 23 ),
		onNext( 320, 33 ),
		onNext( 330, 34 ),
		onCompleted( 390 )
	}, res.messages )

	rxTest:expectEqual( 4, spy._nCallsToFactory )

end

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

function WFRxRetryObservableTest.rxTestFilterTrue( rxTest )

	local scheduler = TestScheduler()

	local xs1 = scheduler:createHotObservable(
		onNext( 210, 1 ),
		onNext( 220, 2 ),
		onNext( 230, 3 ),
		onNext( 240, 4 ),
		onError( 250, "expected error" )
	)

	local xs2 = scheduler:createHotObservable(
		onNext( 251, 20 ),
		onNext( 260, 21 ),
		onNext( 270, 22 ),
		onNext( 280, 23 ),
		onNext( 290, 24 ),
		onCompleted( 300 )
	)

	local spy = observableFactorySpy( rxTest, xs1, xs2 )

	local nCallsToFilter = 0

	local res = scheduler:startScheduler(
		function()

			return WFRxRetryObservable {

				maxAttempts = 2,
				factory = spy:makeFactory(),
				scheduler = scheduler,

				retryFilter = function( message )
					nCallsToFilter = nCallsToFilter + 1
					rxTest:expectEqual( "expected error", message )
					return true
				end,

			}

		end )

	rxTest:expectEqual( {
		onNext( 210, 1 ),
		onNext( 220, 2 ),
		onNext( 230, 3 ),
		onNext( 240, 4 ),
		onNext( 251, 20 ),
		onNext( 260, 21 ),
		onNext( 270, 22 ),
		onNext( 280, 23 ),
		onNext( 290, 24 ),
		onCompleted( 300 )
	}, res.messages )

	rxTest:expectEqual( 2, spy._nCallsToFactory )

	rxTest:expectEqual( 1, nCallsToFilter )

end

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

function WFRxRetryObservableTest.rxTestFilterFalse( rxTest )

	local scheduler = TestScheduler()

	local xs1 = scheduler:createHotObservable(
		onNext( 210, 1 ),
		onNext( 220, 2 ),
		onNext( 230, 3 ),
		onNext( 240, 4 ),
		onError( 250, "expected error", "tuple" )
	)

	local xs2 = scheduler:createHotObservable(
		onNext( 251, 20 ),
		onNext( 260, 21 ),
		onNext( 270, 22 ),
		onNext( 280, 23 ),
		onNext( 290, 24 ),
		onCompleted( 300 )
	)

	local spy = observableFactorySpy( rxTest, xs1, xs2 )

	local nCallsToFilter = 0

	local res = scheduler:startScheduler(
		function()

			return WFRxRetryObservable {

				maxAttempts = 2,
				factory = spy:makeFactory(),
				scheduler = scheduler,

				retryFilter = function( message, arg2 )
					nCallsToFilter = nCallsToFilter + 1
					rxTest:expectEqual( "expected error", message )
					rxTest:expectEqual( "tuple", arg2 )
					return false
				end,

			}

		end )

	rxTest:expectEqual( {
		onNext( 210, 1 ),
		onNext( 220, 2 ),
		onNext( 230, 3 ),
		onNext( 240, 4 ),
		onError( 250, "expected error", "tuple" )
	}, res.messages )

	rxTest:expectEqual( 1, spy._nCallsToFactory )

	rxTest:expectEqual( 1, nCallsToFilter )

end

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

function WFRxRetryObservableTest.rxTestFilterThrows( rxTest )

	local scheduler = TestScheduler()

	local xs1 = scheduler:createHotObservable(
		onNext( 210, 1 ),
		onNext( 220, 2 ),
		onNext( 230, 3 ),
		onNext( 240, 4 ),
		onError( 250, "expected error" )
	)

	local xs2 = scheduler:createHotObservable(
		onNext( 251, 20 ),
		onNext( 260, 21 ),
		onNext( 270, 22 ),
		onNext( 280, 23 ),
		onNext( 290, 24 ),
		onCompleted( 300 )
	)

	local spy = observableFactorySpy( rxTest, xs1, xs2 )

	local nCallsToFilter = 0

	local res = scheduler:startScheduler(
		function()

			return WFRxRetryObservable {

				maxAttempts = 2,
				factory = spy:makeFactory(),
				scheduler = scheduler,

				retryFilter = function( message )
					nCallsToFilter = nCallsToFilter + 1
					error "error from within filter function"
				end,

			}

		end )

	rxTest:expectEqual( {
		onNext( 210, 1 ),
		onNext( 220, 2 ),
		onNext( 230, 3 ),
		onNext( 240, 4 ),
		onError( 250, "error from within filter function" )
	}, res.messages )

	rxTest:expectEqual( 1, spy._nCallsToFactory )

	rxTest:expectEqual( 1, nCallsToFilter )

end

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

function WFRxRetryObservableTest.rxTestFilterNotCalledOnLastAttempt( rxTest )

	local scheduler = TestScheduler()

	local xs1 = scheduler:createHotObservable(
		onNext( 210, 1 ),
		onNext( 220, 2 ),
		onNext( 230, 3 ),
		onNext( 240, 4 ),
		onError( 250, "expected error" )
	)

	local xs2 = scheduler:createHotObservable(
		onNext( 251, 20 ),
		onNext( 260, 21 ),
		onNext( 270, 22 ),
		onNext( 280, 23 ),
		onNext( 290, 24 ),
		onError( 300, "fail whale number 2" )
	)

	local spy = observableFactorySpy( rxTest, xs1, xs2 )

	local nCallsToFilter = 0

	local res = scheduler:startScheduler(
		function()

			return WFRxRetryObservable {

				maxAttempts = 2,
				factory = spy:makeFactory(),
				scheduler = scheduler,

				retryFilter = function( message )
					nCallsToFilter = nCallsToFilter + 1
					return true
				end,

			}

		end )

	rxTest:expectEqual( {
		onNext( 210, 1 ),
		onNext( 220, 2 ),
		onNext( 230, 3 ),
		onNext( 240, 4 ),
		onNext( 251, 20 ),
		onNext( 260, 21 ),
		onNext( 270, 22 ),
		onNext( 280, 23 ),
		onNext( 290, 24 ),
		onError( 300, "fail whale number 2" )
	}, res.messages )

	rxTest:expectEqual( 2, spy._nCallsToFactory )

	rxTest:expectEqual( 1, nCallsToFilter )

end

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

function WFRxRetryObservableTest.rxTestFilterNotCalledOnSuccess( rxTest )

	local scheduler = TestScheduler()

	local xs = scheduler:createHotObservable(
		onNext( 210, 1 ),
		onNext( 220, 2 ),
		onNext( 230, 3 ),
		onNext( 240, 4 ),
		onCompleted( 250 )
	)

	local spy = observableFactorySpy( rxTest, xs )

	local nCallsToFilter = 0

	local res = scheduler:startScheduler(
		function()

			return WFRxRetryObservable {

				maxAttempts = 2,
				factory = spy:makeFactory(),
				scheduler = scheduler,

				retryFilter = function( message )
					nCallsToFilter = nCallsToFilter + 1
					return true
				end,

			}

		end )

	rxTest:expectEqual( {
		onNext( 210, 1 ),
		onNext( 220, 2 ),
		onNext( 230, 3 ),
		onNext( 240, 4 ),
		onCompleted( 250 )
	}, res.messages )

	rxTest:expectEqual( 1, spy._nCallsToFactory )

	rxTest:expectEqual( 0, nCallsToFilter )

end

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

function WFRxRetryObservableTest.rxTestFilterCanChangeDelay( rxTest )

	local scheduler = TestScheduler()

	local xs1 = scheduler:createHotObservable(
		onNext( 210, 1 ),
		onNext( 220, 2 ),
		onNext( 230, 3 ),
		onNext( 240, 4 ),
		onError( 250, "expected error" )
	)

	local xs2 = scheduler:createHotObservable(
		onNext( 251, 20 ),
		onNext( 260, 21 ),
		onNext( 270, 22 ),
		onNext( 280, 23 ),
		onNext( 290, 24 ),
		onCompleted( 300 )
	)

	local spy = observableFactorySpy( rxTest, xs1, xs2 )

	local nCallsToFilter = 0

	local res = scheduler:startScheduler(
		function()

			return WFRxRetryObservable {

				maxAttempts = 2,
				factory = spy:makeFactory(),
				scheduler = scheduler,

				retryFilter = function( message )
					nCallsToFilter = nCallsToFilter + 1
					rxTest:expectEqual( "expected error", message )
					return true, 25
				end,

			}

		end )

	rxTest:expectEqual( {
		onNext( 210, 1 ),
		onNext( 220, 2 ),
		onNext( 230, 3 ),
		onNext( 240, 4 ),
		onNext( 280, 23 ),
		onNext( 290, 24 ),
		onCompleted( 300 )
	}, res.messages )

	rxTest:expectEqual( 2, spy._nCallsToFactory )

	rxTest:expectEqual( 1, nCallsToFilter )

end


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

return WFRxTestCase.wrapSuite( 'WFRxRetryObservableTest', WFRxRetryObservableTest )
