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

PixelPathProviderStateMachineTest.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
local KSPathUtils = require 'KSPathUtils'
local WFFunctionUtils = require 'WFFunctionUtils'
local WFFiniteStateMachine = require 'WFFiniteStateMachine'
local WFFiniteStateMachineUtils = require 'WFFiniteStateMachineUtils'
local WFRxTestCase = require 'WFRxTestCase'

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

local PixelPathProviderStateMachine = require "PixelPathProviderStateMachine"

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

local PixelProviderSharedBlobCache = require "PixelProviderSharedBlobCache"

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

local function rxTestMake( rxTest )

	local pixPathProFSM = PixelPathProviderStateMachine.make()

	-- no orphaned states
	rxTest:expectEqual( {}, WFFiniteStateMachine.getOrphanedStates( pixPathProFSM ) )

end

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

local function rxTestDocstoreInfoStates( rxTest )

	local fsm = PixelPathProviderStateMachine.make()

	rxTest:expectEqual( 
		PixelPathProviderStateMachine.DocstoreInfoStates.FetchingAssetInfoForRender,
		WFFiniteStateMachine.getNextState( 
			fsm, 
			PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRenderFromDocstore,
			'start_docstore_read' ) )

	-- simulate getting before it was scheduled
	rxTest:expectEqual( 
		PixelPathProviderStateMachine.DocstoreInfoStates.NeedCheckRenderSettings,
		WFFiniteStateMachine.getNextState( 
			fsm, 
			PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRenderFromDocstore,
			'received_docstore_read' ) )

	-- simulate getting the info while scheduled
	rxTest:expectEqual( 
		PixelPathProviderStateMachine.DocstoreInfoStates.NeedCheckRenderSettings,
		WFFiniteStateMachine.getNextState( 
			fsm, 
			PixelPathProviderStateMachine.DocstoreInfoStates.FetchingAssetInfoForRender,
			'received_docstore_read' ) )

end

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

local function isFullSizeRenditionDownloadForExportState( state )

	return state == "TryExistingFullSizeRenditionDownload" or 
			state == "TryJustCreatedFullSizeRenditionDownload" or
			state == "TryUpdatedFullSizeRenditionDownload"

end

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

local function rxTestNewDevelopSettings( rxTest )

	-- These tests make sure that we can always handle dev settings changing
	-- out from underneath us
	local pixPathProFSM = PixelPathProviderStateMachine.make()

	-- First enumerate all the states that should respond to new_docstore_render_info
	local statesThatHandleNewDevSettings = {}

	statesThatHandleNewDevSettings[ 
		PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRenderFromDocstore ] =
			PixelPathProviderStateMachine.DocstoreInfoStates.NeedCheckRenderSettings

	statesThatHandleNewDevSettings[ 
		PixelPathProviderStateMachine.DocstoreInfoStates.FetchingAssetInfoForRender ] =
			PixelPathProviderStateMachine.DocstoreInfoStates.NeedCheckRenderSettings

	statesThatHandleNewDevSettings[ 
		PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForLoupeRenditionFromDocstore ] =
			PixelPathProviderStateMachine.DocstoreInfoStates.NeedCheckRenderSettingsForLoupeRendition

	statesThatHandleNewDevSettings[ 
		PixelPathProviderStateMachine.DocstoreInfoStates.FetchingAssetInfoForLoupeRendition ] =
			PixelPathProviderStateMachine.DocstoreInfoStates.NeedCheckRenderSettingsForLoupeRendition

	statesThatHandleNewDevSettings[ 
		PixelPathProviderStateMachine.PixelCacheCheckStates.NeedCheckForPixelPathInCache ] =
 			PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender

	statesThatHandleNewDevSettings[ 
		PixelPathProviderStateMachine.PixelCacheCheckStates.ChooseRenditionStrategy ] =
 			PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender

	for _, state in ipairs( PixelProviderSharedBlobCache.needRenderDownloadStates ) do
	
		if not isFullSizeRenditionDownloadForExportState( state ) then

			statesThatHandleNewDevSettings[ state ] =
				( state == PixelPathProviderStateMachine.DownloadStates.TryBiggestPossibleRenditionDownload and
						PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForExportBiggestPossibleRendition ) or
				( state == PixelPathProviderStateMachine.DownloadStates.NeedLoupeRenditionDownload and
						PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForLoupeRendition ) or
				PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender

		end

	end

	local needsRenderStates = {
			WFFiniteStateMachine.makeRetryableStates( 
				PixelPathProviderStateMachine.RenderRetryCount,
				PixelPathProviderStateMachine.RenderStates.NeedsRender ) }

	for _, state in ipairs( needsRenderStates ) do
	
		statesThatHandleNewDevSettings[ state ] =
			PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender

	end

	statesThatHandleNewDevSettings[ PixelPathProviderStateMachine.EndStates.FinishedSendingRender ] =
		PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender

	statesThatHandleNewDevSettings[ PixelPathProviderStateMachine.SendDataStates.SendNoDownloadableRenderOrBinary ] =
		PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender

	statesThatHandleNewDevSettings[ PixelPathProviderStateMachine.EndStates.NoDownloadableRenderOrBinary ] =
		PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForRender

	statesThatHandleNewDevSettings[ PixelPathProviderStateMachine.SendDataStates.SendNoDownloadableXmpDevelop ] =
		PixelPathProviderStateMachine.DocstoreInfoStates.NeedXmpDevelopInfo
		
	statesThatHandleNewDevSettings[ PixelPathProviderStateMachine.EndStates.NoDownloadableXmpDevelop ] =
		PixelPathProviderStateMachine.DocstoreInfoStates.NeedXmpDevelopInfo

	-- Now iterate over all the states in the state machine and make sure we
	-- either respond to new_docstore_render_info or ignore them.

	local function asTransitionStr( s1, s2 )

		return string.format( "%s->%s", s1, s2 )

	end

	for _, state in ipairs( WFFiniteStateMachine.getStates( pixPathProFSM ) ) do

		if statesThatHandleNewDevSettings[ state ] then

			rxTest:expectEqual( 
					asTransitionStr( 
						state,
						statesThatHandleNewDevSettings[ state ] ),
					asTransitionStr(
						state,
						WFFiniteStateMachine.getNextState( 
							pixPathProFSM, state, 'new_docstore_render_info' ) ) )

		end

	end

end

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

local function rxTestDownloadRenderStates( rxTest )

	local fsm = PixelPathProviderStateMachine.make()

	local currState = PixelPathProviderStateMachine.DownloadStates.NeedRenderDownload

	-- simulate a loop trying to fetch the render info
	for _ = 1, PixelPathProviderStateMachine.DownloadRetryCount do

		local downloadingState = WFFiniteStateMachine.getNextState( fsm, currState, 'start_download' )

		-- simulate getting the render we need
		rxTest:expectEqual( 
			PixelPathProviderStateMachine.SaveRenderStates.NeedToSaveRender,
			WFFiniteStateMachine.getNextState( fsm, downloadingState, 'received_download' ) )

		currState = WFFiniteStateMachine.getNextState( fsm, downloadingState, 'retry_download' )

	end

	-- check grab proxy on exhaustion
	rxTest:expectEqual( PixelPathProviderStateMachine.LocalBinaryPathCheckStates.NeedCheckAnyLocalNegativeForRender,
						currState )

end

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

local function rxTestDownloadOriginalStates( rxTest )

	local fsm = PixelPathProviderStateMachine.make()

	local currState = PixelPathProviderStateMachine.DownloadStates.NeedOriginalDownload

	-- simulate a loop trying to fetch the render info
	for _ = 1, PixelPathProviderStateMachine.DownloadRetryCount do

		local downloadingState = WFFiniteStateMachine.getNextState( fsm, currState, 'start_download' )

		-- simulate getting the proxy
		rxTest:expectEqual( 
			PixelPathProviderStateMachine.SaveDownloadStates.NeedToSaveOriginal,
			WFFiniteStateMachine.getNextState( fsm, downloadingState, 'received_download' ) )
		
		currState = WFFiniteStateMachine.getNextState( fsm, downloadingState, 'retry_download' )

	end

	-- check grab orig on exhaustion
	rxTest:expectEqual( PixelPathProviderStateMachine.SendDataStates.SendPermanentDownloadError,
						currState )	

end

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

local function rxTestDownloadProxyStates( rxTest )

	local fsm = PixelPathProviderStateMachine.make()

	local currState = PixelPathProviderStateMachine.DownloadStates.NeedProxyDownload

	-- simulate a loop trying to fetch the render info
	for _ = 1, PixelPathProviderStateMachine.DownloadRetryCount do

		local downloadingState = WFFiniteStateMachine.getNextState( fsm, currState, 'start_download' )

		-- simulate getting the proxy
		rxTest:expectEqual( 
			PixelPathProviderStateMachine.SaveDownloadStates.NeedToSaveProxy,
			WFFiniteStateMachine.getNextState( fsm, downloadingState, 'received_download' ) )

		currState = WFFiniteStateMachine.getNextState( fsm, downloadingState, 'retry_download' )

	end

	-- Used to be NeedCheckForLocalOriginal.  But now proxy failures in the state machine will go 
	-- to an error state to update the doc store which will then cause the original to be 
	-- downloaded (if possible). See NMB-3111 for more details.
	rxTest:expectEqual( PixelPathProviderStateMachine.SendDataStates.SendPermanentDownloadError,
						currState )	

end

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

local function rxTestDownloadXmpSidecarStates( rxTest )

	local fsm = PixelPathProviderStateMachine.make()

	local currState = PixelPathProviderStateMachine.DownloadStates.NeedXmpSidecarDownload

	-- simulate a loop trying to fetch the xmp sidecar info
	for _ = 1, PixelPathProviderStateMachine.DownloadRetryCount do

		local downloadingState = WFFiniteStateMachine.getNextState( fsm, currState, 'start_download' )

		-- simulate getting the sidecar
		rxTest:expectEqual(
			PixelPathProviderStateMachine.LocalBinaryPathCheckStates.AnalyzeXmpSidecar,
			WFFiniteStateMachine.getNextState( fsm, downloadingState, 'received_download' ) )

		currState = WFFiniteStateMachine.getNextState( fsm, downloadingState, 'retry_download' )

	end

	rxTest:expectEqual( PixelPathProviderStateMachine.SendDataStates.SendPermanentDownloadError,
						currState )

end

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

local function rxTestXmpSidecarHappyPath( rxTest )

	local fsm = PixelPathProviderStateMachine.make()

	local mostRecentNewState = nil

	local function nextStateEquals( currState, transition, expectedNewState )

		if mostRecentNewState and currState ~= mostRecentNewState then
			return false
		end

		mostRecentNewState = WFFiniteStateMachine.getNextState( fsm, currState, transition )

		return mostRecentNewState == expectedNewState

	end

	local DocstoreInfoStates = PixelPathProviderStateMachine.DocstoreInfoStates
	local LocalBinaryPathCheckStates = PixelPathProviderStateMachine.LocalBinaryPathCheckStates
	local DownloadStates = PixelPathProviderStateMachine.DownloadStates
	local SaveDownloadStates = PixelPathProviderStateMachine.SaveDownloadStates
	local SendDataStates = PixelPathProviderStateMachine.SendDataStates
	local EndStates = PixelPathProviderStateMachine.EndStates

	-- Typical happy path for /rels/xmp stored on oz that was from a sidecar:

	rxTest:expectEqual( true, nextStateEquals( DocstoreInfoStates.NeedXmpSidecarInfo, 'must_fetch', DocstoreInfoStates.NeedXmpSidecarInfoFromDocstore ) )
	rxTest:expectEqual( true, nextStateEquals( DocstoreInfoStates.NeedXmpSidecarInfoFromDocstore, 'start_docstore_read', DocstoreInfoStates.FetchingXmpSidecarInfo ) )
	rxTest:expectEqual( true, nextStateEquals( DocstoreInfoStates.FetchingXmpSidecarInfo, 'received_docstore_read', LocalBinaryPathCheckStates.NeedCheckForLocalXmpSidecar ) )
	rxTest:expectEqual( true, nextStateEquals( LocalBinaryPathCheckStates.NeedCheckForLocalXmpSidecar, 'start_non_rendition_check',	LocalBinaryPathCheckStates.CheckingForLocalXmpSidecar ) )
	rxTest:expectEqual( true, nextStateEquals( LocalBinaryPathCheckStates.CheckingForLocalXmpSidecar, 'need_download_binary', DownloadStates.NeedXmpSidecarDownload ) )
	rxTest:expectEqual( true, nextStateEquals( DownloadStates.NeedXmpSidecarDownload, 'start_download', DownloadStates.DownloadingXmpSidecar ) )
	rxTest:expectEqual( true, nextStateEquals( DownloadStates.DownloadingXmpSidecar, 'received_download', LocalBinaryPathCheckStates.AnalyzeXmpSidecar ) )
	rxTest:expectEqual( true, nextStateEquals( LocalBinaryPathCheckStates.AnalyzeXmpSidecar, 'start_analyze_xmp_sidecar', LocalBinaryPathCheckStates.AnalyzingXmpSidecar ) )
	rxTest:expectEqual( true, nextStateEquals( LocalBinaryPathCheckStates.AnalyzingXmpSidecar, 'save_sidecar_xmp', SaveDownloadStates.NeedToSaveXmpSidecar ) )
	rxTest:expectEqual( true, nextStateEquals( SaveDownloadStates.NeedToSaveXmpSidecar, 'start_save_download', SaveDownloadStates.SavingXmpSidecar ) )
	rxTest:expectEqual( true, nextStateEquals( SaveDownloadStates.SavingXmpSidecar, 'saved_valid_download', SendDataStates.HaveLocalXmpSidecarPath ) )
	rxTest:expectEqual( true, nextStateEquals( SendDataStates.HaveLocalXmpSidecarPath, 'sent', EndStates.FinishedSendingXmpSidecarPath ) )


	-- Typical happy path for /rels/xmp stored on oz not from a sidecar:

	mostRecentNewState = nil

	rxTest:expectEqual( true, nextStateEquals( DocstoreInfoStates.NeedXmpSidecarInfo, 'must_fetch', DocstoreInfoStates.NeedXmpSidecarInfoFromDocstore ) )
	rxTest:expectEqual( true, nextStateEquals( DocstoreInfoStates.NeedXmpSidecarInfoFromDocstore, 'start_docstore_read', DocstoreInfoStates.FetchingXmpSidecarInfo ) )
	rxTest:expectEqual( true, nextStateEquals( DocstoreInfoStates.FetchingXmpSidecarInfo, 'received_docstore_read', LocalBinaryPathCheckStates.NeedCheckForLocalXmpSidecar ) )
	rxTest:expectEqual( true, nextStateEquals( LocalBinaryPathCheckStates.NeedCheckForLocalXmpSidecar, 'start_non_rendition_check',	LocalBinaryPathCheckStates.CheckingForLocalXmpSidecar ) )
	rxTest:expectEqual( true, nextStateEquals( LocalBinaryPathCheckStates.CheckingForLocalXmpSidecar, 'need_download_binary', DownloadStates.NeedXmpSidecarDownload ) )
	rxTest:expectEqual( true, nextStateEquals( DownloadStates.NeedXmpSidecarDownload, 'start_download', DownloadStates.DownloadingXmpSidecar ) )
	rxTest:expectEqual( true, nextStateEquals( DownloadStates.DownloadingXmpSidecar, 'received_download', LocalBinaryPathCheckStates.AnalyzeXmpSidecar ) )
	rxTest:expectEqual( true, nextStateEquals( LocalBinaryPathCheckStates.AnalyzeXmpSidecar, 'start_analyze_xmp_sidecar', LocalBinaryPathCheckStates.AnalyzingXmpSidecar ) )
	rxTest:expectEqual( true, nextStateEquals( LocalBinaryPathCheckStates.AnalyzingXmpSidecar, 'no_sidecar_xmp', SendDataStates.NotXmpSidecar ) )
	rxTest:expectEqual( true, nextStateEquals( SendDataStates.NotXmpSidecar, 'sent', EndStates.FinishedSendingXmpSidecarPath ) )

end

--------------------------------------------------------------------------------
-- README
-- 
-- How to generate state machine diagrams.
-- 
-- The state machine can generate a DOT file which in turn can generate
-- a state diagram.
-- 
-- In order to generate PNGs from a DOT file, you need to use graphviz.
-- See http://graphviz.org for more info.  
-- 
-- To install graphviz on the mac, use homebrew or Macports
-- 
-- % # homebrew instructions
-- % brew install graphviz
-- 
-- 
-- The output of rxTestGenerateFSMDiagram is written to your desktop as a
-- series of dot files, one for each start state specified in
-- rxTestGenerateFSMDiagram to your desktop along
-- with a shell script named ~/Desktop/makePixelProviderStateMachinePngs.sh
-- 
-- Assuming you have graphviz installed, use these steps to update the
-- state machine diagrams:
-- 
-- 1) update this for your source directory 
local FSM_SRC_DIR="$HOME/adobe_github_code/lightroom/nimbus/imported/adobe/wichitafoundation/lightroomcc/pixel_provider/scheduler/state_machine/" 

--------------------------------------------------------------------------------
-- 2) set generateFSMDiagrams to true 
local generateFSMDiagrams = false

-- 3) Run the unit test.
-- 4) Run the generated shell script to generate the pngs:
-- 
-- % sh ~/Desktop/makePixelProviderStateMachinePngs.sh
-- 
-- 5) Set generateFSMDiagrams back to false

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

local function getDesktopFileName( startState, ext )

	return KSPathUtils.child( KSPathUtils.getStandardFilePath( 'desktop' ),
								startState .. ext )

end

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

local function isPixelCacheCheckStartState( stateName )

	if not string.find( stateName, "^Need" ) then
		
		return

	end
	
	return stateName

end

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

local function isLocalBinaryPathCheckStartState( stateName )

	if not string.find( stateName, "^Need" ) then
		
		return

	end
	
	return stateName

end

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

local function isStartDocstoreInfoState( stateName )

	if not string.find( stateName, "^Need" ) or 
			string.find( stateName, "FromDocstore$" ) then
		
		return

	end
	
	return stateName

end

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

local function isStartDownloadBinaryState( stateName )

	if not string.find( stateName, "^Need.*[^0-9]$" ) or
			stateName == "NeedRenderDownload" or
			stateName == "NeedLoupeRenditionDownload" then
		
		return

	end
	
	return stateName

end

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

local function isStartSaveDownloadState( stateName )

	if not string.find( stateName, "^NeedTo.*[^0-9]$" ) then
		
		return

	end
	
	return stateName

end

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

local function appendState( states, state )

	states[ #states + 1 ] = state

	return states

end

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

local function appendStateGroupStates( states, terminatingStateGroupName, fsm )

	return WFFunctionUtils.reduce( states, 
				appendState,
					WFFiniteStateMachine.getStateGroupStates( 
						fsm, terminatingStateGroupName ) )

end

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

local function makeDotFileForStartStates( dotFileData, fsm )

	local stateGroupName, startStates, terminatingStateGroupNames = 
			unpack( dotFileData )

	local terminatingStates = 
			WFFunctionUtils.reduce( {}, 
				appendStateGroupStates, terminatingStateGroupNames, fsm )

	local title = string.format( "State machine for %s", stateGroupName )

	local fsmDiagram = 
		WFFiniteStateMachineUtils.getAsDotDiagramStr( 
			fsm, title, 48, startStates, terminatingStates )

	local fh = io.open( getDesktopFileName( stateGroupName, '.dot' ), 'w' )

	fh:write( fsmDiagram )

	fh:close()

end

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

local function getGeneratePNGStr( dotFileData )

	local startState = dotFileData[ 1 ]

	return string.format( "dot -Tpng %s > %s\nmv %s $FSM_SRC_DIR", 
							getDesktopFileName( startState, '.dot' ), 
							getDesktopFileName( startState, '.png' ),
							getDesktopFileName( startState, '.*' ) )

end

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

local function rxTestGenerateFSMDiagram()

	if not generateFSMDiagrams then

		return

	end

	local fsm = PixelPathProviderStateMachine.make()

	local dotFileData = {

		{ 'ChooseRenditionStrategy',
			{ PixelPathProviderStateMachine.PixelCacheCheckStates.ChooseRenditionStrategy },
			PixelPathProviderStateMachine.allStateGroupsExcept( 'PixelCacheCheckStates' ) }, 

		{ 'ChooseRenderOnlyRenditionStrategy',
			{ PixelPathProviderStateMachine.PixelCacheCheckStates.ChooseRenderOnlyRenditionStrategy },
			PixelPathProviderStateMachine.allStateGroupsExcept( 'PixelCacheCheckStates' ) }, 

		{ 'ChooseBestPossibleLocalNegativePath',
			{ PixelPathProviderStateMachine.LocalBinaryPathCheckStates.ChooseBestPossibleLocalNegativePath },
			PixelPathProviderStateMachine.allStateGroupsExcept( 'LocalBinaryPathCheckStates' ) }, 

		{ 'NeedsRender',
			{ PixelPathProviderStateMachine.RenderStates.NeedsRender },
			PixelPathProviderStateMachine.allStateGroupsExcept( 'RenderStates' ) },

		{ 'NeedRenderDownload',
			{ PixelPathProviderStateMachine.DownloadStates.NeedRenderDownload },
			PixelPathProviderStateMachine.allStateGroupsExcept( 'DownloadStates' ) },

		{ 'NeedInvalidRenderDownload',
			{ PixelPathProviderStateMachine.DownloadStates.NeedInvalidRenderDownload },
			PixelPathProviderStateMachine.allStateGroupsExcept( 'DownloadStates' ) },

		{ 'NeedToSaveRender',
			{ PixelPathProviderStateMachine.SaveRenderStates.NeedToSaveRender },
			PixelPathProviderStateMachine.allStateGroupsExcept( 'SaveRenderStates' ) },
	}

	local startPixelCacheCheckStates =
			WFFunctionUtils.filter(
				isPixelCacheCheckStartState,
					WFFiniteStateMachine.getStateGroupStates( 
						fsm, "PixelCacheCheckStates" ) )

	dotFileData[ #dotFileData + 1 ] =
		{ "PixelCacheCheckStates", 
			startPixelCacheCheckStates,
			PixelPathProviderStateMachine.allStateGroupsExcept( 'PixelCacheCheckStates' ) }

	local startLocalBinaryPathCheckStates =
			WFFunctionUtils.filter(
				isLocalBinaryPathCheckStartState,
					WFFiniteStateMachine.getStateGroupStates( 
						fsm, "LocalBinaryPathCheckStates" ) )

	dotFileData[ #dotFileData + 1 ] =
		{ "LocalBinaryPathCheckStates", 
			startLocalBinaryPathCheckStates,
			PixelPathProviderStateMachine.allStateGroupsExcept( 'LocalBinaryPathCheckStates' ) }

	local startDocstoreInfoStates = 
			WFFunctionUtils.filter(
				isStartDocstoreInfoState,
					WFFiniteStateMachine.getStateGroupStates( 
						fsm, "DocstoreInfoStates" ) )

	dotFileData[ #dotFileData + 1 ] =
		{ "DocstoreInfoStates", 
			startDocstoreInfoStates,
			PixelPathProviderStateMachine.allStateGroupsExcept( 'DocstoreInfoStates' ) }

	local startDownloadBinaryStates = 
			WFFunctionUtils.filter(
				isStartDownloadBinaryState,
					WFFiniteStateMachine.getStateGroupStates( 
						fsm, "DownloadStates" ) )

	dotFileData[ #dotFileData + 1 ] =
		{ "DownloadBinaryStates", 
			startDownloadBinaryStates,
			PixelPathProviderStateMachine.allStateGroupsExcept( 'DownloadStates' ) }

	local startSaveDownloadStates = 
			WFFunctionUtils.filter(
				isStartSaveDownloadState,
					WFFiniteStateMachine.getStateGroupStates( 
						fsm, "SaveDownloadStates" ) )

	dotFileData[ #dotFileData + 1 ] =
		{ "SaveDownloadStates", 
			startSaveDownloadStates,
			PixelPathProviderStateMachine.allStateGroupsExcept( 'SaveDownloadStates' ) }

	-- generate a start to finish diagram for loupe rendition state machine
	dotFileData[ #dotFileData + 1 ] =
		{ "LoupeRendition", 
			{ PixelPathProviderStateMachine.SendDataStates.MaybeSendLoupeRenditionPathFromInMemoryCache },
			PixelPathProviderStateMachine.EndStates }

	-- generate a start to finish diagram for export full size rendition state machine
	dotFileData[ #dotFileData + 1 ] =
		{ "ExportFullRendition", 
			{ PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForExportFullRendition },
			PixelPathProviderStateMachine.EndStates }

	-- generate a start to finish diagram for export full size rendition state machine
	dotFileData[ #dotFileData + 1 ] =
		{ "ExportBiggestPossibleRendition", 
			{ PixelPathProviderStateMachine.DocstoreInfoStates.NeedAssetInfoForExportBiggestPossibleRendition },
			PixelPathProviderStateMachine.EndStates }

	WFFunctionUtils.map( makeDotFileForStartStates, dotFileData, fsm )

	local pngGenerators = WFFunctionUtils.map( getGeneratePNGStr, dotFileData )

	local fh = io.open( getDesktopFileName( 
							"makePixelProviderStateMachinePngs", '.sh' ), 'w' )

	local shellScript = string.format( [[
#!/bin/sh
FSM_SRC_DIR="%s"
%s]], FSM_SRC_DIR, table.concat( pngGenerators, "\n" ) )

	fh:write( shellScript )

	fh:close()


end

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

return WFRxTestCase.wrapSuite( {

	rxTestMake = rxTestMake,

	rxTestDocstoreInfoStates = rxTestDocstoreInfoStates,

	rxTestNewDevelopSettings = rxTestNewDevelopSettings,

	rxTestDownloadRenderStates = rxTestDownloadRenderStates,

	rxTestDownloadProxyStates = rxTestDownloadProxyStates,

	rxTestDownloadOriginalStates = rxTestDownloadOriginalStates,

	rxTestDownloadXmpSidecarStates = rxTestDownloadXmpSidecarStates,

	rxTestXmpSidecarHappyPath = rxTestXmpSidecarHappyPath,

	rxTestGenerateFSMDiagram = rxTestGenerateFSMDiagram,
} )
