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

KSP-CKAN / CKAN / 15889942513

26 Jun 2025 12:20AM UTC coverage: 47.631% (+5.4%) from 42.239%
15889942513

push

github

HebaruSan
Merge #4400 Make hash caches thread-safe, Netkan warning for uncompiled plugins

3880 of 8730 branches covered (44.44%)

Branch coverage included in aggregate %.

33 of 75 new or added lines in 9 files covered. (44.0%)

6 existing lines in 3 files now uncovered.

8334 of 16913 relevant lines covered (49.28%)

1.01 hits per line

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

28.17
/Netkan/Validators/InstallsFilesValidator.cs
1
using System;
2
using System.Linq;
3

4
using log4net;
5

6
using CKAN.NetKAN.Model;
7
using CKAN.NetKAN.Services;
8
using CKAN.Extensions;
9
using CKAN.Games;
10

11
namespace CKAN.NetKAN.Validators
12
{
13
    internal sealed class InstallsFilesValidator : IValidator
14
    {
15
        private readonly IHttpService _http;
16
        private readonly IModuleService _moduleService;
17
        private readonly IGame _game;
18

19
        public InstallsFilesValidator(IHttpService http, IModuleService moduleService, IGame game)
2✔
20
        {
2✔
21
            _http = http;
2✔
22
            _moduleService = moduleService;
2✔
23
            _game = game;
2✔
24
        }
2✔
25

26
        public void Validate(Metadata metadata)
27
        {
2✔
28
            var mod = CkanModule.FromJson(metadata.AllJson.ToString());
2✔
29
            if (!mod.IsDLC && _http.DownloadModule(metadata) is string file)
2✔
30
            {
2✔
31
                // Make sure this would actually generate an install.
32
                if (!_moduleService.HasInstallableFiles(mod, file))
2!
33
                {
2✔
34
                    throw new Kraken(string.Format(
2✔
35
                        "Module contains no files matching: {0}",
36
                        mod.DescribeInstallStanzas(_game)));
37
                }
38

39
                // Get the files the module will install
40
                var allFiles = _moduleService.FileDestinations(mod, file).Memoize();
×
41

42
                // Make sure no paths include GameData other than at the start
43
                foreach (var dir in Enumerable.Repeat(_game.PrimaryModDirectoryRelative, 1)
×
44
                                              .Concat(_game.AlternateModDirectoriesRelative))
45
                {
×
46
                    var gamedatas = allFiles
×
47
                        .Where(p => p.StartsWith(dir, StringComparison.InvariantCultureIgnoreCase)
×
48
                                    && p.LastIndexOf($"/{dir}/", StringComparison.InvariantCultureIgnoreCase) > 0)
49
                        .OrderBy(f => f)
×
50
                        .ToList();
51
                    if (gamedatas.Count != 0)
×
52
                    {
×
53
                        var badPaths = string.Join("\r\n", gamedatas);
×
54
                        throw new Kraken($"{dir} directory found within {dir}:\r\n{badPaths}");
×
55
                    }
56
                }
×
57

58
                // Make sure we won't try to overwrite our own files
59
                var duplicates = allFiles
×
60
                    .GroupBy(f => f)
×
61
                    .SelectMany(grp => grp.Skip(1).OrderBy(f => f))
×
62
                    .ToList();
63
                if (duplicates.Count != 0)
×
64
                {
×
65
                    var badPaths = string.Join("\r\n", duplicates);
×
66
                    throw new Kraken($"Multiple files attempted to install to:\r\n{badPaths}");
×
67
                }
68

69
                // Not a perfect check (subject to false negatives)
70
                // but better than nothing
71
                if (mod.install != null)
×
72
                {
×
73
                    var unmatchedIncludeOnlys = mod.install
×
74
                        .SelectMany(stanza => stanza.include_only ?? Enumerable.Empty<string>())
×
75
                        .Distinct()
76
                        .Where(incl => !allFiles.Any(f => f.Contains(incl)))
×
77
                        .ToList();
78
                    if (unmatchedIncludeOnlys.Count != 0)
×
79
                    {
×
NEW
80
                        log.WarnFormat("No matches for include_only: {0}",
×
81
                                       string.Join(", ", unmatchedIncludeOnlys));
82
                    }
×
83
                }
×
84
            }
×
85
        }
2✔
86

87
        private static readonly ILog log = LogManager.GetLogger(typeof(InstallsFilesValidator));
2✔
88
    }
89
}
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