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

WFRxRunUntilDoneTest.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 Observable = Rx.Observable

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

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


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

local runUntilDone = WFRxRunUntilDone


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

local WFRxRunUntilDoneTest = {}

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

function WFRxRunUntilDoneTest.rxTestHappyPath( rxTest )

	local didStart = false

	local observable = Observable.create( function()
		didStart = true
	end )

	local rudObservable = observable:call( runUntilDone )

	rxTest:expectEqual( Rx.Observable:isClassOf( rudObservable ), true )

	rxTest:expectEqual( true, didStart )

end

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

function WFRxRunUntilDoneTest.rxTestAddedSubscription( rxTest )

	local observable = Observable.just( 1 )

	local tapCount = 0
	local count = 0
	
	local rudObservable = observable
		:tap( function()
			tapCount = tapCount + 1
		end )
		:call( runUntilDone )
		
	local subscription = rudObservable
		:subscribe( function( ... )
			count = count + 1
		end )
		
	rxTest:expectEqual( Rx.Observable:isClassOf( rudObservable ), true )

	rxTest:expectEqual( tapCount, 1 )
	
	rxTest:expectEqual( count, 1 )

	subscription:dispose()

end


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

function WFRxRunUntilDoneTest.rxTestStrongReference( rxTest )

	local didStartTimer = false
	local didStartDest = false

	-- This test is admittedly rather complicated. Some Observables are eligible
	-- for garbage collection; others are not. This is based primarily on their
	-- implementation. It's hard to directly create an Observable that yields
	-- results later and isn't eligible for garbage collection. The implementation
	-- for Observable.timer happens to keep the source observable alive via a
	-- strong reference in the timer callback function. More on the expected
	-- lifetimes below:

	local weak = setmetatable( {}, { __mode = 'kv' } )

	-- As noted above, the timer is kept alive by the implementation of Observable.timer.
	-- Thus we don't even bother putting it in the weak table.

	local timerSubscription = 
		Observable.timer( .5, WFRxScheduler.sharedScheduler() )
			:subscribe( function()
				didStartTimer = true
				if weak.src then
					weak.src:onNext( true )
				end
			end )

	-- The timer's subscribe function attempts to set weak.src, which may or may not
	-- be available, depending on GC. We don't attach the :map function to this observable
	-- because

	weak.src = Rx.Subject()

	-- We attach the side effect to another entry in the weak table. weak.dest should
	-- be eligible for collection (and will be collected by the collectgarbage() calls
	-- below), but the internal implementation of runUntilDone should preserve the
	-- underlying function. (Exercise: remove the :call( runUntilDone ) at the end
	-- of this chain and this test should fail.)

	weak.dest = weak.src
		:take( 1 )
		:map( function()
			didStartDest = true
		end )
		:call( runUntilDone )

	-- Get rid of references to weak.src and weak.dest.

	collectgarbage()
	collectgarbage()
	collectgarbage()

	-- Give time for the timer to fire.

	rxTest:sleep( 1 )

	-- Make sure the side effect in weak.dest still happened.

	rxTest:expectEqual( true, didStartTimer )
	rxTest:expectEqual( true, didStartDest )

	-- Dispose of the timer object
	
	timerSubscription:dispose()
	timerSubscription = nil
	
	collectgarbage()
	collectgarbage()
	collectgarbage()
	
	-- Make sure the weak references have gone
	
	assert( not weak.src )
	assert( not weak.dest )

end

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

return WFRxTestCase.wrapSuite( 'WFRxRunUntilDoneTest', WFRxRunUntilDoneTest )
