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

KSP-CKAN / CKAN / 17807868538

17 Sep 2025 07:01PM UTC coverage: 74.179% (+2.1%) from 72.067%
17807868538

Pull #4439

github

web-flow
Merge 8136ad1e9 into 263645d92
Pull Request #4439: Even more tests

5129 of 7278 branches covered (70.47%)

Branch coverage included in aggregate %.

17 of 28 new or added lines in 7 files covered. (60.71%)

3 existing lines in 1 file now uncovered.

11051 of 14534 relevant lines covered (76.04%)

1.56 hits per line

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

57.14
/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
        {
2✔
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
        {
2✔
86
        }
2✔
87

88
        public ModChange(CkanModule                   mod,
2✔
89
                         GUIModChangeType             changeType,
90
                         IEnumerable<SelectionReason> reasons,
91
                         IConfiguration               config)
92
        {
2✔
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)
NEW
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
                          bool             userReinstall,
145
                          bool             metadataChanged,
146
                          IConfiguration   config)
147
            : base(mod, GUIModChangeType.Update, config)
×
148
        {
×
149
            this.targetMod       = targetMod;
×
150
            this.userReinstall   = userReinstall;
×
151
            this.metadataChanged = metadataChanged;
×
152
        }
×
153

154
        public override string NameAndStatus(NetModuleCache cache)
NEW
155
            => cache.DescribeAvailability(config, targetMod);
×
156

157
        public override string Description
158
            => IsReinstall
×
159
                ? userReinstall ? Properties.Resources.MainChangesetReinstallUser
160
                                : metadataChanged ? Properties.Resources.MainChangesetReinstallMetadataChanged
161
                                                  : Properties.Resources.MainChangesetReinstallMissing
162
                : string.Format(Properties.Resources.MainChangesetUpdateSelected,
163
                                targetMod.version);
164

165
        /// <summary>
166
        /// The target version for upgrading
167
        /// </summary>
168
        public CkanModule targetMod;
169

170
        private bool IsReinstall
171
            => targetMod.identifier == Mod.identifier
×
172
                && targetMod.version == Mod.version;
173

174
        private readonly bool userReinstall;
175
        private readonly bool metadataChanged;
176
    }
177
}
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