public void ContextDoesNotLeak()
        {
            // Note: this test will fail if you set regular event handlers
            // for PropertyChanged/CollectionChanged events in dirty profiles (use WeakEventManager instead)

            var project = CreateTestProject();

            project.Options.Profiles["kana"].Actions.Add(new ActionProfileOptions {
                Name = "kana-action"
            });

            var refs = new List <WeakReference>();

            new Action(() =>
            {
                var context = new ProfileOptionsWindowContext(project, null, null);

                refs.Add(new WeakReference(context, true));
                refs.Add(new WeakReference(context.Pages.First(p => p is ProfileOptionsActionsPage), true));

                GetDirtyProfile(context, "kana").Actions[0].Name = "renamed-kana-action";
                context.SaveChanges();
            })();

            GC.Collect();
            GC.WaitForPendingFinalizers();

            foreach (var r in refs)
            {
                Assert.Null(r.Target);
            }
        }
        public void SaveChangesNameConflictIgnoredTest()
        {
            var project = CreateTestProject();

            // Add a third profile at the end
            project.Options.SetProfiles(project.Options.Profiles.Append(new KeyValuePair <string, ProfileOptions>("miz", new ProfileOptions())).ToDictionary(p => p.Key, p => p.Value),
                                        project.Options.ActiveProfile);

            var nameResolver = new Mock <ProfileOptionsWindowContext.AskProfileNameDelegate>(MockBehavior.Strict);
            var context      = new ProfileOptionsWindowContext(project, null, nameResolver.Object);

            context.SelectedProfile = GetDirtyProfile(context, "kana");
            var asa = GetDirtyProfile(context, "asa");

            asa.General.ProfileName   = "kana";
            asa.General.RemoteWorkDir = "~asa-edited/code";

            nameResolver.Setup(n => n("Rename", It.IsAny <string>(), It.IsAny <IEnumerable <string> >(), "kana")).Returns("kana");

            Assert.Null(context.SaveChanges()); // no error

            // Selected profile is transparently switched to the replacement
            Assert.Equal(asa, context.SelectedProfile);
            Assert.Equal(2, context.DirtyProfiles.Count);
            Assert.Equal(asa, context.DirtyProfiles[0]);

            Assert.Equal(2, project.Options.Profiles.Count);
            Assert.True(project.Options.Profiles.TryGetValue("kana", out var renamed));
            Assert.Equal("~asa-edited/code", renamed.General.RemoteWorkDir);
            Assert.True(project.Options.Profiles.ContainsKey("miz"));
        }
        public void SaveChangesNameConflictResolvedTest()
        {
            var project      = CreateTestProject();
            var nameResolver = new Mock <ProfileOptionsWindowContext.AskProfileNameDelegate>(MockBehavior.Strict);
            var context      = new ProfileOptionsWindowContext(project, null, nameResolver.Object);

            var kana = GetDirtyProfile(context, "kana");

            kana.General.RemoteWorkDir = "~kana-edited/code";
            var asa = GetDirtyProfile(context, "asa");

            asa.General.ProfileName   = "kana";
            asa.General.RemoteWorkDir = "~asa-edited/code";

            nameResolver.Setup(n => n("Rename", "Profile kana already exists. Enter a new name or leave it as is to overwrite the profile:", It.IsAny <IEnumerable <string> >(), "kana"))
            .Returns("asa-renamed").Verifiable();

            Assert.Null(context.SaveChanges()); // no error
            nameResolver.Verify();

            Assert.Equal(2, project.Options.Profiles.Count);
            Assert.True(project.Options.Profiles.TryGetValue("kana", out var kanaSaved));
            Assert.Equal("~kana-edited/code", kanaSaved.General.RemoteWorkDir);
            Assert.True(project.Options.Profiles.TryGetValue("asa-renamed", out var asaSaved));
            Assert.Equal("~asa-edited/code", asaSaved.General.RemoteWorkDir);

            // Don't forget to synchronize dirty profiles!
            Assert.Equal("~kana-edited/code", GetDirtyProfile(context, "kana").General.RemoteWorkDir);
            Assert.Equal("~asa-edited/code", GetDirtyProfile(context, "asa-renamed").General.RemoteWorkDir);
        }
        public void RemoveProfileTest()
        {
            var project = CreateTestProject();
            var context = new ProfileOptionsWindowContext(project, null, null);

            // a crude emulation of WPF control behavior when removing selected profile
            context.DirtyProfiles.CollectionChanged += (s, e) =>
            {
                if (e.Action == NotifyCollectionChangedAction.Remove && e.OldItems[0] == context.SelectedProfile)
                {
                    context.SelectedProfile = null;
                }
            };

            context.SelectedProfile = GetDirtyProfile(context, "kana");
            context.RemoveSelectedProfile();

            Assert.Single(context.DirtyProfiles);
            Assert.Null(context.SelectedProfile);
            Assert.Null(context.SelectedPage);
            Assert.Empty(context.Pages);

            context.SaveChanges();

            Assert.Equal(1, project.Options.Profiles.Count);
            // Switches to the first existing profile
            Assert.Equal("asa", project.Options.ActiveProfile);

            context.SelectedProfile = GetDirtyProfile(context, "asa");
            context.RemoveSelectedProfile();

            Assert.Empty(context.DirtyProfiles);
            Assert.Null(context.SelectedProfile);
            Assert.Null(context.SelectedPage);
            Assert.Empty(context.Pages);

            context.SaveChanges();

            Assert.Null(project.Options.Profile);
            Assert.False(project.Options.HasProfiles);

            // Does not fail when there are no profiles
            context.RemoveSelectedProfile();
        }
        public void DirtyTrackingTest()
        {
            var project = CreateTestProject();

            project.Options.ActiveProfile = "kana";
            var context = new ProfileOptionsWindowContext(project, null, null);

            GetAction(context, "Debug").Steps.Add(new ExecuteStep {
                Executable = "bun"
            });
            Assert.Empty(project.Options.Profile.Actions[0].Steps);
            context.SaveChanges();
            Assert.Equal(project.Options.Profile.Actions[0].Steps[0], new ExecuteStep {
                Executable = "bun"
            });

            ((ExecuteStep)GetAction(context, "Debug").Steps[0]).Arguments = "--stuffed";
            Assert.Equal("", ((ExecuteStep)project.Options.Profile.Actions[0].Steps[0]).Arguments);
            context.SaveChanges();
            Assert.Equal("--stuffed", ((ExecuteStep)project.Options.Profile.Actions[0].Steps[0]).Arguments);
        }
        public void SaveChangesNameConflictResolutionCancelledTest()
        {
            var project      = CreateTestProject();
            var nameResolver = new Mock <ProfileOptionsWindowContext.AskProfileNameDelegate>(MockBehavior.Strict);
            var context      = new ProfileOptionsWindowContext(project, null, nameResolver.Object);

            context.SelectedProfile = GetDirtyProfile(context, "kana");
            var asa = GetDirtyProfile(context, "asa");

            asa.General.ProfileName = "kana";

            nameResolver.Setup(n => n("Rename", It.IsAny <string>(), It.IsAny <IEnumerable <string> >(), "kana")).Returns((string)null);

            Assert.Equal("Profile options were not saved.", ((Error)context.SaveChanges()).Message);

            // Profiles are unchanged
            Assert.Equal(2, project.Options.Profiles.Count);
            Assert.True(project.Options.Profiles.ContainsKey("kana"));
            Assert.True(project.Options.Profiles.ContainsKey("asa"));
        }
        public void SaveChangesPreservesSelectedPageTest()
        {
            var project = CreateTestProject();

            project.Options.Profiles["kana"].Actions.Add(new ActionProfileOptions {
                Name = "kana-action"
            });
            var context = new ProfileOptionsWindowContext(project, null, null);

            // a crude emulation of WPF control behavior when resetting dirty profiles on save
            context.DirtyProfiles.CollectionChanged += (s, e) => { if (e.Action == NotifyCollectionChangedAction.Reset)
                                                                   {
                                                                       context.SelectedPage = null;
                                                                   }
            };

            context.SelectedPage = GetAction(context, "kana-action");
            context.SaveChanges();

            Assert.IsType <ActionProfileOptions>(context.SelectedPage);
            Assert.Equal("kana-action", ((ActionProfileOptions)context.SelectedPage).Name);
        }