text:
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using Microsoft.WebMatrix.Extensibility;
using NuGet.WebMatrix.Data;
using NuGet.WebMatrix.Tests.Utilities;
using Xunit;
using Moq;

namespace NuGet.WebMatrix.Tests.ViewModelTests
{

    public class EndToEndViewModelTests
    {
        public EndToEndViewModelTests()
        {
            NuGetModel.ClearCache();

            this.CloseCommand = new Mock<ICommand>().Object;

            this.Descriptor = new Mock<INuGetGalleryDescriptor>();

            // makes sure tests report a failure if an exception is caught by the view model
            this.Host = new Mock<IWebMatrixHost>();
            this.Host
                .Setup(host => host.ShowExceptionMessage(null, null, null))
                .Throws(new Exception("Test tried to report an error to the user"));

            this.PackageManager = new InMemoryPackageManager();

            var feedSource = new FeedSource("http://oo.com", "test source");
            var feedSourceStore = new InMemoryFeedSourceStore(feedSource);
            this.PackageSourcesViewModel = new PackageSourcesViewModel(new PackageSourcesModel(feedSource, feedSourceStore));

            this.ReadonlyDestination = Path.GetFullPath(@".\DO_NOT_WRITE_HERE\");
        }

        /// <summary>
        /// Cancelling an install from the details page (clicking no), should take you back to the list
        /// </summary>
        public void CancelInstall()
        {
            using (var thread = new TemporaryDispatcherThread())
            {
                var package = PackageFactory.Create("install");
                this.PackageManager.RemotePackages.Add(package);

                ButtonBarViewModel buttonBarViewModel = this.CreateViewModel(thread);
                this.SelectPackage(thread, buttonBarViewModel.NuGetViewModel, Resources.Filter_All, package.Id);

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.InstallButton, PackageViewModelAction.InstallOrUninstall);
                Assert.True(buttonBarViewModel.NuGetViewModel.IsDetailsPaneVisible, "The details page should be shown.");
                Assert.Equal<PackageViewModelAction>(PackageViewModelAction.InstallOrUninstall, buttonBarViewModel.NuGetViewModel.PackageAction);

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.NoButton);

                Assert.False(buttonBarViewModel.NuGetViewModel.IsDetailsPaneVisible, "The details page should not be shown.");
                Assert.False(buttonBarViewModel.NuGetViewModel.IsLicensePageVisible, "The license page should not be shown.");

                Assert.False(this.PackageManager.InstalledPackages.Any(), "Nothing should be installed.");
            }
        }

        /// <summary>
        /// Cancelling an install from the license page (clicking no), should take you back to the list
        /// </summary>
        [Fact]
        public void DeclineEula()
        {
            using (var thread = new TemporaryDispatcherThread())
            {
                var package = PackageFactory.Create("install");
                this.PackageManager.RemotePackages.Add(package);

                ButtonBarViewModel buttonBarViewModel = this.CreateViewModel(thread);
                this.SelectPackage(thread, buttonBarViewModel.NuGetViewModel, Resources.Filter_All, package.Id);

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.InstallButton, PackageViewModelAction.InstallOrUninstall);
                Assert.True(buttonBarViewModel.NuGetViewModel.IsDetailsPaneVisible, "The details page should be shown.");
                Assert.Equal<PackageViewModelAction>(PackageViewModelAction.InstallOrUninstall, buttonBarViewModel.NuGetViewModel.PackageAction);

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.YesButton);
                Assert.True(buttonBarViewModel.NuGetViewModel.IsLicensePageVisible, "The license page should be shown.");

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.DeclineButton);

                Assert.False(buttonBarViewModel.NuGetViewModel.IsDetailsPaneVisible, "The details page should not be shown.");
                Assert.False(buttonBarViewModel.NuGetViewModel.IsLicensePageVisible, "The license page should not be shown.");

                Assert.False(this.PackageManager.InstalledPackages.Any(), "Nothing should be installed.");
            }
        }

        /// <summary>
        /// Select a package from the 'all' view and walk through the installer
        /// </summary>
        [Fact]
        public void InstallPackage()
        {
            using (var thread = new TemporaryDispatcherThread())
            {
                var package = PackageFactory.Create("install");
                this.PackageManager.RemotePackages.Add(package);

                ButtonBarViewModel buttonBarViewModel = this.CreateViewModel(thread);
                this.SelectPackage(thread, buttonBarViewModel.NuGetViewModel, Resources.Filter_All, package.Id);

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.InstallButton, PackageViewModelAction.InstallOrUninstall);
                Assert.True(buttonBarViewModel.NuGetViewModel.IsDetailsPaneVisible, "The details page should be shown.");
                Assert.Equal<PackageViewModelAction>(PackageViewModelAction.InstallOrUninstall, buttonBarViewModel.NuGetViewModel.PackageAction);

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.YesButton);
                Assert.True(buttonBarViewModel.NuGetViewModel.IsLicensePageVisible, "The license page should be shown.");

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.AcceptButton);

                var installedPackage = this.PackageManager.InstalledPackages.FirstOrDefault();
                Assert.Equal(package, installedPackage);
            }
        }

        /// <summary>
        /// Select a package from the 'installed' view and walk through the uninstaller
        /// </summary>
        [Fact]
        public void UninstallPackageFromInstalled()
        {
            using (var thread = new TemporaryDispatcherThread())
            {
                var package = PackageFactory.Create("uninstall");
                this.PackageManager.InstalledPackages.Add(package);

                ButtonBarViewModel buttonBarViewModel = this.CreateViewModel(thread);
                this.SelectPackage(thread, buttonBarViewModel.NuGetViewModel, Resources.Filter_Installed, package.Id);

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.UninstallButton);
                Assert.True(buttonBarViewModel.NuGetViewModel.IsUninstallPageVisible, "The uninstall page should be shown.");

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.YesButton);

                Assert.False(this.PackageManager.InstalledPackages.Any(), "The package should be uninstalled");
            }
        }

        /// <summary>
        /// Select a package from the 'all' view and walk through the uninstaller
        /// </summary>
        [Fact]
        public void UninstallPackageFromAll()
        {
            using (var thread = new TemporaryDispatcherThread())
            {
                var package = PackageFactory.Create("uninstall");
                this.PackageManager.InstalledPackages.Add(package);
                this.PackageManager.RemotePackages.Add(package);

                ButtonBarViewModel buttonBarViewModel = this.CreateViewModel(thread);
                this.SelectPackage(thread, buttonBarViewModel.NuGetViewModel, Resources.Filter_All, package.Id);

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.UninstallButton);
                Assert.True(buttonBarViewModel.NuGetViewModel.IsUninstallPageVisible, "The uninstall page should be shown.");

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.YesButton);

                Assert.False(this.PackageManager.InstalledPackages.Any(), "The package should be uninstalled");
            }
        }

        /// <summary>
        /// Select a package from the 'all' view and walk through the updater
        /// </summary>
        [Fact]
        public void UpdatePackageFromAll()
        {
            using (var thread = new TemporaryDispatcherThread())
            {
                var installed = PackageFactory.Create("update", new Version(1, 0));
                this.PackageManager.InstalledPackages.Add(installed);

                var remote = PackageFactory.Create("update", new Version(2, 0));
                this.PackageManager.RemotePackages.Add(remote);

                ButtonBarViewModel buttonBarViewModel = this.CreateViewModel(thread);
                this.SelectPackage(thread, buttonBarViewModel.NuGetViewModel, Resources.Filter_All, installed.Id);

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.UpdateButton, PackageViewModelAction.Update);
                Assert.True(buttonBarViewModel.NuGetViewModel.IsDetailsPaneVisible, "The details page should be shown.");
                Assert.Equal<PackageViewModelAction>(PackageViewModelAction.Update, buttonBarViewModel.NuGetViewModel.PackageAction);

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.YesButton);
                Assert.True(buttonBarViewModel.NuGetViewModel.IsLicensePageVisible, "The license page should be shown.");

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.AcceptButton);

                var installedPackage = this.PackageManager.InstalledPackages.FirstOrDefault();
                Assert.Equal(remote, installedPackage);
            }
        }

        /// <summary>
        /// Select a package from the 'updates' view and walk through the updater
        /// </summary>
        [Fact]
        public void UpdatePackageFromUpdates()
        {
            using (var thread = new TemporaryDispatcherThread())
            {
                var installed = PackageFactory.Create("update", new Version(1, 0));
                this.PackageManager.InstalledPackages.Add(installed);

                var remote = PackageFactory.Create("update", new Version(2, 0));
                this.PackageManager.RemotePackages.Add(remote);

                ButtonBarViewModel buttonBarViewModel = this.CreateViewModel(thread);
                this.SelectPackage(thread, buttonBarViewModel.NuGetViewModel, Resources.Filter_Updated, installed.Id);

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.UpdateButton, PackageViewModelAction.Update);
                Assert.True(buttonBarViewModel.NuGetViewModel.IsDetailsPaneVisible, "The details page should be shown.");
                Assert.Equal<PackageViewModelAction>(PackageViewModelAction.Update, buttonBarViewModel.NuGetViewModel.PackageAction);

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.YesButton);
                Assert.True(buttonBarViewModel.NuGetViewModel.IsLicensePageVisible, "The license page should be shown.");

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.AcceptButton);

                var installedPackage = this.PackageManager.InstalledPackages.FirstOrDefault();
                Assert.Equal(remote, installedPackage);
            }
        }

        /// <summary>
        /// Select a package from the 'installed' view and walk through the updater
        /// </summary>
        [Fact]
        public void UpdatePackageFromInstalled()
        {
            using (var thread = new TemporaryDispatcherThread())
            {
                var installed = PackageFactory.Create("update", new Version(1, 0));
                this.PackageManager.InstalledPackages.Add(installed);

                var remote = PackageFactory.Create("update", new Version(2, 0));
                this.PackageManager.RemotePackages.Add(remote);

                ButtonBarViewModel buttonBarViewModel = this.CreateViewModel(thread);
                this.SelectPackage(thread, buttonBarViewModel.NuGetViewModel, Resources.Filter_Installed, installed.Id);

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.UpdateButton, PackageViewModelAction.Update);
                Assert.True(buttonBarViewModel.NuGetViewModel.IsDetailsPaneVisible, "The details page should be shown.");
                Assert.Equal<PackageViewModelAction>(PackageViewModelAction.Update, buttonBarViewModel.NuGetViewModel.PackageAction);

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.YesButton);
                Assert.True(buttonBarViewModel.NuGetViewModel.IsLicensePageVisible, "The license page should be shown.");

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.AcceptButton);

                var installedPackage = this.PackageManager.InstalledPackages.FirstOrDefault();
                Assert.Equal(remote, installedPackage);
            }
        }

        /// <summary>
        /// Select a package from the 'updates' view and click 'updateall' to update all packages
        /// </summary>
        [Fact]
        public void UpdateAllPackagesFromUpdates()
        {
            using (var thread = new TemporaryDispatcherThread())
            {
                var installed1 = PackageFactory.Create("update1", new Version(1, 0));
                var installed2 = PackageFactory.Create("update2", new Version(1, 0));
                this.PackageManager.InstalledPackages.Add(installed1);
                this.PackageManager.InstalledPackages.Add(installed2);

                var remote1 = PackageFactory.Create("update1", new Version(2, 0));
                var remote2 = PackageFactory.Create("update2", new Version(2, 0));
                this.PackageManager.RemotePackages.Add(remote1);
                this.PackageManager.RemotePackages.Add(remote2);

                ButtonBarViewModel buttonBarViewModel = this.CreateViewModel(thread);
                this.SelectPackage(thread, buttonBarViewModel.NuGetViewModel, Resources.Filter_Updated, installed1.Id);

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.UpdateAllButton, PackageViewModelAction.UpdateAll);
                Assert.True(buttonBarViewModel.NuGetViewModel.IsLicensePageVisible, "The license page should be shown.");
                Assert.Equal<PackageViewModelAction>(PackageViewModelAction.UpdateAll, buttonBarViewModel.NuGetViewModel.PackageAction);

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.AcceptButton);

                var installedPackages = this.PackageManager.InstalledPackages.ToArray();
                Assert.Equal(remote1, installedPackages[0]);
                Assert.Equal(remote2, installedPackages[1]);
            }
        }

        /// <summary>
        /// Select a package from the 'updates' view and click 'updateall' to update all packages
        /// And, then install another package to ensure that it is installed properly
        /// </summary>
        [Fact]
        public void UpdateAllPackagesFromUpdatesAndInstallAnotherPackage()
        {
            using (var thread = new TemporaryDispatcherThread())
            {
                var installed1 = PackageFactory.Create("update1", new Version(1, 0));
                var installed2 = PackageFactory.Create("update2", new Version(1, 0));
                this.PackageManager.InstalledPackages.Add(installed1);
                this.PackageManager.InstalledPackages.Add(installed2);

                var remote1 = PackageFactory.Create("update1", new Version(2, 0));
                var remote2 = PackageFactory.Create("update2", new Version(2, 0));
                var remote3 = PackageFactory.Create("install1", new Version(1, 0));
                this.PackageManager.RemotePackages.Add(remote1);
                this.PackageManager.RemotePackages.Add(remote2);
                this.PackageManager.RemotePackages.Add(remote3);

                // Perform UpdateAll now

                ButtonBarViewModel buttonBarViewModel = this.CreateViewModel(thread);
                this.SelectPackage(thread, buttonBarViewModel.NuGetViewModel, Resources.Filter_Updated, installed1.Id);

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.UpdateAllButton, PackageViewModelAction.UpdateAll);
                Assert.True(buttonBarViewModel.NuGetViewModel.IsLicensePageVisible, "The license page should be shown.");
                Assert.True(buttonBarViewModel.NuGetViewModel.IsUpdatingAll, "Updating all should be true");
                Assert.False(buttonBarViewModel.NuGetViewModel.NotUpdatingAll, "Not Updating all should be false");
                Assert.Equal<PackageViewModelAction>(PackageViewModelAction.UpdateAll, buttonBarViewModel.NuGetViewModel.PackageAction);

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.AcceptButton);

                // Perform Install Now
                this.SelectPackage(thread, buttonBarViewModel.NuGetViewModel, Resources.Filter_All, remote3.Id);

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.InstallButton, PackageViewModelAction.InstallOrUninstall);
                Assert.True(buttonBarViewModel.NuGetViewModel.IsDetailsPaneVisible, "The details page should be shown.");
                Assert.Equal<PackageViewModelAction>(PackageViewModelAction.InstallOrUninstall, buttonBarViewModel.NuGetViewModel.PackageAction);

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.YesButton);
                Assert.True(buttonBarViewModel.NuGetViewModel.IsLicensePageVisible, "The license page should be shown.");
                Assert.False(buttonBarViewModel.NuGetViewModel.IsUpdatingAll, "Updating all should be false");
                Assert.True(buttonBarViewModel.NuGetViewModel.NotUpdatingAll, "Not Updating all should be true");
                Assert.Equal<PackageViewModelAction>(PackageViewModelAction.InstallOrUninstall, buttonBarViewModel.NuGetViewModel.PackageAction);

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.AcceptButton);

                var installedPackages = this.PackageManager.InstalledPackages.ToArray();
                Assert.Equal(remote1, installedPackages[0]);
                Assert.Equal(remote2, installedPackages[1]);
                Assert.Equal(remote3, installedPackages[2]);
            }
        }

        /// <summary>
        /// Enables a package and checks that it's no longer shown as disabled
        /// </summary>
        [Fact]
        public void EnablePackage()
        {
            using (var thread = new TemporaryDispatcherThread())
            {
                this.PackageManager.SupportsEnableDisable = true;

                var installed = PackageFactory.Create("enable", new Version(1, 0));
                this.PackageManager.InstalledPackages.Add(installed);
                this.PackageManager.DisabledPackages.Add(installed);

                ButtonBarViewModel buttonBarViewModel = this.CreateViewModel(thread);
                this.SelectPackage(thread, buttonBarViewModel.NuGetViewModel, Resources.Filter_Installed, installed.Id);

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.EnableButton);

                Assert.False(buttonBarViewModel.NuGetViewModel.IsDetailsPaneVisible, "The details page should not be shown.");
                Assert.False(buttonBarViewModel.NuGetViewModel.IsLicensePageVisible, "The license page should not be shown.");

                Assert.False(this.PackageManager.DisabledPackages.Any(), "No packages should be disabled.");
            }
        }

        /// <summary>
        /// Disables a package and checks that it's no longer shown as enabled
        /// </summary>
        [Fact]
        public void DisablePackage()
        {
            using (var thread = new TemporaryDispatcherThread())
            {
                this.PackageManager.SupportsEnableDisable = true;

                var installed = PackageFactory.Create("disable", new Version(1, 0));
                this.PackageManager.InstalledPackages.Add(installed);

                ButtonBarViewModel buttonBarViewModel = this.CreateViewModel(thread);
                this.SelectPackage(thread, buttonBarViewModel.NuGetViewModel, Resources.Filter_Installed, installed.Id);

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.DisableButton);

                Assert.False(buttonBarViewModel.NuGetViewModel.IsDetailsPaneVisible, "The details page should not be shown.");
                Assert.False(buttonBarViewModel.NuGetViewModel.IsLicensePageVisible, "The license page should not be shown.");

                Assert.True(this.PackageManager.DisabledPackages.Contains(installed), "The package should be disabled.");

                // check for the package in disabled filter
                this.SelectPackage(thread, buttonBarViewModel.NuGetViewModel, Resources.Filter_Disabled, installed.Id);
            }
        }

        /// <summary>
        /// Select a mandatory package and ensure that uninstall button is disabled and disabled button is enabled
        /// </summary>
        [Fact]
        public void SelectMandatoryPackage()
        {
            using (var thread = new TemporaryDispatcherThread())
            {
                this.PackageManager.SupportsEnableDisable = true;

                var installed = PackageFactory.Create("WebMatrixExtensionsGallery", new Version(1, 0));
                this.PackageManager.InstalledPackages.Add(installed);

                ButtonBarViewModel buttonBarViewModel = this.CreateViewModel(thread);
                this.SelectPackage(thread, buttonBarViewModel.NuGetViewModel, Resources.Filter_Installed, installed.Id);

                Assert.False(buttonBarViewModel.NuGetViewModel.UninstallCommand.CanExecute(null), "The uninstall command should be disabled");
                Assert.True(buttonBarViewModel.NuGetViewModel.DisableCommand.CanExecute(null), "The disable command should be enabled");
            }
        }

        /// <summary>
        /// Test for prerelease filter visibility for NuGet Gallery
        /// NuGetViewModel.ShouldShowPrereleaseFilter is set to true
        /// </summary>
        [Fact]
        public void PrereleaseFilterVisibilityForNuGetGallery()
        {
            using (var thread = new TemporaryDispatcherThread())
            {
                this.PackageManager.SupportsEnableDisable = true;

                var installed = PackageFactory.Create("update", new Version(1, 0));
                this.PackageManager.InstalledPackages.Add(installed);

                var remote = PackageFactory.Create("update", new Version(2, 0));
                this.PackageManager.RemotePackages.Add(remote);

                installed = PackageFactory.Create("disable", new Version(1, 0));
                this.PackageManager.InstalledPackages.Add(installed);

                ButtonBarViewModel buttonBarViewModel = this.CreateViewModel(thread);
                this.SelectPackage(thread, buttonBarViewModel.NuGetViewModel, Resources.Filter_Installed, installed.Id);

                buttonBarViewModel.NuGetViewModel.ShouldShowPrereleaseFilter = true;

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.DisableButton);

                // At this point, there are 2 packages installed; 1 has an update and 1 is disabled

                // Select All Filter and check that the prerelease filter is visible
                this.SelectFilter(thread, buttonBarViewModel.NuGetViewModel, Resources.Filter_All);
                Assert.Equal<Visibility>(buttonBarViewModel.NuGetViewModel.ShowPrereleaseFilter, Visibility.Visible);

                // Select Disabled Filter and check that the prerelease filter is NOT visible
                this.SelectFilter(thread, buttonBarViewModel.NuGetViewModel, Resources.Filter_Disabled);
                Assert.NotEqual<Visibility>(buttonBarViewModel.NuGetViewModel.ShowPrereleaseFilter, Visibility.Visible);

                // Select Updates Filter and check that the prerelease filter is visible
                this.SelectFilter(thread, buttonBarViewModel.NuGetViewModel, Resources.Filter_Updated);
                Assert.Equal<Visibility>(buttonBarViewModel.NuGetViewModel.ShowPrereleaseFilter, Visibility.Visible);

                // Select Installed Filter and check that the prerelease filter is NOT visible
                this.SelectFilter(thread, buttonBarViewModel.NuGetViewModel, Resources.Filter_Installed);
                Assert.NotEqual<Visibility>(buttonBarViewModel.NuGetViewModel.ShowPrereleaseFilter, Visibility.Visible);
            }
        }

        /// <summary>
        /// Test for prerelease filter visibility for extension Gallery
        /// NuGetViewModel.ShouldShowPrereleaseFilter is set to false
        /// </summary>
        [Fact]
        public void PrereleaseFilterVisibilityForExtensionsGallery()
        {
            using (var thread = new TemporaryDispatcherThread())
            {
                this.PackageManager.SupportsEnableDisable = true;

                var installed = PackageFactory.Create("update", new Version(1, 0));
                this.PackageManager.InstalledPackages.Add(installed);

                var remote = PackageFactory.Create("update", new Version(2, 0));
                this.PackageManager.RemotePackages.Add(remote);

                installed = PackageFactory.Create("disable", new Version(1, 0));
                this.PackageManager.InstalledPackages.Add(installed);

                ButtonBarViewModel buttonBarViewModel = this.CreateViewModel(thread);
                this.SelectPackage(thread, buttonBarViewModel.NuGetViewModel, Resources.Filter_Installed, installed.Id);

                buttonBarViewModel.NuGetViewModel.ShouldShowPrereleaseFilter = false;

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.DisableButton);

                // At this point, there are 2 packages installed; 1 has an update and 1 is disabled

                // Select All Filter and check that the prerelease filter is visible
                this.SelectFilter(thread, buttonBarViewModel.NuGetViewModel, Resources.Filter_All);
                Assert.NotEqual<Visibility>(buttonBarViewModel.NuGetViewModel.ShowPrereleaseFilter, Visibility.Visible);

                // Select Disabled Filter and check that the prerelease filter is NOT visible
                this.SelectFilter(thread, buttonBarViewModel.NuGetViewModel, Resources.Filter_Disabled);
                Assert.NotEqual<Visibility>(buttonBarViewModel.NuGetViewModel.ShowPrereleaseFilter, Visibility.Visible);

                // Select Updates Filter and check that the prerelease filter is visible
                this.SelectFilter(thread, buttonBarViewModel.NuGetViewModel, Resources.Filter_Updated);
                Assert.NotEqual<Visibility>(buttonBarViewModel.NuGetViewModel.ShowPrereleaseFilter, Visibility.Visible);

                // Select Installed Filter and check that the prerelease filter is NOT visible
                this.SelectFilter(thread, buttonBarViewModel.NuGetViewModel, Resources.Filter_Installed);
                Assert.NotEqual<Visibility>(buttonBarViewModel.NuGetViewModel.ShowPrereleaseFilter, Visibility.Visible);
            }
        }

        /// <summary>
        /// Test for FeedSource Combobox visibility
        /// </summary>
        [Fact]
        public void FeedSourceVisibility()
        {
            using (var thread = new TemporaryDispatcherThread())
            {
                this.PackageManager.SupportsEnableDisable = true;

                var installed = PackageFactory.Create("update", new Version(1, 0));
                this.PackageManager.InstalledPackages.Add(installed);

                var remote = PackageFactory.Create("update", new Version(2, 0));
                this.PackageManager.RemotePackages.Add(remote);

                installed = PackageFactory.Create("disable", new Version(1, 0));
                this.PackageManager.InstalledPackages.Add(installed);

                ButtonBarViewModel buttonBarViewModel = this.CreateViewModel(thread);
                this.SelectPackage(thread, buttonBarViewModel.NuGetViewModel, Resources.Filter_Installed, installed.Id);

                this.ClickButton(thread, buttonBarViewModel, buttonBarViewModel.DisableButton);

                // At this point, there are 2 packages installed; 1 has an update and 1 is disabled

                // Select All Filter and check that the prerelease filter is visible
                this.SelectFilter(thread, buttonBarViewModel.NuGetViewModel, Resources.Filter_All);
                Assert.Equal<Visibility>(buttonBarViewModel.NuGetViewModel.ShowFeedSourceComboBox, Visibility.Visible);

                // Select Disabled Filter and check that the prerelease filter is NOT visible
                this.SelectFilter(thread, buttonBarViewModel.NuGetViewModel, Resources.Filter_Disabled);
                Assert.NotEqual<Visibility>(buttonBarViewModel.NuGetViewModel.ShowFeedSourceComboBox, Visibility.Visible);

                // Select Updates Filter and check that the prerelease filter is visible
                this.SelectFilter(thread, buttonBarViewModel.NuGetViewModel, Resources.Filter_Updated);
                Assert.Equal<Visibility>(buttonBarViewModel.NuGetViewModel.ShowFeedSourceComboBox, Visibility.Visible);

                // Select Installed Filter and check that the prerelease filter is NOT visible
                this.SelectFilter(thread, buttonBarViewModel.NuGetViewModel, Resources.Filter_Installed);
                Assert.NotEqual<Visibility>(buttonBarViewModel.NuGetViewModel.ShowFeedSourceComboBox, Visibility.Visible);
            }
        }

        /// <summary>
        /// Creates a NuGetViewModel and ButtonBarViewModel for testing
        /// </summary>
        private ButtonBarViewModel CreateViewModel(TemporaryDispatcherThread thread)
        {
            ButtonBarViewModel buttonBarViewModel = null;
            NuGetViewModel nuGetViewModel = null;
            thread.Invoke(() =>
            {
                nuGetViewModel = new NuGetViewModel(
                    this.Descriptor.Object,
                    this.Host.Object,
                    this.PackageSourcesViewModel,
                    (uri, site) => this.PackageManager,
                    this.ReadonlyDestination,
                    thread.Scheduler);

                buttonBarViewModel = new ButtonBarViewModel(nuGetViewModel, this.CloseCommand);
            });
            nuGetViewModel.WaitUntilComplete();

            return buttonBarViewModel;
        }

        private void SelectPackage(TemporaryDispatcherThread thread, NuGetViewModel viewModel, string filterName, string packageId)
        {
            this.SelectFilter(thread, viewModel, filterName);

            object packageItem;
            if (viewModel.SelectedFilter is Data.VirtualizingListViewFilter)
            {
                packageItem = viewModel.SelectedFilter.FilteredItems
                    .OfType<Data.VirtualizingListEntry>()
                    .Where(entry => entry.Item != null && ((PackageViewModel)entry.Item).Id == packageId)
                    .FirstOrDefault();
            }
            else
            {
                packageItem = viewModel.SelectedFilter.FilteredItems
                    .OfType<ListViewItemWrapper>()
                    .Where(item => item.Item != null && ((PackageViewModel)item.Item).Id == packageId)
                    .FirstOrDefault();
            }

            Assert.NotNull(packageItem);

            thread.Invoke(() =>
            {
                viewModel.SelectedItem = packageItem;
            });

            Assert.Equal(packageItem, viewModel.SelectedItem);
            Assert.NotNull(viewModel.SelectedPackage);
            Assert.Equal<string>(packageId, viewModel.SelectedPackage.Id);
        }

        private void SelectFilter(TemporaryDispatcherThread thread, NuGetViewModel viewModel, string filterName)
        {
            var filterToSelect = viewModel.Filters.Where(f => f.Name == filterName).FirstOrDefault();
            Assert.NotNull(filterToSelect);

            thread.Invoke(() =>
            {
                viewModel.SelectedFilter = filterToSelect;
            });
            Assert.Equal(filterName, viewModel.SelectedFilter.Name);
        }

        private void ClickButton(TemporaryDispatcherThread thread, ButtonBarViewModel viewModel, ButtonViewModel button, object parameter = null)
        {
            Assert.True(button.Command.CanExecute(parameter), "The button should be enabled.");
            Assert.True(viewModel.ActionButtons.Contains(button), "The button should be visible.");
            thread.Invoke(() => { button.Command.Execute(parameter); });
            viewModel.NuGetViewModel.WaitUntilComplete();
        }

        private ICommand CloseCommand
        {
            get;
            set;
        }

        private Mock<INuGetGalleryDescriptor> Descriptor
        {
            get;
            set;
        }

        private Mock<IWebMatrixHost> Host
        {
            get;
            set;
        }

        private InMemoryPackageManager PackageManager
        {
            get;
            set;
        }

        private PackageSourcesViewModel PackageSourcesViewModel
        {
            get;
            set;
        }

        private string ReadonlyDestination
        {
            get;
            set;
        }
    }
}


text:
namespace explorer_msgs
{
template <class ContainerAllocator>
struct explorer_reset_
{
  typedef explorer_reset_<ContainerAllocator> Type;

  explorer_reset_()
    : need_reset(false)  {
    }
  explorer_reset_(const ContainerAllocator& _alloc)
    : need_reset(false)  {
    }

   typedef uint8_t _need_reset_type;
  _need_reset_type need_reset;

  typedef boost::shared_ptr< ::explorer_msgs::explorer_reset_<ContainerAllocator> > Ptr;
  typedef boost::shared_ptr< ::explorer_msgs::explorer_reset_<ContainerAllocator> const> ConstPtr;

}; // struct explorer_reset_

typedef ::explorer_msgs::explorer_reset_<std::allocator<void> > explorer_reset;

typedef boost::shared_ptr< ::explorer_msgs::explorer_reset > explorer_resetPtr;
typedef boost::shared_ptr< ::explorer_msgs::explorer_reset const> explorer_resetConstPtr;

// constants requiring out of line definition

template<typename ContainerAllocator>
std::ostream& operator<<(std::ostream& s, const ::explorer_msgs::explorer_reset_<ContainerAllocator> & v)
{
ros::message_operations::Printer< ::explorer_msgs::explorer_reset_<ContainerAllocator> >::stream(s, "", v);
return s;
}

} // namespace explorer_msgs

namespace ros
{
namespace message_traits
{

// BOOLTRAITS {'IsFixedSize': True, 'IsMessage': True, 'HasHeader': False}
// {'std_msgs': ['/opt/ros/indigo/share/std_msgs/cmake/../msg'], 'explorer_msgs': ['/home/exbot/Document/ROS/explorer_test/src/explorer_msgs/msg']}

// !!!!!!!!!!! ['__class__', '__delattr__', '__dict__', '__doc__', '__eq__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_parsed_fields', 'constants', 'fields', 'full_name', 'has_header', 'header_present', 'names', 'package', 'parsed_fields', 'short_name', 'text', 'types']

template <class ContainerAllocator>
struct IsFixedSize< ::explorer_msgs::explorer_reset_<ContainerAllocator> >
  : TrueType
  { };

template <class ContainerAllocator>
struct IsFixedSize< ::explorer_msgs::explorer_reset_<ContainerAllocator> const>
  : TrueType
  { };

template <class ContainerAllocator>
struct IsMessage< ::explorer_msgs::explorer_reset_<ContainerAllocator> >
  : TrueType
  { };

template <class ContainerAllocator>
struct IsMessage< ::explorer_msgs::explorer_reset_<ContainerAllocator> const>
  : TrueType
  { };

template <class ContainerAllocator>
struct HasHeader< ::explorer_msgs::explorer_reset_<ContainerAllocator> >
  : FalseType
  { };

template <class ContainerAllocator>
struct HasHeader< ::explorer_msgs::explorer_reset_<ContainerAllocator> const>
  : FalseType
  { };

template<class ContainerAllocator>
struct MD5Sum< ::explorer_msgs::explorer_reset_<ContainerAllocator> >
{
  static const char* value()
  {
    return "b365914262b897ba7fefd344e01f202f";
  }

  static const char* value(const ::explorer_msgs::explorer_reset_<ContainerAllocator>&) { return value(); }
  static const uint64_t static_value1 = 0xb365914262b897baULL;
  static const uint64_t static_value2 = 0x7fefd344e01f202fULL;
};

template<class ContainerAllocator>
struct DataType< ::explorer_msgs::explorer_reset_<ContainerAllocator> >
{
  static const char* value()
  {
    return "explorer_msgs/explorer_reset";
  }

  static const char* value(const ::explorer_msgs::explorer_reset_<ContainerAllocator>&) { return value(); }
};

template<class ContainerAllocator>
struct Definition< ::explorer_msgs::explorer_reset_<ContainerAllocator> >
{
  static const char* value()
  {
    return "bool need_reset\n\
";
  }

  static const char* value(const ::explorer_msgs::explorer_reset_<ContainerAllocator>&) { return value(); }
};

} // namespace message_traits
} // namespace ros

namespace ros
{
namespace serialization
{

  template<class ContainerAllocator> struct Serializer< ::explorer_msgs::explorer_reset_<ContainerAllocator> >
  {
    template<typename Stream, typename T> inline static void allInOne(Stream& stream, T m)
    {
      stream.next(m.need_reset);
    }

    ROS_DECLARE_ALLINONE_SERIALIZER;
  }; // struct explorer_reset_

} // namespace serialization
} // namespace ros

namespace ros
{
namespace message_operations
{

template<class ContainerAllocator>
struct Printer< ::explorer_msgs::explorer_reset_<ContainerAllocator> >
{
  template<typename Stream> static void stream(Stream& s, const std::string& indent, const ::explorer_msgs::explorer_reset_<ContainerAllocator>& v)
  {
    s << indent << "need_reset: ";
    Printer<uint8_t>::stream(s, indent + "  ", v.need_reset);
  }
};

} // namespace message_operations
} // namespace ros

#endif // EXPLORER_MSGS_MESSAGE_EXPLORER_RESET_H

redpajama_set_name: RedPajamaGithub


text:
require 'test_helper'
include Devise::TestHelpers

class HomeControllerTest < ActionController::TestCase

  setup  do
    dump_database
    collection_fixtures 'users'
    @admin = User.where({email: 'admin@test.com'}).first
    @user = User.where({email: 'noadmin@test.com'}).first
  end

  test "user can change reporting period" do
    sign_in @user
    time = Time.gm(2015,5,1).to_i
    post "set_reporting_period", username: @user.username, effective_date: '05/01/2015'
    assert_response :success
  end

  test "default effective date for new user" do
    sign_in @admin
    time = Time.gm(2016,12,31).to_i
    user = User.new
    assert_equal time, user.effective_date
  end
end


text:
const router = require('express').Router();

const Webhooks = require('./../controllers/webhooks');

// /apiv1/webhooks/strava-receiver/
router.get('/strava-receiver', Webhooks.stravaGetReceiver);
router.post('/strava-receiver', Webhooks.stravaPostReceiver);

module.exports = router;


text:
#include "cmsis.h"

// This function is called after RAM initialization and before main.
void mbed_sdk_init()
{
    // Update the SystemCoreClock variable.
    SystemCoreClockUpdate();
    HAL_Init();

#if TARGET_STM32F7
    // The mbed_sdk_init can be called either during cold boot or during
    // application boot after bootloader has been executed.
    // In case the bootloader has already enabled the cache,
    // is is needed to not enable it again.
    if (SCB->CCR & (uint32_t)SCB_CCR_IC_Msk == 0) { // If ICache is disabled
        SCB_EnableICache();
    }
    if (SCB->CCR & (uint32_t)SCB_CCR_DC_Msk == 0) { // If DCache is disabled
        SCB_EnableDCache();
    }
#endif /* TARGET_STM32F7 */

    /* Configure the System clock source, PLL Multiplier and Divider factors,
       AHB/APBx prescalers and Flash settings */
    SetSysClock();
    SystemCoreClockUpdate();
}


text:
<?php

namespace allejo\Socrata\Exceptions;

/**
 * An exception thrown if a given dataset's resource ID is invalid
 *
 * @package allejo\Socrata\Exceptions
 * @since   0.1.0
 */
class InvalidResourceException extends \Exception
{
    /**
     * Create an exception
     *
     * @param string $message The message of the exception
     *
     * @since 0.1.0
     */
    public function __construct ($message)
    {
        $this->message = $message;
    }
}


text:
<?php
  # Include FusionCharts PHP Class
  include("../Class/FusionCharts_Gen.php");

  #---------- Configuring First Chart ----------
  # Create Column3D chart object
  $FC = new FusionCharts("Column3D","300","250");

  # set the relative path of the swf file
  $FC->setSWFPath("../FusionCharts/");

  # Define chart attributes
  $strParam="caption=Weekly Sales;subcaption=Revenue;xAxisName=Week;yAxisName=Revenue;numberPrefix=$";

  # Set chart attributes
  $FC->setChartParams($strParam);

  # Add chart values and category names for the First Chart
  $FC->addChartData("40800","Label=Week 1");
  $FC->addChartData("31400","Label=Week 2");
  $FC->addChartData("26700","Label=Week 3");
  $FC->addChartData("54400","Label=Week 4");

  #---------------------------------------------------------

  #---------- Configuring Second Chart ----------
  # Create another Column3D chart object
  $FC2 = new FusionCharts("Column3D","300","250");

  # set the relative path of the swf file
  $FC2->setSWFPath("../FusionCharts/");

  # Store chart attributes in a variable
  $strParam="caption=Weekly Sales;subcaption=Quantity;xAxisName=Week;yAxisName=Quantity;numberSuffix= U";

  # Set chart attributes
  $FC2->setChartParams($strParam);

  # Add chart values and category names for the second chart
  $FC2->addChartData("32","Label=Week 1");
  $FC2->addChartData("35","Label=Week 2");
  $FC2->addChartData("26","Label=Week 3");
  $FC2->addChartData("44","Label=Week 4");

  # Set Chart Caching Off for to Fix Caching error in FireFox
  $FC2->setOffChartCaching(true);

  //--------------------------------------------------------

?>
<html>
  <head>
    <title>Multiple Charts Using FusionCharts PHP Class</title>
    <script language='javascript' src='../FusionCharts/FusionCharts.js'></script>
  </head>
  <body>

    <?php
      # Render First Chart
      $FC->renderChart();

      # Render Second Chart
      $FC2->renderChart();

    ?>

  </body>
</html>


text:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Diagnostics — impyute 0.0.7 documentation</title>
    <link rel="stylesheet" href="../_static/alabaster.css" type="text/css" />
    <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />

    <link rel="index" title="Index" href="../genindex.html" />
    <link rel="search" title="Search" href="../search.html" />
    <link rel="next" title="Rules of Thumb" href="rules_of_thumb.html" />
    <link rel="prev" title="Tutorial" href="tutorial.html" />

  <link rel="stylesheet" href="../_static/custom.css" type="text/css" />

  <meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />

Diagnostics¶

Little's MCAR Test [1]¶

Take the mean of the data with missing values and take the mean of the data without missing values. If they're the same/simlar, then it's more likely that your data is MCAR.

<colgroup><col class="label" /><col /></colgroup>

[1]	Roderick J. A. Little. (1988). A Test of Missing Completely at Random for Multivariate Data with Missing Values. Journal of the American Statistical Association, 83(404), 1198-1202. doi:10.2307/2290157

impyute

Navigation

Overview

Getting Started

Tutorial

Diagnostics

Little's MCAR Test

Rules of Thumb

API

GitHub Repo

Contributing Guidelines

Philosophy

Current Goals

Papers Master List

Related Topics

Documentation overview

Previous: Tutorial

Next: Rules of Thumb

Quick search

    <form class="search" action="../search.html" method="get">
      <input type="text" name="q" />
      <input type="submit" value="Go" />
      <input type="hidden" name="check_keywords" value="yes" />
      <input type="hidden" name="area" value="default" />
    </form>

      ©2017, Elton Law.

      |
      Powered by Sphinx 1.8.1
      & Alabaster 0.7.12


text:
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "cc/animation/keyframe_effect.h"

#include <algorithm>
#include <memory>
#include <string>
#include <utility>

#include "base/containers/cxx20_erase.h"
#include "base/notreached.h"
#include "base/ranges/algorithm.h"
#include "base/time/time.h"
#include "cc/animation/animation.h"
#include "cc/animation/animation_host.h"
#include "cc/animation/animation_timeline.h"
#include "cc/animation/scroll_offset_animation_curve.h"
#include "cc/trees/property_animation_state.h"
#include "ui/gfx/animation/keyframe/animation_curve.h"
#include "ui/gfx/animation/keyframe/target_property.h"
#include "ui/gfx/geometry/transform_operations.h"
#include "ui/gfx/geometry/vector2d_f.h"

namespace cc {

namespace {

bool NeedsFinishedEvent(KeyframeModel* keyframe_model) {
  // The controlling instance (i.e., impl instance), sends the finish event and
  // does not need to receive it.
  if (keyframe_model->is_controlling_instance())
    return false;

  return !keyframe_model->received_finished_event();
}

// Returns indices for keyframe_models that have matching group id.
std::vector<size_t> FindAnimationsWithSameGroupId(
    const std::vector<std::unique_ptr<gfx::KeyframeModel>>& keyframe_models,
    int group_id) {
  std::vector<size_t> group;
  for (size_t i = 0; i < keyframe_models.size(); ++i) {
    auto* cc_keyframe_model =
        KeyframeModel::ToCcKeyframeModel(keyframe_models[i].get());
    if (cc_keyframe_model->group() != group_id)
      continue;
    group.push_back(i);
  }
  return group;
}

}  // namespace

KeyframeEffect::KeyframeEffect(Animation* animation)
    : animation_(animation),
      element_animations_(),
      needs_to_start_keyframe_models_(false),
      scroll_offset_animation_was_interrupted_(false),
      is_ticking_(false),
      needs_push_properties_(false) {}

KeyframeEffect::~KeyframeEffect() {
  DCHECK(!has_bound_element_animations());
}

void KeyframeEffect::SetNeedsPushProperties() {
  needs_push_properties_ = true;

  // The keyframe effect may have been removed from the main thread while
  // an event was in flight from the compositor. In this case, we may need
  // to push the removal to the compositor but do not expect to have a bound
  // element animations instance.
  // TODO(smcgruer): We only need the below calls when needs_push_properties_
  // goes from false to true - see http://crbug.com/764405
  if (element_animations()) {
    element_animations_->SetNeedsPushProperties();
  }

  animation_->SetNeedsPushProperties();
}

void KeyframeEffect::BindElementAnimations(
    ElementAnimations* element_animations) {
  DCHECK(element_animations);
  DCHECK(!element_animations_);
  element_animations_ = element_animations;

  DCHECK(element_id_);
  DCHECK(element_id_ == element_animations->element_id());

  if (has_any_keyframe_model())
    KeyframeModelAdded();
  SetNeedsPushProperties();
}

void KeyframeEffect::UnbindElementAnimations() {
  SetNeedsPushProperties();
  element_animations_ = nullptr;
}

void KeyframeEffect::AttachElement(ElementId element_id) {
  DCHECK(!element_id_);
  DCHECK(element_id);
  element_id_ = element_id;
}

void KeyframeEffect::DetachElement() {
  DCHECK(element_id_);
  element_id_ = ElementId();
}

void KeyframeEffect::Tick(base::TimeTicks monotonic_time) {
  DCHECK(has_bound_element_animations());
  if (needs_to_start_keyframe_models_)
    StartKeyframeModels(monotonic_time);

  for (auto& keyframe_model : keyframe_models()) {
    TickKeyframeModel(monotonic_time, keyframe_model.get());
  }

  last_tick_time_ = monotonic_time;
  element_animations_->UpdateClientAnimationState();
}

void KeyframeEffect::RemoveFromTicking() {
  is_ticking_ = false;
  // Resetting last_tick_time_ here ensures that calling ::UpdateState
  // before ::Animate doesn't start a keyframe model.
  last_tick_time_ = absl::nullopt;
  animation_->RemoveFromTicking();
}

void KeyframeEffect::UpdateState(bool start_ready_keyframe_models,
                                 AnimationEvents* events) {
  DCHECK(has_bound_element_animations());

  // Animate hasn't been called, this happens if an element has been added
  // between the Commit and Draw phases.
  if (last_tick_time_ == absl::nullopt)
    start_ready_keyframe_models = false;

  if (start_ready_keyframe_models)
    PromoteStartedKeyframeModels(events);

  auto last_tick_time = last_tick_time_.value_or(base::TimeTicks());
  MarkFinishedKeyframeModels(last_tick_time);
  MarkKeyframeModelsForDeletion(last_tick_time, events);
  PurgeKeyframeModelsMarkedForDeletion(/* impl_only */ true);

  if (start_ready_keyframe_models) {
    if (needs_to_start_keyframe_models_) {
      StartKeyframeModels(last_tick_time);
      PromoteStartedKeyframeModels(events);
    }
  }
}

void KeyframeEffect::UpdateTickingState() {
  if (animation_->has_animation_host()) {
    bool was_ticking = is_ticking_;
    is_ticking_ = HasNonDeletedKeyframeModel();

    if (is_ticking_ && !was_ticking) {
      animation_->AddToTicking();
    } else if (!is_ticking_ && was_ticking) {
      RemoveFromTicking();
    }
  }
}

void KeyframeEffect::Pause(base::TimeDelta pause_offset,
                           PauseCondition pause_condition) {
  bool did_pause = false;
  for (auto& keyframe_model : keyframe_models()) {
    // TODO(crbug.com/1076012): KeyframeEffect is paused with local time for
    // scroll-linked animations. To make sure the start event of a keyframe
    // model is sent to blink, we should not set its run state to PAUSED until
    // such event is sent. This should be revisited once KeyframeEffect is able
    // to tick scroll-linked keyframe models directly.
    if (pause_condition == PauseCondition::kAfterStart &&
        (keyframe_model->run_state() ==
             gfx::KeyframeModel::WAITING_FOR_TARGET_AVAILABILITY ||
         keyframe_model->run_state() == gfx::KeyframeModel::STARTING))
      continue;
    keyframe_model->Pause(pause_offset);
    did_pause = true;
  }

  if (!did_pause || !has_bound_element_animations())
    return;
  animation_->SetNeedsCommit();
  SetNeedsPushProperties();
}

void KeyframeEffect::AddKeyframeModel(
    std::unique_ptr<gfx::KeyframeModel> keyframe_model) {
  KeyframeModel* cc_keyframe_model =
      KeyframeModel::ToCcKeyframeModel(keyframe_model.get());
  DCHECK(!cc_keyframe_model->is_impl_only() ||
         keyframe_model->TargetProperty() == TargetProperty::SCROLL_OFFSET);
  // This is to make sure that keyframe models in the same group, i.e., start
  // together, don't animate the same property.
  DCHECK(base::ranges::none_of(
      keyframe_models(), [&](const auto& existing_keyframe_model) {
        auto* cc_existing_keyframe_model =
            KeyframeModel::ToCcKeyframeModel(existing_keyframe_model.get());
        return keyframe_model->TargetProperty() ==
                   existing_keyframe_model->TargetProperty() &&
               cc_keyframe_model->group() ==
                   cc_existing_keyframe_model->group();
      }));

  if (keyframe_model->TargetProperty() == TargetProperty::SCROLL_OFFSET) {
    // We should never have more than one scroll offset animation queued on the
    // same scrolling element as this would result in multiple automated
    // scrolls.
    DCHECK(base::ranges::none_of(
        keyframe_models(), [&](const auto& existing_keyframe_model) {
          auto* cc_existing_keyframe_model =
              KeyframeModel::ToCcKeyframeModel(existing_keyframe_model.get());
          return cc_existing_keyframe_model->TargetProperty() ==
                     TargetProperty::SCROLL_OFFSET &&
                 !cc_existing_keyframe_model->is_finished() &&
                 (!cc_existing_keyframe_model->is_controlling_instance() ||
                  cc_existing_keyframe_model->affects_pending_elements());
        }));
  }

  gfx::KeyframeEffect::AddKeyframeModel(std::move(keyframe_model));

  if (has_bound_element_animations()) {
    KeyframeModelAdded();
    SetNeedsPushProperties();
  }
}

void KeyframeEffect::PauseKeyframeModel(int keyframe_model_id,
                                        base::TimeDelta time_offset) {
  for (auto& keyframe_model : keyframe_models()) {
    if (keyframe_model->id() == keyframe_model_id) {
      keyframe_model->Pause(time_offset);
    }
  }

  if (has_bound_element_animations()) {
    animation_->SetNeedsCommit();
    SetNeedsPushProperties();
  }
}

void KeyframeEffect::AbortKeyframeModel(int keyframe_model_id) {
  if (gfx::KeyframeModel* keyframe_model =
          GetKeyframeModelById(keyframe_model_id)) {
    if (!keyframe_model->is_finished()) {
      keyframe_model->SetRunState(gfx::KeyframeModel::ABORTED,
                                  last_tick_time_.value_or(base::TimeTicks()));
      if (has_bound_element_animations())
        element_animations_->UpdateClientAnimationState();
    }
  }

  if (has_bound_element_animations()) {
    animation_->SetNeedsCommit();
    SetNeedsPushProperties();
  }
}

void KeyframeEffect::AbortKeyframeModelsWithProperty(
    TargetProperty::Type target_property,
    bool needs_completion) {
  if (needs_completion)
    DCHECK(target_property == TargetProperty::SCROLL_OFFSET);

  bool aborted_keyframe_model = false;
  for (auto& keyframe_model : keyframe_models()) {
    if (keyframe_model->TargetProperty() == target_property &&
        !keyframe_model->is_finished()) {
      // Currently only impl-only scroll offset KeyframeModels can be completed
      // on the main thread.
      if (needs_completion &&
          KeyframeModel::ToCcKeyframeModel(keyframe_model.get())
              ->is_impl_only()) {
        keyframe_model->SetRunState(
            gfx::KeyframeModel::ABORTED_BUT_NEEDS_COMPLETION,
            last_tick_time_.value_or(base::TimeTicks()));
      } else {
        keyframe_model->SetRunState(
            gfx::KeyframeModel::ABORTED,
            last_tick_time_.value_or(base::TimeTicks()));
      }
      aborted_keyframe_model = true;
    }
  }

  if (has_bound_element_animations()) {
    if (aborted_keyframe_model)
      element_animations_->UpdateClientAnimationState();
    animation_->SetNeedsCommit();
    SetNeedsPushProperties();
  }
}

void KeyframeEffect::ActivateKeyframeModels() {
  DCHECK(has_bound_element_animations());

  bool keyframe_model_activated = false;
  for (auto& keyframe_model : keyframe_models()) {
    auto* cc_keyframe_model =
        KeyframeModel::ToCcKeyframeModel(keyframe_model.get());
    if (cc_keyframe_model->affects_active_elements() !=
        cc_keyframe_model->affects_pending_elements()) {
      keyframe_model_activated = true;
    }
    cc_keyframe_model->set_affects_active_elements(
        cc_keyframe_model->affects_pending_elements());
  }

  if (keyframe_model_activated)
    element_animations_->UpdateClientAnimationState();

  scroll_offset_animation_was_interrupted_ = false;
}

void KeyframeEffect::KeyframeModelAdded() {
  DCHECK(has_bound_element_animations());

  animation_->SetNeedsCommit();
  needs_to_start_keyframe_models_ = true;

  UpdateTickingState();
  for (auto& keyframe_model : keyframe_models()) {
    element_animations_->AttachToCurve(keyframe_model->curve());
  }
  element_animations_->UpdateClientAnimationState();
}

bool KeyframeEffect::DispatchAnimationEventToKeyframeModel(
    const AnimationEvent& event) {
  DCHECK(!event.is_impl_only);
  KeyframeModel* keyframe_model = KeyframeModel::ToCcKeyframeModel(
      GetKeyframeModelById(event.uid.model_id));
  bool dispatched = false;
  switch (event.type) {
    case AnimationEvent::STARTED:
      if (keyframe_model && keyframe_model->needs_synchronized_start_time()) {
        keyframe_model->set_needs_synchronized_start_time(false);
        if (!keyframe_model->has_set_start_time())
          keyframe_model->set_start_time(event.monotonic_time);
        dispatched = true;
      }
      break;

    case AnimationEvent::FINISHED:
      if (keyframe_model) {
        keyframe_model->set_received_finished_event(true);
        dispatched = true;
      } else {
        // This is for the case when a keyframe_model is already removed on main
        // thread, but the impl version of it sent a finished event and is now
        // waiting for deletion. We would need to delete that keyframe_model
        // during push properties.
        SetNeedsPushProperties();
      }
      break;

    case AnimationEvent::ABORTED:
      if (keyframe_model) {
        keyframe_model->SetRunState(gfx::KeyframeModel::ABORTED,
                                    event.monotonic_time);
        keyframe_model->set_received_finished_event(true);
        dispatched = true;
        animation_->animation_host()
            ->UpdateClientAnimationStateForElementAnimations(element_id());
      }
      break;

    case AnimationEvent::TAKEOVER:
      // TODO(crbug.com/1018213): Routing TAKEOVER events is broken.
      // We need to purge KeyframeModels marked for deletion on CT.
      SetNeedsPushProperties();
      dispatched = true;
      break;

    case AnimationEvent::TIME_UPDATED:
      // TIME_UPDATED events are used to synchronize effect time between cc and
      // main thread worklet animations. Keyframe models are not involved in
      // this process.
      NOTREACHED();
      break;
  }
  return dispatched;
}

bool KeyframeEffect::HasTickingKeyframeModel() const {
  for (const auto& keyframe_model : keyframe_models()) {
    if (!keyframe_model->is_finished())
      return true;
  }
  return false;
}

bool KeyframeEffect::RequiresInvalidation() const {
  for (const auto& it : keyframe_models()) {
    if (it->TargetProperty() == TargetProperty::NATIVE_PROPERTY ||
        it->TargetProperty() == TargetProperty::CSS_CUSTOM_PROPERTY) {
      return true;
    }
  }
  return false;
}

bool KeyframeEffect::AffectsNativeProperty() const {
  for (const auto& it : keyframe_models()) {
    // TODO(crbug.com/1257778): include the SCROLL_OFFSET here so that we won't
    // create a compositor animation frame sequence tracker when there is a
    // composited scroll.
    if (it->TargetProperty() != TargetProperty::CSS_CUSTOM_PROPERTY &&
        it->TargetProperty() != TargetProperty::NATIVE_PROPERTY)
      return true;
  }
  return false;
}

bool KeyframeEffect::HasNonDeletedKeyframeModel() const {
  for (const auto& keyframe_model : keyframe_models()) {
    if (keyframe_model->run_state() != gfx::KeyframeModel::WAITING_FOR_DELETION)
      return true;
  }
  return false;
}

bool KeyframeEffect::AnimationsPreserveAxisAlignment() const {
  for (const auto& keyframe_model : keyframe_models()) {
    if (keyframe_model->is_finished())
      continue;

    if (!keyframe_model->curve()->PreservesAxisAlignment())
      return false;
  }
  return true;
}

float KeyframeEffect::MaximumScale(ElementId element_id,
                                   ElementListType list_type) const {
  float maximum_scale = kInvalidScale;
  for (const auto& keyframe_model : keyframe_models()) {
    if (keyframe_model->is_finished())
      continue;

    auto* cc_keyframe_model =
        KeyframeModel::ToCcKeyframeModel(keyframe_model.get());

    ElementId model_element_id = cc_keyframe_model->element_id();
    if (!model_element_id)
      model_element_id = element_id_;
    if (model_element_id != element_id)
      continue;

    if ((list_type == ElementListType::ACTIVE &&
         !cc_keyframe_model->affects_active_elements()) ||
        (list_type == ElementListType::PENDING &&
         !cc_keyframe_model->affects_pending_elements()))
      continue;

    float curve_maximum_scale = kInvalidScale;
    if (keyframe_model->curve()->MaximumScale(&curve_maximum_scale))
      maximum_scale = std::max(maximum_scale, curve_maximum_scale);
  }
  return maximum_scale;
}

bool KeyframeEffect::IsPotentiallyAnimatingProperty(
    TargetProperty::Type target_property,
    ElementListType list_type) const {
  for (const auto& keyframe_model : keyframe_models()) {
    if (!keyframe_model->is_finished() &&
        keyframe_model->TargetProperty() == target_property) {
      auto* cc_keyframe_model =
          KeyframeModel::ToCcKeyframeModel(keyframe_model.get());
      if ((list_type == ElementListType::ACTIVE &&
           cc_keyframe_model->affects_active_elements()) ||
          (list_type == ElementListType::PENDING &&
           cc_keyframe_model->affects_pending_elements()))
        return true;
    }
  }
  return false;
}

bool KeyframeEffect::IsCurrentlyAnimatingProperty(
    TargetProperty::Type target_property,
    ElementListType list_type) const {
  for (const auto& keyframe_model : keyframe_models()) {
    auto* cc_keyframe_model =
        KeyframeModel::ToCcKeyframeModel(keyframe_model.get());
    if (!keyframe_model->is_finished() &&
        cc_keyframe_model->InEffect(
            last_tick_time_.value_or(base::TimeTicks())) &&
        keyframe_model->TargetProperty() == target_property) {
      if ((list_type == ElementListType::ACTIVE &&
           cc_keyframe_model->affects_active_elements()) ||
          (list_type == ElementListType::PENDING &&
           cc_keyframe_model->affects_pending_elements()))
        return true;
    }
  }
  return false;
}

void KeyframeEffect::GetPropertyAnimationState(
    PropertyAnimationState* pending_state,
    PropertyAnimationState* active_state) const {
  pending_state->Clear();
  active_state->Clear();

  for (const auto& keyframe_model : keyframe_models()) {
    if (!keyframe_model->is_finished()) {
      auto* cc_keyframe_model =
          KeyframeModel::ToCcKeyframeModel(keyframe_model.get());
      bool in_effect = cc_keyframe_model->InEffect(
          last_tick_time_.value_or(base::TimeTicks()));
      bool active = cc_keyframe_model->affects_active_elements();
      bool pending = cc_keyframe_model->affects_pending_elements();
      int property = keyframe_model->TargetProperty();

      if (pending)
        pending_state->potentially_animating[property] = true;
      if (pending && in_effect)
        pending_state->currently_running[property] = true;

      if (active)
        active_state->potentially_animating[property] = true;
      if (active && in_effect)
        active_state->currently_running[property] = true;
    }
  }
}

void KeyframeEffect::MarkAbortedKeyframeModelsForDeletion(
    KeyframeEffect* keyframe_effect_impl) {
  bool keyframe_model_aborted = false;

  auto& keyframe_models_impl = keyframe_effect_impl->keyframe_models();
  for (const auto& keyframe_model_impl : keyframe_models_impl) {
    // If the keyframe_model has been aborted on the main thread, mark it for
    // deletion.
    if (gfx::KeyframeModel* keyframe_model =
            GetKeyframeModelById(keyframe_model_impl->id())) {
      if (keyframe_model->run_state() == gfx::KeyframeModel::ABORTED) {
        keyframe_model_impl->SetRunState(
            gfx::KeyframeModel::WAITING_FOR_DELETION,
            keyframe_effect_impl->last_tick_time_.value_or(base::TimeTicks()));
        keyframe_model->SetRunState(
            gfx::KeyframeModel::WAITING_FOR_DELETION,
            last_tick_time_.value_or(base::TimeTicks()));
        keyframe_model_aborted = true;
      }
    }
  }

  if (has_bound_element_animations() && keyframe_model_aborted)
    element_animations_->SetNeedsPushProperties();
}

void KeyframeEffect::PurgeKeyframeModelsMarkedForDeletion(bool impl_only) {
  base::EraseIf(keyframe_models(), [impl_only](const auto& keyframe_model) {
    return keyframe_model->run_state() ==
               gfx::KeyframeModel::WAITING_FOR_DELETION &&
           (!impl_only || KeyframeModel::ToCcKeyframeModel(keyframe_model.get())
                              ->is_impl_only());
  });
}

void KeyframeEffect::PurgeDeletedKeyframeModels() {
  base::EraseIf(keyframe_models(), [](const auto& keyframe_model) {
    return keyframe_model->run_state() ==
               gfx::KeyframeModel::WAITING_FOR_DELETION &&
           !KeyframeModel::ToCcKeyframeModel(keyframe_model.get())
                ->affects_pending_elements();
  });
}

void KeyframeEffect::PushNewKeyframeModelsToImplThread(
    KeyframeEffect* keyframe_effect_impl) const {
  // Any new KeyframeModels owned by the main thread's Animation are
  // cloned and added to the impl thread's Animation.
  for (const auto& keyframe_model : keyframe_models()) {
    // If the keyframe_model is finished, do not copy it over to impl since the
    // impl instance, if there was one, was just removed in
    // |RemoveKeyframeModelsCompletedOnMainThread|.
    if (keyframe_model->is_finished())
      continue;
    // If the keyframe_model is already running on the impl thread, there is no
    // need to copy it over.
    if (keyframe_effect_impl->GetKeyframeModelById(keyframe_model->id()))
      continue;

    if (keyframe_model->TargetProperty() == TargetProperty::SCROLL_OFFSET &&
        !ScrollOffsetAnimationCurve::ToScrollOffsetAnimationCurve(
             keyframe_model->curve())
             ->HasSetInitialValue()) {
      absl::optional<gfx::PointF> current_scroll_offset;
      // If the scroller was already composited, prefer using its current scroll
      // offset.
      current_scroll_offset = keyframe_effect_impl->ScrollOffsetForAnimation();
      // Otherwise, take the scroll offset from the commit with the animation.
      if (!current_scroll_offset.has_value())
        current_scroll_offset = ScrollOffsetForAnimation();
      DCHECK(current_scroll_offset);
      ScrollOffsetAnimationCurve* curve =
          ScrollOffsetAnimationCurve::ToScrollOffsetAnimationCurve(
              keyframe_model->curve());
      curve->SetInitialValue(*current_scroll_offset);
    }

    // The new keyframe_model should be set to run as soon as possible.
    gfx::KeyframeModel::RunState initial_run_state =
        gfx::KeyframeModel::WAITING_FOR_TARGET_AVAILABILITY;
    std::unique_ptr<KeyframeModel> to_add(
        KeyframeModel::ToCcKeyframeModel(keyframe_model.get())
            ->CreateImplInstance(initial_run_state));
    DCHECK(!to_add->needs_synchronized_start_time());
    to_add->set_affects_active_elements(false);
    keyframe_effect_impl->AddKeyframeModel(std::move(to_add));
  }
}

namespace {
bool IsCompleted(gfx::KeyframeModel* keyframe_model,
                 const KeyframeEffect* main_thread_keyframe_effect) {
  if (KeyframeModel::ToCcKeyframeModel(keyframe_model)->is_impl_only()) {
    return (keyframe_model->run_state() ==
            gfx::KeyframeModel::WAITING_FOR_DELETION);
  } else {
    gfx::KeyframeModel* main_thread_keyframe_model =
        main_thread_keyframe_effect->GetKeyframeModelById(keyframe_model->id());
    return !main_thread_keyframe_model ||
           main_thread_keyframe_model->is_finished();
  }
}
}  // namespace

void KeyframeEffect::RemoveKeyframeModelsCompletedOnMainThread(
    KeyframeEffect* keyframe_effect_impl) const {
  bool keyframe_model_completed = false;

  // Animations removed on the main thread should no longer affect pending
  // elements, and should stop affecting active elements after the next call
  // to ActivateKeyframeEffects. If already WAITING_FOR_DELETION, they can be
  // removed immediately.
  for (auto& keyframe_model : keyframe_effect_impl->keyframe_models()) {
    if (IsCompleted(keyframe_model.get(), this)) {
      KeyframeModel::ToCcKeyframeModel(keyframe_model.get())
          ->set_affects_pending_elements(false);
      keyframe_model_completed = true;
    }
  }

  keyframe_effect_impl->PurgeDeletedKeyframeModels();
  if (has_bound_element_animations() && keyframe_model_completed)
    element_animations_->SetNeedsPushProperties();
}

void KeyframeEffect::PushPropertiesTo(KeyframeEffect* keyframe_effect_impl) {
  if (!needs_push_properties_)
    return;
  needs_push_properties_ = false;

  // Synchronize the keyframe_model target between main and impl side.
  if (element_id_ != keyframe_effect_impl->element_id_) {
    // We have to detach/attach via the Animation as it may need to inform
    // the host as well.
    if (keyframe_effect_impl->has_attached_element())
      keyframe_effect_impl->animation_->DetachElement();
    if (element_id_) {
      if (element_id_.GetStableId() == ElementId::kReservedElementId)
        keyframe_effect_impl->animation_->AttachNoElement();
      else
        keyframe_effect_impl->animation_->AttachElement(element_id_);
    }
  }

  keyframe_effect_impl->scroll_offset_animation_was_interrupted_ =
      scroll_offset_animation_was_interrupted_;
  scroll_offset_animation_was_interrupted_ = false;

  // If neither main nor impl have any KeyframeModels, there is nothing further
  // to synchronize.
  if (!has_any_keyframe_model() &&
      !keyframe_effect_impl->has_any_keyframe_model())
    return;

  // Synchronize the main-thread and impl-side keyframe model lists, removing
  // aborted KeyframeModels and pushing any new animations.
  MarkAbortedKeyframeModelsForDeletion(keyframe_effect_impl);
  PurgeKeyframeModelsMarkedForDeletion(/* impl_only */ false);
  RemoveKeyframeModelsCompletedOnMainThread(keyframe_effect_impl);
  PushNewKeyframeModelsToImplThread(keyframe_effect_impl);

  // Now that the keyframe model lists are synchronized, push the properties for
  // the individual KeyframeModels.
  for (const auto& keyframe_model : keyframe_models()) {
    KeyframeModel* current_impl = KeyframeModel::ToCcKeyframeModel(
        keyframe_effect_impl->GetKeyframeModelById(keyframe_model->id()));
    if (current_impl)
      KeyframeModel::ToCcKeyframeModel(keyframe_model.get())
          ->PushPropertiesTo(current_impl);
  }

  keyframe_effect_impl->UpdateTickingState();
}

std::string KeyframeEffect::KeyframeModelsToString() const {
  std::string str;
  for (size_t i = 0; i < keyframe_models().size(); i++) {
    if (i > 0)
      str.append(", ");
    str.append(KeyframeModel::ToCcKeyframeModel(keyframe_models()[i].get())
                   ->ToString());
  }
  return str;
}

base::TimeDelta KeyframeEffect::MinimumTickInterval() const {
  base::TimeDelta min_interval = base::TimeDelta::Max();
  for (const auto& model : keyframe_models()) {
    base::TimeDelta interval = model->curve()->TickInterval();
    if (interval.is_zero())
      return interval;
    if (interval < min_interval)
      min_interval = interval;
  }
  return min_interval;
}

void KeyframeEffect::RemoveKeyframeModelRange(
    typename KeyframeModels::iterator to_remove_begin,
    typename KeyframeModels::iterator to_remove_end) {
  bool keyframe_model_removed = false;
  for (auto it = to_remove_begin; it != to_remove_end; ++it) {
    if ((*it)->TargetProperty() == TargetProperty::SCROLL_OFFSET) {
      if (has_bound_element_animations())
        scroll_offset_animation_was_interrupted_ = true;
    } else if (!(*it)->is_finished()) {
      keyframe_model_removed = true;
    }
  }

  gfx::KeyframeEffect::RemoveKeyframeModelRange(to_remove_begin, to_remove_end);

  if (has_bound_element_animations()) {
    UpdateTickingState();
    if (keyframe_model_removed)
      element_animations_->UpdateClientAnimationState();
    animation_->SetNeedsCommit();
    SetNeedsPushProperties();
  }
}

void KeyframeEffect::StartKeyframeModels(base::TimeTicks monotonic_time) {
  DCHECK(needs_to_start_keyframe_models_);
  needs_to_start_keyframe_models_ = false;

  // First collect running properties affecting each type of element.
  gfx::TargetProperties blocked_properties_for_active_elements;
  gfx::TargetProperties blocked_properties_for_pending_elements;
  std::vector<size_t> keyframe_models_waiting_for_target;

  keyframe_models_waiting_for_target.reserve(keyframe_models().size());
  for (size_t i = 0; i < keyframe_models().size(); ++i) {
    auto* cc_keyframe_model =
        KeyframeModel::ToCcKeyframeModel(keyframe_models()[i].get());
    if (cc_keyframe_model->run_state() == gfx::KeyframeModel::STARTING ||
        cc_keyframe_model->run_state() == gfx::KeyframeModel::RUNNING) {
      int property = cc_keyframe_model->TargetProperty();
      if (cc_keyframe_model->affects_active_elements()) {
        blocked_properties_for_active_elements[property] = true;
      }
      if (cc_keyframe_model->affects_pending_elements()) {
        blocked_properties_for_pending_elements[property] = true;
      }
    } else if (cc_keyframe_model->run_state() ==
               gfx::KeyframeModel::WAITING_FOR_TARGET_AVAILABILITY) {
      keyframe_models_waiting_for_target.push_back(i);
    }
  }

  for (size_t i = 0; i < keyframe_models_waiting_for_target.size(); ++i) {
    // Collect all properties for KeyframeModels with the same group id (they
    // should all also be in the list of KeyframeModels).
    size_t keyframe_model_index = keyframe_models_waiting_for_target[i];
    KeyframeModel* keyframe_model_waiting_for_target =
        KeyframeModel::ToCcKeyframeModel(
            keyframe_models()[keyframe_model_index].get());
    // Check for the run state again even though the keyframe_model was waiting
    // for target because it might have changed the run state while handling
    // previous keyframe_model in this loop (if they belong to same group).
    if (keyframe_model_waiting_for_target->run_state() ==
        gfx::KeyframeModel::WAITING_FOR_TARGET_AVAILABILITY) {
      gfx::TargetProperties enqueued_properties;
      bool affects_active_elements =
          keyframe_model_waiting_for_target->affects_active_elements();
      bool affects_pending_elements =
          keyframe_model_waiting_for_target->affects_pending_elements();
      enqueued_properties[keyframe_model_waiting_for_target->TargetProperty()] =
          true;
      for (size_t j = keyframe_model_index + 1; j < keyframe_models().size();
           ++j) {
        auto* cc_keyframe_model =
            KeyframeModel::ToCcKeyframeModel(keyframe_models()[j].get());
        if (keyframe_model_waiting_for_target->group() ==
            cc_keyframe_model->group()) {
          enqueued_properties[cc_keyframe_model->TargetProperty()] = true;
          affects_active_elements |=
              cc_keyframe_model->affects_active_elements();
          affects_pending_elements |=
              cc_keyframe_model->affects_pending_elements();
        }
      }

      // Check to see if intersection of the list of properties affected by
      // the group and the list of currently blocked properties is null, taking
      // into account the type(s) of elements affected by the group. In any
      // case, the group's target properties need to be added to the lists of
      // blocked properties.
      bool null_intersection = true;
      for (int property = TargetProperty::FIRST_TARGET_PROPERTY;
           property <= TargetProperty::LAST_TARGET_PROPERTY; ++property) {
        if (enqueued_properties[property]) {
          if (affects_active_elements) {
            if (blocked_properties_for_active_elements[property])
              null_intersection = false;
            else
              blocked_properties_for_active_elements[property] = true;
          }
          if (affects_pending_elements) {
            if (blocked_properties_for_pending_elements[property])
              null_intersection = false;
            else
              blocked_properties_for_pending_elements[property] = true;
          }
        }
      }

      // If the intersection is null, then we are free to start the
      // KeyframeModels in the group.
      if (null_intersection) {
        keyframe_model_waiting_for_target->SetRunState(
            gfx::KeyframeModel::STARTING, monotonic_time);
        for (size_t j = keyframe_model_index + 1; j < keyframe_models().size();
             ++j) {
          auto* cc_keyframe_model =
              KeyframeModel::ToCcKeyframeModel(keyframe_models()[j].get());
          if (keyframe_model_waiting_for_target->group() ==
              cc_keyframe_model->group()) {
            cc_keyframe_model->SetRunState(gfx::KeyframeModel::STARTING,
                                           monotonic_time);
          }
        }
      } else {
        needs_to_start_keyframe_models_ = true;
      }
    }
  }
}

void KeyframeEffect::PromoteStartedKeyframeModels(AnimationEvents* events) {
  for (auto& keyframe_model : keyframe_models()) {
    if (keyframe_model->run_state() == gfx::KeyframeModel::STARTING &&
        KeyframeModel::ToCcKeyframeModel(keyframe_model.get())
            ->affects_active_elements()) {
      auto* cc_keyframe_model =
          KeyframeModel::ToCcKeyframeModel(keyframe_model.get());
      cc_keyframe_model->SetRunState(
          gfx::KeyframeModel::RUNNING,
          last_tick_time_.value_or(base::TimeTicks()));
      if (!cc_keyframe_model->has_set_start_time() &&
          !cc_keyframe_model->needs_synchronized_start_time())
        cc_keyframe_model->set_start_time(
            last_tick_time_.value_or(base::TimeTicks()));

      base::TimeTicks start_time;
      if (cc_keyframe_model->has_set_start_time())
        start_time = cc_keyframe_model->start_time();
      else
        start_time = last_tick_time_.value_or(base::TimeTicks());

      GenerateEvent(events, *cc_keyframe_model, AnimationEvent::STARTED,
                    start_time);
    }
  }
}

void KeyframeEffect::MarkKeyframeModelsForDeletion(
    base::TimeTicks monotonic_time,
    AnimationEvents* events) {
  bool marked_keyframe_model_for_deletion = false;
  auto MarkForDeletion = [&](KeyframeModel* keyframe_model) {
    keyframe_model->SetRunState(gfx::KeyframeModel::WAITING_FOR_DELETION,
                                monotonic_time);
    marked_keyframe_model_for_deletion = true;
  };

  // Non-aborted KeyframeModels are marked for deletion after a corresponding
  // AnimationEvent::FINISHED event is sent or received. This means that if
  // we don't have an events vector, we must ensure that non-aborted
  // KeyframeModels have received a finished event before marking them for
  // deletion.
  for (auto& keyframe_model : keyframe_models()) {
    KeyframeModel* cc_keyframe_model =
        KeyframeModel::ToCcKeyframeModel(keyframe_model.get());
    if (cc_keyframe_model->run_state() == gfx::KeyframeModel::ABORTED) {
      GenerateEvent(events, *cc_keyframe_model, AnimationEvent::ABORTED,
                    monotonic_time);
      // If this is the controlling instance or it has already received finish
      // event, keyframe model can be marked for deletion.
      if (!NeedsFinishedEvent(cc_keyframe_model))
        MarkForDeletion(cc_keyframe_model);
      continue;
    }

    // If this is an aborted controlling instance that need completion on the
    // main thread, generate takeover event.
    if (cc_keyframe_model->is_controlling_instance() &&
        cc_keyframe_model->run_state() ==
            gfx::KeyframeModel::ABORTED_BUT_NEEDS_COMPLETION) {
      GenerateTakeoverEventForScrollAnimation(events, *cc_keyframe_model,
                                              monotonic_time);
      // Remove the keyframe model from the impl thread.
      MarkForDeletion(cc_keyframe_model);
      continue;
    }

    if (cc_keyframe_model->run_state() != gfx::KeyframeModel::FINISHED)
      continue;

    // Since deleting an animation on the main thread leads to its deletion
    // on the impl thread, we only mark a FINISHED main thread animation for
    // deletion once it has received a FINISHED event from the impl thread.
    if (NeedsFinishedEvent(cc_keyframe_model))
      continue;

    // If a keyframe model is finished, and not already marked for deletion,
    // find out if all other keyframe models in the same group are also
    // finished.
    std::vector<size_t> keyframe_models_in_same_group =
        FindAnimationsWithSameGroupId(keyframe_models(),
                                      cc_keyframe_model->group());

    bool a_keyframe_model_in_same_group_is_not_finished =
        base::ranges::any_of(keyframe_models_in_same_group, [&](size_t index) {
          auto* keyframe_model =
              KeyframeModel::ToCcKeyframeModel(keyframe_models()[index].get());
          return !keyframe_model->is_finished() ||
                 (keyframe_model->run_state() == gfx::KeyframeModel::FINISHED &&
                  NeedsFinishedEvent(keyframe_model));
        });

    if (a_keyframe_model_in_same_group_is_not_finished)
      continue;

    // Now remove all the keyframe models which belong to the same group and are
    // not yet aborted. These will be set to WAITING_FOR_DELETION which also
    // ensures we don't try to delete them again.
    for (size_t j = 0; j < keyframe_models_in_same_group.size(); ++j) {
      KeyframeModel* same_group_keyframe_model =
          KeyframeModel::ToCcKeyframeModel(
              keyframe_models()[keyframe_models_in_same_group[j]].get());

      // Skip any keyframe model in this group which is already processed.
      if (same_group_keyframe_model->run_state() ==
              gfx::KeyframeModel::WAITING_FOR_DELETION ||
          same_group_keyframe_model->run_state() == gfx::KeyframeModel::ABORTED)
        continue;

      GenerateEvent(events, *same_group_keyframe_model,
                    AnimationEvent::FINISHED, monotonic_time);
      MarkForDeletion(same_group_keyframe_model);
    }
  }

  // We need to purge KeyframeModels marked for deletion, which happens in
  // PushPropertiesTo().
  if (marked_keyframe_model_for_deletion)
    SetNeedsPushProperties();
}

void KeyframeEffect::MarkFinishedKeyframeModels(
    base::TimeTicks monotonic_time) {
  DCHECK(has_bound_element_animations());

  bool keyframe_model_finished = false;
  for (auto& keyframe_model : keyframe_models()) {
    if (!keyframe_model->is_finished() &&
        keyframe_model->IsFinishedAt(monotonic_time)) {
      keyframe_model->SetRunState(gfx::KeyframeModel::FINISHED, monotonic_time);
      keyframe_model_finished = true;
      SetNeedsPushProperties();
    }
    auto* cc_keyframe_model =
        KeyframeModel::ToCcKeyframeModel(keyframe_model.get());

    if (!cc_keyframe_model->affects_active_elements() &&
        !cc_keyframe_model->affects_pending_elements()) {
      switch (keyframe_model->run_state()) {
        case gfx::KeyframeModel::WAITING_FOR_TARGET_AVAILABILITY:
        case gfx::KeyframeModel::STARTING:
        case gfx::KeyframeModel::RUNNING:
        case gfx::KeyframeModel::PAUSED:
          keyframe_model->SetRunState(gfx::KeyframeModel::FINISHED,
                                      monotonic_time);
          keyframe_model_finished = true;
          break;
        default:
          break;
      }
    }
  }
  if (keyframe_model_finished)
    element_animations_->UpdateClientAnimationState();
}

absl::optional<gfx::PointF> KeyframeEffect::ScrollOffsetForAnimation() const {
  return element_animations_->ScrollOffsetForAnimation();
}

void KeyframeEffect::GenerateEvent(AnimationEvents* events,
                                   const KeyframeModel& keyframe_model,
                                   AnimationEvent::Type type,
                                   base::TimeTicks monotonic_time) {
  if (!events)
    return;

  AnimationEvent event(type,
                       {animation_->animation_timeline()->id(),
                        animation_->id(), keyframe_model.id()},
                       keyframe_model.group(), keyframe_model.TargetProperty(),
                       monotonic_time);
  event.is_impl_only =
      KeyframeModel::ToCcKeyframeModel(&keyframe_model)->is_impl_only();
  if (!event.is_impl_only) {
    events->events_.push_back(event);
    return;
  }
  // For impl only animations notify delegate directly, do not record the event.
  animation_->DispatchAndDelegateAnimationEvent(event);
}

void KeyframeEffect::GenerateTakeoverEventForScrollAnimation(
    AnimationEvents* events,
    const KeyframeModel& keyframe_model,
    base::TimeTicks monotonic_time) {
  DCHECK_EQ(keyframe_model.TargetProperty(), TargetProperty::SCROLL_OFFSET);
  if (!events)
    return;

  AnimationEvent takeover_event(
      AnimationEvent::TAKEOVER,
      {animation_->animation_timeline()->id(), animation_->id(),
       keyframe_model.id()},
      keyframe_model.group(), keyframe_model.TargetProperty(), monotonic_time);
  takeover_event.animation_start_time = keyframe_model.start_time();
  const ScrollOffsetAnimationCurve* scroll_offset_animation_curve =
      ScrollOffsetAnimationCurve::ToScrollOffsetAnimationCurve(
          keyframe_model.curve());
  takeover_event.curve = scroll_offset_animation_curve->Clone();
  // Notify main thread.
  events->events_.push_back(takeover_event);

  AnimationEvent finished_event(
      AnimationEvent::FINISHED,
      {animation_->animation_timeline()->id(), animation_->id(),
       keyframe_model.id()},
      keyframe_model.group(), keyframe_model.TargetProperty(), monotonic_time);
  // Notify the compositor that the animation is finished.
  finished_event.is_impl_only = true;
  animation_->DispatchAndDelegateAnimationEvent(finished_event);
}

}  // namespace cc


text:
package org.thingsboard.server.msa;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableMap;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.SSLContexts;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.Listeners;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.DeviceId;

import java.net.URI;
import java.util.Map;
import java.util.Random;

@Slf4j
@Listeners(TestListener.class)
public abstract class AbstractContainerTest {
    protected static long timeoutMultiplier = 1;
    protected ObjectMapper mapper = new ObjectMapper();
    private static final ContainerTestSuite containerTestSuite = ContainerTestSuite.getInstance();
    protected static TestRestClient testRestClient;

    @BeforeSuite
    public void beforeSuite() {
        if ("false".equals(System.getProperty("runLocal", "false"))) {
            containerTestSuite.start();
        }
        testRestClient = new TestRestClient(TestProperties.getBaseUrl());
        if (!"kafka".equals(System.getProperty("blackBoxTests.queue", "kafka"))) {
            timeoutMultiplier = 10;
        }
    }

    @AfterSuite
    public void afterSuite() {
        if (containerTestSuite.isActive()) {
            containerTestSuite.stop();
        }
    }

    protected WsClient subscribeToWebSocket(DeviceId deviceId, String scope, CmdsType property) throws Exception {
        String webSocketUrl = TestProperties.getWebSocketUrl();
        WsClient wsClient = new WsClient(new URI(webSocketUrl + "/api/ws/plugins/telemetry?token=" + testRestClient.getToken()), timeoutMultiplier);
        if (webSocketUrl.matches("^(wss)://.*$")) {
            SSLContextBuilder builder = SSLContexts.custom();
            builder.loadTrustMaterial(null, (TrustStrategy) (chain, authType) -> true);
            wsClient.setSocketFactory(builder.build().getSocketFactory());
        }
        wsClient.connectBlocking();

        JsonObject cmdsObject = new JsonObject();
        cmdsObject.addProperty("entityType", EntityType.DEVICE.name());
        cmdsObject.addProperty("entityId", deviceId.toString());
        cmdsObject.addProperty("scope", scope);
        cmdsObject.addProperty("cmdId", new Random().nextInt(100));

        JsonArray cmd = new JsonArray();
        cmd.add(cmdsObject);
        JsonObject wsRequest = new JsonObject();
        wsRequest.add(property.toString(), cmd);
        wsClient.send(wsRequest.toString());
        wsClient.waitForFirstReply();
        return wsClient;
    }

    protected Map<String, Long> getExpectedLatestValues(long ts) {
        return ImmutableMap.<String, Long>builder()
                .put("booleanKey", ts)
                .put("stringKey", ts)
                .put("doubleKey", ts)
                .put("longKey", ts)
                .build();
    }

    protected JsonObject createGatewayConnectPayload(String deviceName){
        JsonObject payload = new JsonObject();
        payload.addProperty("device", deviceName);
        return payload;
    }

    protected JsonObject createGatewayPayload(String deviceName, long ts){
        JsonObject payload = new JsonObject();
        payload.add(deviceName, createGatewayTelemetryArray(ts));
        return payload;
    }

    protected JsonArray createGatewayTelemetryArray(long ts){
        JsonArray telemetryArray = new JsonArray();
        if (ts > 0)
            telemetryArray.add(createPayload(ts));
        else
            telemetryArray.add(createPayload());
        return telemetryArray;
    }

    protected JsonObject createPayload(long ts) {
        JsonObject values = createPayload();
        JsonObject payload = new JsonObject();
        payload.addProperty("ts", ts);
        payload.add("values", values);
        return payload;
    }

    protected JsonObject createPayload() {
        JsonObject values = new JsonObject();
        values.addProperty("stringKey", "value1");
        values.addProperty("booleanKey", true);
        values.addProperty("doubleKey", 42.0);
        values.addProperty("longKey", 73L);

        return values;
    }

    protected enum CmdsType {
        TS_SUB_CMDS("tsSubCmds"),
        HISTORY_CMDS("historyCmds"),
        ATTR_SUB_CMDS("attrSubCmds");

        private final String text;

        CmdsType(final String text) {
            this.text = text;
        }

        @Override
        public String toString() {
            return text;
        }
    }

}


text:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<meta http-equiv="Content-Type" content="text/html" charset="UTF-8">
<title>freeseawind.lf.geom (LittleLuck 2.0.0 API)</title>
<meta name="date" content="2016-10-02">
<link rel="stylesheet" type="text/css" href="../../../stylesheet.css" title="Style">

freeseawind.lf.geom

类

LuckProperty

LuckRectangle

枚举

LuckProperty.LuckPropertyType


text:
'use strict'

var inProgress, retryTimeout

pb.addEventListener('signed_in', function(e) {
    inProgress = false

    if (localStorage.device) {
        try {
            pb.local.device = JSON.parse(localStorage.device)
        } catch (e) {
            delete localStorage.device
        }
    }

    pb.addEventListener('devices_ready', function(e) {
        createOrAttachDevice()
    })
})

var createOrAttachDevice = function() {
    clearTimeout(retryTimeout)

    if (inProgress) {
        pb.log('Device is already being created or attached')
        return
    } else {
        inProgress = true
    }

    var attachTo, device
    Object.keys(pb.local.devices).forEach(function(key) {
        device = pb.local.devices[key]
        if (device.active) {
            if ((pb.local.device && pb.local.device.iden == device.iden)
                || (device.type == pb.browser)) {
                attachTo = device
            }
        }
    })

    if (attachTo) {
        if (!pb.local.device) {
            pb.log('Attaching to existing device:')
            pb.log(attachTo)
        }

        pb.local.device = attachTo
        localStorage.device = JSON.stringify(attachTo)

        var needsUpdating = false
        var deviceValues = getDeviceValues()

        if (localStorage['keyFingerprintDirty']) {
            deviceValues['key_fingerprint'] = pb.e2e.getKeyFingerprint()
        }

        Object.keys(deviceValues).forEach(function(key) {
            if (deviceValues[key] != pb.local.device[key]) {
                needsUpdating = true
            }
        })
        if (needsUpdating) {
           updateDevice(deviceValues, function(response) {
               if (response) {
                    pb.local.device = response
                    localStorage.device = JSON.stringify(attachTo)
                    localStorage.removeItem('keyFingerprintDirty')
                } else {
                    var retryTimeout = setTimeout(function() {
                         pb.dispatchEvent('devices_ready')
                    }, 30 * 1000)
                }

                inProgress = false
            })
        } else {
            inProgress = false
        }
    } else if (pb.local.device && !attachTo) { // Device has been deleted
        inProgress = false
        pb.signOut()
    } else {
        createDevice(function(response) {
            if (response) {
                pb.local.device = response
                localStorage.device = JSON.stringify(response)
            } else {
                var retryTimeout = setTimeout(function() {
                     pb.dispatchEvent('devices_ready')
                }, 30 * 1000)
            }

            inProgress = false
        })
    }
}

var getDeviceValues = function() {
    var body = {}
    if (pb.browser == 'opera') {
        body['type'] = 'opera'
        body['manufacturer'] = 'Opera'
        body['model'] = 'Opera'
    } else if (pb.browser == 'firefox') {
        body['type'] = 'firefox'
        body['manufacturer'] = 'Mozilla'
        body['model'] = 'Firefox'
    } else {
        body['type'] = 'chrome'
        body['manufacturer'] = 'Google'
        body['model'] = 'Chrome'
    }
    return body
}

var createDevice = function(done) {
    pb.log('Creating device')

    var body = getDeviceValues()
    body['nickname'] = pb.browser.charAt(0).toUpperCase() + pb.browser.slice(1)
    body['app_version'] = pb.version

    pb.post(pb.api + '/v2/devices', body, function(response) {
        done(response)
    })
}

var updateDevice = function(deviceValues, done) {
    pb.log('Updating device')

    pb.post(pb.api + '/v2/devices/' + pb.local.device.iden, deviceValues, function(response) {
        done(response)
    })
}