public void CreateAndDestroyTimelineAssets()
        {
            PlayableDirector director          = CreateDirectorWithTimelineAsset(TIMELINE_ASSET_PATH, out TimelineAsset _);
            string           timelineAssetPath = AssetDatabase.GetAssetPath(director.playableAsset);

            TimelineEditorUtility.DestroyAssets(director.playableAsset);
            Assert.IsFalse(File.Exists(timelineAssetPath));
        }
        public IEnumerator EnsureMatchingAndLimitedFramesAreLoadedToScene()
        {
            InitTest(true, out PlayableDirector director, out SceneCachePlayer sceneCachePlayer, out TimelineClip clip0);
            yield return(null);


            ISceneCacheInfo scInfo = sceneCachePlayer.ExtractSceneCacheInfo(forceOpen: true);

            Assert.IsNotNull(scInfo);
            int    numFrames    = scInfo.GetNumFrames();
            double timePerFrame = 1.0f / scInfo.GetSampleRate();

            //Setup clip0 duration
            int halfFrames = Mathf.FloorToInt(numFrames * 0.5f);

            clip0.duration = halfFrames * timePerFrame;

            //Setup clip1
            TimelineClip clip1 = clip0.GetParentTrack().CreateClip <SceneCachePlayableAsset>();

            clip1.start    = clip0.start + clip0.duration;
            clip1.clipIn   = clip0.duration;
            clip1.duration = clip0.duration;
            SceneCachePlayableAsset playableAsset1 = clip1.asset as SceneCachePlayableAsset;

            Assert.IsNotNull(playableAsset1);
            director.SetReferenceValue(playableAsset1.GetSceneCachePlayerRef().exposedName, sceneCachePlayer);

            TimelineEditorUtility.RefreshTimelineEditor(RefreshReason.ContentsAddedOrRemoved | RefreshReason.WindowNeedsRedraw | RefreshReason.ContentsModified);
            yield return(null);

            //Setup Limited Animation
            const int NUM_FRAMES_TO_HOLD = 3;
            const int OFFSET             = 1;

            LimitedAnimationController limitedAnimationController1 = playableAsset1.GetOverrideLimitedAnimationController();

            limitedAnimationController1.Enable(NUM_FRAMES_TO_HOLD, OFFSET);

            yield return(IterateAllSceneCacheFrames(director, clip0, sceneCachePlayer, (int timelineFrame) => {
                EditorApplication.isPaused = true;
                Assert.AreEqual(timelineFrame, sceneCachePlayer.GetFrame());
            }));

            yield return(IterateAllSceneCacheFrames(director, clip1, sceneCachePlayer, (int timelineFrame) => {
                int shownFrame = sceneCachePlayer.GetFrame();
                if (shownFrame == (scInfo.GetNumFrames() - 1)) //clamped to the end frame
                {
                    return;
                }

                Assert.Zero(shownFrame % NUM_FRAMES_TO_HOLD - OFFSET);
            }));
        }
//----------------------------------------------------------------------------------------------------------------------

        private static void InitTest(bool enableSceneCacheGo, out PlayableDirector director,
                                     out SceneCachePlayer sceneCachePlayer, out TimelineClip clip)
        {
            EditorSceneManager.NewScene(NewSceneSetup.DefaultGameObjects);

            director         = CreateTestDirector();
            sceneCachePlayer = CreateTestSceneCachePlayer();
            sceneCachePlayer.gameObject.SetActive(enableSceneCacheGo);
            clip = SceneCachePlayerEditorUtility.AddSceneCacheTrackAndClip(director, "TestSceneCacheTrack", sceneCachePlayer);

            TimelineEditorUtility.SelectDirectorInTimelineWindow(director); //trigger the TimelineWindow's update etc.
            TimelineEditorUtility.RefreshTimelineEditor();
        }
//----------------------------------------------------------------------------------------------------------------------


        private static PlayableDirector CreateDirectorWithTimelineAsset(string candidatePath,
                                                                        out TimelineAsset timelineAsset)
        {
            string           timelineAssetPath = AssetDatabase.GenerateUniqueAssetPath(candidatePath);
            PlayableDirector director          = new GameObject("Director").AddComponent <PlayableDirector>();

            timelineAsset = TimelineEditorUtility.CreateAsset(timelineAssetPath);
            Assert.IsNotNull(timelineAsset);

            director.playableAsset = timelineAsset;
            Assert.IsTrue(File.Exists(timelineAssetPath));
            return(director);
        }
        public IEnumerator DetectActiveClip()
        {
            PlayableDirector director = CreateDirectorWithTimelineAsset(TIMELINE_ASSET_PATH, out TimelineAsset timelineAsset);

            TimelineEditorUtility.SelectDirectorInTimelineWindow(director); //trigger the TimelineWindow's update etc.
            yield return(YieldEditorUtility.WaitForFramesAndIncrementUndo(1));

            TrackAsset          track     = timelineAsset.CreateTrack <DummyTimelineTrack>(null, "FooTrack");
            List <TimelineClip> clips     = new List <TimelineClip>();
            const int           NUM_CLIPS = 3;

            double nextClipStart = 0;

            for (int i = 0; i < NUM_CLIPS; ++i)
            {
                TimelineClip curClip = TimelineEditorReflection.CreateClipOnTrack(typeof(DummyTimelinePlayableAsset), track, 0);
                curClip.asset.name = $"Clip Asset {i}";
                curClip.start      = nextClipStart;
                nextClipStart     += curClip.duration;
                clips.Add(curClip);

                yield return(YieldEditorUtility.WaitForFramesAndIncrementUndo(1));
            }

            //Check that all clips have been created successfully
            List <TimelineClip>    trackClips        = new List <TimelineClip>(track.GetClips());
            HashSet <TimelineClip> trackClipsToCheck = new HashSet <TimelineClip>(trackClips);

            for (int i = 0; i < NUM_CLIPS; ++i)
            {
                Assert.IsTrue(trackClipsToCheck.Contains(clips[i]));
                trackClipsToCheck.Remove(clips[i]);
            }
            NUnit.Framework.Assert.Zero(trackClipsToCheck.Count);
            yield return(YieldEditorUtility.WaitForFramesAndIncrementUndo(3));

            //Ensure the active clip can be detected
            for (int i = 0; i < NUM_CLIPS; ++i)
            {
                TimelineClip curClip = clips[i];
                double       time    = curClip.start + curClip.duration * 0.5f;
                TimelineUtility.GetActiveTimelineClipInto(trackClips, time, out TimelineClip detectedClip, out TimelineAsset _);
                Assert.AreEqual(curClip, detectedClip);
            }

            yield return(YieldEditorUtility.WaitForFramesAndIncrementUndo(1));

            TimelineEditorUtility.DestroyAssets(timelineAsset); //Cleanup
        }
        public IEnumerator CreateClip()
        {
            PlayableDirector director = CreateDirectorWithTimelineAsset(TIMELINE_ASSET_PATH, out TimelineAsset timelineAsset);

            yield return(YieldEditorUtility.WaitForFramesAndIncrementUndo(1));

            TimelineClip clip = TimelineEditorUtility.CreateTrackAndClip <DummyTimelineTrack, DummyTimelinePlayableAsset>(
                timelineAsset, "FirstTrack");

            VerifyClip(clip);
            TimelineEditorUtility.SelectDirectorInTimelineWindow(director); //trigger the TimelineWindow's update etc.
            yield return(YieldEditorUtility.WaitForFramesAndIncrementUndo(3));

            DummyTimelineClipData clipData = clip.GetClipData <DummyTimelineClipData>();

            Assert.IsNotNull(clipData);

            TimelineEditorUtility.DestroyAssets(clip); //Cleanup
        }
        public IEnumerator ShowClipInInspector()
        {
            CreateDirectorWithTimelineAsset(TIMELINE_ASSET_PATH, out TimelineAsset timelineAsset);
            yield return(YieldEditorUtility.WaitForFramesAndIncrementUndo(1));

            TrackAsset   track = timelineAsset.CreateTrack <DummyTimelineTrack>(null, "FooTrack");
            TimelineClip clip  = TimelineEditorReflection.CreateClipOnTrack(typeof(DummyTimelinePlayableAsset), track, 0);

            VerifyClip(clip);

            ScriptableObject editorClip = TimelineEditorUtility.SelectTimelineClipInInspector(clip);

            Assert.IsNotNull(editorClip);
            yield return(YieldEditorUtility.WaitForFramesAndIncrementUndo(1));

            Object.DestroyImmediate(editorClip);
            yield return(YieldEditorUtility.WaitForFramesAndIncrementUndo(1));

            TimelineEditorUtility.DestroyAssets(clip); //Cleanup
        }
        public IEnumerator CheckGameObjectActiveStateReferredInMultipleTracks()
        {
            InitTest(true, out PlayableDirector director, out SceneCachePlayer sceneCachePlayer, out TimelineClip clip0);
            yield return(null);

            TimelineClip clip1 = SceneCachePlayerEditorUtility.AddSceneCacheTrackAndClip(director, "TestSceneCacheTrack 1", sceneCachePlayer);

            clip1.start = clip0.end;
            TimelineEditorUtility.RefreshTimelineEditor(RefreshReason.ContentsAddedOrRemoved);
            yield return(null);

            GameObject scGameObject = sceneCachePlayer.gameObject;

            SetDirectorTime(director, clip0.start);
            yield return(null);

            Assert.IsTrue(scGameObject.activeSelf);

            SetDirectorTime(director, clip1.start + clip1.duration * 0.5f);
            yield return(null);

            Assert.IsTrue(scGameObject.activeSelf);
        }