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

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

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

This file allows us to share the test suite for WFRxDocStoreImp and
WFRxDocStore.

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

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


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

local TEST_MIN_DOCS = 1000
local TEST_MAX_DOCS = 1000000


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

return function( docStoreClass, options )

	local RxDocStorePerformanceTestSuite = {}

	options = options or {}

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

	local function setTestTimeout( rxTest, nDocs, typicalTimeFor100KDocsOnMac )

		local adjustedTimeout = math.ceil( ( typicalTimeFor100KDocsOnMac * 3 ) / 100000 * nDocs ) + 3
		
		adjustedTimeout = math.max( adjustedTimeout, 5 )
		rxTest:setTimeLimit( adjustedTimeout )
	
	end

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

	local function setUpDocStore( rxTest, optDocStore )

		local docStore = optDocStore or docStoreClass( ':memory:' )
		
		if tostring( docStore ):match( 'WFRxDocStoreImp' )then
			docStore._TEST_unpackDocValue = true
		end
		
		-- Work around an issue where the test will time out because we don't
		-- sleep until after 60 seconds. We have to sleep for a little bit to
		-- make the test mechanism aware of the time limit if it's >60 seconds.
		-- This implies that setUpDocStore should be called after setTestTimeout.
		
		rxTest:sleep( .1 )

		rxTest:finally( function()

			docStore:close()
			docStore = nil

		end )

		return docStore

	end

	RxDocStorePerformanceTestSuite.setUpDocStore = setUpDocStore

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

	local function mapAwayRevHashCode( value )

		if type( value ) ~= 'table' then
			return value
		end

		local result = {}
		for k, v in pairs( value ) do
			if k ~= '_revId' and k ~= 'revId' then
				result[ k ] = v
			else
				if #v == 32 and v:match( '^[0-9a-f]+$' ) then
					result[ k ] = "(valid hash)"
				else
					result[ k ] = v
				end
			end
		end

		return result

	end

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

	local function bulkSetDocValues( rxTest, docStore, nDocs )
		
		local fullDocIds = {}
		for i = 1, nDocs / 100 do

			local docValues = {}
			for j = 1, 100 do
				local docId = 'testDoc' .. i .. '_' .. j
				fullDocIds[ #fullDocIds + 1 ] = docId
				docValues[ j ] = {
					_id = docId,
					foo = 'bar',
					counter = i + j - 1,
				}
			end

			rxTest:expectSingleValue( docStore:bulkSetDocValue( docValues ), ':ignore:' )

		end
		return fullDocIds
	end

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

	local function createTestsForNDocs( nDocs )

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

		if not options.suppressIndividualCalls then

			RxDocStorePerformanceTestSuite[ 'rxTestSetDocValue_' .. nDocs .. 'Docs_testPerformance' ] = function( rxTest )

				local randomDocId = 'testDoc_' .. 4

				setTestTimeout( rxTest, nDocs, 19 )

				local docStore = setUpDocStore( rxTest )

				rxTest:measurePerformance(
					function()

						for i = 1, nDocs do

							local docValue = {
								_id = 'testDoc_' .. i,
								test = i,
							}

							docStore:setDocValue( docValue )

						end

					end )

				local expectedValue = {
						_id = randomDocId,
						_localId = 4,
						test = 4,
						_revId = "(valid hash)",
						_changeCounter = 4,
					}

				rxTest:expectSingleValue(

					docStore:docValue( randomDocId )
						:map( function( value )
							return mapAwayRevHashCode( value ), value
						end ),

					expectedValue, ':ignore:' )

			end

		end

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

		RxDocStorePerformanceTestSuite[ 'rxTestBulkSetDocValue_' .. nDocs .. 'Docs_testPerformance' ] = function( rxTest )

			setTestTimeout( rxTest, nDocs, 15 )

			local docStore = setUpDocStore( rxTest )
	
			rxTest:measurePerformance(
				function()
					bulkSetDocValues( rxTest, docStore, nDocs )
				end )

			rxTest:expectSingleValue(

				docStore:docValue( 'testDoc1_1' )
					:map( function( value )
						return mapAwayRevHashCode( value ), value
					end ),

				{
					_id = 'testDoc1_1',
					_localId = 1,
					_changeCounter = 1,
					_revId = "(valid hash)",
					foo = 'bar',
					counter = 1,
				},
				':ignore:' )

		end

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

		if not options.suppressIndividualCalls then

			RxDocStorePerformanceTestSuite[ 'rxTestGetDocValue_' .. nDocs .. 'Docs_testPerformance' ] = function( rxTest )

				setTestTimeout( rxTest, nDocs, 39 )

				local docStore = setUpDocStore( rxTest )

				bulkSetDocValues( rxTest, docStore, nDocs )

				rxTest:measurePerformance(

					function()

						for i = 1, nDocs do
							rxTest:expectSingleValue(
								docStore:docValue( 'testDoc_' .. i ),
								':ignore:' )
						end

					end )

			end

		end

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

		RxDocStorePerformanceTestSuite[ 'rxTestChangedDocs_' .. nDocs .. 'Docs_testPerformance' ] = function( rxTest )

			setTestTimeout( rxTest, nDocs, 17 )

			local docStore = setUpDocStore( rxTest )

			bulkSetDocValues( rxTest, docStore, nDocs )

			local fromChangeCounter = 0

			rxTest:measurePerformance(
				function()

					while true do

						local changes

						rxTest:expectSingleValue(
							docStore:changedDocs { fromChangeCounter = fromChangeCounter }
								:tap( function( result )
									changes = result
								end ),
							':ignore:' )

						if #changes == 0 then break end

						fromChangeCounter = changes.continueFrom

					end

				end )

		end

		------------------------------------------------------------------------
		
		RxDocStorePerformanceTestSuite[ 'rxTestBulkLocalIdToDocIdAsMap_' .. nDocs .. 'Docs_testPerformance' ] = function( rxTest )

			setTestTimeout( rxTest, nDocs, 17 )

			local docStore = setUpDocStore( rxTest )

			bulkSetDocValues( rxTest, docStore, nDocs )

			local localIds = { }
			for i = 1, nDocs, 1 do
				localIds[ i ] = i
			end
			
			local docIds
			docStore:_DEBUG_clearIdCache()
			rxTest:measurePerformance(
				function()

					rxTest:expectSingleValue(
						docStore:bulkLocalIdToDocIdAsMap( localIds )
							:tap( function( result )
								docIds = result
							end ),
						':ignore:' )


				end )
				
			local numDocIds = 0
			for _, docId in pairs( docIds ) do
				numDocIds = numDocIds + 1
			end
			rxTest:expectEqual( nDocs, numDocIds )

		end
	
		------------------------------------------------------------------------
		
		RxDocStorePerformanceTestSuite[ 'rxTestBulkDocIdToLocalIdAsMap_' .. nDocs .. 'Docs_testPerformance' ] = function( rxTest )

			setTestTimeout( rxTest, nDocs, 17 )

			local docStore = setUpDocStore( rxTest )

			local docIds = bulkSetDocValues( rxTest, docStore, nDocs )

			local localIds
			docStore:_DEBUG_clearIdCache()
			rxTest:measurePerformance(
				function()

					rxTest:expectSingleValue(
						docStore:bulkDocIdToLocalIdAsMap( docIds )
							:tap( function( result )
								localIds = result
							end ),
						':ignore:' )


				end )
				
			local numDocIds = 0
			for _, localId in pairs( localIds ) do
				numDocIds = numDocIds + 1
			end
			rxTest:expectEqual( nDocs, numDocIds )

		end
		
		------------------------------------------------------------------------

	end
	
	----------------------------------------------------------------------------

	local nDocs = TEST_MAX_DOCS

	while nDocs >= TEST_MIN_DOCS do

		createTestsForNDocs( nDocs )
		nDocs = nDocs / 10

	end


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

	return WFRxTestCase.wrapSuite( 'WFRxDocStorePerformanceTest', RxDocStorePerformanceTestSuite )

end
