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

KSStringUtilsTest.lua

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

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

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

local KSStringUtilsTest = {}

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

local KSStringUtils = require 'KSStringUtils'

local WFPlatformInfo = require 'WFPlatformInfo'

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

function KSStringUtilsTest.testi18n_collate()
	
	assert( KSStringUtils.localizedStringCompare( "Abc", "Def" ) < 0, "'Abc' not < 'Def'" )
	assert( KSStringUtils.localizedStringCompare( "Abc", "Abc" ) == 0, "'Abc' != 'Abc'" )
	assert( KSStringUtils.localizedStringCompare( "Def", "Abc" ) > 0, "'Def' not > 'Abc'" )
	
	assert( KSStringUtils.localizedStringCompare( "A", "N" ) < 0, "1: 'A' not < 'N'" )
	assert( KSStringUtils.localizedStringCompare( "N", "n" ) < 0, "2: 'N' not < 'n'" )
	assert( KSStringUtils.localizedStringCompare( "n", "N1" ) < 0, "3: 'n' not < 'N1'" )
	assert( KSStringUtils.localizedStringCompare( "N1", "n1" ) < 0, "4: 'N1' not < 'n1'" )
	assert( KSStringUtils.localizedStringCompare( "n1", "N2" ) < 0, "5: 'n1' not < 'N2'" )
	assert( KSStringUtils.localizedStringCompare( "N2", "n2" ) < 0, "6: 'N2' not < 'n2'" )
		
	if not WFPlatformInfo.isAndroid and not WFPlatformInfo.isWindows then
		assert( KSStringUtils.localizedStringCompare( "n2", "N10" ) < 0, "7: 'n2' not < 'N10'" )
	end
		
	assert( KSStringUtils.localizedStringCompare( "N10", "n10" ) < 0, "8: 'N10' not < 'n10'" )
	assert( KSStringUtils.localizedStringCompare( "n10", "\195\145" ) < 0, "9: 'n10' not < '\195\145'" )
	assert( KSStringUtils.localizedStringCompare( "\195\145", "\195\1451" ) < 0, "10: '\195\145' not < '\195\1451'" )
	assert( KSStringUtils.localizedStringCompare( "\195\1451", "\195\1452" ) < 0, "11: '\195\1451' not < '\195\1452'" )
		
	if not WFPlatformInfo.isAndroid and not WFPlatformInfo.isWindows then
		assert( KSStringUtils.localizedStringCompare( "\195\1452", "\195\14510" ) < 0, "12: '\195\1452' not < '\195\14510'" )
		assert( KSStringUtils.localizedStringCompare( "\195\14510", "Z" ) < 0, "13: '\195\14510' not < 'Z'" )
	end
	
end

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

if not WFPlatformInfo.isMac then

-- localizedStandardCompare does not guarantee stable resuts on Mac
-- https://lightroom.slack.com/archives/C03ENETBD/p1715982160922039?thread_ts=1709142733.832739&cid=C03ENETBD

function KSStringUtilsTest.testi18n_localized_standard_compare()
	
	assert( KSStringUtils.localizedStandardStringCompare( "Abc", "Def" ) < 0, "'Abc' not < 'Def'" )
	assert( KSStringUtils.localizedStandardStringCompare( "Abc", "Abc" ) == 0, "'Abc' != 'Abc'" )
	assert( KSStringUtils.localizedStandardStringCompare( "Def", "Abc" ) > 0, "'Def' not > 'Abc'" )

	assert( KSStringUtils.localizedStandardStringCompare( "A", "n" ) < 0, "1: 'A' not < 'n'" )
	
	-- FIXME_WIN32:  This exception can be eliminated if the parameters are reversed for the case sensitive compare (see Android).
	if not WFPlatformInfo.isWindows then
		assert( KSStringUtils.localizedStandardStringCompare( "n", "N" ) < 0, "2: 'n' not < 'N'" )
	end
	
	assert( KSStringUtils.localizedStandardStringCompare( "N", "\195\145" ) < 0, "3: 'N' not < '\195\145'" )

	if not WFPlatformInfo.isAndroid and not WFPlatformInfo.isWindows then
		assert( KSStringUtils.localizedStandardStringCompare( "\195\145", "n1" ) < 0, "4: '\195\145' not < 'n1'" )
	end

	-- FIXME_WIN32:  This exception can be eliminated if the parameters are reversed for the case sensitive compare (see Android).
	if not WFPlatformInfo.isWindows then
		assert( KSStringUtils.localizedStandardStringCompare( "n1", "N1" ) < 0, "5: 'n1' not < 'N1'" )
	end

	assert( KSStringUtils.localizedStandardStringCompare( "N1", "\195\1451" ) < 0, "6: 'N1' not < '\195\1451'" )

	if not WFPlatformInfo.isAndroid and not WFPlatformInfo.isWindows then
		assert( KSStringUtils.localizedStandardStringCompare( "\195\1451", "n2" ) < 0, "7: '\195\1451' not < 'n2'" )
	end

	-- FIXME_WIN32:  This exception can be eliminated if the parameters are reversed for the case sensitive compare (see Android).
	if not WFPlatformInfo.isWindows then
		assert( KSStringUtils.localizedStandardStringCompare( "n2", "N2" ) < 0, "8: 'n2' not < 'N2'" )
	end

	assert( KSStringUtils.localizedStandardStringCompare( "N2", "\195\1452" ) < 0, "9: 'N2' not < '\195\1452'" )

	if not WFPlatformInfo.isAndroid and not WFPlatformInfo.isWindows then
		assert( KSStringUtils.localizedStandardStringCompare( "\195\1452", "n10" ) < 0, "10: '\195\1452' not < 'n10'" )
	end

	-- FIXME_WIN32:  This exception can be eliminated if the parameters are reversed for the case sensitive compare (see Android).
	if not WFPlatformInfo.isWindows then
		assert( KSStringUtils.localizedStandardStringCompare( "n10", "N10" ) < 0, "11: 'n10' not < 'N10'" )
	end

	assert( KSStringUtils.localizedStandardStringCompare( "N10", "\195\14510" ) < 0, "12: 'N10' not < '\195\14510'" )

	if not WFPlatformInfo.isAndroid and not WFPlatformInfo.isWindows then
		assert( KSStringUtils.localizedStandardStringCompare( "\195\14510", "Z" ) < 0, "13: '\195\14510' not < 'Z'" )
	end
	
end

end

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

local kUpperADoubleDot = "\195\132"
local kLowerADoubleDot = "\195\164"


function KSStringUtilsTest.testLower()
	
	local function t( input, expected )
		local result = KSStringUtils.lower( input )
		local success = result == expected
		if not success then
			assert( success, "'" .. input .. "' not lowered" )
		end
	end
	
	t( "ABC", 'abc' )
	t( "AbC", 'abc' )
	t( "  AbC  ", '  abc  ' )
	t( "", '' )
	
	t( kUpperADoubleDot, kLowerADoubleDot )
	t( kLowerADoubleDot, kLowerADoubleDot )

end

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


function KSStringUtilsTest.testUpper()

	local function t( input, expected )
		local result = KSStringUtils.upper( input )
		local success = result == expected
		if not success then
			assert( success, "'" .. input .. "' not lowered" )
		end
	end
	
	t( "ABC", 'ABC' )
	t( "AbC", 'ABC' )
	t( "  AbC  ", '  ABC  ' )
	
	t( kUpperADoubleDot, kUpperADoubleDot )
	t( kLowerADoubleDot, kUpperADoubleDot )

end

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


function KSStringUtilsTest.testTrim()

	assert( KSStringUtils.trim( "ABC" ) == 'ABC', "'ABC' not trimmed" )
	assert( KSStringUtils.trim( "  AbC" ) == 'AbC', "'  AbC' not trimmed" )
	assert( KSStringUtils.trim( "AbC  " ) == 'AbC', "'AbC  ' not trimmed" )
	assert( KSStringUtils.trim( "   AbC  " ) == 'AbC', "'   AbC  ' not trimmed" )

end

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

function KSStringUtilsTest.testBase64()

	local KSData = require "KSData"
	
	local input = KSData.make( "Revel\n" )
	local encoded = KSStringUtils.encodeBase64( input )
	assert( encoded == 'UmV2ZWwK', "'Revel' not base64 encoded" )
	
	input = "V2ljaGl0YSBGb3VuZGF0aW9uCg=="
	local decodedString = KSStringUtils.decodeBase64AsString( input )
	assert( decodedString == 'Wichita Foundation\n', "'Wichita Foundation' not base64 decoded" )
	local decoded = KSStringUtils.decodeBase64( input )
	local shaSum = decoded:digest( "SHA1" )
	assert( shaSum == '2ab9cb1ad8ad25cd2ba1448da3cb705b44a44c84', "'Revel' not base64 decoded" )

end

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

function KSStringUtilsTest.testEscapeHtml()

	local str = [[
	<html lang="en">
		<head>Test</head>
		<body>Just a small html example with an ampersand (&) in it.</body>
	</html>
	]]

	local escapedHtmlStr = KSStringUtils.escapeHtml(str)

	local expectedStr = [[
	&lt;html lang=&quot;en&quot;&gt;
		&lt;head&gt;Test&lt;/head&gt;
		&lt;body&gt;Just a small html example with an ampersand (&amp;) in it.&lt;/body&gt;
	&lt;/html&gt;
	]]
	
	assert( escapedHtmlStr == expectedStr )
end

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

function KSStringUtilsTest.testGetBaseContentType()

	assert( KSStringUtils.getBaseContentType( "image/jpeg" ) == "image/jpeg", "no property" )
	assert( KSStringUtils.getBaseContentType( "image/jpeg;key=value" ) == "image/jpeg", "one property" )
	assert( KSStringUtils.getBaseContentType( "image/jpeg;key=value;key2=value2" ) == "image/jpeg", "two properties" )
	
end

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

function KSStringUtilsTest.testSplit()

	local a = KSStringUtils.split( "hello,world", "," )
	assert( a[ 1 ] == 'hello' )
	assert( a[ 2 ] == 'world' )

end

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

function KSStringUtilsTest.testConvertToHex()

	local KSData = require "KSData"
	
	local input = KSData.make( "TestKSData" )
	local hexOutput = KSStringUtils.convertToHex( input )
	assert( hexOutput == '546573744b5344617461', "'TestKSData' not converted to hex." )
	
	input = "TestStringData"
	local stringHexOutput = KSStringUtils.convertToHex( input )
	assert( stringHexOutput == '54657374537472696e6744617461', "'TestStringData' not converted to hex." )

end

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

local function testByteStringFn( byteStringFn, isPlatformSpecific, acceptNonNumber, noFractionalKB )

	if acceptNonNumber then
	
		assert( byteStringFn "a string" == "a string", "a string" )
		assert( byteStringFn {} == '', 'not string nor number' )
		
	end 

	if isPlatformSpecific and WFPlatformInfo.isCocoa then
		
		assert( byteStringFn( 0/0 ) == '', 'nan' )
		assert( byteStringFn( -0.9 ) == noFractionalKB and '-0 bytes' or '-0.90 bytes', 'nonint' )
		assert( byteStringFn( -5 ) == '-5 bytes', 'nonint' )
		assert( byteStringFn( 1 ) == '1 byte', 'nonint' )
		assert( byteStringFn( 100 ) == '100 bytes', '100' )
		assert( byteStringFn( 1000 ) == '1 KB', 'KB' )
		assert( byteStringFn( 1100 ) == noFractionalKB and '1KB' or '1.10 KB', 'KB' )
		assert( byteStringFn( 1100 * 1000 ) == '1.10 MB', 'MB' )
		assert( byteStringFn( 1100 * 1000  * 1000 ) == '1.10 GB', 'GB' )
		assert( byteStringFn( 1100 * 1000 * 1000 * 1000 ) == '1.10 TB', 'TB' )
		assert( byteStringFn( 1100 * 1000 * 1000 * 1000 * 1000 ) == '1.10 PB', 'PB' )
		assert( byteStringFn( 123456 ) == noFractionalKB and '123 KB' or '123.45 KB', '123456' )
		
	else
	
		assert( byteStringFn( 0/0 ) == '', 'nan' )
		assert( byteStringFn( -0.9 ) == noFractionalKB and '-0 bytes' or '-0.90 bytes', 'nonint' )
		assert( byteStringFn( -5 ) == '-5 bytes', 'nonint' )
		assert( byteStringFn( 1 ) == '1 byte', 'nonint' )
		assert( byteStringFn( 100 ) == '100 bytes', 'bytes' )
		assert( byteStringFn( 1000 ) == '1000 bytes', 'bytes' )
		assert( byteStringFn( 1024 ) == '1 KB', 'KB' )
		assert( byteStringFn( 1100 ) == noFractionalKB and '1 KB' or '1.07 KB', 'KB' )
		assert( byteStringFn( 1100 * 1024 ) == '1.07 MB', 'MB' )
		assert( byteStringFn( 1100 * 1024  * 1024 ) == '1.07 GB', 'GB' )
		assert( byteStringFn( 1100 * 1024 * 1024 * 1024 ) == '1.07 TB', 'TB' )
		assert( byteStringFn( 1100 * 1024 * 1024 * 1024 * 1024 ) == '1.07 PB', 'PB' )
		assert( byteStringFn( 123456 ) == noFractionalKB and '120 KB' or '120.56 KB', '123456' )
	
	end

end

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

function KSStringUtilsTest.testByteString()

	local byteStringFn = assert( KSStringUtils.byteString, 'fn' )
	
	local isPlatformSpecific = false
	local acceptNonNumber = true
	local noFractionalKB = false
	
	testByteStringFn( byteStringFn, isPlatformSpecific, acceptNonNumber, noFractionalKB )

	
end


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

function KSStringUtilsTest.testPlatformByteString()

	local byteStringFn = assert( KSStringUtils.platformByteString, 'fn' )
	
	local isPlatformSpecific = true
	local acceptNonNumber = true
	local noFractionalKB = true
	
	testByteStringFn( byteStringFn, isPlatformSpecific, acceptNonNumber, noFractionalKB )

	
end

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

function KSStringUtilsTest.testStorageByteString()

	local byteStringFn = assert( KSStringUtils.storageByteString, 'fn' )
	
	local isPlatformSpecific = true
	local acceptNonNumber = false
	local noFractionalKB = false
	
	testByteStringFn( byteStringFn, isPlatformSpecific, acceptNonNumber, noFractionalKB )
	
end

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

function KSStringUtilsTest.testTrimWhitespace()

	local t = assert( KSStringUtils.trimWhitespace, 'KSSU.tW' )

	assert( t 'hello' == 'hello', 'no ws' )
	assert( t ' hello' == 'hello', 'leading ws' )
	assert( t 'hello ' == 'hello', 'trailing ws' )
	assert( t 'hel lo' == 'hel lo', 'internal ws' )
	assert( t ' hel lo ' == 'hel lo', 'l/t/i ws' )
	assert( t '' == '', 'empty' )
	assert( t '\009\010\011\012\013\014X\008' == '\014X\008', 'ws control chars' )
	assert( t(
			'\226\128\128'
			.. '\226\128\129'
			.. '\226\128\130'
			.. '\226\128\131'
			.. '\226\128\132'
			.. '\226\128\133'
			.. '\226\128\134'
			.. '\226\128\135'
			.. '\226\128\136'
			.. '\226\128\137'
			.. '\226\128\138'
			.. '\226\128\139'
			.. 'X'
			.. '\225\191\191'
		) == (
			'\226\128\139'
			.. 'X'
			.. '\225\191\191'
		) , 'ws control chars' )
	assert( t '    ' == '', 'all ws' )
	assert( t '\078NO-BREAK SPACE\078', 'NO-BREAK SPACE', 'UTF-8 0x78' )
	assert( t '\227\128\128IDEOGRAPHIC SPACE\227\128\128', 'IDEOGRAPHIC SPACE', 'UTF-8 0xE38080' )

end

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

function KSStringUtilsTest.testMakeUTF8Processor()

	local m = assert( KSStringUtils.makeUTF8Processor, 'KSSU.mUTF8P' )
	
	local noExtraArgs = true
	local lastCharProcessed, lastCharUnicode
	local nCharsProcessed = 0
	
	local charProcessor = function( strChr, unicode, ... )
		noExtraArgs = noExtraArgs and select( '#', ... ) == 0
		lastCharProcessed = strChr
		lastCharUnicode = unicode
		nCharsProcessed = nCharsProcessed + 1
	end
	
	local lastMultiByteProcessed
	local nMultiBytesProcessed = 0
	
	local multiByteProcessor = function( unicode, ... )
		noExtraArgs = noExtraArgs and select( '#', ... ) == 0
		lastMultiByteProcessed = unicode
		nMultiBytesProcessed = nMultiBytesProcessed + 1
		return string.format( "{%08x}", unicode )
	end
	
	local p = assert( m( charProcessor, multiByteProcessor ), 'make processor' )
	
	assert( select( '#', p( 'x' ) ) == 0, 'processor returns none' )
	
	assert( nCharsProcessed == 1, 'nCP 1' )
	assert( lastCharProcessed == 'x', 'lCP 1' )
	assert( lastCharUnicode == 120, 'lCU 1' )
	assert( nMultiBytesProcessed == 0, 'nMBP 1' )

	assert( select( '#', p( '\232' ) ) == 0, 'processor returns none' )
	assert( select( '#', p( '\143' ) ) == 0, 'processor returns none' )
	assert( select( '#', p( '\176' ) ) == 0, 'processor returns none' )
	
	assert( nCharsProcessed == 2, 'nCP 2' )
	assert( lastCharProcessed == '{000083f0}', 'lCP 2' )
	assert( lastCharUnicode == 0x83F0, 'lCU 2' )
	assert( nMultiBytesProcessed == 1, 'nMBP 2' )
	assert( lastMultiByteProcessed == 0x83F0, 'lMBP 2' )

	assert( noExtraArgs, 'no extra args' )

end

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

function KSStringUtilsTest.testNumberToString()

	local f = assert( KSStringUtils.numberToString, 'KSSUT.nTS' )

	assert( f( 1001.2002, 5 ) == '1001.20020', '1001.2002 5' )
	assert( f( 1001.2002, 4 ) == '1001.2002', '1001.2002 4' )
	assert( f( 1001.2002, 3 ) == '1001.200', '1001.2002 3' )
	assert( f( 1001.2006, 3 ) == '1001.201', '1001.2006 3' )

end

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

function KSStringUtilsTest.testSystemInternationalString()

	local si = assert( KSStringUtils.systemInternationalString, 'KSSUT.sIS' )

	assert( si( 0, 'm' ) == '0 m', '0 m' )
	assert( si( 999, 'm', 2 ) == '999 m', '999 m' )
	assert( si( -1000, 'm', 2 ) == '-1 km', '1000 m' )
	assert( si( 9876, 'm', 3 ) == '9.876 km', '9876 m' )
	assert( si( 1000000000000000000000, 'B', 3 ) == '1000 EB', '1 ZB' )
	assert( si( 'a string' ) == 'a string', 'invalid type' )
	
end

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

function KSStringUtilsTest.testFormattedFraction()

	local ff = assert( KSStringUtils.formattedFraction, 'KSSUT.fF' )

	assert( ff( "wrong type" ), "?", 'invalid entry' )

	assert( ff( 8, 1, false ), "8", '8/1 U' )
	assert( ff( 41, -1, false ), "-41", '-41/1 U' )
	assert( ff( 10, 9, false ), "1 \226\129\176\226\129\132\226\130\137", '10/9 U' )
	assert( ff( 1, 5, false ), "\226\133\149", '1/5 U' )
	assert( ff( -1, 5, false ), "\226\128\146 \226\133\149", '-1/5 U' )

	assert( ff( 6, 10, true ), "3/5", '6/10 A' )
	assert( ff( 6.2, -10, true ), "3/5", '6.2/10 A' )
	assert( ff( -12, 10, true ), "1 1/5", '-12/10 A' )
	assert( ff( -20, -10, true ), "2", '20/10 A' )
	assert( ff( 0, 10, true ), "0", '0/10 A' )

end

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

function KSStringUtilsTest.testValidateUTF8()

	local validate = assert( KSStringUtils.validateUTF8, 'KSSUT.validateUTF8' )

	assert( validate( "Should Be Valid" ), "?", 'invalid entry' )
	
	local appName = WFPlatformInfo.getAppName()
	
	assert( validate( appName ), "?", 'invalid entry' )
	
	local maybeUtf16Str = KSStringUtils.getUTF16String_DEBUG()
	
	if maybeUtf16Str then
		assert( not validate( maybeUtf16Str ), "?", 'false positive' )
	end
	
end

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

function KSStringUtilsTest.testLengthOfUTF8String()

	local ascii = "1234"
	assert( KSStringUtils.lengthOfUTF8String( ascii ) == 4 )

	local doubleByteChar = '\195\165'
	local threeDoubleByte = doubleByteChar .. doubleByteChar .. doubleByteChar
	assert( KSStringUtils.lengthOfUTF8String( threeDoubleByte ) == 3 )

	local japaneseseChar = '\229\137\141'
	assert( KSStringUtils.lengthOfUTF8String( japaneseseChar ) == 1 )
	
	local asciiChar = "9"
	local mixed = asciiChar .. japaneseseChar .. asciiChar .. doubleByteChar .. asciiChar
	assert( KSStringUtils.lengthOfUTF8String( mixed ) == 5 )

end

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

function KSStringUtilsTest.testUTF8Substring()

	local ascii = "1234"
	assert( KSStringUtils.UTF8Substring( ascii, 0, 1 ) == "1" )
	assert( KSStringUtils.UTF8Substring( ascii, 1, 2 ) == "12" )
	assert( KSStringUtils.UTF8Substring( ascii, 1, 4 ) == "1234" )
	assert( KSStringUtils.UTF8Substring( ascii, 4, 4 ) == "4" )
	assert( KSStringUtils.UTF8Substring( ascii, 3 ) == "34" )
	
	local doubleByteChar = '\195\165'
	local threeDoubleByte = doubleByteChar .. doubleByteChar .. doubleByteChar
	assert( KSStringUtils.UTF8Substring( threeDoubleByte, 1, 1 ) == doubleByteChar )
	assert( KSStringUtils.UTF8Substring( threeDoubleByte, 1, 3 ) == threeDoubleByte )
	assert( KSStringUtils.UTF8Substring( threeDoubleByte, 3, 3 ) == doubleByteChar )

	local japaneseseChar = '\229\137\141'
	local asciiChar = "9"
	local mixed = asciiChar .. japaneseseChar .. asciiChar .. doubleByteChar .. asciiChar
	assert( KSStringUtils.UTF8Substring( mixed, 1, 1 ) == asciiChar )
	assert( KSStringUtils.UTF8Substring( mixed, 1, 3 ) == asciiChar .. japaneseseChar .. asciiChar )
	assert( KSStringUtils.UTF8Substring( mixed, 1, 5 ) == mixed )
	assert( KSStringUtils.UTF8Substring( mixed, 4, 5 ) == doubleByteChar .. asciiChar )

end

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

function KSStringUtilsTest.testObfuscateDeobfuscate()

	local o = assert( KSStringUtils.obfuscate, 'KSSUT.obfuscate' )
	local d = assert( KSStringUtils.deobfuscate, 'KSSUT.deobfuscate' )

	-- Test basic string
	local testString = "Hello World"
	local obfuscated = o( testString )
	local deobfuscated = d( obfuscated )
	assert( deobfuscated == testString, "Basic string obfuscation/deobfuscation failed" )

	-- Test empty string
	local emptyString = ""
	local obfuscatedEmpty = o( emptyString )
	local deobfuscatedEmpty = d( obfuscatedEmpty )
	assert( deobfuscatedEmpty == emptyString, "Empty string obfuscation/deobfuscation failed" )

	-- Test string with special characters
	local specialString = "!@#$%^&*()_+"
	local obfuscatedSpecial = o( specialString )
	local deobfuscatedSpecial = d( obfuscatedSpecial )
	assert( deobfuscatedSpecial == specialString, "Special characters obfuscation/deobfuscation failed" )

	-- Test long string
	local longString = string.rep( "Test", 100 )
	local obfuscatedLong = o( longString )
	local deobfuscatedLong = d( obfuscatedLong )
	assert( deobfuscatedLong == longString, "Long string obfuscation/deobfuscation failed" )

	-- Test that obfuscated strings are different from original
	assert( obfuscated ~= testString, "Obfuscated string should be different from original" )
	assert( obfuscatedSpecial ~= specialString, "Obfuscated special string should be different from original" )
	assert( obfuscatedLong ~= longString, "Obfuscated long string should be different from original" )

end

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

return KSStringUtilsTest
