• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

KSP-CKAN / CKAN / 29608480056

17 Jul 2026 07:41PM UTC coverage: 87.836% (-0.004%) from 87.84%
29608480056

Pull #4695

github

web-flow
Merge 68af4aa6a into 0daeea8d7
Pull Request #4695: Fix freeze on loading installed modules

2018 of 2144 branches covered (94.12%)

Branch coverage included in aggregate %.

12 of 14 new or added lines in 4 files covered. (85.71%)

1 existing line in 1 file now uncovered.

8633 of 9982 relevant lines covered (86.49%)

1.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.0
/GUI/Model/ModChange.cs
1
using System;
2
using System.Collections.Generic;
3
using System.Linq;
4
using System.ComponentModel.DataAnnotations;
5

6
#if NET5_0_OR_GREATER
7
using System.Runtime.Versioning;
8
#endif
9

10
using CKAN.Configuration;
11
using CKAN.Extensions;
12

13
namespace CKAN.GUI
14
{
15
    public enum GUIModChangeType
16
    {
17
        [Display(Name         = "ChangeTypeNone",
18
                 Description  = "ChangeTypeNone",
19
                 ResourceType = typeof(Properties.Resources))]
20
        None    = 0,
21

22
        [Display(Name         = "ChangeTypeInstall",
23
                 Description  = "ChangeTypeInstall",
24
                 ResourceType = typeof(Properties.Resources))]
25
        Install = 1,
26

27
        [Display(Name         = "ChangeTypeRemove",
28
                 Description  = "ChangeTypeRemove",
29
                 ResourceType = typeof(Properties.Resources))]
30
        Remove  = 2,
31

32
        [Display(Name         = "ChangeTypeUpdate",
33
                 Description  = "ChangeTypeUpdate",
34
                 ResourceType = typeof(Properties.Resources))]
35
        Update  = 3,
36

37
        [Display(Name         = "ChangeTypeReplace",
38
                 Description  = "ChangeTypeReplace",
39
                 ResourceType = typeof(Properties.Resources))]
40
        Replace = 4,
41
    }
42

43
    /// <summary>
44
    /// Everything the GUI needs to know about a change, including
45
    /// the mod itself, the change we're making, and the reason why.
46
    /// </summary>
47
    #if NET5_0_OR_GREATER
48
    [SupportedOSPlatform("windows")]
49
    #endif
50
    public class ModChange
51
    {
52
        public CkanModule        Mod        { get; private set; }
53
        public GUIModChangeType  ChangeType { get; private set; }
54
        public SelectionReason[] Reasons    { get; private set; }
55

56
        /// <summary>
57
        /// true if the reason for this change is that an installed dependency is no longer needed,
58
        /// false otherwise
59
        /// </summary>
60
        public readonly bool IsAutoRemoval;
61

62
        /// <summary>
63
        /// true if this change is user requested and no other changes depend on it, false otherwise.
64
        /// </summary>
65
        public readonly bool IsUserRequested;
66

67
        /// <summary>
68
        /// true if this change can be removed from a changeset, false otherwise
69
        /// </summary>
70
        public bool IsRemovable => IsAutoRemoval || IsUserRequested;
×
71

72
        // If we don't have a Reason, the user probably wanted to install it
73
        public ModChange(CkanModule       mod,
74
                         GUIModChangeType changeType,
75
                         IConfiguration   config)
76
            : this(mod, changeType, new SelectionReason.UserRequested(), config)
2✔
77
        {
78
        }
2✔
79

80
        public ModChange(CkanModule       mod,
81
                         GUIModChangeType changeType,
82
                         SelectionReason  reason,
83
                         IConfiguration   config)
84
            : this(mod, changeType, Enumerable.Repeat(reason, 1), config)
2✔
85
        {
86
        }
2✔
87

88
        public ModChange(CkanModule                   mod,
2✔
89
                         GUIModChangeType             changeType,
90
                         IEnumerable<SelectionReason> reasons,
91
                         IConfiguration               config)
92
        {
93
            Mod        = mod;
2✔
94
            ChangeType = changeType;
2✔
95
            Reasons    = reasons.ToArray();
2✔
96
            this.config = config;
2✔
97
            IsAutoRemoval   = Reasons.All(r => r is SelectionReason.NoLongerUsed);
2✔
98
            IsUserRequested = Reasons.All(r => r is SelectionReason.UserRequested);
2✔
99
        }
2✔
100

101
        public override bool Equals(object? obj)
102
            => obj is not null
2✔
103
               && (ReferenceEquals(this, obj)
104
                   || (obj is ModChange ch
105
                       && ch.Mod.Equals(Mod)
106
                       && ch.ChangeType.Equals(ChangeType)));
107

108
        private static readonly int maxEnumVal = Enum.GetValues(typeof(GUIModChangeType)).Cast<int>().Max();
2✔
109

110
        public override int GetHashCode()
111
            // Distinguish between installing and removing
112
            => Mod == null
2✔
113
                ? 0
114
                : (((maxEnumVal + 1) * Mod.GetHashCode()) + (int)ChangeType);
115

116
        public override string ToString()
117
            => $"{ChangeType.LocalizeDescription()} {Mod} ({Description})";
2✔
118

119
        public virtual string NameAndStatus(NetModuleCache cache)
120
            => cache.DescribeAvailability(config, Mod);
×
121

122
        private static string DescribeGroup(IEnumerable<SelectionReason> reasons)
123
            => reasons.First().DescribeWith(reasons.Skip(1));
2✔
124

125
        public virtual string Description
126
            => string.Join("; ",
2✔
127
                Reasons.GroupBy(r => r.GetType(), (t, reasons) =>
2✔
128
                    DescribeGroup(
2✔
129
                        t.IsSubclassOf(typeof(SelectionReason.RelationshipReason))
130
                            ? reasons.OfType<SelectionReason.RelationshipReason>()
131
                                     .OrderBy(r => r.Parent.name)
×
132
                            : reasons)));
133

134
        protected IConfiguration config;
135
    }
136

137
    #if NET5_0_OR_GREATER
138
    [SupportedOSPlatform("windows")]
139
    #endif
140
    public class ModUpgrade : ModChange
141
    {
142
        public ModUpgrade(CkanModule     mod,
143
                          CkanModule     targetMod,
144
                          IConfiguration config)
145
            : base(mod, GUIModChangeType.Update, config)
2✔
146
        {
147
            this.targetMod = targetMod;
2✔
148
        }
2✔
149

150
        public override string NameAndStatus(NetModuleCache cache)
151
            => cache.DescribeAvailability(config, targetMod);
×
152

153
        public override string Description
NEW
154
            => string.Format(Properties.Resources.MainChangesetUpdateSelected,
×
155
                             targetMod.version);
156

157
        /// <summary>
158
        /// The target version for upgrading
159
        /// </summary>
160
        public CkanModule targetMod;
161
    }
162

163
    #if NET5_0_OR_GREATER
164
    [SupportedOSPlatform("windows")]
165
    #endif
166
    public class ModReinstall : ModChange
167
    {
168
        public ModReinstall(CkanModule     mod,
169
                            bool           userReinstall,
170
                            bool           metadataChanged,
171
                            bool           reinstallFiles,
172
                            IConfiguration config)
173
            : base(mod, GUIModChangeType.Update, config)
2✔
174
        {
175
            this.userReinstall   = userReinstall;
2✔
176
            this.metadataChanged = metadataChanged;
2✔
177
            this.reinstallFiles  = reinstallFiles;
2✔
178
        }
2✔
179

180
        private readonly bool userReinstall;
181
        private readonly bool metadataChanged;
182
        private readonly bool reinstallFiles;
183

184
        public override string Description
185
            => userReinstall   ? Properties.Resources.MainChangesetReinstallUser
2✔
186
             : metadataChanged ? Properties.Resources.MainChangesetReinstallMetadataChanged
187
             :                   Properties.Resources.MainChangesetReinstallMissing;
188

189
        public bool SkipReinstallingFiles
190
            => !userReinstall && metadataChanged && !reinstallFiles;
2✔
191
    }
192
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc