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

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

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

	-- WF.core.logging
local WFLogger = require 'WFLogger'

	-- WF.core.os
local KSPathUtils = require 'KSPathUtils'
local WFFileUtils = require 'WFFileUtils'
local WFPlatformInfo = require 'WFPlatformInfo'

	-- WF.core.utils
local KSIdGenerator = require 'KSIdGenerator'
local KSTableUtils = require 'KSTableUtils'

local logger = WFLogger "WFTestFileUtils"
--	:setLevel( 'debug' )

--============================================================================--
--[[!
	\class WFTestFileUtils
	\brief This class provides a way to verify tests don't leave behind files.
		Every test should delete any files it creates.
]]--

local WFTestFileUtils = {}

local checkFoldersAfterClassTearDown = false
	-- default to check folders after every test

local dynamicFoldersToScan = {}
local folderNamesForTests = {}

local ignoredFilePatterns = {
	'^oz%-user%-',
		-- oz user is difficult to close see ozUserMethods:logoutP
}

local ignoredDirectoryNames = {
}

if WFPlatformInfo.isiOS then

	ignoredFilePatterns[ #ignoredFilePatterns + 1 ] = '^Cache.db'
		-- iOS NSUrlCache may create a Cache.db file

	ignoredFilePatterns[ #ignoredFilePatterns + 1 ] = '^test.log'
		-- We create a test.log in the documents folder on iOS, no API to delete.

	ignoredDirectoryNames[ #ignoredFilePatterns + 1 ] = '/fsCachedData/'
		-- iOS NSUrlCache may create a fsCachedData folder with random named files

end

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

local function ignoreFile( path )

	local fileName = KSPathUtils.leafName(path)

	for _, ignoreFile in pairs( ignoredFilePatterns ) do
		if string.match(fileName, ignoreFile) then
			return true
		end
	end

	for _, ignorePath in pairs( ignoredDirectoryNames ) do
		if string.match(path, ignorePath) then
			return true
		end
	end

	return false

end

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

--local function getMetricsFolder()
--
--	local WFDeviceInfo = require 'WFDeviceInfo'
--	local home = KSPathUtils.getStandardFilePath( "home" )
--
--	local metricsHome
--	local platform
--	local metrics = {}
--	metrics[ "platform" ] = WFPlatformInfo.getAppDeviceClass()
--	metrics[ "os" ] = WFPlatformInfo.getOS and WFPlatformInfo.getOS()
--
--	if WFPlatformInfo.isiOS then
--		metricsHome = KSPathUtils.child( "/Users", WFDeviceInfo.userName() )
--		platform = metrics[ "os" ]
--	else
--		metricsHome = home
--		platform = metrics[ "platform" ]
--	end
--
--	local metricsFolder = KSPathUtils.child( metricsHome, "WFPerformanceTestMetrics" )
--
--	return KSPathUtils.child( metricsFolder, platform )
--
--end

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

local WFFolderWalker

local function searchAndDeleteFilesInFolder( folder, folderWalkerOptions )

	if WFPlatformInfo.isAndroid then
		return nil
		-- WFFolderWalker isn't implemented on Android
	end

	WFFolderWalker = WFFolderWalker or require 'WFFolderWalker'

	local firstFileFound

	for path in WFFolderWalker( folder, folderWalkerOptions ) do

		logger:debugf( "WFFolderWalker delete path: %s", path )

		pcall( WFFileUtils.deleteFile, path )
			-- Delete the file so every test doesn't generate an error.

		if not firstFileFound and not ignoreFile( path ) then
			firstFileFound = path
		end

	end

	return firstFileFound

end

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

function WFTestFileUtils.checkForUndeletedFiles( testName )

	if checkFoldersAfterClassTearDown and testName ~= "tearDownClass" then
		return nil
	end

	checkFoldersAfterClassTearDown = false

	local foldersToScan = {}

	for k, _ in pairs(dynamicFoldersToScan) do
		table.insert(foldersToScan, k)
	end

	-- Mac: ~/Pictures/Adobe Revel/Adobe Revel Library.rvl
	-- iOS: ~/Library/Developer/CoreSimulator/Devices/C7719AEB-B9D3-4B44-A5F3-3F0FA85365D4/data/Containers/Data/Application/15EE7DF5-2152-4FB6-A2CE-1251FBBCFEB3/Documents
	-- Win: \Users\<user>\AppData\Local\Adobe\unknownApplication
	-- And: /data/data/com.adobe.wichitafoundation/files/carouselDocuments
	table.insert( foldersToScan, KSPathUtils.getStandardFilePath( "carouselDocuments" ) )

	-- Mac: ~/Library/Caches/Adobe Revel
	-- iOS: ~Library/Developer/CoreSimulator/Devices/C7719AEB-B9D3-4B44-A5F3-3F0FA85365D4/data/Containers/Data/Application/15EE7DF5-2152-4FB6-A2CE-1251FBBCFEB3/Library/Caches
	-- Win: \Users\<user>\AppData\Local\Adobe\unknownApplication\caches
	-- And: /data/data/com.adobe.wichitafoundation/files/carouselCaches
	table.insert( foldersToScan, KSPathUtils.getStandardFilePath( "carouselCaches" ) )

	-- ~/WFPerformanceTestMetrics/mac"
--	table.insert( foldersToScan, getMetricsFolder() )
		-- Disabled because:
		-- Performance tests intentionally leave files in this directory.
		-- You can uncomment this to see if any performance tests are misnamed.

	-- Mac: ~/Library/Caches/Adobe/WF Unit Tests
	-- iOS: ~/Library/Developer/CoreSimulator/Devices/C7719AEB-B9D3-4B44-A5F3-3F0FA85365D4/data/Containers/Data/Application/15EE7DF5-2152-4FB6-A2CE-1251FBBCFEB3/Library/Caches/Adobe/WF Unit Tests
	-- Win: ~/Library/Caches/Adobe/WichitaFoundation
	-- And: /data/data/com.adobe.wichitafoundation/files/caches/Adobe/com.adobe.wichitafoundation
	local cachePath = KSPathUtils.getStandardFilePath( 'caches' )
	local adobeCachePath = KSPathUtils.child(cachePath, 'Adobe' )
	local appIdentifier = WFPlatformInfo.getAppIdentifier()
	if appIdentifier then
		local userInfoCachePathParent = KSPathUtils.child( adobeCachePath, appIdentifier )
		table.insert( foldersToScan, userInfoCachePathParent )
	else
		logger:errorf( "checkForUndeletedFiles expecting getAppIdentifier() to be set" )
	end

	local numStandardFolders = #foldersToScan

	local firstFileFound

	for index, folder in ipairs( foldersToScan ) do

		if testName == 'tearDownClass' and not checkFoldersAfterClassTearDown
		or testName == 'setUpClass' then

			-- delete entire folders on class teardown in case the server has built
			-- up too many subfolders which we can't scan through fast enough.
			-- (Build machines had 100k empty folders)

			logger:debugf( "Now deleting folder: %s", folder )

			pcall( WFFileUtils.delete, folder )

			if index <= numStandardFolders then
				pcall( WFFileUtils.mkdir, folder )
					-- Android caches the results from getStandardFilePath in
					-- WFCoreEnv::KSPathUtils_getStandardFilePath so recreate.
			end

		else

			local folderWalkerOptions

			if WFPlatformInfo.isiOS or WFPlatformInfo.isMac then
				folderWalkerOptions = { includeHiddenFiles = true }
			end

			logger:debugf( "checkForUndeletedFiles start in folder: %s", folder )

			local success, firstFileFoundOrError = pcall( searchAndDeleteFilesInFolder,
				folder, folderWalkerOptions )

			if success then

				if not firstFileFound then
					firstFileFound = firstFileFoundOrError
				end

			else

				logger:debugf( "checkForUndeletedFiles failed, error: %s", firstFileFoundOrError )

			end

			logger:debugf( "checkForUndeletedFiles end, firstFileFound: %s", firstFileFoundOrError )

		end

	end

	return firstFileFound

end

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

function WFTestFileUtils.addTestFolder( folder )

	dynamicFoldersToScan[ folder ] = true

end

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

function WFTestFileUtils.createTestFolder( rxTest, testName, forceCreateNewTestFolder )

	if testName == nil then
		testName = rxTest:testName()
	end

	local luaClassName = rxTest:luaClassName()
	local retryCount = rxTest:retryCount()
	
	local dstFolderName = KSTableUtils.derefOpt( folderNamesForTests, luaClassName, testName, retryCount )

	local needToCreateNewFolder = not not forceCreateNewTestFolder

	if needToCreateNewFolder or not dstFolderName then
		
		-- have seen windows test fail with failures when test names are too long,
	 	-- unable to write files when path string is too long.
	 	
		local shortTestName = testName
	 	if string.len( shortTestName ) > 50 then
	 		shortTestName = string.sub( shortTestName, 1, 24 ) .. '_' .. string.sub( shortTestName, -25, -1 )
	 	end
	
		dstFolderName = shortTestName .. '-' .. KSIdGenerator.uuidInLowercaseHex()
		needToCreateNewFolder = true
	end

	local dstPath = KSPathUtils.child( KSPathUtils.getStandardFilePath( 'carouselDocuments' ), dstFolderName )

	if needToCreateNewFolder then

		pcall( WFFileUtils.mkdir, dstPath )

		WFTestFileUtils.addTestFolder( dstPath )
		KSTableUtils.setNested( folderNamesForTests, dstFolderName, luaClassName, testName, retryCount )

		rxTest:finally( function()
			WFFileUtils.delete( dstPath )
		end )

	end

	return dstPath, dstFolderName

end

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

function WFTestFileUtils.getTestFolderName( rxTest )

	return KSTableUtils.derefOpt( folderNamesForTests, rxTest:luaClassName(), rxTest:testName(), rxTest:retryCount() )

end

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

function WFTestFileUtils.getTestFolderPath( rxTest )

	local testFolderName = WFTestFileUtils.getTestFolderName( rxTest )

	return testFolderName
			and KSPathUtils.child( KSPathUtils.getStandardFilePath( 'carouselDocuments' ), testFolderName )
			or nil

end

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

function WFTestFileUtils.checkForUndeletedFilesAfterClassTeardown()

	checkFoldersAfterClassTearDown = true

end

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

return WFTestFileUtils
