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

WFSortOrderStringsTest.lua

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

ADOBE CONFIDENTIAL
------------------
Copyright 2013 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.

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

	-- WF.core.strings
local WFSortOrderStrings = require 'WFSortOrderStrings'

	-- WF.core.utils
local KSTestUtils = require 'KSTestUtils'


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

local WFSortOrderStringsTest = {}

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

local function assertMakeEncodingFails( testName, failValue )

	WFSortOrderStringsTest[ 'testMakeEncoding_FAIL_' .. testName ] = function()
	
		KSTestUtils.assertFails( function()
		
			return WFSortOrderStrings.makeEncoding( failValue )
			
		end )
	
	end

end

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

local function assertMakeEncodingSucceeds( testName, okValue )

	WFSortOrderStringsTest[ 'testMakeEncoding_' .. testName ] = function()
	
		local result = WFSortOrderStrings.makeEncoding( okValue )
		
		if type( result ) ~= 'userdata' then
			error( string.format( "result should have been userdata, was %s( %s )",
								  type( result ), tostring( result ) ) )
		end
	
	end

end

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

assertMakeEncodingFails( 'number', 42 )
assertMakeEncodingFails( 'emptyString', '' )
assertMakeEncodingFails( 'oneChar', '1' )
assertMakeEncodingFails( 'tooMany', string.rep( 'x', 256 ) )
assertMakeEncodingFails( 'duplicateChars', 'abcc' )

assertMakeEncodingSucceeds( 'abc', 'abc' )
assertMakeEncodingSucceeds( 'lex64',
	'-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz' )

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

local function testInsertBefore_lex64Fails( testName, right, optBias )
	
	if optBias then
		testName = string.format( "%s-%s", testName, optBias )
	end
	
	WFSortOrderStringsTest[ 'testInsertBefore_lex64_FAIL_' .. testName ] = function( test )
	
		KSTestUtils.assertFails( function()
	
			local result = WFSortOrderStrings.insertBefore_lex64( right, optBias )
			
			test:log( "Expected failure, but got result " .. tostring( result ) )
		
		end )
		
	end

end

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

local function testInsertBefore_lex64( testName, right, optBias, expected )
	
	if not expected then
		expected = optBias
		optBias = nil
	end
	
	WFSortOrderStringsTest[ 'testInsertBefore_lex64_' .. testName ] = function()
	
		local actualResult = WFSortOrderStrings.insertBefore_lex64( right, optBias )
		
		assert( actualResult < right, "after result not before right" )
		
		if actualResult ~= expected then
			error( string.format( "\n\nexpected %s\n  actual %s",
								  expected, actualResult ) )
		end
		
	end

end

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

local biases = { 'low', 'high', nil }

for i = 1, 3 do
	local biasValue = biases[ i ]
	
	testInsertBefore_lex64Fails( "bufferOverflow", string.rep( '-', 1024 ), biasValue )
	testInsertBefore_lex64Fails( "invalidChar", '@', biasValue )
	testInsertBefore_lex64Fails( "empty", '', biasValue ) -- nowhere to insert before 0
	testInsertBefore_lex64Fails( "illegal", '-', biasValue ) -- nowhere to insert before 0
end

testInsertBefore_lex64Fails( "invalidSecondChar", 'V@', 'high' )

testInsertBefore_lex64( "simple", 'test', 'R' )
testInsertBefore_lex64( "no-room-for-bias", '0', '-V' )
testInsertBefore_lex64( "bias-low", 'test', 'low', '0' )
testInsertBefore_lex64( "bias-middle", 'test', 'middle', 'R' )
testInsertBefore_lex64( "bias-high", 'test', 'high', 'tess' )
testInsertBefore_lex64( "---0", '---0', '----V' )

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

local function testInsertAfter_lex64Fails( testName, left, optBias )
	
	if optBias then
		testName = string.format( "%s-%s", testName, optBias )
	end
	
	WFSortOrderStringsTest[ 'testInsertAfter_lex64_FAIL_' .. testName ] = function( test )
	
		KSTestUtils.assertFails( function()
	
			local result = WFSortOrderStrings.insertAfter_lex64( left, optBias )
			
			test:log( "Expected failure, but got result " .. tostring( result ) )
		
		end )
		
	end

end

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

local function testInsertAfter_lex64( testName, left, optBias, expected )
	
	if not expected then
		expected = optBias
		optBias = nil
	end
	
	WFSortOrderStringsTest[ 'testInsertAfter_lex64_' .. testName ] = function()
	
		local actualResult = WFSortOrderStrings.insertAfter_lex64( left, optBias )
		
		assert( actualResult > left, "after result not after left" )
		
		if actualResult ~= expected then
			error( string.format( "\n\nexpected %s\n  actual %s",
								  expected, actualResult ) )
		end
		
	end

end

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

for i = 1, 3 do
	local biasValue = biases[ i ]
	
	testInsertAfter_lex64Fails( "bufferOverflow", string.rep( 'z', 1024 ), biasValue )
	testInsertAfter_lex64Fails( "invalidChar", '@', biasValue )
end

testInsertAfter_lex64Fails( "invalidSecondChar", 'V@', 'low' )

testInsertAfter_lex64( "empty", '', 'V' )
testInsertAfter_lex64( "simple", 'test', 'w' )
testInsertAfter_lex64( "no-room-for-bias", 'y', 'z' )
testInsertAfter_lex64( "bias-low", 'test', 'low', 'tesu' )
testInsertAfter_lex64( "bias-middle", 'test', 'middle', 'w' )
testInsertAfter_lex64( "bias-high", 'test', 'high', 'z' )
testInsertAfter_lex64( "zzz", 'zzz', 'zzzV' )
testInsertAfter_lex64( "zzzV", 'zzzV', 'zzzk' )

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

local function testInsertBetween_lex64Fails( testName, left, right, optBias )
	
	WFSortOrderStringsTest[ 'testInsertBetween_lex64_FAIL_' .. testName ] = function( test )
	
		KSTestUtils.assertFails( function()
	
			local result = WFSortOrderStrings.insertBetween_lex64( left, right, optBias )
			
			test:log( "Expected failure, but got result " .. tostring( result ) )
		
		end )
		
	end

end

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

local function testInsertBetween_lex64( testName, left, right, optBias, expected )
	
	if not expected then
		expected = optBias
		optBias = nil
	end
	
	WFSortOrderStringsTest[ 'testInsertBetween_lex64_' .. testName ] = function()
	
		local actualResult = WFSortOrderStrings.insertBetween_lex64( left, right, optBias )
		
		assert( left < actualResult, "between result not after left" )
		assert( right > actualResult, "between result not before right" )
		
		if actualResult ~= expected then
			error( string.format( "\n\nexpected %s\n  actual %s",
								  expected, actualResult ) )
		end
		
	end

end

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

testInsertBetween_lex64Fails( "bufferOverflow",
							  string.rep( 'z', 1024 ), string.rep( 'z', 1024 ) )

testInsertBetween_lex64Fails( "invalidChar", '@', '~' )
testInsertBetween_lex64Fails( "stringsMatch", 'abc', 'abc' )
testInsertBetween_lex64Fails( "outOfOrder", 'abd', 'abc' )

testInsertBetween_lex64( "simple", '', 'test', 'R' )
testInsertBetween_lex64( "nested", 'abca', 'abcx', 'abcl' )
testInsertBetween_lex64( "nested-close", 'abca', 'abcb', 'abcaV' )

testInsertBetween_lex64( "simple-low", '', 'test', 'low', '0' )
testInsertBetween_lex64( "nested-low", 'abca', 'abcx', 'low', 'abcb' )
testInsertBetween_lex64( "nested-close-low", 'abca', 'abcb', 'low', 'abca---0' )

testInsertBetween_lex64( "simple-high", '', 'test', 'high', 's' )
testInsertBetween_lex64( "nested-high", 'abca', 'abcx', 'high', 'abcw' )
testInsertBetween_lex64( "nested-close-high", 'abca', 'abcb', 'high', 'abcaz' )

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

local function assertf( condition, format, ... )
	if not condition then
		assert( condition, string.format( format, ... ) )
	end
end

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

-- FIXME [jandrew 2/5/2015]: This test fails on Android.
local WFPlatformInfo = require 'WFPlatformInfo'
if not WFPlatformInfo.isAndroid then

function WFSortOrderStringsTest.testFullEnumerationOfCustomEncoding()
	-- pick an encoding small enough that a full traversal is fast
	local chars = "0123"
	local encoding = WFSortOrderStrings.makeEncoding( chars )
	
	local first = string.rep( chars:sub( 1, 1 ), 3 ) .. chars:sub( 2, 2 ) -- '0001'
	local last = string.rep( chars:sub( -1 ), 1023 )
	
	local function checkInvalidTrailingChar( value, name )
		assertf( value:sub( -1 ) ~= chars:sub( 1, 1 ), "invalid trailing char in %s", name )
		return value
	end
	
	-- wrap the APIs to validate postconditions and provide the bias arg
	local function after( v )
		local result = WFSortOrderStrings.insertAfter( encoding, v, 'low' )
		assertf( result > v, "%s <= %s", result, v )
		return checkInvalidTrailingChar( result, "after" )
	end

	local function before( v )
		local result = WFSortOrderStrings.insertBefore( encoding, v, 'high' )
		assertf( result < v, "%s >= %s", result, v )
		return checkInvalidTrailingChar( result, "before" )
	end

	local function between( v1, v2 )
		local middle = WFSortOrderStrings.insertBetween( encoding, v1, v2 )
		assertf( middle > v1, "%s <= %s", middle, v1 )
		assertf( middle < v2, "%s >= %s", middle, v2 )
		return checkInvalidTrailingChar( middle, "between" )
	end
	
	assert( not pcall( before, first ) )
	assert( not pcall( after, last ) )

	local function scanRange( first, last, nextFn, prevFn, between, reqCount )
		local count = 0
		local value = first
		
		while value ~= last do
			local nextValue = nextFn( value )
			local previousValue = prevFn( nextValue )
	
			if #value < 1022 then
				between( previousValue, nextValue )
			end
			
			value = nextValue
			count = count + 1
		end
		
		-- the exact value would be too sensitive here, but changes that
		-- drastically reduce the range of values achieved by iterating
		-- the space with after/low and before/high ("near" bias) would
		-- be a problematic bug
		assertf( count >= reqCount, "%d < %d", count, reqCount )
	end

	-- completely scan forward
	scanRange( first, last, after, before, between, 34000 )
	
	local function invertedBetween( a, b )
		return between( b, a )
	end
	
	-- completely scan backward
	scanRange( last, first, before, after, invertedBetween, 34000 )

end

end

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

return WFSortOrderStringsTest
