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

WFBinaryRequesterTest.lua

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

ADOBE CONFIDENTIAL
------------------
Copyright 2016 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.lua
local KSData = require 'KSData'
local KSIdGenerator = require 'KSIdGenerator'
local WFCloseSupport = require 'WFCloseSupport'
local WFFileUtils = require 'WFFileUtils'
local KSPathUtils = require 'KSPathUtils'
local WFPlatformInfo = require 'WFPlatformInfo'

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

	-- WF.oz_client.managed_catalog
local WFBinaryRequester = require 'WFBinaryRequester'

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

local MockBinaryDownloader = {
	WFCloseSupport.addCloseSupportMethods,	
}

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

function MockBinaryDownloader:__init()
	-- no-op
end

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

local function downloadOneBinary( self, docId, linksKey )

	-- The 'binary' returned from MockBinaryDownloader is just the linksKey + the
	-- doc Id. It's unique for each binary, so it works great for testing.
	
	local delay = 0.1
	
	if docId == 'in_flight_test' then
		delay = 1.5
	end
	
	local revId = KSIdGenerator.uuidInLowercaseHex()
	
	return Observable.timer( delay, WFRxScheduler.sharedScheduler() )
		:flatMapLatest( function()
			if docId == 'error_id' then
				return Observable.throw( 'ERROR' )
			else
				return Observable.just( KSData.make( linksKey .. docId ), revId )
			end
		end )
	
end

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

function MockBinaryDownloader:getRendition( docId, renditionType )

	return downloadOneBinary( self, docId, '/rels/rendition_type/' .. renditionType )

end

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

function MockBinaryDownloader:getProxy( docId, proxyType )

	return downloadOneBinary( self, docId, '/rels/proxy_type/' .. proxyType )

end

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

function MockBinaryDownloader:getMaster( docId )

	return downloadOneBinary( self, docId, '/rels/master' )

end

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

MockBinaryDownloader = Rx.Class 'MockBinaryDownloader'( MockBinaryDownloader )


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

local WFBinaryRequesterTest = {}

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

local function mapKSDataToString( okay, maybeKSData )

	if okay and not KSData.isData( maybeKSData ) then
		return string.format( "Expected KSData, got %s instead", type( maybeKSData ) ),
			   maybeKSData
	elseif not okay then
		assert( okay == false )
		assert( maybeKSData == 'ERROR' )
		return tostring( maybeKSData )
	else
		return tostring( maybeKSData )
	end

end

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

local expectedOzLinkPaths = {
	rendition_t2X 		= '/rels/rendition_type/thumbnail2x',
	rendition_640 		= '/rels/rendition_type/640',
	rendition_1280 		= '/rels/rendition_type/1280',
	rendition_2048		= '/rels/rendition_type/2048',
	rendition_pano		= '/rels/rendition_type/panorama',
	proxy_2560 			= '/rels/proxy_type/2560',
	proxy_pano 			= '/rels/proxy_type/panorama',
	master				= '/rels/master',
}

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

local function setup( rxTest )

	local downloader = MockBinaryDownloader()
	rxTest:addCloseTarget( downloader )
	
	local catalogId = KSIdGenerator.uuidInLowercaseHex()
	local rootPath = KSPathUtils.getStandardFilePath( 'temp' )
	
	local binaryRequester = WFBinaryRequester( downloader )
	rxTest:addCloseTarget( binaryRequester )

	rxTest:finally( function()
	
		local catalogPath = rootPath .. '/' .. catalogId
		WFFileUtils.delete( catalogPath )
		
	end )

	return binaryRequester, catalogId

end

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

function WFBinaryRequesterTest.rxTestSingleRequest( rxTest )

	local binaryRequester = setup( rxTest )
	
	local binaryRequestStream = binaryRequester:getBinaryRequestStream()
	
	local listener = rxTest:observe( binaryRequestStream:map( mapKSDataToString ) )
	
	local assetId = KSIdGenerator.uuidInLowercaseHex()
	
	binaryRequester:requestBinary( assetId,
								'rendition',
								'thumbnail2x',
								1 )

	listener:expectDataWithinTime( 1, expectedOzLinkPaths.rendition_t2X .. assetId )

end

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

function WFBinaryRequesterTest.rxTestErrorResponse( rxTest )

	local binaryRequester = setup( rxTest )
	
	local binaryRequestStream = binaryRequester:getBinaryRequestStream()
	
	local listener = rxTest:observe( binaryRequestStream:map( mapKSDataToString ) )
	
	local assetId = 'error_id'
	
	binaryRequester:requestBinary( assetId,
								'rendition',
								'thumbnail2x',
								1 )

	listener:expectDataWithinTime( 1, 'ERROR' ) -- Errors are mapped to data and sent over the stream.

end

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

function WFBinaryRequesterTest.rxTestFIFOOrdering( rxTest )

	local binaryRequester = setup( rxTest )
	
	local binaryRequestStream = binaryRequester:getBinaryRequestStream()
	
	local requestCount = 0
	
	local listener = rxTest:observe( binaryRequestStream
		
		:map( function( ... )
		
			requestCount = requestCount + 1
			return mapKSDataToString( ... )
			
		end ) )
	
	local assetId1 = KSIdGenerator.uuidInLowercaseHex()
	
	binaryRequester:requestBinary( assetId1,
								'original',
								'master', 1 )

	local assetId2 = KSIdGenerator.uuidInLowercaseHex()

	binaryRequester:requestBinary( assetId2,
								'rendition',
								'2048', 1 )

	local assetId3 = KSIdGenerator.uuidInLowercaseHex()

	binaryRequester:requestBinary( assetId3,
								'rendition',
								'thumbnail2x', 1 )

	local assetId4 = KSIdGenerator.uuidInLowercaseHex()

	binaryRequester:requestBinary( assetId4,
								'rendition',
								'2048', 1 )
								
	local assetId5 = KSIdGenerator.uuidInLowercaseHex()

	binaryRequester:requestBinary( assetId5,
								'original',
								'master', 1 )

	listener:expectData( expectedOzLinkPaths.master .. assetId1 )

	rxTest:expectEqual( 1, requestCount )

	listener:expectData( expectedOzLinkPaths.rendition_2048 .. assetId2 )

	rxTest:expectEqual( 2, requestCount )

	listener:expectData( expectedOzLinkPaths.rendition_t2X .. assetId3 )

	rxTest:expectEqual( 3, requestCount )

	listener:expectData( expectedOzLinkPaths.rendition_2048 .. assetId4 )

	rxTest:expectEqual( 4, requestCount )

	listener:expectData( expectedOzLinkPaths.master .. assetId5 )

	rxTest:expectEqual( 5, requestCount )
end

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

function WFBinaryRequesterTest.rxTestRequestCancellation( rxTest )

	rxTest:setTimeLimit( 10 )

	local binaryRequester = setup( rxTest )
	
	local binaryRequestStream = binaryRequester:getBinaryRequestStream()
	
	local requestCount = 0
	
	rxTest:observe( binaryRequestStream
		
		:tap( function()
			requestCount = requestCount + 1
		end ) )
	
	local assetId = KSIdGenerator.uuidInLowercaseHex()
	
	-- basic cancel should work
	
	local cancelFunc = binaryRequester:requestBinary( 
								assetId,
								'original',
								'master', 1 )

	cancelFunc()

	rxTest:expectEqual( requestCount, 0 )

	-- if there are two requests for a given binary, canceling one request
	-- shouldn't affect the other one, both need to be canceled to fully remove the request
	
	 binaryRequester:requestBinary( 
							assetId,
							'original',
							'master', 1 )
								
	local cancelFunc2 = binaryRequester:requestBinary( 
								assetId,
								'original',
								'master', 1 )
	cancelFunc2()

	rxTest:sleep( 2 )

	rxTest:expectEqual( requestCount, 1 )

end

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

function WFBinaryRequesterTest.rxTestInFlightRequestCancellation( rxTest )

	local binaryRequester = setup( rxTest )
	
	rxTest:setTimeLimit( 15 )
	
	local binaryRequestStream = binaryRequester:getBinaryRequestStream()
	
	local requestCount = 0
	
	rxTest:observe( binaryRequestStream
		
		:tap( function()
			requestCount = requestCount + 1
		end ) )
	
	local assetId = 'in_flight_test'
	
	local cancelFunc = binaryRequester:requestBinary( 
								assetId,
								'original',
								'master', 1 )

	rxTest:sleep( 0.1 )

	cancelFunc()

	rxTest:sleep( 2 )

	rxTest:expectEqual( requestCount, 0 )

	-- if there are two requests open for a single binary that's in-flight, canceling one request
	-- shouldn't affect the other one. Both need to be canceled to stop the in-flight request.
	
	local cancelFunc1 = binaryRequester:requestBinary( 
								assetId,
								'original',
								'master', 1 )
								
	local cancelFunc2 = binaryRequester:requestBinary( 
								assetId,
								'original',
								'master', 1 )

	rxTest:sleep( 0.1 )
		
	cancelFunc2()
	
	rxTest:sleep( 2 )

	rxTest:expectEqual( 1, requestCount )

	-- Canceling both requests should cancel the in-flight request and our
	-- completed request count should not increment.
	
	cancelFunc1 = binaryRequester:requestBinary( 
								assetId,
								'original',
								'master', 1 )
								
	cancelFunc2 = binaryRequester:requestBinary( 
								assetId,
								'original',
								'master', 1 )

	rxTest:sleep( 0.1 )
		
	cancelFunc2()
	cancelFunc1()
	
	rxTest:sleep( 2 )

	rxTest:expectEqual( 1, requestCount )

end

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

function WFBinaryRequesterTest.rxTestAddAlreadyInFlightRequest( rxTest )

	local binaryRequester = setup( rxTest )
	
	rxTest:setTimeLimit( 5 )
	
	local binaryRequestStream = binaryRequester:getBinaryRequestStream()
	
	local requestCount = 0
	
	rxTest:observe( binaryRequestStream
		
		:tap( function()
			requestCount = requestCount + 1
		end ) )
	
	local assetId = 'in_flight_test'
	
	binaryRequester:requestBinary( assetId,
								'original',
								'master',
								1 )

	rxTest:sleep( 0.1 )

	binaryRequester:requestBinary( assetId,
								'original',
								'master',
								1 )

	rxTest:sleep( 2 )

	rxTest:expectEqual( requestCount, 1 )

end

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

return WFRxTestCase.wrapSuite( WFBinaryRequesterTest )
