public void GetsInternalAvcCorrectly() { //Arrange var json = new JObject(); json["spec_version"] = 1; json["identifier"] = "DogeCoinFlag"; json["version"] = "1.0.0"; json["download"] = "https://awesomemod.example/AwesomeMod.zip"; var sut = new ModuleService(); // Act var result = sut.GetInternalAvc(CkanModule.FromJson(json.ToString()), TestData.DogeCoinFlagAvcZip()); // Assert Assert.That(result, Is.Not.Null, "ModuleService should get an internal AVC file." ); Assert.That(result.version, Is.EqualTo(new Version("1.1.0")), "ModuleService should get correct version from the internal AVC file." ); Assert.That(result.ksp_version, Is.EqualTo(new KSPVersion("0.24.2")), "ModuleService should get correct ksp_version from the internal AVC file." ); Assert.That(result.ksp_version_min, Is.EqualTo(new KSPVersion("0.24.0")), "ModuleService should get correct ksp_version_min from the internal AVC file." ); Assert.That(result.ksp_version_max, Is.EqualTo(new KSPVersion("0.24.2")), "ModuleService should get correct ksp_version_max from the internal AVC file." ); }
public void IsConsistent_MultipleVersionsOfSelfProvidesConflictingModule_Consistent() { // Arrange List <CkanModule> modules = new List <CkanModule>() { CkanModule.FromJson(@"{ ""identifier"": ""provides-conflictor"", ""version"": ""1.0.0"", ""download"": ""https://kerbalstuff.com/mod/269/Dogecoin%20Flag/download/1.01"", ""provides"": [ ""providee"" ], ""conflicts"": [ { ""name"": ""providee"" } ] }"), CkanModule.FromJson(@"{ ""identifier"": ""provides-conflictor"", ""version"": ""1.2.3"", ""download"": ""https://kerbalstuff.com/mod/269/Dogecoin%20Flag/download/1.01"", ""provides"": [ ""providee"" ], ""conflicts"": [ { ""name"": ""providee"" } ] }") }; // Act & Assert Assert.IsTrue(CKAN.SanityChecker.IsConsistent(modules)); }
public void Validate(Metadata metadata) { Log.Info("Validating that metadata dependencies are consistent with cfg file syntax"); JObject json = metadata.Json(); CkanModule mod = CkanModule.FromJson(json.ToString()); if (!mod.IsDLC) { var package = _http.DownloadModule(metadata); if (!string.IsNullOrEmpty(package)) { ZipFile zip = new ZipFile(package); var mmConfigs = _moduleService.GetConfigFiles(mod, zip) .Where(cfg => moduleManagerRegex.IsMatch( new StreamReader(zip.GetInputStream(cfg.source)).ReadToEnd())) .Memoize(); bool dependsOnMM = mod?.depends?.Any(r => r.ContainsAny(identifiers)) ?? false; if (!dependsOnMM && mmConfigs.Any()) { Log.WarnFormat( "ModuleManager syntax used without ModuleManager dependency: {0}", string.Join(", ", mmConfigs.Select(cfg => cfg.source)) ); } else if (dependsOnMM && !mmConfigs.Any()) { Log.Warn("ModuleManager dependency may not be needed, no ModuleManager syntax found"); } } } }
public void HasUpdate_WithUpgradeableManuallyInstalledMod_ReturnsTrue() { // Arrange using (var gameInstWrapper = new DisposableKSP()) { CkanModule mod = CkanModule.FromJson(@"{ ""spec_version"": ""v1.4"", ""identifier"": ""AutoDetectedMod"", ""version"": ""1.0"", ""ksp_version"": ""1.11.1"", ""download"": ""https://mymods/AD/1.0"" }"); registry.AddAvailable(mod); GameInstance gameInst = gameInstWrapper.KSP; registry.RegisterDll(gameInst, Path.Combine( gameInst.GameDir(), "GameData", $"{mod.identifier}.dll")); GameVersionCriteria crit = new GameVersionCriteria(mod.ksp_version); // Act bool has = registry.HasUpdate(mod.identifier, crit); // Assert Assert.IsTrue(has, "Can't upgrade manually installed DLL"); } }
public void Validate(Metadata metadata) { Log.Info("Validating that craft files are installed into Ships"); JObject json = metadata.Json(); CkanModule mod = CkanModule.FromJson(json.ToString()); if (!mod.IsDLC) { var package = _http.DownloadModule(metadata); if (!string.IsNullOrEmpty(package)) { var zip = new ZipFile(package); var ksp = new KSP("/", "dummy", null, false); var badCrafts = _moduleService.GetCrafts(mod, zip, ksp) .Where(f => !AllowedCraftPath(ksp.ToRelativeGameDir(f.destination))) .ToList(); if (badCrafts.Any()) { Log.WarnFormat( "Craft files installed outside Ships folder: {0}", string.Join(", ", badCrafts.Select(f => ksp.ToRelativeGameDir(f.destination) )) ); } } } }
public void CompatibleModules_PastAndFutureCompatibility_ReturnsCurrentOnly() { // Arrange CkanModule modFor161 = CkanModule.FromJson(@"{ ""identifier"": ""TypicalMod"", ""version"": ""0.9.0"", ""download"": ""https://kerbalstuff.com/mod/269/Dogecoin%20Flag/download/1.01"", ""ksp_version"": ""1.6.1"" }"); CkanModule modFor173 = CkanModule.FromJson(@"{ ""identifier"": ""TypicalMod"", ""version"": ""1.0.0"", ""download"": ""https://kerbalstuff.com/mod/269/Dogecoin%20Flag/download/1.01"", ""ksp_version"": ""1.7.3"" }"); CkanModule modFor181 = CkanModule.FromJson(@"{ ""identifier"": ""TypicalMod"", ""version"": ""1.1.0"", ""download"": ""https://kerbalstuff.com/mod/269/Dogecoin%20Flag/download/1.01"", ""ksp_version"": ""1.8.1"" }"); registry.AddAvailable(modFor161); registry.AddAvailable(modFor173); registry.AddAvailable(modFor181); // Act GameVersionCriteria v173 = new GameVersionCriteria(GameVersion.Parse("1.7.3")); List <CkanModule> compat = registry.CompatibleModules(v173).ToList(); // Assert Assert.IsFalse(compat.Contains(modFor161)); Assert.IsTrue(compat.Contains(modFor173)); Assert.IsFalse(compat.Contains(modFor181)); }
public void Validate(Metadata metadata) { Log.Info("Validating that metadata compatibility is appropriate for DLLs"); JObject json = metadata.Json(); CkanModule mod = CkanModule.FromJson(json.ToString()); if (!mod.IsDLC) { var package = _http.DownloadModule(metadata); if (!string.IsNullOrEmpty(package)) { ZipFile zip = new ZipFile(package); bool hasPlugin = _moduleService.GetPlugins(mod, zip).Any(); bool boundedCompatibility = json.ContainsKey("ksp_version") || json.ContainsKey("ksp_version_max"); if (hasPlugin && !boundedCompatibility) { Log.Warn("Unbounded future compatibility for module with a plugin, consider setting $vref or ksp_version or ksp_version_max"); } } } }
public void KSPCompatibility_OutOfOrderGameVersions_TrueMaxVersion() { using (var tidy = new DisposableKSP()) { // Arrange CkanModule mainVersion = CkanModule.FromJson(@"{ ""identifier"": ""OutOfOrderMod"", ""version"": ""1.2.0"", ""ksp_version"": ""0.90"", ""download"": ""http://www.ksp-ckan.org"" }"); CkanModule prevVersion = CkanModule.FromJson(@"{ ""identifier"": ""OutOfOrderMod"", ""version"": ""1.1.0"", ""ksp_version"": ""1.4.2"", ""download"": ""http://www.ksp-ckan.org"" }"); Registry registry = Registry.Empty(); registry.AddAvailable(mainVersion); registry.AddAvailable(prevVersion); // Act GUIMod m = new GUIMod(mainVersion, registry, tidy.KSP.VersionCriteria(), false); // Assert Assert.AreEqual("1.4.2", m.KSPCompatibility); } }
public void MetaData() { CkanModule module = CkanModule.FromJson(TestData.kOS_014()); // TODO: Test all the metadata here! Assert.AreEqual("https://github.com/KSP-KOS/KOS/issues", module.resources.bugtracker.ToString()); }
public void Validate(Metadata metadata) { JObject json = metadata.Json(); CkanModule mod = CkanModule.FromJson(json.ToString()); // The Harmony2 module is allowed to install a Harmony DLL; // anybody else must have "provides":["Harmony1"] to do so if (!mod.IsDLC && mod.identifier != "Harmony2") { // Need to peek at the mod's files var package = _http.DownloadModule(metadata); if (!string.IsNullOrEmpty(package)) { ZipFile zip = new ZipFile(package); GameInstance inst = new GameInstance(new KerbalSpaceProgram(), "/", "dummy", new NullUser()); var harmonyDLLs = _moduleService.GetPlugins(mod, zip, inst) .Select(instF => instF.source.Name) .Where(f => f.IndexOf("Harmony", StringComparison.InvariantCultureIgnoreCase) != -1) .ToList(); bool bundlesHarmony = harmonyDLLs.Any(); bool providesHarmony1 = mod.ProvidesList.Contains("Harmony1"); if (bundlesHarmony && !providesHarmony1) { throw new Kraken($"Harmony DLL found at {string.Join(", ", harmonyDLLs)}, but Harmony1 is not in the provides list"); } else if (providesHarmony1 && !bundlesHarmony) { Log.Warn("Harmony1 provided by module that doesn't install a Harmony DLL, consider removing from provides list"); } } } }
public void Validate(Metadata metadata) { var mod = CkanModule.FromJson(metadata.Json().ToString()); var file = _http.DownloadPackage(metadata.Download, metadata.Identifier, metadata.RemoteTimestamp); // Make sure this would actually generate an install. if (!_moduleService.HasInstallableFiles(mod, file)) { throw new Kraken(string.Format( "Module contains no files matching: {0}", mod.DescribeInstallStanzas() )); } // Make sure no paths include GameData other than at the start var gamedatas = _moduleService.FileDestinations(mod, file) .Where(p => p.StartsWith("GameData") && p.LastIndexOf("/GameData/") > 0) .ToList(); if (gamedatas.Any()) { var badPaths = string.Join("\r\n", gamedatas); throw new Kraken($"GameData directory found within GameData:\r\n{badPaths}"); } }
public void multilicense_986() { CkanModule mod = CkanModule.FromJson(TestData.kOS_014_multilicense()); Assert.AreEqual(2, mod.license.Count, "Dual-license"); Assert.AreEqual("GPL-3.0", mod.license[0].ToString()); Assert.AreEqual("GPL-2.0", mod.license[1].ToString()); }
public void FutureModule() { // Modules from the future are unsupported. Assert.Throws <UnsupportedKraken>(delegate { CkanModule.FromJson(TestData.FutureMetaData()); }); }
public void Validate(Metadata metadata) { var mod = CkanModule.FromJson(metadata.Json().ToString()); if (!mod.IsCompatibleKSP(new KspVersionCriteria(null, buildMap.KnownVersions))) { throw new Kraken($"{metadata.Identifier} doesn't match any valid game version"); } }
public void good_resource_1208() { CkanModule mod = CkanModule.FromJson(TestData.kOS_014()); Assert.AreEqual( "http://forum.kerbalspaceprogram.com/threads/68089-0-23-kOS-Scriptable-Autopilot-System-v0-11-2-13", mod.resources.homepage.ToString() ); }
public void Validate(Metadata metadata) { Log.Info("Validating that metadata vref is consistent with download contents"); JObject json = metadata.Json(); var noVersion = metadata.Version == null; if (noVersion) { json["version"] = "0"; } var mod = CkanModule.FromJson(json.ToString()); if (noVersion) { json.Remove("version"); } if (!mod.IsDLC) { var file = _http.DownloadModule(metadata); if (!string.IsNullOrEmpty(file)) { bool hasVref = (metadata.Vref != null); bool hasVersionFile = false; try { // Pass a regex that matches anything so it returns the first if found var avc = _moduleService.GetInternalAvc(mod, file, "."); hasVersionFile = (avc != null); } catch (BadMetadataKraken k) { // This means the install stanzas don't match any files. // That's not our problem; someone else will report it. } catch (Kraken k) { // If GetInternalAvc throws anything else, then there's a version file with a syntax error. // This shouldn't cause the inflation to fail. hasVersionFile = true; Log.Warn(k.Message); } if (hasVref && !hasVersionFile) { Log.Warn("$vref present, version file missing"); } else if (!hasVref && hasVersionFile) { Log.Warn("$vref absent, version file present"); } } } }
public void FilterRead() { CkanModule module = CkanModule.FromJson(TestData.DogeCoinFlag_101()); // Assert known things about this mod. Assert.IsNotNull(module.install[0].filter); Assert.IsNotNull(module.install[0].filter_regexp); Assert.AreEqual(2, module.install[0].filter.Count); }
public void StandardName() { CkanModule module = CkanModule.FromJson(TestData.kOS_014()); Assert.AreEqual(module.StandardName(), "kOS-0.14.zip"); CkanModule module2 = CkanModule.FromJson(TestData.kOS_014_with_invalid_version_characters()); Assert.AreEqual(module2.StandardName(), "kOS-0-14-0.zip"); }
internal static JObject HTTP(JObject orig_metadata, string remote_id, NetFileCache cache, IUser user) { var metadata = orig_metadata; // Check if we should auto-inflate. string kref = (string)metadata[expand_token]; if (kref == (string)orig_metadata[expand_token] || kref == "#/ckan/http") { log.InfoFormat("Inflating from HTTP download... {0}", metadata[expand_token]); metadata["download"] = remote_id; metadata["version"] = "0.0.0"; // add a dummy version that will be replaced by the KSP-AVC parser later metadata.Remove(expand_token); var remote_uri = new Uri(remote_id); var downloaded_file = Net.Download(remote_uri); var module = CkanModule.FromJson(metadata.ToString()); if (metadata[version_token] != null && (metadata[version_token].ToString()).StartsWith("#/ckan/ksp-avc")) { // TODO pass the correct vref here... var versionRemote = FindVersionRemote(metadata, metadata[version_token].ToString()); metadata.Remove(version_token); try { AVC avc = AVC.FromZipFile(module, downloaded_file, versionRemote.id); avc.InflateMetadata(metadata, null, null); metadata["version"] = avc.version.ToString(); module.version = avc.version; } catch (JsonReaderException) { user.RaiseMessage("Bad embedded KSP-AVC file for {0}, halting.", module); return(null); } // If we've done this, we need to re-inflate our mod, too. module = CkanModule.FromJson(metadata.ToString()); } else { throw new Kraken("No $vref specified, $kref HTTP method requires it, bailing out.."); } ModuleInstaller.CachedOrDownload(module.identifier, module.version, module.download, cache); } else { log.WarnFormat("Not inflating metadata for {0}", orig_metadata["identifier"]); } // metadata["download"] = metadata["download"].ToString() + '#' + metadata["version"].ToString(); return(metadata); }
public void bad_resource_1208() { JObject metadata = JObject.Parse(TestData.kOS_014()); // Guess which string totally isn't a valid Url? This one. metadata["resources"]["homepage"] = "https://included%in%the%download"; CkanModule mod = CkanModule.FromJson(metadata.ToString()); Assert.IsNotNull(mod); Assert.IsNull(mod.resources.homepage); }
public void Validate(Metadata metadata) { var mod = CkanModule.FromJson(metadata.Json().ToString()); var file = _http.DownloadPackage(metadata.Download, metadata.Identifier); // Make sure this would actually generate an install. if (!_moduleService.HasInstallableFiles(mod, file)) { throw new Kraken("Module contains no files to install."); } }
public void FutureModule() { if (CKAN.Meta.ReleaseNumber() == null) { Assert.Inconclusive("Dev build"); } // Modules form the future are unsupported. Assert.Throws <UnsupportedKraken>(delegate { CkanModule.FromJson(TestData.FutureMetaData()); }); }
public void Validate(Metadata metadata) { var mod = CkanModule.FromJson(metadata.Json().ToString()); if (!mod.IsCompatibleKSP(new KspVersionCriteria(null, buildMap.KnownVersions))) { KspVersion minKsp = null, maxKsp = null; Registry.GetMinMaxVersions(new List <CkanModule>() { mod }, out _, out _, out minKsp, out maxKsp); throw new Kraken($"{metadata.Identifier} doesn't match any valid game version: {KspVersionRange.VersionSpan(minKsp, maxKsp)}"); } }
public void Validate(Metadata metadata) { var mod = CkanModule.FromJson(metadata.Json().ToString()); var file = _http.DownloadPackage(metadata.Download, metadata.Identifier, metadata.RemoteTimestamp); // Make sure this would actually generate an install. if (!_moduleService.HasInstallableFiles(mod, file)) { throw new Kraken(string.Format( "Module contains no files matching: {0}", mod.DescribeInstallStanzas() )); } }
public void HasInstallableFilesReturnsTrueWhenInstallableFiles() { // Arrange var zip = TestData.DogeCoinFlagZip(); var json = JObject.Parse(TestData.DogeCoinFlag_101()); var sut = new ModuleService(); // Act var result = sut.HasInstallableFiles(CkanModule.FromJson(json.ToString()), zip); // Assert Assert.IsTrue(result, "HasInstallableFiles() should return true when there are installable files." ); }
public void HasUpdate_OtherModDependsOnCurrent_ReturnsFalse() { // Arrange using (var gameInstWrapper = new DisposableKSP()) { CkanModule olderDepMod = CkanModule.FromJson(@"{ ""spec_version"": ""v1.4"", ""identifier"": ""DependencyMod"", ""version"": ""1.0"", ""ksp_version"": ""1.11.1"", ""download"": ""https://mymods/DM/1.0"" }"); CkanModule newerDepMod = CkanModule.FromJson(@"{ ""spec_version"": ""v1.4"", ""identifier"": ""DependencyMod"", ""version"": ""2.0"", ""ksp_version"": ""1.11.1"", ""download"": ""https://mymods/DM/2.0"" }"); CkanModule dependingMod = CkanModule.FromJson(@"{ ""spec_version"": ""v1.4"", ""identifier"": ""DependingMod"", ""version"": ""1.0"", ""ksp_version"": ""1.11.1"", ""download"": ""https://mymods/DM/2.0"", ""depends"": [ { ""name"": ""DependencyMod"", ""version"": ""1.0"" } ] }"); registry.AddAvailable(olderDepMod); registry.AddAvailable(newerDepMod); registry.AddAvailable(dependingMod); GameInstance gameInst = gameInstWrapper.KSP; registry.RegisterModule(olderDepMod, new string[0], gameInst, false); registry.RegisterModule(dependingMod, new string[0], gameInst, false); GameVersionCriteria crit = new GameVersionCriteria(olderDepMod.ksp_version); // Act bool has = registry.HasUpdate(olderDepMod.identifier, crit); // Assert Assert.IsFalse(has, "Upgrade allowed that would break another mod's dependency"); } }
/// <summary> /// Apply the locale transformation to the metadata /// </summary> /// <param name="metadata">Data about the module</param> /// <returns> /// Updated metadata with the `locales` property set /// </returns> public IEnumerable <Metadata> Transform(Metadata metadata, TransformOptions opts) { JObject json = metadata.Json(); if (json.ContainsKey(localizationsProperty)) { log.Debug("Localizations property already set, skipping"); // Already set, don't override (skips a bunch of file processing) yield return(metadata); } else { CkanModule mod = CkanModule.FromJson(json.ToString()); ZipFile zip = new ZipFile(_http.DownloadPackage( metadata.Download, metadata.Identifier, metadata.RemoteTimestamp )); log.Debug("Extracting locales"); // Extract the locale names from the ZIP's cfg files var locales = _moduleService.GetConfigFiles(mod, zip) .Select(cfg => new StreamReader(zip.GetInputStream(cfg.source)).ReadToEnd()) .SelectMany(contents => localizationRegex.Matches(contents).Cast <Match>() .Select(m => m.Groups["contents"].Value)) .SelectMany(contents => localeRegex.Matches(contents).Cast <Match>() .Where(m => m.Groups["contents"].Value.Contains("=")) .Select(m => m.Groups["locale"].Value)) .Distinct() .Memoize(); log.Debug("Locales extracted"); if (locales.Any()) { json.SafeAdd(localizationsProperty, new JArray(locales)); log.Debug("Localizations property set"); yield return(new Metadata(json)); } else { log.Debug("No localizations found"); yield return(metadata); } } }
public void AllowInstallsToScenarios() { // Bogus zip with example to install. var zip = ZipFile.Create(new MemoryStream()); zip.BeginUpdate(); zip.AddDirectory("saves"); zip.AddDirectory("saves/scenarios"); zip.Add(new ZipEntry("/saves/scenarios/AwesomeRace.sfs") { Size = 0, CompressedSize = 0 }); zip.CommitUpdate(); // NB: The spec version really would be "v1.14", but travis is sad when it sees // releases that don't exist yet. var mod = CkanModule.FromJson(@" { ""spec_version"": ""1"", ""identifier"": ""AwesomeMod"", ""version"": ""1.0.0"", ""download"": ""https://awesomemod.example/AwesomeMod.zip"", ""install"": [ { ""file"": ""saves/scenarios/AwesomeRace.sfs"", ""install_to"": ""Scenarios"" } ] }") ; List <InstallableFile> results; using (var ksp = new DisposableKSP()) { results = CKAN.ModuleInstaller.FindInstallableFiles(mod.install.First(), zip, ksp.KSP); Assert.AreEqual( CKAN.KSPPathUtils.NormalizePath( Path.Combine(ksp.KSP.GameDir(), "saves/scenarios/AwesomeRace.sfs") ), results.First().destination ); } }
public void HasInstallableFilesReturnsFalseWhenNoInstallableFiles() { // Arrange var zip = TestData.DogeCoinFlagZip(); var json = JObject.Parse(TestData.DogeCoinFlag_101()); json["install"][0]["file"] = "DOES_NOT_EXIST"; var sut = new ModuleService(); // Act var result = sut.HasInstallableFiles(CkanModule.FromJson(json.ToString()), zip); // Assert Assert.IsFalse(result, "HasInstallableFiles() should return false when there are no installable files." ); }
public void Validate(Metadata metadata) { Log.Info("Validating that metadata is appropriate for DLLs"); JObject json = metadata.Json(); CkanModule mod = CkanModule.FromJson(json.ToString()); if (!mod.IsDLC) { var package = _http.DownloadModule(metadata); if (!string.IsNullOrEmpty(package)) { ZipFile zip = new ZipFile(package); GameInstance inst = new GameInstance(new KerbalSpaceProgram(), "/", "dummy", new NullUser()); var plugins = _moduleService.GetPlugins(mod, zip, inst).ToList(); bool hasPlugin = plugins.Any(); if (hasPlugin) { var dllPaths = plugins .Select(f => inst.ToRelativeGameDir(f.destination)) .OrderBy(f => f) .ToList(); var dllIdentifiers = dllPaths .Select(p => inst.DllPathToIdentifier(p)) .Where(ident => !string.IsNullOrEmpty(ident) && !identifiersToIgnore.Contains(ident)) .ToHashSet(); if (dllIdentifiers.Any() && !dllIdentifiers.Contains(metadata.Identifier)) { Log.WarnFormat( "No plugin matching the identifier, manual installations won't be detected: {0}", string.Join(", ", dllPaths)); } bool boundedCompatibility = json.ContainsKey("ksp_version") || json.ContainsKey("ksp_version_max"); if (!boundedCompatibility) { Log.Warn("Unbounded future compatibility for module with a plugin, consider setting $vref or ksp_version or ksp_version_max"); } } } } }