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

WFRxWeakEventTableTest.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 ReplaySubject = Rx.ReplaySubject
local Subject = Rx.Subject

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

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


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

local WFRxWeakEventTableTest = {}

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

local function debugDisposables( rxTest )

	Rx.config.debugDisposables = true

	Rx.Disposable.resetDebuggingStats()

	rxTest:finally( function()
		Rx.config.debugDisposables = false
	end )

end

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

function WFRxWeakEventTableTest.rxTestObservableForKey_noInitialValue( rxTest )

	debugDisposables( rxTest )

	local wot = WFRxWeakEventTable()

	rxTest:expectEqual( {}, wot:activeKeys() )

	local s = wot:observableForKey( 'baz' )
	rxTest:expectEqual( true, Subject:isClassOf( s ) )

	rxTest:expectEqual( { baz = true }, wot:activeKeys() )

	local os = rxTest:observe( s )

	wot:postToKey( 'baz', "Hello, world" )
	os:expectData( "Hello, world" )

	wot:postToKey( 'baz', "This", "is", 1, nil, "tuple" )
	os:expectData( "This", "is", 1, nil, "tuple" )

	wot:postCompletedToKey( 'baz' )
	os:expectCompleted()

	rxTest:expectEqual( {}, wot:activeKeys() )

	os:dispose()
	wot:dispose()

	rxTest:expectEqual( Rx.Disposable.numCreated(), Rx.Disposable.numDisposed() )

end

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

function WFRxWeakEventTableTest.rxTestDispose( rxTest )

	debugDisposables( rxTest )

	local wot = WFRxWeakEventTable()

	rxTest:expectEqual( {}, wot:activeKeys() )

	local s = wot:observableForKey( 'baz' )
	rxTest:expectEqual( true, Subject:isClassOf( s ) )

	rxTest:expectEqual( { baz = true }, wot:activeKeys() )

	local os = rxTest:observe( s )

	wot:postToKey( 'baz', "Hello, world" )
	os:expectData( "Hello, world" )

	rxTest:expectEqual( false, s:isDisposed() )
	rxTest:expectEqual( { baz = true }, wot:activeKeys() )

	os:dispose()

	rxTest:expectEqual( true, s:isDisposed() )

	rxTest:expectEqual( {}, wot:activeKeys() )

	wot:dispose()

	rxTest:expectEqual( Rx.Disposable.numCreated(), Rx.Disposable.numDisposed() )

end

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

function WFRxWeakEventTableTest.rxTestMultipleObserversForSameKey( rxTest )

	debugDisposables( rxTest )

	local wot = WFRxWeakEventTable()

	rxTest:expectEqual( {}, wot:activeKeys() )

	local s1 = wot:observableForKey( 'baz' )
	rxTest:expectEqual( true, Subject:isClassOf( s1 ) )

	rxTest:expectEqual( { baz = true }, wot:activeKeys() )

	local os1 = rxTest:observe( s1 )

	wot:postToKey( 'baz', "Hello, world" )
	os1:expectData( "Hello, world" )

	local s2 = wot:observableForKey( 'baz' )

	local os2 = rxTest:observe( s2 )

	wot:postToKey( 'baz', "This", "is", 1, nil, "tuple" )
	os1:expectData( "This", "is", 1, nil, "tuple" )
	os2:expectData( "This", "is", 1, nil, "tuple" )

	os1:dispose()

	rxTest:expectEqual( { baz = true }, wot:activeKeys() )

	wot:postToKey( 'baz', "And, one more thing..." )
	os2:expectData( "And, one more thing..." )

	wot:postCompletedToKey( 'baz' )
	os2:expectCompleted()

	rxTest:expectEqual( {}, wot:activeKeys() )

	os2:dispose()
	wot:dispose()

	rxTest:expectEqual( Rx.Disposable.numCreated(), Rx.Disposable.numDisposed() )

end

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

function WFRxWeakEventTableTest.rxTestMultipleObserversForSameKey_replaySubject( rxTest )

	debugDisposables( rxTest )

	local wot = WFRxWeakEventTable( 1 )

	rxTest:expectEqual( {}, wot:activeKeys() )

	local r1 = wot:observableForKey( 'baz' )
	rxTest:expectEqual( true, ReplaySubject:isClassOf( r1 ) )

	rxTest:expectEqual( { baz = true }, wot:activeKeys() )

	local or1 = rxTest:observe( r1 )

	wot:postToKey( 'baz', "Hello, world" )
	or1:expectData( "Hello, world" )

	local r2 = wot:observableForKey( 'baz' )

	local or2 = rxTest:observe( r2 )

	or2:expectData( "Hello, world" )

	wot:postToKey( 'baz', "This", "is", 1, nil, "tuple" )
	or1:expectData( "This", "is", 1, nil, "tuple" )
	or2:expectData( "This", "is", 1, nil, "tuple" )

	or1:dispose()

	rxTest:expectEqual( { baz = true }, wot:activeKeys() )

	wot:postToKey( 'baz', "And, one more thing..." )
	or2:expectData( "And, one more thing..." )

	wot:postCompletedToKey( 'baz' )
	or2:expectCompleted()

	rxTest:expectEqual( {}, wot:activeKeys() )

	wot:dispose()
	or2:dispose()

	rxTest:expectEqual( Rx.Disposable.numCreated(), Rx.Disposable.numDisposed() )

end

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

function WFRxWeakEventTableTest.rxTestNoCrossContaminationAmongKeys( rxTest )

	debugDisposables( rxTest )

	local wot = WFRxWeakEventTable()

	rxTest:expectEqual( {}, wot:activeKeys() )

	local s1 = wot:observableForKey( 'baz' )
	rxTest:expectEqual( true, Subject:isClassOf( s1 ) )

	rxTest:expectEqual( { baz = true }, wot:activeKeys() )

	local os1 = rxTest:observe( s1 )

	local s2 = wot:observableForKey( 'blah' )

	local os2 = rxTest:observe( s2 )

	rxTest:expectEqual( { baz = true, blah = true }, wot:activeKeys() )

	wot:postToKey( 'blah', "First blah" )
	os2:expectData( "First blah" )

	wot:postToKey( 'baz', "Hello, world" )
	os1:expectData( "Hello, world" )

	wot:postToKey( 'baz', "This", "is", 1, nil, "tuple" )
	os1:expectData( "This", "is", 1, nil, "tuple" )

	wot:postToKey( 'blah', "Another blah" )
	os2:expectData( "Another blah" )

	wot:postCompletedToKey( 'baz' )
	os1:expectCompleted()

	rxTest:expectEqual( { blah = true }, wot:activeKeys() )

	wot:postCompletedToKey( 'blah' )
	os2:expectCompleted()

	rxTest:expectEqual( {}, wot:activeKeys() )

	os1:dispose()
	os2:dispose()
	wot:dispose()

	rxTest:expectEqual( Rx.Disposable.numCreated(), Rx.Disposable.numDisposed() )

end

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

function WFRxWeakEventTableTest.rxTestCreateNewObservableAfterDispose( rxTest )

	debugDisposables( rxTest )

	local wot = WFRxWeakEventTable()

	rxTest:expectEqual( {}, wot:activeKeys() )

	repeat

		local b1 = wot:observableForKey( 'baz' )

		rxTest:expectEqual( { baz = true }, wot:activeKeys() )

		local ob1 = rxTest:observe( b1 )

		wot:postToKey( 'baz', "Hello, world" )
		ob1:expectData( "Hello, world" )

		ob1:dispose()

	until true -- let b1 and ob1 fall out of scope

	collectgarbage()
	collectgarbage()
	collectgarbage()

	rxTest:expectEqual( {}, wot:activeKeys() )

	-- Create a new observable for previously-used key.

	local b2 = wot:observableForKey( 'baz' )

	local ob2 = rxTest:observe( b2 )

	wot:postToKey( 'baz', "This", "is", 1, nil, "tuple" )
	ob2:expectData( "This", "is", 1, nil, "tuple" )

	rxTest:expectEqual( { baz = true }, wot:activeKeys() )

	wot:postToKey( 'baz', "And, one more thing..." )
	ob2:expectData( "And, one more thing..." )

	wot:postCompletedToKey( 'baz' )
	ob2:expectCompleted()

	rxTest:expectEqual( {}, wot:activeKeys() )

	ob2:dispose()
	wot:dispose()

	rxTest:expectEqual( Rx.Disposable.numCreated(), Rx.Disposable.numDisposed() )

end

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

function WFRxWeakEventTableTest.rxTestHasObservableForKey( rxTest )

	debugDisposables( rxTest )

	local wot = WFRxWeakEventTable()

	rxTest:expectEqual( false, wot:hasObservableForKey( 'baz' ) )

	local s = wot:observableForKey( 'baz' )

	rxTest:expectEqual( true, wot:hasObservableForKey( 'baz' ) )

	local os = rxTest:observe( s )

	wot:postToKey( 'baz', "Hello, world" )
	os:expectData( "Hello, world" )

	wot:postToKey( 'baz', "This", "is", 1, nil, "tuple" )
	os:expectData( "This", "is", 1, nil, "tuple" )

	wot:postCompletedToKey( 'baz' )
	os:expectCompleted()

	rxTest:expectEqual( false, wot:hasObservableForKey( 'baz' ) )

	os:dispose()
	wot:dispose()

	rxTest:expectEqual( Rx.Disposable.numCreated(), Rx.Disposable.numDisposed() )

end

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

function WFRxWeakEventTableTest.rxTestPostErrorToKey( rxTest )

	debugDisposables( rxTest )

	local wot = WFRxWeakEventTable()

	rxTest:expectEqual( {}, wot:activeKeys() )

	local s = wot:observableForKey( 'baz' )
	rxTest:expectEqual( true, Subject:isClassOf( s ) )

	rxTest:expectEqual( { baz = true }, wot:activeKeys() )

	local os = rxTest:observe( s )

	wot:postToKey( 'baz', "Hello, world" )
	os:expectData( "Hello, world" )

	wot:postErrorToKey( 'baz', "Something's", "not", "right" )
	os:expectError( "Something's", "not", "right" )

	rxTest:expectEqual( true, s:isDisposed() )

	rxTest:expectEqual( {}, wot:activeKeys() )

	os:dispose()
	wot:dispose()

	rxTest:expectEqual( Rx.Disposable.numCreated(), Rx.Disposable.numDisposed() )

end

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

function WFRxWeakEventTableTest.rxTestPostToKey_errorInvalidKey( rxTest )

	debugDisposables( rxTest )

	local wot = WFRxWeakEventTable()

	rxTest:expectError( function()
		return wot:postToKey( nil, "blah" )
	end )

	rxTest:expectError( function()
		return wot:postToKey( 0/0, "blah" )
	end )

	wot:dispose()

	rxTest:expectEqual( Rx.Disposable.numCreated(), Rx.Disposable.numDisposed() )

end

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

function WFRxWeakEventTableTest.rxTestPostErrorToKey_errorInvalidKey( rxTest )

	debugDisposables( rxTest )

	local wot = WFRxWeakEventTable()

	rxTest:expectError( function()
		return wot:postErrorToKey( nil, "blah" )
	end )

	rxTest:expectError( function()
		return wot:postErrorToKey( 0/0, "blah" )
	end )

	wot:dispose()

	rxTest:expectEqual( Rx.Disposable.numCreated(), Rx.Disposable.numDisposed() )

end

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

function WFRxWeakEventTableTest.rxTestPostCompletedToKey_errorInvalidKey( rxTest )

	debugDisposables( rxTest )

	local wot = WFRxWeakEventTable()

	rxTest:expectError( function()
		return wot:postCompletedToKey( nil, "blah" )
	end )

	rxTest:expectError( function()
		return wot:postCompletedToKey( 0/0, "blah" )
	end )

	wot:dispose()

	rxTest:expectEqual( Rx.Disposable.numCreated(), Rx.Disposable.numDisposed() )

end

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

function WFRxWeakEventTableTest.rxTestGarbageCollection( rxTest )

	debugDisposables( rxTest )

	local wot = WFRxWeakEventTable()

	rxTest:expectEqual( {}, wot:activeKeys() )

	repeat

		local s = wot:observableForKey( 'bax' )

		rxTest:expectEqual( true, Subject:isClassOf( s ) )

		rxTest:expectEqual( { bax = true }, wot:activeKeys() )

		local os = rxTest:observe( s )

		wot:postToKey( 'bax', "Hello, world" )
		os:expectData( "Hello, world" )

		wot:postToKey( 'bax', "This", "is", 1, nil, "tuple" )
		os:expectData( "This", "is", 1, nil, "tuple" )

		os:dispose()

	until true -- let e and oe fall out of scope

	collectgarbage()
	collectgarbage()
	collectgarbage()

	rxTest:expectEqual( {}, wot:activeKeys() )

	wot:dispose()

	rxTest:expectEqual( Rx.Disposable.numCreated(), Rx.Disposable.numDisposed() )

end

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

function WFRxWeakEventTableTest.rxTestDispose( rxTest )

	debugDisposables( rxTest )

	local wot = WFRxWeakEventTable()

	local s = wot:observableForKey( 'baz' )

	rxTest:expectEqual( { baz = true }, wot:activeKeys() )

	wot:dispose()

	rxTest:expectEqual( true, wot:isDisposed() )
	rxTest:expectEqual( true, s:isDisposed() )

	rxTest:expectEqual( Rx.Disposable.numCreated(), Rx.Disposable.numDisposed() )

end

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

return WFRxTestCase.wrapSuite( WFRxWeakEventTableTest )
