├── .gitignore ├── CopyLocalFalse.txt ├── Makefile ├── ModuleManager.sln ├── ModuleManager.sln.DotSettings ├── ModuleManager.userprefs ├── ModuleManager ├── Cats │ ├── CatAnimator.cs │ ├── CatManager.cs │ ├── CatMover.cs │ └── CatOrbiter.cs ├── Collections │ ├── ArrayEnumerator.cs │ ├── ImmutableStack.cs │ ├── KeyValueCache.cs │ └── MessageQueue.cs ├── Command.cs ├── CommandParser.cs ├── CustomConfigsManager.cs ├── ExceptionIntercept │ └── InterceptLogHandler.cs ├── Extensions │ ├── ByteArrayExtensions.cs │ ├── ConfigNodeExtensions.cs │ ├── IBasicLoggerExtensions.cs │ ├── NodeStackExtensions.cs │ ├── StringExtensions.cs │ ├── UrlConfigExtensions.cs │ ├── UrlDirExtensions.cs │ └── UrlFileExtensions.cs ├── FatalErrorHandler.cs ├── FilePathRepository.cs ├── Fix16.cs ├── Logging │ ├── IBasicLogger.cs │ ├── ILogMessage.cs │ ├── LogMessage.cs │ ├── LogSplitter.cs │ ├── PrefixLogger.cs │ ├── QueueLogRunner.cs │ ├── QueueLogger.cs │ ├── StreamLogger.cs │ └── UnityLogger.cs ├── MMPatchLoader.cs ├── MMPatchRunner.cs ├── ModListGenerator.cs ├── ModuleManager.cs ├── ModuleManager.csproj ├── ModuleManagerTestRunner.cs ├── NeedsChecker.cs ├── NodeMatcher.cs ├── Operator.cs ├── OperatorParser.cs ├── Pass.cs ├── PatchApplier.cs ├── PatchContext.cs ├── PatchExtractor.cs ├── PatchList.cs ├── Patches │ ├── CopyPatch.cs │ ├── DeletePatch.cs │ ├── EditPatch.cs │ ├── IPatch.cs │ ├── InsertPatch.cs │ ├── PassSpecifiers │ │ ├── AfterPassSpecifier.cs │ │ ├── BeforePassSpecifier.cs │ │ ├── FinalPassSpecifier.cs │ │ ├── FirstPassSpecifier.cs │ │ ├── ForPassSpecifier.cs │ │ ├── IPassSpecifier.cs │ │ ├── InsertPassSpecifier.cs │ │ ├── LastPassSpecifier.cs │ │ └── LegacyPassSpecifier.cs │ ├── PatchCompiler.cs │ ├── ProtoPatch.cs │ └── ProtoPatchBuilder.cs ├── PostPatchLoader.cs ├── Progress │ ├── IPatchProgress.cs │ ├── PatchProgress.cs │ └── ProgressCounter.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── cat-1.png │ ├── cat-10.png │ ├── cat-11.png │ ├── cat-12.png │ ├── cat-2.png │ ├── cat-3.png │ ├── cat-4.png │ ├── cat-5.png │ ├── cat-6.png │ ├── cat-7.png │ ├── cat-8.png │ ├── cat-9.png │ └── rainbow2.png ├── ProtoUrlConfig.cs ├── Tags │ ├── Tag.cs │ ├── TagList.cs │ └── TagListParser.cs ├── Threading │ ├── BackgroundTask.cs │ ├── ITaskStatus.cs │ ├── TaskStatus.cs │ └── TaskStatusWrapper.cs ├── Utils │ ├── Counter.cs │ └── FileUtils.cs ├── copy_build.sh └── packages.config ├── ModuleManagerTests ├── Collections │ ├── ArrayEnumeratorTest.cs │ ├── ImmutableStackTest.cs │ ├── KeyValueCacheTest.cs │ └── MessageQueueTest.cs ├── CommandParserTest.cs ├── DummyTest.cs ├── Extensions │ ├── ByteArrayExtensionsTest.cs │ ├── ConfigNodeExtensionsTest.cs │ ├── IBasicLoggerExtensionsTest.cs │ ├── NodeStackExtensionsTest.cs │ ├── StringExtensionsTest.cs │ ├── UrlConfigExtensionsTest.cs │ ├── UrlDirExtensionsTest.cs │ └── UrlFileExtensionsTest.cs ├── InGameTestRunnerTest.cs ├── Logging │ ├── LogMessageTest.cs │ ├── LogSplitterTest.cs │ ├── PrefixLoggerTest.cs │ ├── QueueLogRunnerTest.cs │ ├── QueueLoggerTest.cs │ ├── StreamLoggerTest.cs │ └── UnityLoggerTest.cs ├── LoggingAssertionHelpers.cs ├── MMPatchLoaderTest.cs ├── ModuleManagerTests.csproj ├── NeedsCheckerTest.cs ├── NodeMatcherTest.cs ├── OperatorParserTest.cs ├── PassTest.cs ├── PatchApplierTest.cs ├── PatchExtractorTest.cs ├── PatchListTest.cs ├── Patches │ ├── CopyPatchTest.cs │ ├── DeletePatchTest.cs │ ├── EditPatchTest.cs │ ├── InsertPatchTest.cs │ ├── PassSpecifiers │ │ ├── AfterPassSpecifierTest.cs │ │ ├── BeforePassSpecifierTest.cs │ │ ├── FinalPassSpecifierTest.cs │ │ ├── FirstPassSpecifierTest.cs │ │ ├── ForPassSpecifierTest.cs │ │ ├── InsertPassSpecifierTest.cs │ │ ├── LastPassSpecifierTest.cs │ │ └── LegacyPassSpecifierTest.cs │ ├── PatchCompilerTest.cs │ └── ProtoPatchBuilderTest.cs ├── Progress │ └── PatchProgressTest.cs ├── Properties │ └── AssemblyInfo.cs ├── ProtoUrlConfigTest.cs ├── Tags │ ├── TagListParserTest.cs │ ├── TagListTest.cs │ └── TagTest.cs ├── Threading │ ├── BackgroundTaskTest.cs │ └── TaskStatusTest.cs ├── Utils │ └── CounterTest.cs ├── app.config └── packages.config ├── README.md ├── TestUtils ├── Properties │ └── AssemblyInfo.cs ├── TestConfigNode.cs ├── TestUtils.csproj └── UrlBuilder.cs ├── TestUtilsTests ├── DummyTest.cs ├── Properties │ └── AssemblyInfo.cs ├── TestConfigNodeTest.cs ├── TestUtilsTests.csproj ├── UrlBuilderTest.cs └── packages.config └── Tests ├── AltEdits.cfg ├── BadlyFormed.cfg ├── NameLessNode.cfg ├── Needs.cfg ├── NodeCopy.cfg ├── NodeCreate.cfg ├── NodeDelete.cfg ├── NodeEdit.cfg ├── NodeEditWildcard.cfg ├── NodeHas.cfg ├── NodeInsert.cfg ├── NodeReplace.cfg ├── ValueCopy.cfg ├── ValueCreate.cfg ├── ValueDelete.cfg ├── ValueEdit.cfg ├── ValueEmpty.cfg ├── ValueInsert.cfg └── ValueReplace.cfg /.gitignore: -------------------------------------------------------------------------------- 1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs) 2 | [Oo]bj/ 3 | 4 | # mstest test results 5 | TestResults 6 | 7 | ## Ignore Visual Studio temporary files, build results, and 8 | ## files generated by popular Visual Studio add-ons. 9 | 10 | # User-specific files 11 | *.suo 12 | *.user* 13 | *.sln.docstates 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Rr]elease/ 18 | x64/ 19 | *_i.c 20 | *_p.c 21 | *.ilk 22 | *.meta 23 | *.obj 24 | *.pch 25 | *.pdb 26 | *.pgc 27 | *.pgd 28 | *.rsp 29 | *.sbr 30 | *.tlb 31 | *.tli 32 | *.tlh 33 | *.tmp 34 | *.log 35 | *.vspscc 36 | *.vssscc 37 | .builds 38 | 39 | # Visual C++ cache files 40 | ipch/ 41 | *.aps 42 | *.ncb 43 | *.opensdf 44 | *.sdf 45 | 46 | # Visual Studio profiler 47 | *.psess 48 | *.vsp 49 | *.vspx 50 | 51 | # Guidance Automation Toolkit 52 | *.gpState 53 | 54 | # ReSharper is a .NET coding add-in 55 | _ReSharper* 56 | 57 | # NCrunch 58 | *.ncrunch* 59 | .*crunch*.local.xml 60 | 61 | # Installshield output folder 62 | [Ee]xpress 63 | 64 | # DocProject is a documentation generator add-in 65 | DocProject/buildhelp/ 66 | DocProject/Help/*.HxT 67 | DocProject/Help/*.HxC 68 | DocProject/Help/*.hhc 69 | DocProject/Help/*.hhk 70 | DocProject/Help/*.hhp 71 | DocProject/Help/Html2 72 | DocProject/Help/html 73 | 74 | # Click-Once directory 75 | publish 76 | 77 | # Publish Web Output 78 | *.Publish.xml 79 | 80 | # NuGet Packages Directory 81 | packages 82 | 83 | # Windows Azure Build Output 84 | csx 85 | *.build.csdef 86 | 87 | # Windows Store app package directory 88 | AppPackages/ 89 | 90 | # Others 91 | [Bb]in 92 | [Oo]bj 93 | sql 94 | TestResults 95 | [Tt]est[Rr]esult* 96 | *.Cache 97 | ClientBin 98 | [Ss]tyle[Cc]op.* 99 | ~$* 100 | *.dbmdl 101 | Generated_Code #added for RIA/Silverlight projects 102 | 103 | # Backup & report files from converting an old project file to a newer 104 | # Visual Studio version. Backup files are not needed, because we have git ;-) 105 | _UpgradeReport_Files/ 106 | Backup*/ 107 | UpgradeLog*.XML 108 | 109 | ModuleManager.csproj.user 110 | 111 | ModuleManager.*.dll 112 | 113 | .vs* 114 | -------------------------------------------------------------------------------- /CopyLocalFalse.txt: -------------------------------------------------------------------------------- 1 | Copy Local has been set to false for all dependencies. 2 | Remark by Jan Van der Haegen: This file has been added because Nuget would installs packages to the solution instead of the project if there are only PowerShell scripts... (So yes: it's safe to remove this file!) -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for building ModuleManager 2 | 3 | KSPDIR := ${HOME}/.local/share/Steam/SteamApps/common/Kerbal\ Space\ Program 4 | MANAGED := KSP_Data/Managed/ 5 | 6 | INCLUDEFILES := $(wildcard *.cs) \ 7 | $(wildcard Properties/*.cs) 8 | 9 | RESGEN2 := resgen2 10 | GMCS := /usr/bin/gmcs 11 | GIT := /usr/bin/git 12 | TAR := /usr/bin/tar 13 | ZIP := /usr/bin/zip 14 | 15 | all: build 16 | 17 | info: 18 | @echo "== ModuleManager Build Information ==" 19 | @echo " gmcs: ${GMCS}" 20 | @echo " git: ${GIT}" 21 | @echo " tar: ${TAR}" 22 | @echo " zip: ${ZIP}" 23 | @echo " KSP Data: ${KSPDIR}" 24 | @echo "=====================================" 25 | 26 | build: info 27 | mkdir -p build 28 | ${RESGEN2} -usesourcepath Properties/Resources.resx build/Resources.resources 29 | ${GMCS} -t:library -lib:${KSPDIR}/${MANAGED} \ 30 | -r:Assembly-CSharp,Assembly-CSharp-firstpass,UnityEngine \ 31 | -out:build/ModuleManager.dll \ 32 | -resource:build/Resources.resources,ModuleManager.Properties.Resources.resources \ 33 | ${INCLUDEFILES} 34 | 35 | tar.gz: build 36 | ${TAR} zcf ModuleManager-0.$(shell ${GIT} rev-list --count HEAD).g$(shell ${GIT} log -1 --format="%h").tar.gz build/ModuleManager.dll 37 | 38 | zip: build 39 | ${ZIP} -9 -r ModuleManager-0.$(shell ${GIT} rev-list --count HEAD).g$(shell ${GIT} log -1 --format="%h").zip build/ModuleManager.dll 40 | 41 | clean: 42 | @echo "Cleaning up build and package directories..." 43 | rm -rf build/ package/ 44 | 45 | install: build 46 | mkdir -p ${KSPDIR}/GameData/ 47 | cp build/ModuleManager.dll ${KSPDIR}/GameData/ 48 | 49 | uninstall: info 50 | rm -f ${KSPDIR}/GameData/ModuleManager.dll 51 | 52 | 53 | .PHONY : all info build tar.gz zip clean install uninstall 54 | -------------------------------------------------------------------------------- /ModuleManager.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26730.12 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModuleManager", "ModuleManager\ModuleManager.csproj", "{02C8E3AF-69F9-4102-AB60-DD6DE60662D3}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModuleManagerTests", "ModuleManagerTests\ModuleManagerTests.csproj", "{BC2A08C8-64EF-4823-A40B-8889C1CCFD75}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestUtils", "TestUtils\TestUtils.csproj", "{20EAAFE6-510D-4374-8D2F-6B52D0178E85}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestUtilsTests", "TestUtilsTests\TestUtilsTests.csproj", "{E695C11F-4217-4014-9B51-7232A654C205}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {02C8E3AF-69F9-4102-AB60-DD6DE60662D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {02C8E3AF-69F9-4102-AB60-DD6DE60662D3}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {02C8E3AF-69F9-4102-AB60-DD6DE60662D3}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {02C8E3AF-69F9-4102-AB60-DD6DE60662D3}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {BC2A08C8-64EF-4823-A40B-8889C1CCFD75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {BC2A08C8-64EF-4823-A40B-8889C1CCFD75}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {BC2A08C8-64EF-4823-A40B-8889C1CCFD75}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {BC2A08C8-64EF-4823-A40B-8889C1CCFD75}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {20EAAFE6-510D-4374-8D2F-6B52D0178E85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {20EAAFE6-510D-4374-8D2F-6B52D0178E85}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {20EAAFE6-510D-4374-8D2F-6B52D0178E85}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {20EAAFE6-510D-4374-8D2F-6B52D0178E85}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {E695C11F-4217-4014-9B51-7232A654C205}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {E695C11F-4217-4014-9B51-7232A654C205}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {E695C11F-4217-4014-9B51-7232A654C205}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {E695C11F-4217-4014-9B51-7232A654C205}.Release|Any CPU.Build.0 = Release|Any CPU 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | GlobalSection(ExtensibilityGlobals) = postSolution 41 | SolutionGuid = {4DCC10BA-6036-4DCF-A78B-086DF4487922} 42 | EndGlobalSection 43 | GlobalSection(MonoDevelopProperties) = preSolution 44 | StartupItem = ModuleManager.csproj 45 | outputpath = ..\..\Plugins 46 | EndGlobalSection 47 | EndGlobal 48 | -------------------------------------------------------------------------------- /ModuleManager.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True 3 | DO_NOT_SHOW 4 | DO_NOT_SHOW 5 | DO_NOT_SHOW 6 | DO_NOT_SHOW 7 | DO_NOT_SHOW 8 | DO_NOT_SHOW 9 | DO_NOT_SHOW 10 | DO_NOT_SHOW 11 | DO_NOT_SHOW 12 | FAR 13 | ISP 14 | KSP 15 | ME 16 | SFS 17 | UI 18 | <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb"><ExtraRule Prefix="" Suffix="" Style="aaBb" /></Policy> 19 | <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb"><ExtraRule Prefix="" Suffix="" Style="aaBb" /></Policy> 20 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="" Suffix="" Style="aaBb" /></Policy> 21 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="" Suffix="" Style="aaBb" /></Policy> 22 | GUI 23 | ID 24 | UV 25 | XZU 26 | KL 27 | SRB 28 | TS 29 | SPH 30 | VAB 31 | -------------------------------------------------------------------------------- /ModuleManager.userprefs: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /ModuleManager/Cats/CatAnimator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Diagnostics.CodeAnalysis; 3 | using UnityEngine; 4 | 5 | namespace ModuleManager.Cats 6 | { 7 | class CatAnimator : MonoBehaviour 8 | { 9 | 10 | public Sprite[] frames; 11 | public float secFrame = 0.07f; 12 | 13 | private SpriteRenderer spriteRenderer; 14 | private int spriteIdx; 15 | 16 | [SuppressMessage("CodeQuality", "IDE0051", Justification = "Called by Unity")] 17 | void Start() 18 | { 19 | spriteRenderer = GetComponent(); 20 | spriteRenderer.sortingOrder = 3; 21 | StartCoroutine(Animate()); 22 | } 23 | 24 | 25 | IEnumerator Animate() 26 | { 27 | if (frames.Length == 0) 28 | yield return null; 29 | 30 | WaitForSeconds yield = new WaitForSeconds(secFrame); 31 | 32 | while (true) 33 | { 34 | spriteIdx = (spriteIdx + 1) % frames.Length; 35 | spriteRenderer.sprite = frames[spriteIdx]; 36 | yield return yield; 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ModuleManager/Cats/CatMover.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | using UnityEngine; 3 | 4 | namespace ModuleManager.Cats 5 | { 6 | public class CatMover : MonoBehaviour 7 | { 8 | public Vector3 spos; 9 | 10 | public float vel = 5; 11 | private float offsetY; 12 | 13 | public TrailRenderer trail; 14 | private SpriteRenderer spriteRenderer; 15 | 16 | private int totalLenth = 100; 17 | private float activePos = 0; 18 | 19 | public float scale = 2; 20 | 21 | private const float time = 5; 22 | private const float trailTime = time / 4; 23 | 24 | private bool clearTrail = false; 25 | 26 | // Use this for initialization 27 | [SuppressMessage("CodeQuality", "IDE0051", Justification = "Called by Unity")] 28 | void Start() 29 | { 30 | trail = GetComponent(); 31 | trail.sortingOrder = 2; 32 | 33 | spriteRenderer = GetComponent(); 34 | 35 | offsetY = Mathf.FloorToInt(0.2f * Screen.height); 36 | 37 | spos.z = -1; 38 | 39 | totalLenth = (int) (Screen.width / time * trail.time) + 150; 40 | trail.time = trailTime; 41 | trail.widthCurve = new AnimationCurve(new Keyframe(0, trail.startWidth ), new Keyframe(0.7f, trail.startWidth), new Keyframe(1, trail.startWidth * 0.9f)); 42 | clearTrail = true; 43 | } 44 | 45 | [SuppressMessage("CodeQuality", "IDE0051", Justification = "Called by Unity")] 46 | void Update() 47 | { 48 | if (trail.time <= 0f) 49 | { 50 | trail.time = trailTime; 51 | } 52 | 53 | activePos += ((Screen.width / time) * Time.deltaTime); 54 | 55 | if (activePos > (Screen.width + totalLenth)) 56 | { 57 | activePos = -spriteRenderer.sprite.rect.width; 58 | clearTrail = true; 59 | } 60 | 61 | float f = 2f * Mathf.PI * (activePos) / (Screen.width * 0.5f); 62 | 63 | float heightOffset = Mathf.Sin(f) * (spriteRenderer.sprite.rect.height * scale); 64 | 65 | spos.x = activePos; 66 | spos.y = offsetY + heightOffset; 67 | 68 | transform.position = KSP.UI.UIMainCamera.Camera.ScreenToWorldPoint(spos); 69 | transform.rotation = Quaternion.Euler(0, 0, Mathf.Cos(f) * 0.25f * Mathf.PI * Mathf.Rad2Deg); 70 | 71 | if (clearTrail) 72 | { 73 | trail.Clear(); 74 | clearTrail = false; 75 | } 76 | 77 | } 78 | 79 | 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /ModuleManager/Collections/ArrayEnumerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | namespace ModuleManager.Collections 6 | { 7 | public struct ArrayEnumerator : IEnumerator 8 | { 9 | private readonly T[] array; 10 | private readonly int startIndex; 11 | private readonly int length; 12 | 13 | private int index; 14 | 15 | public ArrayEnumerator(params T[] array) : this(array, 0) { } 16 | 17 | public ArrayEnumerator(T[] array, int startIndex) : this(array, startIndex, (array?.Length ?? -1) - startIndex) { } 18 | 19 | public ArrayEnumerator(T[] array, int startIndex, int length) 20 | { 21 | this.array = array ?? throw new ArgumentNullException(nameof(array)); 22 | 23 | if (startIndex < 0) 24 | throw new ArgumentException($"must be non-negative (got {startIndex})", nameof(startIndex)); 25 | if (startIndex > array.Length) 26 | throw new ArgumentException( 27 | $"must be less than or equal to array length (array length {array.Length}, startIndex {startIndex})", 28 | nameof(startIndex) 29 | ); 30 | if (length < 0) 31 | throw new ArgumentException($"must be non-negative (got {length})", nameof(length)); 32 | if (startIndex + length > array.Length) 33 | throw new ArgumentException( 34 | $"must fit within the string (array length {array.Length}, startIndex {startIndex}, length {length})", 35 | nameof(length) 36 | ); 37 | 38 | this.startIndex = startIndex; 39 | this.length = length; 40 | index = startIndex - 1; 41 | } 42 | 43 | public T Current => array[index]; 44 | object IEnumerator.Current => Current; 45 | 46 | public void Dispose() { } 47 | 48 | public bool MoveNext() 49 | { 50 | index++; 51 | return index < startIndex + length; 52 | } 53 | 54 | public void Reset() 55 | { 56 | index = startIndex - 1; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /ModuleManager/Collections/ImmutableStack.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | namespace ModuleManager.Collections 6 | { 7 | public class ImmutableStack : IEnumerable 8 | { 9 | public struct Enumerator : IEnumerator 10 | { 11 | private readonly ImmutableStack head; 12 | private ImmutableStack currentStack; 13 | 14 | public Enumerator(ImmutableStack stack) 15 | { 16 | head = stack; 17 | currentStack = null; 18 | } 19 | 20 | public T Current => currentStack.value; 21 | object IEnumerator.Current => Current; 22 | 23 | public void Dispose() { } 24 | 25 | public bool MoveNext() 26 | { 27 | if (currentStack == null) 28 | { 29 | currentStack = head; 30 | return true; 31 | } 32 | else if (!currentStack.IsRoot) 33 | { 34 | currentStack = currentStack.parent; 35 | return true; 36 | } 37 | else 38 | { 39 | return false; 40 | } 41 | } 42 | 43 | public void Reset() => currentStack = null; 44 | } 45 | 46 | public readonly T value; 47 | public readonly ImmutableStack parent; 48 | 49 | public ImmutableStack(T value) 50 | { 51 | this.value = value; 52 | } 53 | 54 | private ImmutableStack(T value, ImmutableStack parent) 55 | { 56 | this.value = value; 57 | this.parent = parent; 58 | } 59 | 60 | public bool IsRoot => parent == null; 61 | public ImmutableStack Root => IsRoot? this : parent.Root; 62 | 63 | public int Depth => IsRoot ? 1 : parent.Depth + 1; 64 | 65 | public ImmutableStack Push(T newValue) 66 | { 67 | return new ImmutableStack(newValue, this); 68 | } 69 | 70 | public ImmutableStack Pop() 71 | { 72 | if (IsRoot) throw new InvalidOperationException("Cannot pop from the root of a stack"); 73 | return parent; 74 | } 75 | 76 | public ImmutableStack ReplaceValue(T newValue) => new ImmutableStack(newValue, parent); 77 | 78 | public Enumerator GetEnumerator() => new Enumerator(this); 79 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 80 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /ModuleManager/Collections/KeyValueCache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace ModuleManager.Collections 5 | { 6 | public class KeyValueCache 7 | { 8 | private readonly Dictionary dict = new Dictionary(); 9 | private readonly object lockObject = new object(); 10 | 11 | public TValue Fetch(TKey key, Func createValue) 12 | { 13 | if (createValue == null) throw new ArgumentNullException(nameof(createValue)); 14 | lock(lockObject) 15 | { 16 | if (dict.TryGetValue(key, out TValue value)) 17 | { 18 | return value; 19 | } 20 | else 21 | { 22 | TValue newValue = createValue(); 23 | dict.Add(key, newValue); 24 | return newValue; 25 | } 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ModuleManager/Collections/MessageQueue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | namespace ModuleManager.Collections 6 | { 7 | public interface IMessageQueue : IEnumerable 8 | { 9 | void Add(T value); 10 | IMessageQueue TakeAll(); 11 | } 12 | 13 | public class MessageQueue : IMessageQueue, IEnumerable 14 | { 15 | public sealed class Enumerator : IEnumerator 16 | { 17 | private readonly MessageQueue queue; 18 | private Node current; 19 | 20 | public Enumerator(MessageQueue queue) 21 | { 22 | this.queue = queue; 23 | } 24 | 25 | public T Current => current.value; 26 | object IEnumerator.Current => Current; 27 | 28 | public void Dispose() { } 29 | 30 | public bool MoveNext() 31 | { 32 | if (current == null) 33 | current = queue.head; 34 | else 35 | current = current.next; 36 | 37 | return current != null; 38 | } 39 | 40 | public void Reset() 41 | { 42 | current = null; 43 | } 44 | } 45 | 46 | private class Node 47 | { 48 | public Node next; 49 | public readonly T value; 50 | 51 | public Node(T value) 52 | { 53 | this.value = value; 54 | } 55 | } 56 | 57 | private readonly object lockObject = new object(); 58 | private Node head; 59 | private Node tail; 60 | 61 | public void Add(T value) 62 | { 63 | Node node = new Node(value); 64 | lock (lockObject) 65 | { 66 | if (head == null) 67 | { 68 | head = node; 69 | tail = node; 70 | } 71 | else 72 | { 73 | tail.next = node; 74 | tail = node; 75 | } 76 | } 77 | } 78 | 79 | public IMessageQueue TakeAll() 80 | { 81 | MessageQueue queue = new MessageQueue(); 82 | lock(lockObject) 83 | { 84 | queue.head = head; 85 | queue.tail = tail; 86 | head = null; 87 | tail = null; 88 | } 89 | return queue; 90 | } 91 | 92 | public Enumerator GetEnumerator() => new Enumerator(this); 93 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 94 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /ModuleManager/Command.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ModuleManager 4 | { 5 | public enum Command 6 | { 7 | Insert, 8 | 9 | Delete, 10 | 11 | Edit, 12 | 13 | Replace, 14 | 15 | Copy, 16 | 17 | Rename, 18 | 19 | Paste, 20 | 21 | Special, 22 | 23 | Create 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ModuleManager/CommandParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ModuleManager 4 | { 5 | public static class CommandParser 6 | { 7 | public static Command Parse(string name, out string valueName) 8 | { 9 | if (name.Length == 0) 10 | { 11 | valueName = string.Empty; 12 | return Command.Insert; 13 | } 14 | Command ret; 15 | switch (name[0]) 16 | { 17 | case '@': 18 | ret = Command.Edit; 19 | break; 20 | 21 | case '%': 22 | ret = Command.Replace; 23 | break; 24 | 25 | case '-': 26 | case '!': 27 | ret = Command.Delete; 28 | break; 29 | 30 | case '+': 31 | case '$': 32 | ret = Command.Copy; 33 | break; 34 | 35 | case '|': 36 | ret = Command.Rename; 37 | break; 38 | 39 | case '#': 40 | ret = Command.Paste; 41 | break; 42 | 43 | case '*': 44 | ret = Command.Special; 45 | break; 46 | 47 | case '&': 48 | ret = Command.Create; 49 | break; 50 | 51 | default: 52 | valueName = name; 53 | return Command.Insert; 54 | } 55 | valueName = name.Substring(1); 56 | return ret; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /ModuleManager/CustomConfigsManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using UnityEngine; 4 | 5 | using static ModuleManager.FilePathRepository; 6 | 7 | namespace ModuleManager 8 | { 9 | [KSPAddon(KSPAddon.Startup.SpaceCentre, false)] 10 | public class CustomConfigsManager : MonoBehaviour 11 | { 12 | internal void Start() 13 | { 14 | if (HighLogic.CurrentGame.Parameters.Career.TechTreeUrl != techTreeFile && File.Exists(techTreePath)) 15 | { 16 | Log("Setting modded tech tree as the active one"); 17 | HighLogic.CurrentGame.Parameters.Career.TechTreeUrl = techTreeFile; 18 | } 19 | } 20 | 21 | public static void Log(String s) 22 | { 23 | print("[CustomConfigsManager] " + s); 24 | } 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ModuleManager/ExceptionIntercept/InterceptLogHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Reflection; 6 | using UnityEngine; 7 | using Object = UnityEngine.Object; 8 | 9 | namespace ModuleManager.UnityLogHandle 10 | { 11 | class InterceptLogHandler : ILogHandler 12 | { 13 | private readonly ILogHandler baseLogHandler; 14 | private readonly List brokenAssemblies = new List(); 15 | private readonly int gamePathLength; 16 | 17 | public static string Warnings { get; private set; } = ""; 18 | 19 | public InterceptLogHandler(ILogHandler baseLogHandler) 20 | { 21 | this.baseLogHandler = baseLogHandler ?? throw new ArgumentNullException(nameof(baseLogHandler)); 22 | gamePathLength = Path.GetFullPath(KSPUtil.ApplicationRootPath).Length; 23 | } 24 | 25 | public void LogFormat(LogType logType, Object context, string format, params object[] args) 26 | { 27 | baseLogHandler.LogFormat(logType, context, format, args); 28 | } 29 | 30 | public void LogException(Exception exception, Object context) 31 | { 32 | baseLogHandler.LogException(exception, context); 33 | 34 | if (exception is ReflectionTypeLoadException ex) 35 | { 36 | string message = "Intercepted a ReflectionTypeLoadException. List of broken DLLs:\n"; 37 | try 38 | { 39 | var assemblies = ex.Types.Where(x => x != null).Select(x => x.Assembly).Distinct(); 40 | foreach (Assembly assembly in assemblies) 41 | { 42 | if (Warnings == "") 43 | { 44 | Warnings = "Mod(s) DLL that are not compatible with this version of KSP\n"; 45 | } 46 | string modInfo = assembly.GetName().Name + " " + assembly.GetName().Version + " " + 47 | assembly.Location.Remove(0, gamePathLength) + "\n"; 48 | if (!brokenAssemblies.Contains(assembly)) 49 | { 50 | brokenAssemblies.Add(assembly); 51 | Warnings += modInfo; 52 | } 53 | message += modInfo; 54 | } 55 | } 56 | catch (Exception e) 57 | { 58 | message += "Exception " + e.GetType().Name + " while handling the exception..."; 59 | } 60 | ModuleManager.Log(message); 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /ModuleManager/Extensions/ByteArrayExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ModuleManager.Extensions 4 | { 5 | public static class ByteArrayExtensions 6 | { 7 | public static string ToHex(this byte[] data) 8 | { 9 | if (data == null) throw new ArgumentNullException(nameof(data)); 10 | char[] result = new char[data.Length * 2]; 11 | 12 | for (int i = 0; i < data.Length; i++) 13 | { 14 | result[i * 2] = GetHexValue(data[i] / 16); 15 | result[i * 2 + 1] = GetHexValue(data[i] % 16); 16 | } 17 | 18 | return new string(result); 19 | } 20 | 21 | private static char GetHexValue(int i) 22 | { 23 | if (i < 10) 24 | return (char)(i + '0'); 25 | else 26 | return (char)(i - 10 + 'a'); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ModuleManager/Extensions/ConfigNodeExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace ModuleManager.Extensions 5 | { 6 | public static class ConfigNodeExtensions 7 | { 8 | public static void ShallowCopyFrom(this ConfigNode toNode, ConfigNode fromeNode) 9 | { 10 | toNode.ClearData(); 11 | foreach (ConfigNode.Value value in fromeNode.values) 12 | toNode.values.Add(value); 13 | foreach (ConfigNode node in fromeNode.nodes) 14 | toNode.nodes.Add(node); 15 | } 16 | 17 | // KSP implementation of ConfigNode.CreateCopy breaks with badly formed nodes (nodes with a blank name) 18 | public static ConfigNode DeepCopy(this ConfigNode from) 19 | { 20 | ConfigNode to = new ConfigNode(from.name); 21 | foreach (ConfigNode.Value value in from.values) 22 | to.AddValueSafe(value.name, value.value); 23 | foreach (ConfigNode node in from.nodes) 24 | { 25 | ConfigNode newNode = DeepCopy(node); 26 | to.nodes.Add(newNode); 27 | } 28 | return to; 29 | } 30 | 31 | public static void PrettyPrint(this ConfigNode node, ref StringBuilder sb, string indent) 32 | { 33 | if (sb == null) throw new ArgumentNullException(nameof(sb)); 34 | if (indent == null) indent = string.Empty; 35 | if (node == null) 36 | { 37 | sb.Append(indent + "\n"); 38 | return; 39 | } 40 | sb.AppendFormat("{0}{1}\n{2}{{\n", indent, node.name ?? "", indent); 41 | string newindent = indent + " "; 42 | if (node.values == null) 43 | { 44 | sb.AppendFormat("{0}\n", newindent); 45 | } 46 | else 47 | { 48 | foreach (ConfigNode.Value value in node.values) 49 | { 50 | if (value == null) 51 | sb.AppendFormat("{0}\n", newindent); 52 | else 53 | sb.AppendFormat("{0}{1} = {2}\n", newindent, value.name ?? "", value.value ?? ""); 54 | } 55 | } 56 | 57 | if (node.nodes == null) 58 | { 59 | sb.AppendFormat("{0}\n", newindent); 60 | } 61 | else 62 | { 63 | foreach (ConfigNode subnode in node.nodes) 64 | { 65 | subnode.PrettyPrint(ref sb, newindent); 66 | } 67 | } 68 | 69 | sb.AppendFormat("{0}}}\n", indent); 70 | } 71 | 72 | public static void AddValueSafe(this ConfigNode node, string name, string value) 73 | { 74 | node.values.Add(new ConfigNode.Value(name, value)); 75 | } 76 | 77 | public static void EscapeValuesRecursive(this ConfigNode theNode) 78 | { 79 | foreach (ConfigNode subNode in theNode.nodes) 80 | { 81 | subNode.EscapeValuesRecursive(); 82 | } 83 | 84 | foreach (ConfigNode.Value value in theNode.values) 85 | { 86 | value.value = value.value.Replace("\n", "\\n"); 87 | value.value = value.value.Replace("\r", "\\r"); 88 | value.value = value.value.Replace("\t", "\\t"); 89 | } 90 | } 91 | 92 | public static void UnescapeValuesRecursive(this ConfigNode theNode) 93 | { 94 | foreach (ConfigNode subNode in theNode.nodes) 95 | { 96 | subNode.UnescapeValuesRecursive(); 97 | } 98 | 99 | foreach (ConfigNode.Value value in theNode.values) 100 | { 101 | value.value = value.value.Replace("\\n", "\n"); 102 | value.value = value.value.Replace("\\r", "\r"); 103 | value.value = value.value.Replace("\\t", "\t"); 104 | } 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /ModuleManager/Extensions/IBasicLoggerExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | using ModuleManager.Logging; 4 | 5 | namespace ModuleManager.Extensions 6 | { 7 | public static class IBasicLoggerExtensions 8 | { 9 | public static void Info(this IBasicLogger logger, string message) => logger.Log(new LogMessage(LogType.Log, message)); 10 | public static void Warning(this IBasicLogger logger, string message) => logger.Log(new LogMessage(LogType.Warning, message)); 11 | public static void Error(this IBasicLogger logger, string message) => logger.Log(new LogMessage(LogType.Error, message)); 12 | 13 | public static void Exception(this IBasicLogger logger, Exception exception) 14 | { 15 | if (exception == null) throw new ArgumentNullException(nameof(exception)); 16 | logger.Log(new LogMessage(LogType.Exception, exception.ToString())); 17 | } 18 | 19 | public static void Exception(this IBasicLogger logger, string message, Exception exception) 20 | { 21 | if (message == null) throw new ArgumentNullException(nameof(message)); 22 | if (exception == null) throw new ArgumentNullException(nameof(exception)); 23 | logger.Log(new LogMessage(LogType.Exception, message + ": " + exception.ToString())); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ModuleManager/Extensions/NodeStackExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text; 4 | using NodeStack = ModuleManager.Collections.ImmutableStack; 5 | 6 | namespace ModuleManager.Extensions 7 | { 8 | public static class NodeStackExtensions 9 | { 10 | public static string GetPath(this NodeStack stack) 11 | { 12 | int length = stack.Sum(node => node.name.Length) + stack.Depth - 1; 13 | StringBuilder sb = new StringBuilder(length); 14 | 15 | foreach (ConfigNode node in stack) 16 | { 17 | string nodeName = node.name; 18 | sb.Insert(0, node.name); 19 | if (sb.Length < sb.Capacity) sb.Insert(0, '/'); 20 | } 21 | 22 | return sb.ToString(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ModuleManager/Extensions/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.RegularExpressions; 3 | 4 | namespace ModuleManager.Extensions 5 | { 6 | public static class StringExtensions 7 | { 8 | public static bool IsBracketBalanced(this string s) 9 | { 10 | int level = 0; 11 | foreach (char c in s) 12 | { 13 | if (c == '[') level++; 14 | else if (c == ']') level--; 15 | 16 | if (level < 0) return false; 17 | } 18 | return level == 0; 19 | } 20 | 21 | private static readonly Regex whitespaceRegex = new Regex(@"\s+"); 22 | 23 | public static string RemoveWS(this string withWhite) 24 | { 25 | return whitespaceRegex.Replace(withWhite, ""); 26 | } 27 | 28 | public static bool Contains(this string str, string value, out int index) 29 | { 30 | if (str == null) throw new ArgumentNullException(nameof(str)); 31 | if (value == null) throw new ArgumentNullException(nameof(value)); 32 | 33 | index = str.IndexOf(value, StringComparison.CurrentCultureIgnoreCase); 34 | return index != -1; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ModuleManager/Extensions/UrlConfigExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace ModuleManager.Extensions 5 | { 6 | public static class UrlConfigExtensions 7 | { 8 | public static string SafeUrl(this UrlDir.UrlConfig url) 9 | { 10 | if (url == null) return ""; 11 | 12 | string nodeName; 13 | 14 | if (!string.IsNullOrEmpty(url.type?.Trim())) 15 | { 16 | nodeName = url.type; 17 | } 18 | else if (url.type == null) 19 | { 20 | nodeName = ""; 21 | } 22 | else 23 | { 24 | nodeName = ""; 25 | } 26 | 27 | string parentUrl = null; 28 | 29 | if (url.parent != null) 30 | { 31 | try 32 | { 33 | parentUrl = url.parent.url; 34 | } 35 | catch 36 | { 37 | parentUrl = ""; 38 | } 39 | } 40 | 41 | if (parentUrl == null) 42 | return nodeName; 43 | else 44 | return parentUrl + "/" + nodeName; 45 | } 46 | 47 | public static string PrettyPrint(this UrlDir.UrlConfig config) 48 | { 49 | if (config == null) return ""; 50 | 51 | StringBuilder sb = new StringBuilder(); 52 | 53 | sb.Append(config.SafeUrl()); 54 | sb.Append('\n'); 55 | config.config.PrettyPrint(ref sb, " "); 56 | 57 | return sb.ToString(); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /ModuleManager/Extensions/UrlDirExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace ModuleManager.Extensions 5 | { 6 | public static class UrlDirExtensions 7 | { 8 | public static UrlDir.UrlFile Find(this UrlDir urlDir, string url) 9 | { 10 | if (urlDir == null) throw new ArgumentNullException(nameof(urlDir)); 11 | if (url == null) throw new ArgumentNullException(nameof(url)); 12 | string[] splits = url.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); 13 | 14 | UrlDir currentDir = urlDir; 15 | 16 | for (int i = 0; i < splits.Length - 1; i++) 17 | { 18 | currentDir = currentDir.children.FirstOrDefault(subDir => subDir.name == splits[i]); 19 | if (currentDir == null) return null; 20 | } 21 | 22 | string fileName = splits[splits.Length - 1]; 23 | string fileExtension = null; 24 | 25 | int idx = fileName.LastIndexOf('.'); 26 | 27 | if (idx > -1) 28 | { 29 | fileExtension = fileName.Substring(idx + 1); 30 | fileName = fileName.Substring(0, idx); 31 | } 32 | 33 | foreach (UrlDir.UrlFile file in currentDir.files) 34 | { 35 | if (file.name != fileName) continue; 36 | if (fileExtension != null && fileExtension != file.fileExtension) continue; 37 | return file; 38 | } 39 | 40 | return null; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ModuleManager/Extensions/UrlFileExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ModuleManager.Extensions 4 | { 5 | public static class UrlFileExtensions 6 | { 7 | public static string GetUrlWithExtension(this UrlDir.UrlFile urlFile) 8 | { 9 | return $"{urlFile.url}.{urlFile.fileExtension}"; 10 | } 11 | public static string GetNameWithExtension(this UrlDir.UrlFile urlFile) 12 | { 13 | return $"{urlFile.name}.{urlFile.fileExtension}"; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ModuleManager/FatalErrorHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace ModuleManager 5 | { 6 | public static class FatalErrorHandler 7 | { 8 | public static void HandleFatalError(string message) 9 | { 10 | try 11 | { 12 | PopupDialog.SpawnPopupDialog(new Vector2(0.5f, 0.5f), 13 | new Vector2(0.5f, 0.5f), 14 | new MultiOptionDialog( 15 | "ModuleManagerFatalError", 16 | $"ModuleManager has encountered a fatal error and KSP needs to close.\n\n{message}\n\nPlease see KSP's log for addtional details", 17 | "ModuleManager - Fatal Error", 18 | HighLogic.UISkin, 19 | new Rect(0.5f, 0.5f, 500f, 60f), 20 | new DialogGUIFlexibleSpace(), 21 | new DialogGUIHorizontalLayout( 22 | new DialogGUIFlexibleSpace(), 23 | new DialogGUIButton("Quit", Application.Quit, 140.0f, 30.0f, true), 24 | new DialogGUIFlexibleSpace() 25 | ) 26 | ), 27 | true, 28 | HighLogic.UISkin); 29 | } 30 | catch(Exception ex) 31 | { 32 | Debug.LogError("Exception while trying to create the fatal exception dialog"); 33 | Debug.LogException(ex); 34 | Application.Quit(); 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ModuleManager/FilePathRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace ModuleManager 5 | { 6 | internal static class FilePathRepository 7 | { 8 | internal static readonly string normalizedRootPath = Path.GetFullPath(KSPUtil.ApplicationRootPath); 9 | internal static readonly string cachePath = Path.Combine(normalizedRootPath, "GameData", "ModuleManager.ConfigCache"); 10 | 11 | internal static readonly string techTreeFile = Path.Combine("GameData", "ModuleManager.TechTree"); 12 | internal static readonly string techTreePath = Path.Combine(normalizedRootPath, techTreeFile); 13 | 14 | internal static readonly string physicsFile = Path.Combine("GameData", "ModuleManager.Physics"); 15 | internal static readonly string physicsPath = Path.Combine(normalizedRootPath, physicsFile); 16 | internal static readonly string defaultPhysicsPath = Path.Combine(normalizedRootPath, "Physics.cfg"); 17 | 18 | internal static readonly string partDatabasePath = Path.Combine(normalizedRootPath, "PartDatabase.cfg"); 19 | 20 | internal static readonly string shaPath = Path.Combine(normalizedRootPath, "GameData", "ModuleManager.ConfigSHA"); 21 | 22 | internal static readonly string logsDirPath = Path.Combine(normalizedRootPath, "Logs", "ModuleManager"); 23 | internal static readonly string logPath = Path.Combine(logsDirPath, "ModuleManager.log"); 24 | internal static readonly string patchLogPath = Path.Combine(logsDirPath, "MMPatch.log"); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ModuleManager/Fix16.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace ModuleManager 5 | { 6 | class Fix16 : LoadingSystem 7 | { 8 | [SuppressMessage("CodeQuality", "IDE0051", Justification = "Called by Unity")] 9 | private void Awake() 10 | { 11 | if (Instance != null) 12 | { 13 | DestroyImmediate(this); 14 | return; 15 | } 16 | 17 | Instance = this; 18 | DontDestroyOnLoad(gameObject); 19 | } 20 | 21 | private static Fix16 Instance { get; set; } 22 | 23 | private bool ready; 24 | 25 | private int count; 26 | 27 | private int current; 28 | 29 | private const int yieldStep = 20; 30 | 31 | public override bool IsReady() 32 | { 33 | return ready; 34 | } 35 | 36 | public override string ProgressTitle() 37 | { 38 | return "Fix 1.6.0 " + current + "/" + count; 39 | } 40 | 41 | public override float ProgressFraction() 42 | { 43 | return (float) current / count; 44 | } 45 | 46 | public override void StartLoad() 47 | { 48 | ready = false; 49 | 50 | count = PartLoader.LoadedPartsList.Count; 51 | 52 | StartCoroutine(DoFix()); 53 | } 54 | 55 | private IEnumerator DoFix() 56 | { 57 | int yieldCounter = 0; 58 | for (current = 0; current < count; current++) 59 | { 60 | AvailablePart avp = PartLoader.LoadedPartsList[current]; 61 | if (avp.partPrefab.dragModel == Part.DragModel.CUBE && !avp.partPrefab.DragCubes.Procedural && 62 | !avp.partPrefab.DragCubes.None && avp.partPrefab.DragCubes.Cubes.Count == 0) 63 | { 64 | DragCubeSystem.Instance.LoadDragCubes(avp.partPrefab); 65 | } 66 | 67 | if (yieldCounter++ >= yieldStep) 68 | { 69 | yieldCounter = 0; 70 | yield return null; 71 | } 72 | } 73 | 74 | ready = true; 75 | yield return null; 76 | } 77 | 78 | public override float LoadWeight() 79 | { 80 | return 0.1f; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /ModuleManager/Logging/IBasicLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ModuleManager.Logging 4 | { 5 | // Stripped down version of UnityEngine.ILogger 6 | public interface IBasicLogger 7 | { 8 | void Log(ILogMessage message); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ModuleManager/Logging/ILogMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace ModuleManager.Logging 5 | { 6 | public interface ILogMessage 7 | { 8 | LogType LogType { get; } 9 | DateTime Timestamp { get; } 10 | string Message { get; } 11 | string ToLogString(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ModuleManager/Logging/LogMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace ModuleManager.Logging 5 | { 6 | public class LogMessage : ILogMessage 7 | { 8 | private const string DATETIME_FORMAT_STRING = "HH:mm:ss.fff"; 9 | 10 | public LogType LogType { get; } 11 | public DateTime Timestamp { get; } 12 | public string Message { get; } 13 | 14 | public LogMessage(LogType logType, string message) 15 | { 16 | LogType = logType; 17 | Timestamp = DateTime.Now; 18 | Message = message ?? throw new ArgumentNullException(nameof(message)); 19 | } 20 | 21 | public LogMessage(ILogMessage logMessage, string newMessage) 22 | { 23 | if (logMessage == null) throw new ArgumentNullException(nameof(logMessage)); 24 | LogType = logMessage.LogType; 25 | Timestamp = logMessage.Timestamp; 26 | Message = newMessage ?? throw new ArgumentNullException(nameof(newMessage)); 27 | } 28 | 29 | public string ToLogString() 30 | { 31 | string prefix; 32 | if (LogType == LogType.Log) 33 | prefix = "LOG"; 34 | else if (LogType == LogType.Warning) 35 | prefix = "WRN"; 36 | else if (LogType == LogType.Error) 37 | prefix = "ERR"; 38 | else if (LogType == LogType.Assert) 39 | prefix = "AST"; 40 | else if (LogType == LogType.Exception) 41 | prefix = "EXC"; 42 | else 43 | prefix = "???"; 44 | 45 | return $"[{prefix} {Timestamp.ToString(DATETIME_FORMAT_STRING)}] {Message}"; 46 | } 47 | 48 | public override string ToString() 49 | { 50 | return $"[{GetType().FullName} LogType={LogType} Message={Message}]"; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ModuleManager/Logging/LogSplitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ModuleManager.Logging 4 | { 5 | public class LogSplitter : IBasicLogger 6 | { 7 | private readonly IBasicLogger logger1; 8 | private readonly IBasicLogger logger2; 9 | 10 | public LogSplitter(IBasicLogger logger1, IBasicLogger logger2) 11 | { 12 | this.logger1 = logger1 ?? throw new ArgumentNullException(nameof(logger1)); 13 | this.logger2 = logger2 ?? throw new ArgumentNullException(nameof(logger2)); 14 | } 15 | 16 | public void Log(ILogMessage message) 17 | { 18 | if (message == null) throw new ArgumentNullException(nameof(message)); 19 | logger1.Log(message); 20 | logger2.Log(message); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ModuleManager/Logging/PrefixLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ModuleManager.Logging 4 | { 5 | public class PrefixLogger : IBasicLogger 6 | { 7 | private readonly string prefix; 8 | private readonly IBasicLogger logger; 9 | 10 | public PrefixLogger(string prefix, IBasicLogger logger) 11 | { 12 | if (string.IsNullOrEmpty(prefix)) throw new ArgumentNullException(nameof(prefix)); 13 | this.prefix = $"[{prefix}] "; 14 | this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); 15 | } 16 | 17 | public void Log(ILogMessage message) 18 | { 19 | if (message == null) throw new ArgumentNullException(nameof(message)); 20 | logger.Log(new LogMessage(message, prefix + message.Message)); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ModuleManager/Logging/QueueLogRunner.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | using ModuleManager.Collections; 5 | 6 | namespace ModuleManager.Logging 7 | { 8 | public class QueueLogRunner 9 | { 10 | private enum State 11 | { 12 | Initialized, 13 | Running, 14 | StopRequested, 15 | Stopped, 16 | } 17 | 18 | private State state = State.Initialized; 19 | private readonly IMessageQueue logQueue; 20 | private readonly long timeToWaitForLogsMs; 21 | 22 | public QueueLogRunner(IMessageQueue logQueue, long timeToWaitForLogsMs = 50) 23 | { 24 | this.logQueue = logQueue ?? throw new ArgumentNullException(nameof(logQueue)); 25 | if (timeToWaitForLogsMs < 0) throw new ArgumentException("must be non-negative", nameof(timeToWaitForLogsMs)); 26 | this.timeToWaitForLogsMs = timeToWaitForLogsMs; 27 | } 28 | 29 | public void RequestStop() 30 | { 31 | if (state == State.StopRequested || state == State.Stopped) return; 32 | if (state != State.Running) throw new InvalidOperationException($"Cannot request stop from {state} state"); 33 | state = State.StopRequested; 34 | } 35 | 36 | public void Run(IBasicLogger logger) 37 | { 38 | if (state != State.Initialized) throw new InvalidOperationException($"Cannot run from {state} state"); 39 | if (logger == null) throw new ArgumentNullException(nameof(logger)); 40 | state = State.Running; 41 | 42 | System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch(); 43 | 44 | while (state == State.Running) 45 | { 46 | stopwatch.Start(); 47 | 48 | foreach (ILogMessage message in logQueue.TakeAll()) 49 | { 50 | logger.Log(message); 51 | } 52 | 53 | long timeRemaining = timeToWaitForLogsMs - stopwatch.ElapsedMilliseconds; 54 | if (timeRemaining > 0) 55 | System.Threading.Thread.Sleep((int)timeRemaining); 56 | 57 | stopwatch.Reset(); 58 | } 59 | 60 | foreach (ILogMessage message in logQueue.TakeAll()) 61 | { 62 | logger.Log(message); 63 | } 64 | 65 | state = State.Stopped; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /ModuleManager/Logging/QueueLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ModuleManager.Collections; 3 | 4 | namespace ModuleManager.Logging 5 | { 6 | public class QueueLogger : IBasicLogger 7 | { 8 | private readonly IMessageQueue queue; 9 | 10 | public QueueLogger(IMessageQueue queue) 11 | { 12 | this.queue = queue ?? throw new ArgumentNullException(nameof(queue)); 13 | } 14 | 15 | public void Log(ILogMessage message) 16 | { 17 | if (message == null) throw new ArgumentNullException(nameof(message)); 18 | queue.Add(message); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ModuleManager/Logging/StreamLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace ModuleManager.Logging 5 | { 6 | public sealed class StreamLogger : IBasicLogger, IDisposable 7 | { 8 | private readonly StreamWriter streamWriter; 9 | private bool disposed = false; 10 | 11 | public StreamLogger(Stream stream) 12 | { 13 | streamWriter = new StreamWriter(stream); 14 | } 15 | 16 | public void Log(ILogMessage message) 17 | { 18 | if (disposed) throw new InvalidOperationException("Object has already been disposed"); 19 | if (message == null) throw new ArgumentNullException(nameof(message)); 20 | 21 | streamWriter.WriteLine(message.ToLogString()); 22 | } 23 | 24 | public void Dispose() 25 | { 26 | // Flushes and closes the StreamWriter and the underlying stream 27 | streamWriter.Close(); 28 | 29 | disposed = true; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ModuleManager/Logging/UnityLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace ModuleManager.Logging 5 | { 6 | public class UnityLogger : IBasicLogger 7 | { 8 | private readonly ILogger logger; 9 | 10 | public UnityLogger(ILogger logger) 11 | { 12 | this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); 13 | } 14 | 15 | public void Log(ILogMessage message) 16 | { 17 | if (message == null) throw new ArgumentNullException(nameof(message)); 18 | logger.Log(message.LogType, message.Message); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ModuleManager/MMPatchRunner.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using ModuleManager.Collections; 6 | using ModuleManager.Extensions; 7 | using ModuleManager.Logging; 8 | using ModuleManager.Threading; 9 | 10 | using static ModuleManager.FilePathRepository; 11 | 12 | namespace ModuleManager 13 | { 14 | public class MMPatchRunner 15 | { 16 | private readonly IBasicLogger kspLogger; 17 | 18 | public string Status { get; private set; } = ""; 19 | public string Errors { get; private set; } = ""; 20 | 21 | public MMPatchRunner(IBasicLogger kspLogger) 22 | { 23 | this.kspLogger = kspLogger ?? throw new ArgumentNullException(nameof(kspLogger)); 24 | } 25 | 26 | public IEnumerator Run() 27 | { 28 | PostPatchLoader.Instance.databaseConfigs = null; 29 | 30 | if (!Directory.Exists(logsDirPath)) Directory.CreateDirectory(logsDirPath); 31 | 32 | kspLogger.Info("Patching started on a new thread, all output will be directed to " + logPath); 33 | 34 | MessageQueue mmLogQueue = new MessageQueue(); 35 | QueueLogRunner logRunner = new QueueLogRunner(mmLogQueue); 36 | ITaskStatus loggingThreadStatus = BackgroundTask.Start(delegate 37 | { 38 | using StreamLogger streamLogger = new StreamLogger(new FileStream(logPath, FileMode.Create)); 39 | streamLogger.Info("Log started at " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")); 40 | logRunner.Run(streamLogger); 41 | streamLogger.Info("Done!"); 42 | }); 43 | 44 | // Wait for game database to be initialized for the 2nd time and wait for any plugins to initialize 45 | yield return null; 46 | yield return null; 47 | 48 | IBasicLogger mmLogger = new QueueLogger(mmLogQueue); 49 | 50 | IEnumerable modsAddedByAssemblies = ModListGenerator.GetAdditionalModsFromStaticMethods(mmLogger); 51 | 52 | IEnumerable databaseConfigs = null; 53 | 54 | MMPatchLoader patchLoader = new MMPatchLoader(modsAddedByAssemblies, mmLogger); 55 | 56 | ITaskStatus patchingThreadStatus = BackgroundTask.Start(delegate 57 | { 58 | databaseConfigs = patchLoader.Run(); 59 | }); 60 | 61 | while(true) 62 | { 63 | yield return null; 64 | 65 | if (!patchingThreadStatus.IsRunning) 66 | logRunner.RequestStop(); 67 | 68 | Status = patchLoader.status; 69 | Errors = patchLoader.errors; 70 | 71 | if (!patchingThreadStatus.IsRunning && !loggingThreadStatus.IsRunning) break; 72 | } 73 | 74 | if (patchingThreadStatus.IsExitedWithError) 75 | { 76 | kspLogger.Exception("The patching thread threw an exception", patchingThreadStatus.Exception); 77 | FatalErrorHandler.HandleFatalError("The patching thread threw an exception"); 78 | yield break; 79 | } 80 | 81 | if (loggingThreadStatus.IsExitedWithError) 82 | { 83 | kspLogger.Exception("The logging thread threw an exception", loggingThreadStatus.Exception); 84 | FatalErrorHandler.HandleFatalError("The logging thread threw an exception"); 85 | yield break; 86 | } 87 | 88 | if (databaseConfigs == null) 89 | { 90 | kspLogger.Error("The patcher returned a null collection of configs"); 91 | FatalErrorHandler.HandleFatalError("The patcher returned a null collection of configs"); 92 | yield break; 93 | } 94 | 95 | PostPatchLoader.Instance.databaseConfigs = databaseConfigs; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /ModuleManager/ModuleManagerTestRunner.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using ModuleManager.Extensions; 6 | using ModuleManager.Logging; 7 | 8 | namespace ModuleManager 9 | { 10 | public class InGameTestRunner 11 | { 12 | private readonly IBasicLogger logger; 13 | 14 | public InGameTestRunner(IBasicLogger logger) 15 | { 16 | this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); 17 | } 18 | 19 | public void RunTestCases(UrlDir gameDatabaseRoot) 20 | { 21 | if (gameDatabaseRoot == null) throw new ArgumentNullException(nameof(gameDatabaseRoot)); 22 | logger.Info("Running tests..."); 23 | 24 | foreach (UrlDir.UrlConfig expect in gameDatabaseRoot.GetConfigs("MMTEST_EXPECT")) 25 | { 26 | // So for each of the expects, we expect all the configs before that node to match exactly. 27 | UrlDir.UrlFile parent = expect.parent; 28 | if (parent.configs.Count != expect.config.CountNodes + 1) 29 | { 30 | logger.Error("Test " + parent.name + " failed as expected number of nodes differs expected: " + 31 | expect.config.CountNodes + " found: " + (parent.configs.Count - 1)); 32 | for (int i = 0; i < parent.configs.Count; ++i) 33 | logger.Info(parent.configs[i].config.ToString()); 34 | continue; 35 | } 36 | for (int i = 0; i < expect.config.CountNodes; ++i) 37 | { 38 | ConfigNode gotNode = parent.configs[i].config; 39 | ConfigNode expectNode = expect.config.nodes[i]; 40 | if (!CompareRecursive(expectNode, gotNode)) 41 | { 42 | logger.Error("Test " + parent.name + "[" + i + 43 | "] failed as expected output and actual output differ.\nexpected:\n" + expectNode + 44 | "\nActually got:\n" + gotNode); 45 | } 46 | } 47 | 48 | // Purge the tests 49 | parent.configs.Clear(); 50 | } 51 | logger.Info("tests complete."); 52 | } 53 | 54 | private static bool CompareRecursive(ConfigNode expectNode, ConfigNode gotNode) 55 | { 56 | if (expectNode.values.Count != gotNode.values.Count || expectNode.nodes.Count != gotNode.nodes.Count) 57 | return false; 58 | for (int i = 0; i < expectNode.values.Count; ++i) 59 | { 60 | ConfigNode.Value eVal = expectNode.values[i]; 61 | ConfigNode.Value gVal = gotNode.values[i]; 62 | if (eVal.name != gVal.name || eVal.value != gVal.value) 63 | return false; 64 | } 65 | for (int i = 0; i < expectNode.nodes.Count; ++i) 66 | { 67 | ConfigNode eNode = expectNode.nodes[i]; 68 | ConfigNode gNode = gotNode.nodes[i]; 69 | if (!CompareRecursive(eNode, gNode)) 70 | return false; 71 | } 72 | return true; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /ModuleManager/NodeMatcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ModuleManager.Extensions; 3 | 4 | namespace ModuleManager 5 | { 6 | public interface INodeMatcher 7 | { 8 | bool IsMatch(ConfigNode node); 9 | } 10 | 11 | public class NodeMatcher : INodeMatcher 12 | { 13 | private readonly string type; 14 | private readonly string[] namePatterns = null; 15 | private readonly string constraints = ""; 16 | 17 | public NodeMatcher(string type, string name, string constraints) 18 | { 19 | if (type == string.Empty) throw new ArgumentException("can't be empty", nameof(type)); 20 | this.type = type ?? throw new ArgumentNullException(nameof(type)); 21 | 22 | if (name == string.Empty) throw new ArgumentException("can't be empty (null allowed)", nameof(name)); 23 | if (constraints == string.Empty) throw new ArgumentException("can't be empty (null allowed)", nameof(constraints)); 24 | 25 | if (name != null) namePatterns = name.Split(',', '|'); 26 | if (constraints != null) 27 | { 28 | if (!constraints.IsBracketBalanced()) throw new ArgumentException("is not bracket balanced: " + constraints, nameof(constraints)); 29 | this.constraints = constraints; 30 | } 31 | } 32 | 33 | public bool IsMatch(ConfigNode node) 34 | { 35 | if (node.name != type) return false; 36 | 37 | if (namePatterns != null) 38 | { 39 | string name = node.GetValue("name"); 40 | if (name == null) return false; 41 | 42 | bool match = false; 43 | foreach (string pattern in namePatterns) 44 | { 45 | if (MMPatchLoader.WildcardMatch(name, pattern)) 46 | { 47 | match = true; 48 | break; 49 | } 50 | } 51 | 52 | if (!match) return false; 53 | } 54 | 55 | return MMPatchLoader.CheckConstraints(node, constraints); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /ModuleManager/Operator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ModuleManager 4 | { 5 | public enum Operator 6 | { 7 | Assign, 8 | Add, 9 | Subtract, 10 | Multiply, 11 | Divide, 12 | Exponentiate, 13 | RegexReplace, 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ModuleManager/OperatorParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ModuleManager 4 | { 5 | public static class OperatorParser 6 | { 7 | public static Operator Parse(string name, out string valueName) 8 | { 9 | if (name == null) throw new ArgumentNullException(nameof(name)); 10 | 11 | if (name.Length == 0) 12 | { 13 | valueName = string.Empty; 14 | return Operator.Assign; 15 | } 16 | else if (name.Length == 1 || (name[name.Length - 2] != ' ' && name[name.Length - 2] != '\t')) 17 | { 18 | valueName = name; 19 | return Operator.Assign; 20 | } 21 | 22 | Operator ret; 23 | switch (name[name.Length - 1]) 24 | { 25 | case '+': 26 | ret = Operator.Add; 27 | break; 28 | 29 | case '-': 30 | ret = Operator.Subtract; 31 | break; 32 | 33 | case '*': 34 | ret = Operator.Multiply; 35 | break; 36 | 37 | case '/': 38 | ret = Operator.Divide; 39 | break; 40 | 41 | case '!': 42 | ret = Operator.Exponentiate; 43 | break; 44 | 45 | case '^': 46 | ret = Operator.RegexReplace; 47 | break; 48 | 49 | default: 50 | valueName = name; 51 | return Operator.Assign; 52 | } 53 | valueName = name.Substring(0, name.Length - 1).TrimEnd(); 54 | return ret; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /ModuleManager/Pass.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using ModuleManager.Patches; 5 | 6 | namespace ModuleManager 7 | { 8 | public interface IPass : IEnumerable 9 | { 10 | string Name { get; } 11 | } 12 | 13 | public class Pass : IPass 14 | { 15 | private readonly string name; 16 | private readonly List patches = new List(0); 17 | 18 | public Pass(string name) 19 | { 20 | this.name = name ?? throw new ArgumentNullException(nameof(name)); 21 | if (name == string.Empty) throw new ArgumentException("can't be empty", nameof(name)); 22 | } 23 | 24 | public string Name => name; 25 | 26 | public void Add(IPatch patch) => patches.Add(patch); 27 | 28 | public List.Enumerator GetEnumerator() => patches.GetEnumerator(); 29 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 30 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ModuleManager/PatchApplier.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using ModuleManager.Logging; 4 | using ModuleManager.Extensions; 5 | using ModuleManager.Patches; 6 | using ModuleManager.Progress; 7 | 8 | namespace ModuleManager 9 | { 10 | public class PatchApplier 11 | { 12 | private readonly IBasicLogger logger; 13 | private readonly IPatchProgress progress; 14 | 15 | public PatchApplier(IPatchProgress progress, IBasicLogger logger) 16 | { 17 | this.progress = progress ?? throw new ArgumentNullException(nameof(progress)); 18 | this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); 19 | } 20 | 21 | public IEnumerable ApplyPatches(IEnumerable patches) 22 | { 23 | if (patches == null) throw new ArgumentNullException(nameof(patches)); 24 | 25 | LinkedList databaseConfigs = new LinkedList(); 26 | 27 | foreach (IPass pass in patches) 28 | { 29 | ApplyPatches(databaseConfigs, pass); 30 | } 31 | 32 | return databaseConfigs; 33 | } 34 | 35 | private void ApplyPatches(LinkedList databaseConfigs, IPass pass) 36 | { 37 | progress.PassStarted(pass); 38 | 39 | foreach (IPatch patch in pass) 40 | { 41 | try 42 | { 43 | patch.Apply(databaseConfigs, progress, logger); 44 | if (patch.CountsAsPatch) progress.PatchApplied(); 45 | } 46 | catch (Exception e) 47 | { 48 | progress.Exception(patch.UrlConfig, "Exception while processing node : " + patch.UrlConfig.SafeUrl(), e); 49 | logger.Error("Processed node was\n" + patch.UrlConfig.PrettyPrint()); 50 | } 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ModuleManager/PatchContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using ModuleManager.Logging; 4 | using ModuleManager.Progress; 5 | 6 | namespace ModuleManager 7 | { 8 | public struct PatchContext 9 | { 10 | public readonly UrlDir.UrlConfig patchUrl; 11 | public readonly IEnumerable databaseConfigs; 12 | public readonly IBasicLogger logger; 13 | public readonly IPatchProgress progress; 14 | 15 | public PatchContext(UrlDir.UrlConfig patchUrl, IEnumerable databaseConfigs, IBasicLogger logger, IPatchProgress progress) 16 | { 17 | this.patchUrl = patchUrl; 18 | this.databaseConfigs = databaseConfigs; 19 | this.logger = logger; 20 | this.progress = progress; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ModuleManager/Patches/CopyPatch.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NodeStack = ModuleManager.Collections.ImmutableStack; 4 | using ModuleManager.Extensions; 5 | using ModuleManager.Logging; 6 | using ModuleManager.Patches.PassSpecifiers; 7 | using ModuleManager.Progress; 8 | 9 | namespace ModuleManager.Patches 10 | { 11 | public class CopyPatch : IPatch 12 | { 13 | public UrlDir.UrlConfig UrlConfig { get; } 14 | public INodeMatcher NodeMatcher { get; } 15 | public IPassSpecifier PassSpecifier { get; } 16 | public bool CountsAsPatch => true; 17 | 18 | public CopyPatch(UrlDir.UrlConfig urlConfig, INodeMatcher nodeMatcher, IPassSpecifier passSpecifier) 19 | { 20 | UrlConfig = urlConfig ?? throw new ArgumentNullException(nameof(urlConfig)); 21 | NodeMatcher = nodeMatcher ?? throw new ArgumentNullException(nameof(nodeMatcher)); 22 | PassSpecifier = passSpecifier ?? throw new ArgumentNullException(nameof(passSpecifier)); 23 | } 24 | 25 | public void Apply(LinkedList databaseConfigs, IPatchProgress progress, IBasicLogger logger) 26 | { 27 | if (databaseConfigs == null) throw new ArgumentNullException(nameof(databaseConfigs)); 28 | if (progress == null) throw new ArgumentNullException(nameof(progress)); 29 | if (logger == null) throw new ArgumentNullException(nameof(logger)); 30 | 31 | PatchContext context = new PatchContext(UrlConfig, databaseConfigs, logger, progress); 32 | 33 | for (LinkedListNode listNode = databaseConfigs.First; listNode != null; listNode = listNode.Next) 34 | { 35 | IProtoUrlConfig protoConfig = listNode.Value; 36 | try 37 | { 38 | if (!NodeMatcher.IsMatch(protoConfig.Node)) continue; 39 | 40 | ConfigNode clone = MMPatchLoader.ModifyNode(new NodeStack(protoConfig.Node), UrlConfig.config, context); 41 | if (protoConfig.Node.GetValue("name") is string name && name == clone.GetValue("name")) 42 | { 43 | progress.Error(UrlConfig, $"Error - when applying copy {UrlConfig.SafeUrl()} to {protoConfig.FullUrl} - the copy needs to have a different name than the parent (use @name = xxx)"); 44 | } 45 | else 46 | { 47 | progress.ApplyingCopy(protoConfig, UrlConfig); 48 | listNode = databaseConfigs.AddAfter(listNode, new ProtoUrlConfig(protoConfig.UrlFile, clone)); 49 | } 50 | } 51 | catch (Exception ex) 52 | { 53 | progress.Exception(UrlConfig, $"Exception while applying copy {UrlConfig.SafeUrl()} to {protoConfig.FullUrl}", ex); 54 | } 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /ModuleManager/Patches/DeletePatch.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using ModuleManager.Extensions; 4 | using ModuleManager.Logging; 5 | using ModuleManager.Patches.PassSpecifiers; 6 | using ModuleManager.Progress; 7 | 8 | namespace ModuleManager.Patches 9 | { 10 | public class DeletePatch : IPatch 11 | { 12 | public UrlDir.UrlConfig UrlConfig { get; } 13 | public INodeMatcher NodeMatcher { get; } 14 | public IPassSpecifier PassSpecifier { get; } 15 | public bool CountsAsPatch => true; 16 | 17 | public DeletePatch(UrlDir.UrlConfig urlConfig, INodeMatcher nodeMatcher, IPassSpecifier passSpecifier) 18 | { 19 | UrlConfig = urlConfig ?? throw new ArgumentNullException(nameof(urlConfig)); 20 | NodeMatcher = nodeMatcher ?? throw new ArgumentNullException(nameof(nodeMatcher)); 21 | PassSpecifier = passSpecifier ?? throw new ArgumentNullException(nameof(passSpecifier)); 22 | } 23 | 24 | public void Apply(LinkedList databaseConfigs, IPatchProgress progress, IBasicLogger logger) 25 | { 26 | if (databaseConfigs == null) throw new ArgumentNullException(nameof(databaseConfigs)); 27 | if (progress == null) throw new ArgumentNullException(nameof(progress)); 28 | if (logger == null) throw new ArgumentNullException(nameof(logger)); 29 | 30 | LinkedListNode currentNode = databaseConfigs.First; 31 | while (currentNode != null) 32 | { 33 | IProtoUrlConfig protoConfig = currentNode.Value; 34 | try 35 | { 36 | LinkedListNode nextNode = currentNode.Next; 37 | if (NodeMatcher.IsMatch(protoConfig.Node)) 38 | { 39 | progress.ApplyingDelete(protoConfig, UrlConfig); 40 | databaseConfigs.Remove(currentNode); 41 | } 42 | currentNode = nextNode; 43 | } 44 | catch (Exception ex) 45 | { 46 | progress.Exception(UrlConfig, $"Exception while applying delete {UrlConfig.SafeUrl()} to {protoConfig.FullUrl}", ex); 47 | } 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /ModuleManager/Patches/EditPatch.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NodeStack = ModuleManager.Collections.ImmutableStack; 4 | using ModuleManager.Extensions; 5 | using ModuleManager.Logging; 6 | using ModuleManager.Patches.PassSpecifiers; 7 | using ModuleManager.Progress; 8 | 9 | namespace ModuleManager.Patches 10 | { 11 | public class EditPatch : IPatch 12 | { 13 | private readonly bool loop; 14 | 15 | public UrlDir.UrlConfig UrlConfig { get; } 16 | public INodeMatcher NodeMatcher { get; } 17 | public IPassSpecifier PassSpecifier { get; } 18 | public bool CountsAsPatch => true; 19 | 20 | public EditPatch(UrlDir.UrlConfig urlConfig, INodeMatcher nodeMatcher, IPassSpecifier passSpecifier) 21 | { 22 | UrlConfig = urlConfig ?? throw new ArgumentNullException(nameof(urlConfig)); 23 | NodeMatcher = nodeMatcher ?? throw new ArgumentNullException(nameof(nodeMatcher)); 24 | PassSpecifier = passSpecifier ?? throw new ArgumentNullException(nameof(passSpecifier)); 25 | 26 | loop = urlConfig.config.HasNode("MM_PATCH_LOOP"); 27 | } 28 | 29 | public void Apply(LinkedList databaseConfigs, IPatchProgress progress, IBasicLogger logger) 30 | { 31 | if (databaseConfigs == null) throw new ArgumentNullException(nameof(databaseConfigs)); 32 | if (progress == null) throw new ArgumentNullException(nameof(progress)); 33 | if (logger == null) throw new ArgumentNullException(nameof(logger)); 34 | 35 | PatchContext context = new PatchContext(UrlConfig, databaseConfigs, logger, progress); 36 | for (LinkedListNode listNode = databaseConfigs.First; listNode != null; listNode = listNode.Next) 37 | { 38 | IProtoUrlConfig protoConfig = listNode.Value; 39 | try 40 | { 41 | if (!NodeMatcher.IsMatch(protoConfig.Node)) continue; 42 | if (loop) logger.Info($"Looping on {UrlConfig.SafeUrl()} to {protoConfig.FullUrl}"); 43 | 44 | do 45 | { 46 | progress.ApplyingUpdate(protoConfig, UrlConfig); 47 | listNode.Value = protoConfig = new ProtoUrlConfig(protoConfig.UrlFile, MMPatchLoader.ModifyNode(new NodeStack(protoConfig.Node), UrlConfig.config, context)); 48 | } while (loop && NodeMatcher.IsMatch(protoConfig.Node)); 49 | 50 | if (loop) protoConfig.Node.RemoveNodes("MM_PATCH_LOOP"); 51 | } 52 | catch (Exception ex) 53 | { 54 | progress.Exception(UrlConfig, $"Exception while applying update {UrlConfig.SafeUrl()} to {protoConfig.FullUrl}", ex); 55 | } 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /ModuleManager/Patches/IPatch.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using ModuleManager.Logging; 4 | using ModuleManager.Patches.PassSpecifiers; 5 | using ModuleManager.Progress; 6 | 7 | namespace ModuleManager.Patches 8 | { 9 | public interface IPatch 10 | { 11 | UrlDir.UrlConfig UrlConfig { get; } 12 | IPassSpecifier PassSpecifier { get; } 13 | bool CountsAsPatch { get; } 14 | void Apply(LinkedList configs, IPatchProgress progress, IBasicLogger logger); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ModuleManager/Patches/InsertPatch.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using ModuleManager.Extensions; 4 | using ModuleManager.Logging; 5 | using ModuleManager.Patches.PassSpecifiers; 6 | using ModuleManager.Progress; 7 | 8 | namespace ModuleManager.Patches 9 | { 10 | public class InsertPatch : IPatch 11 | { 12 | public UrlDir.UrlConfig UrlConfig { get; } 13 | public string NodeType { get; } 14 | public IPassSpecifier PassSpecifier { get; } 15 | public bool CountsAsPatch => false; 16 | 17 | public InsertPatch(UrlDir.UrlConfig urlConfig, string nodeType, IPassSpecifier passSpecifier) 18 | { 19 | UrlConfig = urlConfig ?? throw new ArgumentNullException(nameof(urlConfig)); 20 | NodeType = nodeType ?? throw new ArgumentNullException(nameof(nodeType)); 21 | PassSpecifier = passSpecifier ?? throw new ArgumentNullException(nameof(passSpecifier)); 22 | } 23 | 24 | public void Apply(LinkedList configs, IPatchProgress progress, IBasicLogger logger) 25 | { 26 | if (configs == null) throw new ArgumentNullException(nameof(configs)); 27 | if (progress == null) throw new ArgumentNullException(nameof(progress)); 28 | if (logger == null) throw new ArgumentNullException(nameof(logger)); 29 | 30 | ConfigNode node = UrlConfig.config.DeepCopy(); 31 | node.name = NodeType; 32 | configs.AddLast(new ProtoUrlConfig(UrlConfig.parent, node)); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ModuleManager/Patches/PassSpecifiers/AfterPassSpecifier.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ModuleManager.Progress; 3 | 4 | namespace ModuleManager.Patches.PassSpecifiers 5 | { 6 | public class AfterPassSpecifier : IPassSpecifier 7 | { 8 | public readonly string mod; 9 | public readonly UrlDir.UrlConfig urlConfig; 10 | 11 | public AfterPassSpecifier(string mod, UrlDir.UrlConfig urlConfig) 12 | { 13 | if (mod == string.Empty) throw new ArgumentException("can't be empty", nameof(mod)); 14 | this.mod = mod ?? throw new ArgumentNullException(nameof(mod)); 15 | this.urlConfig = urlConfig ?? throw new ArgumentNullException(nameof(urlConfig)); 16 | } 17 | 18 | public bool CheckNeeds(INeedsChecker needsChecker, IPatchProgress progress) 19 | { 20 | if (needsChecker == null) throw new ArgumentNullException(nameof(needsChecker)); 21 | if (progress == null) throw new ArgumentNullException(nameof(progress)); 22 | bool result = needsChecker.CheckNeeds(mod); 23 | if (!result) progress.NeedsUnsatisfiedAfter(urlConfig); 24 | return result; 25 | } 26 | 27 | public string Descriptor => $":AFTER[{mod.ToUpper()}]"; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ModuleManager/Patches/PassSpecifiers/BeforePassSpecifier.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ModuleManager.Progress; 3 | 4 | namespace ModuleManager.Patches.PassSpecifiers 5 | { 6 | public class BeforePassSpecifier : IPassSpecifier 7 | { 8 | public readonly string mod; 9 | public readonly UrlDir.UrlConfig urlConfig; 10 | 11 | public BeforePassSpecifier(string mod, UrlDir.UrlConfig urlConfig) 12 | { 13 | if (mod == string.Empty) throw new ArgumentException("can't be empty", nameof(mod)); 14 | this.mod = mod ?? throw new ArgumentNullException(nameof(mod)); 15 | this.urlConfig = urlConfig ?? throw new ArgumentNullException(nameof(urlConfig)); 16 | } 17 | 18 | public bool CheckNeeds(INeedsChecker needsChecker, IPatchProgress progress) 19 | { 20 | if (needsChecker == null) throw new ArgumentNullException(nameof(needsChecker)); 21 | if (progress == null) throw new ArgumentNullException(nameof(progress)); 22 | bool result = needsChecker.CheckNeeds(mod); 23 | if (!result) progress.NeedsUnsatisfiedBefore(urlConfig); 24 | return result; 25 | } 26 | 27 | public string Descriptor => $":BEFORE[{mod.ToUpper()}]"; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ModuleManager/Patches/PassSpecifiers/FinalPassSpecifier.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ModuleManager.Progress; 3 | 4 | namespace ModuleManager.Patches.PassSpecifiers 5 | { 6 | public class FinalPassSpecifier : IPassSpecifier 7 | { 8 | public bool CheckNeeds(INeedsChecker needsChecker, IPatchProgress progress) 9 | { 10 | if (needsChecker == null) throw new ArgumentNullException(nameof(needsChecker)); 11 | if (progress == null) throw new ArgumentNullException(nameof(progress)); 12 | return true; 13 | } 14 | 15 | public string Descriptor => ":FINAL"; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ModuleManager/Patches/PassSpecifiers/FirstPassSpecifier.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ModuleManager.Progress; 3 | 4 | namespace ModuleManager.Patches.PassSpecifiers 5 | { 6 | public class FirstPassSpecifier : IPassSpecifier 7 | { 8 | public bool CheckNeeds(INeedsChecker needsChecker, IPatchProgress progress) 9 | { 10 | if (needsChecker == null) throw new ArgumentNullException(nameof(needsChecker)); 11 | if (progress == null) throw new ArgumentNullException(nameof(progress)); 12 | return true; 13 | } 14 | 15 | public string Descriptor => ":FIRST"; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ModuleManager/Patches/PassSpecifiers/ForPassSpecifier.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ModuleManager.Progress; 3 | 4 | namespace ModuleManager.Patches.PassSpecifiers 5 | { 6 | public class ForPassSpecifier : IPassSpecifier 7 | { 8 | public readonly string mod; 9 | public readonly UrlDir.UrlConfig urlConfig; 10 | 11 | public ForPassSpecifier(string mod, UrlDir.UrlConfig urlConfig) 12 | { 13 | if (mod == string.Empty) throw new ArgumentException("can't be empty", nameof(mod)); 14 | this.mod = mod ?? throw new ArgumentNullException(nameof(mod)); 15 | this.urlConfig = urlConfig ?? throw new ArgumentNullException(nameof(urlConfig)); 16 | } 17 | 18 | public bool CheckNeeds(INeedsChecker needsChecker, IPatchProgress progress) 19 | { 20 | if (needsChecker == null) throw new ArgumentNullException(nameof(needsChecker)); 21 | if (progress == null) throw new ArgumentNullException(nameof(progress)); 22 | bool result = needsChecker.CheckNeeds(mod); 23 | if (!result) progress.NeedsUnsatisfiedFor(urlConfig); 24 | return result; 25 | } 26 | 27 | public string Descriptor => $":FOR[{mod.ToUpper()}]"; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ModuleManager/Patches/PassSpecifiers/IPassSpecifier.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ModuleManager.Progress; 3 | 4 | namespace ModuleManager.Patches.PassSpecifiers 5 | { 6 | public interface IPassSpecifier 7 | { 8 | bool CheckNeeds(INeedsChecker needsChecker, IPatchProgress progress); 9 | string Descriptor { get; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ModuleManager/Patches/PassSpecifiers/InsertPassSpecifier.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ModuleManager.Progress; 3 | 4 | namespace ModuleManager.Patches.PassSpecifiers 5 | { 6 | public class InsertPassSpecifier : IPassSpecifier 7 | { 8 | public bool CheckNeeds(INeedsChecker needsChecker, IPatchProgress progress) 9 | { 10 | if (needsChecker == null) throw new ArgumentNullException(nameof(needsChecker)); 11 | if (progress == null) throw new ArgumentNullException(nameof(progress)); 12 | return true; 13 | } 14 | 15 | public string Descriptor => ":INSERT (initial)"; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ModuleManager/Patches/PassSpecifiers/LastPassSpecifier.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ModuleManager.Progress; 3 | 4 | namespace ModuleManager.Patches.PassSpecifiers 5 | { 6 | public class LastPassSpecifier : IPassSpecifier 7 | { 8 | public readonly string mod; 9 | 10 | public LastPassSpecifier(string mod) 11 | { 12 | if (mod == string.Empty) throw new ArgumentException("can't be empty", nameof(mod)); 13 | this.mod = mod ?? throw new ArgumentNullException(nameof(mod)); 14 | } 15 | 16 | public bool CheckNeeds(INeedsChecker needsChecker, IPatchProgress progress) => true; 17 | public string Descriptor => $":LAST[{mod.ToUpper()}]"; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ModuleManager/Patches/PassSpecifiers/LegacyPassSpecifier.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ModuleManager.Progress; 3 | 4 | namespace ModuleManager.Patches.PassSpecifiers 5 | { 6 | public class LegacyPassSpecifier : IPassSpecifier 7 | { 8 | public bool CheckNeeds(INeedsChecker needsChecker, IPatchProgress progress) 9 | { 10 | if (needsChecker == null) throw new ArgumentNullException(nameof(needsChecker)); 11 | if (progress == null) throw new ArgumentNullException(nameof(progress)); 12 | return true; 13 | } 14 | 15 | public string Descriptor => ":LEGACY (default)"; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ModuleManager/Patches/PatchCompiler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ModuleManager.Patches 4 | { 5 | public interface IPatchCompiler 6 | { 7 | IPatch CompilePatch(ProtoPatch protoPatch); 8 | } 9 | 10 | public class PatchCompiler : IPatchCompiler 11 | { 12 | public IPatch CompilePatch(ProtoPatch protoPatch) 13 | { 14 | if (protoPatch == null) throw new ArgumentNullException(nameof(protoPatch)); 15 | 16 | return protoPatch.command switch 17 | { 18 | Command.Insert => new InsertPatch(protoPatch.urlConfig, protoPatch.nodeType, protoPatch.passSpecifier), 19 | Command.Edit => new EditPatch(protoPatch.urlConfig, new NodeMatcher(protoPatch.nodeType, protoPatch.nodeName, protoPatch.has), protoPatch.passSpecifier), 20 | Command.Copy => new CopyPatch(protoPatch.urlConfig, new NodeMatcher(protoPatch.nodeType, protoPatch.nodeName, protoPatch.has), protoPatch.passSpecifier), 21 | Command.Delete => new DeletePatch(protoPatch.urlConfig, new NodeMatcher(protoPatch.nodeType, protoPatch.nodeName, protoPatch.has), protoPatch.passSpecifier), 22 | _ => throw new ArgumentException("has an invalid command for a root node: " + protoPatch.command, nameof(protoPatch)), 23 | }; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ModuleManager/Patches/ProtoPatch.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ModuleManager.Patches.PassSpecifiers; 3 | 4 | namespace ModuleManager.Patches 5 | { 6 | public class ProtoPatch 7 | { 8 | public readonly UrlDir.UrlConfig urlConfig; 9 | public readonly Command command; 10 | public readonly string nodeType; 11 | public readonly string nodeName; 12 | public readonly string needs = null; 13 | public readonly string has = null; 14 | public readonly IPassSpecifier passSpecifier; 15 | 16 | public ProtoPatch(UrlDir.UrlConfig urlConfig, Command command, string nodeType, string nodeName, string needs, string has, IPassSpecifier passSpecifier) 17 | { 18 | this.urlConfig = urlConfig; 19 | this.command = command; 20 | this.nodeType = nodeType; 21 | this.nodeName = nodeName; 22 | this.needs = needs; 23 | this.has = has; 24 | this.passSpecifier = passSpecifier; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ModuleManager/Progress/IPatchProgress.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ModuleManager.Progress 4 | { 5 | public interface IPatchProgress 6 | { 7 | ProgressCounter Counter { get; } 8 | 9 | float ProgressFraction { get; } 10 | 11 | EventVoid OnPatchApplied { get; } 12 | EventData OnPassStarted { get; } 13 | 14 | void Warning(UrlDir.UrlConfig url, string message); 15 | void Error(UrlDir.UrlConfig url, string message); 16 | void Error(string message); 17 | void Exception(string message, Exception exception); 18 | void Exception(UrlDir.UrlConfig url, string message, Exception exception); 19 | void NeedsUnsatisfiedRoot(UrlDir.UrlConfig url); 20 | void NeedsUnsatisfiedNode(UrlDir.UrlConfig url, string path); 21 | void NeedsUnsatisfiedValue(UrlDir.UrlConfig url, string path); 22 | void NeedsUnsatisfiedBefore(UrlDir.UrlConfig url); 23 | void NeedsUnsatisfiedFor(UrlDir.UrlConfig url); 24 | void NeedsUnsatisfiedAfter(UrlDir.UrlConfig url); 25 | void ApplyingCopy(IUrlConfigIdentifier original, UrlDir.UrlConfig patch); 26 | void ApplyingDelete(IUrlConfigIdentifier original, UrlDir.UrlConfig patch); 27 | void ApplyingUpdate(IUrlConfigIdentifier original, UrlDir.UrlConfig patch); 28 | void PatchAdded(); 29 | void PatchApplied(); 30 | void PassStarted(IPass pass); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ModuleManager/Progress/ProgressCounter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using ModuleManager.Utils; 4 | 5 | namespace ModuleManager.Progress 6 | { 7 | public class ProgressCounter 8 | { 9 | public readonly Counter totalPatches = new Counter(); 10 | public readonly Counter appliedPatches = new Counter(); 11 | public readonly Counter patchedNodes = new Counter(); 12 | public readonly Counter warnings = new Counter(); 13 | public readonly Counter errors = new Counter(); 14 | public readonly Counter exceptions = new Counter(); 15 | public readonly Counter needsUnsatisfied = new Counter(); 16 | 17 | public readonly Dictionary warningFiles = new Dictionary(); 18 | public readonly Dictionary errorFiles = new Dictionary(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ModuleManager/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | 4 | // Information about this assembly is defined by the following attributes. 5 | // Change them to the values specific to your project. 6 | 7 | [assembly: AssemblyTitle("ModuleManager")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("")] 12 | [assembly: AssemblyCopyright("")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 19 | 20 | [assembly: AssemblyVersion("4.2.3")] 21 | [assembly: KSPAssembly("ModuleManager", 2, 5)] 22 | 23 | // The following attributes are used to specify the signing key for the assembly, 24 | // if desired. See the Mono documentation for more information about signing. 25 | 26 | //[assembly: AssemblyDelaySign(false)] 27 | //[assembly: AssemblyKeyFile("")] 28 | 29 | -------------------------------------------------------------------------------- /ModuleManager/Properties/cat-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sarbian/ModuleManager/c4561925f983e7ae81d9dfd4d11356a35cb6b9b6/ModuleManager/Properties/cat-1.png -------------------------------------------------------------------------------- /ModuleManager/Properties/cat-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sarbian/ModuleManager/c4561925f983e7ae81d9dfd4d11356a35cb6b9b6/ModuleManager/Properties/cat-10.png -------------------------------------------------------------------------------- /ModuleManager/Properties/cat-11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sarbian/ModuleManager/c4561925f983e7ae81d9dfd4d11356a35cb6b9b6/ModuleManager/Properties/cat-11.png -------------------------------------------------------------------------------- /ModuleManager/Properties/cat-12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sarbian/ModuleManager/c4561925f983e7ae81d9dfd4d11356a35cb6b9b6/ModuleManager/Properties/cat-12.png -------------------------------------------------------------------------------- /ModuleManager/Properties/cat-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sarbian/ModuleManager/c4561925f983e7ae81d9dfd4d11356a35cb6b9b6/ModuleManager/Properties/cat-2.png -------------------------------------------------------------------------------- /ModuleManager/Properties/cat-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sarbian/ModuleManager/c4561925f983e7ae81d9dfd4d11356a35cb6b9b6/ModuleManager/Properties/cat-3.png -------------------------------------------------------------------------------- /ModuleManager/Properties/cat-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sarbian/ModuleManager/c4561925f983e7ae81d9dfd4d11356a35cb6b9b6/ModuleManager/Properties/cat-4.png -------------------------------------------------------------------------------- /ModuleManager/Properties/cat-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sarbian/ModuleManager/c4561925f983e7ae81d9dfd4d11356a35cb6b9b6/ModuleManager/Properties/cat-5.png -------------------------------------------------------------------------------- /ModuleManager/Properties/cat-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sarbian/ModuleManager/c4561925f983e7ae81d9dfd4d11356a35cb6b9b6/ModuleManager/Properties/cat-6.png -------------------------------------------------------------------------------- /ModuleManager/Properties/cat-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sarbian/ModuleManager/c4561925f983e7ae81d9dfd4d11356a35cb6b9b6/ModuleManager/Properties/cat-7.png -------------------------------------------------------------------------------- /ModuleManager/Properties/cat-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sarbian/ModuleManager/c4561925f983e7ae81d9dfd4d11356a35cb6b9b6/ModuleManager/Properties/cat-8.png -------------------------------------------------------------------------------- /ModuleManager/Properties/cat-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sarbian/ModuleManager/c4561925f983e7ae81d9dfd4d11356a35cb6b9b6/ModuleManager/Properties/cat-9.png -------------------------------------------------------------------------------- /ModuleManager/Properties/rainbow2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sarbian/ModuleManager/c4561925f983e7ae81d9dfd4d11356a35cb6b9b6/ModuleManager/Properties/rainbow2.png -------------------------------------------------------------------------------- /ModuleManager/ProtoUrlConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ModuleManager 4 | { 5 | public interface IUrlConfigIdentifier 6 | { 7 | string FileUrl { get; } 8 | string NodeType { get; } 9 | string FullUrl { get; } 10 | } 11 | 12 | public interface IProtoUrlConfig : IUrlConfigIdentifier 13 | { 14 | UrlDir.UrlFile UrlFile { get; } 15 | ConfigNode Node { get; } 16 | } 17 | 18 | public class ProtoUrlConfig : IProtoUrlConfig 19 | { 20 | public UrlDir.UrlFile UrlFile { get; } 21 | public ConfigNode Node { get; } 22 | public string FileUrl { get; } 23 | public string NodeType => Node.name; 24 | public string FullUrl { get; } 25 | 26 | public ProtoUrlConfig(UrlDir.UrlFile urlFile, ConfigNode node) 27 | { 28 | UrlFile = urlFile ?? throw new ArgumentNullException(nameof(urlFile)); 29 | Node = node ?? throw new ArgumentNullException(nameof(node)); 30 | FileUrl = UrlFile.url + '.' + urlFile.fileExtension; 31 | FullUrl = FileUrl + '/' + Node.name; 32 | 33 | if (node.GetValue("name") is string nameValue) 34 | FullUrl += '[' + nameValue + ']'; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ModuleManager/Tags/Tag.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ModuleManager.Tags 4 | { 5 | public struct Tag 6 | { 7 | public readonly string key; 8 | public readonly string value; 9 | public readonly string trailer; 10 | 11 | public Tag(string key, string value, string trailer) 12 | { 13 | this.key = key ?? throw new ArgumentNullException(nameof(key)); 14 | if (key == string.Empty) throw new ArgumentException("can't be empty", nameof(key)); 15 | 16 | if (value == null && trailer != null) 17 | throw new ArgumentException("trailer must be null if value is null"); 18 | 19 | if (trailer == string.Empty) throw new ArgumentException("can't be empty (null allowed)", nameof(trailer)); 20 | 21 | this.value = value; 22 | this.trailer = trailer; 23 | } 24 | 25 | public override string ToString() 26 | { 27 | string s = "< '" + key + "' "; 28 | if (value != null) s += "[ '" + value + "' ] "; 29 | if (trailer != null) s += "'" + trailer + "' "; 30 | s += ">"; 31 | return s; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ModuleManager/Tags/TagList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using ModuleManager.Collections; 6 | 7 | namespace ModuleManager.Tags 8 | { 9 | public interface ITagList : IEnumerable 10 | { 11 | Tag PrimaryTag { get; } 12 | } 13 | 14 | public class TagList : ITagList 15 | { 16 | private readonly Tag[] tags; 17 | 18 | public TagList(Tag primaryTag, IEnumerable tags) 19 | { 20 | PrimaryTag = primaryTag; 21 | this.tags = tags?.ToArray() ?? throw new ArgumentNullException(nameof(tags)); 22 | } 23 | 24 | public Tag PrimaryTag { get; private set; } 25 | 26 | public ArrayEnumerator GetEnumerator() => new ArrayEnumerator(tags); 27 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 28 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ModuleManager/Threading/BackgroundTask.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | 4 | namespace ModuleManager.Threading 5 | { 6 | public static class BackgroundTask 7 | { 8 | public static ITaskStatus Start(Action action) 9 | { 10 | if (action == null) throw new ArgumentNullException(nameof(action)); 11 | 12 | TaskStatus status = new TaskStatus(); 13 | 14 | void RunAction() 15 | { 16 | try 17 | { 18 | action(); 19 | status.Finished(); 20 | } 21 | catch (Exception ex) 22 | { 23 | status.Error(ex); 24 | } 25 | } 26 | 27 | Thread thread = new Thread(RunAction); 28 | thread.Start(); 29 | 30 | return new TaskStatusWrapper(status); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /ModuleManager/Threading/ITaskStatus.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ModuleManager.Threading 4 | { 5 | public interface ITaskStatus 6 | { 7 | bool IsRunning { get; } 8 | bool IsFinished { get; } 9 | bool IsExitedWithError { get; } 10 | Exception Exception { get; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ModuleManager/Threading/TaskStatus.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ModuleManager.Threading 4 | { 5 | public class TaskStatus : ITaskStatus 6 | { 7 | private readonly object lockObject = new object(); 8 | 9 | public bool IsRunning { get; private set; } = true; 10 | public Exception Exception { get; private set; } = null; 11 | 12 | public bool IsFinished 13 | { 14 | get 15 | { 16 | lock (lockObject) 17 | { 18 | return !IsRunning && Exception == null; 19 | } 20 | } 21 | } 22 | 23 | public bool IsExitedWithError 24 | { 25 | get 26 | { 27 | lock (lockObject) 28 | { 29 | return !IsRunning && Exception != null; 30 | } 31 | } 32 | } 33 | 34 | public void Finished() 35 | { 36 | lock (lockObject) 37 | { 38 | if (!IsRunning) throw new InvalidOperationException("Task is not running"); 39 | IsRunning = false; 40 | } 41 | } 42 | 43 | public void Error(Exception exception) 44 | { 45 | lock(lockObject) 46 | { 47 | if (!IsRunning) throw new InvalidOperationException("Task is not running"); 48 | this.Exception = exception ?? throw new ArgumentNullException(nameof(exception)); 49 | IsRunning = false; 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ModuleManager/Threading/TaskStatusWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ModuleManager.Threading 4 | { 5 | public class TaskStatusWrapper : ITaskStatus 6 | { 7 | private readonly ITaskStatus inner; 8 | 9 | public TaskStatusWrapper(ITaskStatus inner) 10 | { 11 | this.inner = inner; 12 | } 13 | 14 | public bool IsRunning => inner.IsRunning; 15 | public bool IsFinished => inner.IsFinished; 16 | public bool IsExitedWithError => inner.IsExitedWithError; 17 | public Exception Exception => inner.Exception; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ModuleManager/Utils/Counter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ModuleManager.Utils 4 | { 5 | public class Counter 6 | { 7 | public int Value { get; private set; } = 0; 8 | 9 | public void Increment() 10 | { 11 | Value++; 12 | } 13 | 14 | public override string ToString() 15 | { 16 | return Value.ToString(); 17 | } 18 | 19 | public static implicit operator int(Counter counter) => counter.Value; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ModuleManager/Utils/FileUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Security.Cryptography; 4 | using ModuleManager.Extensions; 5 | 6 | namespace ModuleManager.Utils 7 | { 8 | public static class FileUtils 9 | { 10 | public static string FileSHA(string filename) 11 | { 12 | if (!File.Exists(filename)) throw new FileNotFoundException("File does not exist", filename); 13 | 14 | using SHA256 sha = SHA256.Create(); 15 | using FileStream fs = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); 16 | byte[] data = sha.ComputeHash(fs); 17 | 18 | return data.ToHex(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ModuleManager/copy_build.sh: -------------------------------------------------------------------------------- 1 | if [ -z "${TARGET_PATH}" ] ; then 2 | echo 'Expected $TARGET_PATH to be defined but it is not' >&2 3 | exit 1 4 | elif ! [ -f "${TARGET_PATH}" ] ; then 5 | echo 'Expected $TARGET_PATH to be a file but it is not' >&2 6 | exit 1 7 | fi 8 | 9 | if [ -z "${TARGET_DIR}" ] ; then 10 | echo 'Expected $TARGET_DIR to be defined but it is not' >&2 11 | exit 1 12 | elif ! [ -d "${TARGET_DIR}" ] ; then 13 | echo 'Expected $TARGET_DIR to be a directory but it is not' >&2 14 | exit 1 15 | fi 16 | 17 | if [ -z "${TARGET_NAME}" ] ; then 18 | echo 'Expected $TARGET_NAME to be defined but it is not' >&2 19 | exit 1 20 | fi 21 | 22 | if [ -z "${PDB2MDB}" ] ; then 23 | echo '$PDB2MDB not found' 24 | else 25 | echo "Running '${PDB2MDB}'" 26 | "${PDB2MDB}" "${TARGET_PATH}" 27 | fi 28 | 29 | if [ -z "${KSPDIR}" ] ; then 30 | echo '$KSPDIR not found' 31 | else 32 | if ! [ -d "${KSPDIR}" ] ; then 33 | echo 'Expected $KSPDIR to point to a directory but it is not' >&2 34 | exit 1 35 | fi 36 | if ! [ -d "${KSPDIR}/GameData" ] ; then 37 | echo 'Expected $KSPDIR to contain a GameData subdirectory but it does not' >&2 38 | exit 1 39 | fi 40 | echo "Copying to '${KSPDIR}'" 41 | cp "${TARGET_PATH}" "${KSPDIR}/GameData/" 42 | test -f "${TARGET_DIR}/${TARGET_NAME}.pdb" && cp "${TARGET_DIR}/${TARGET_NAME}.pdb" "${KSPDIR}/GameData/" 43 | test -f "${TARGET_DIR}/${TARGET_NAME}.dll.mdb" && cp "${TARGET_DIR}/${TARGET_NAME}.dll.mdb" "${KSPDIR}/GameData/" 44 | rm -f "${KSPDIR}/GameData/ModuleManager.ConfigCache" 45 | fi 46 | -------------------------------------------------------------------------------- /ModuleManager/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /ModuleManagerTests/Collections/KeyValueCacheTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using ModuleManager.Collections; 4 | 5 | namespace ModuleManagerTests.Collections 6 | { 7 | public class KeyValueCacheTest 8 | { 9 | [Fact] 10 | public void TestFetch__CreateValueNull() 11 | { 12 | KeyValueCache cache = new KeyValueCache(); 13 | ArgumentNullException ex = Assert.Throws(delegate 14 | { 15 | cache.Fetch(new object(), null); 16 | }); 17 | 18 | Assert.Equal("createValue", ex.ParamName); 19 | } 20 | 21 | [Fact] 22 | public void TestFetch__KeyNotPresent() 23 | { 24 | object key = new object(); 25 | object value = new object(); 26 | KeyValueCache cache = new KeyValueCache(); 27 | 28 | object fetchedValue = cache.Fetch(key, () => value); 29 | 30 | Assert.Same(value, fetchedValue); 31 | } 32 | 33 | [Fact] 34 | public void TestFetch__KeyPresent() 35 | { 36 | object key = new object(); 37 | object value = new object(); 38 | KeyValueCache cache = new KeyValueCache(); 39 | 40 | cache.Fetch(key, () => value); 41 | 42 | bool called2ndTime = false; 43 | object fetchedValue = cache.Fetch(key, delegate 44 | { 45 | called2ndTime = true; 46 | return null; 47 | }); 48 | 49 | Assert.Same(value, fetchedValue); 50 | Assert.False(called2ndTime); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ModuleManagerTests/Collections/MessageQueueTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using ModuleManager.Collections; 4 | 5 | namespace ModuleManagerTests.Collections 6 | { 7 | public class MessageQueueTest 8 | { 9 | private class TestClass { } 10 | 11 | private readonly MessageQueue queue = new MessageQueue(); 12 | 13 | [Fact] 14 | public void Test__Empty() 15 | { 16 | Assert.Empty(queue); 17 | } 18 | 19 | [Fact] 20 | public void TestAdd() 21 | { 22 | TestClass o1 = new TestClass(); 23 | TestClass o2 = new TestClass(); 24 | TestClass o3 = new TestClass(); 25 | 26 | queue.Add(o1); 27 | queue.Add(o2); 28 | queue.Add(o3); 29 | 30 | Assert.Equal(new[] { o1, o2, o3 }, queue); 31 | } 32 | 33 | [Fact] 34 | public void TestTakeAll() 35 | { 36 | TestClass o1 = new TestClass(); 37 | TestClass o2 = new TestClass(); 38 | TestClass o3 = new TestClass(); 39 | TestClass o4 = new TestClass(); 40 | 41 | queue.Add(o1); 42 | queue.Add(o2); 43 | queue.Add(o3); 44 | 45 | MessageQueue queue2 = Assert.IsType>(queue.TakeAll()); 46 | 47 | queue.Add(o4); 48 | 49 | Assert.Equal(new[] { o4 }, queue); 50 | Assert.Equal(new[] { o1, o2, o3 }, queue2); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ModuleManagerTests/CommandParserTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Xunit; 6 | using ModuleManager; 7 | 8 | namespace ModuleManagerTests 9 | { 10 | public class CommandParserTest 11 | { 12 | [Fact] 13 | public void TestParse__Insert() 14 | { 15 | Assert.Equal(Command.Insert, CommandParser.Parse("PART", out string newName)); 16 | Assert.Equal("PART", newName); 17 | } 18 | 19 | [Fact] 20 | public void TestParse__Delete() 21 | { 22 | Assert.Equal(Command.Delete, CommandParser.Parse("!PART", out string newName1)); 23 | Assert.Equal("PART", newName1); 24 | Assert.Equal(Command.Delete, CommandParser.Parse("-PART", out string newName2)); 25 | Assert.Equal("PART", newName2); 26 | } 27 | 28 | [Fact] 29 | public void TestParse__Edit() 30 | { 31 | Assert.Equal(Command.Edit, CommandParser.Parse("@PART", out string newName)); 32 | Assert.Equal("PART", newName); 33 | } 34 | 35 | [Fact] 36 | public void TestParse__Replace() 37 | { 38 | Assert.Equal(Command.Replace, CommandParser.Parse("%PART", out string newName)); 39 | Assert.Equal("PART", newName); 40 | } 41 | 42 | [Fact] 43 | public void TestParse__Copy() 44 | { 45 | Assert.Equal(Command.Copy, CommandParser.Parse("+PART", out string newName1)); 46 | Assert.Equal("PART", newName1); 47 | Assert.Equal(Command.Copy, CommandParser.Parse("$PART", out string newName2)); 48 | Assert.Equal("PART", newName2); 49 | } 50 | 51 | [Fact] 52 | public void TestParse__Rename() 53 | { 54 | Assert.Equal(Command.Rename, CommandParser.Parse("|PART", out string newName)); 55 | Assert.Equal("PART", newName); ; 56 | } 57 | 58 | [Fact] 59 | public void TestParse__Paste() 60 | { 61 | Assert.Equal(Command.Paste, CommandParser.Parse("#PART", out string newName)); 62 | Assert.Equal("PART", newName); 63 | } 64 | 65 | [Fact] 66 | public void TestParse__Special() 67 | { 68 | Assert.Equal(Command.Special, CommandParser.Parse("*PART", out string newName)); 69 | Assert.Equal("PART", newName); 70 | } 71 | 72 | [Fact] 73 | public void TestParse__Special__Chained() 74 | { 75 | Assert.Equal(Command.Special, CommandParser.Parse("*@PART", out string newName)); 76 | Assert.Equal("@PART", newName); 77 | } 78 | 79 | [Fact] 80 | public void TestParse__Create() 81 | { 82 | Assert.Equal(Command.Create, CommandParser.Parse("&PART", out string newName)); 83 | Assert.Equal("PART", newName); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /ModuleManagerTests/DummyTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | 4 | namespace ModuleManagerTests 5 | { 6 | public class DummyTest 7 | { 8 | [Fact] 9 | public void PassingTest() 10 | { 11 | Assert.True(true); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ModuleManagerTests/Extensions/ByteArrayExtensionsTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using ModuleManager.Extensions; 4 | 5 | namespace ModuleManagerTests.Extensions 6 | { 7 | public class ByteArrayExtensionsTest 8 | { 9 | [Fact] 10 | public void TestToHex() 11 | { 12 | byte[] data = { 0x00, 0xff, 0x01, 0xfe, 0x02, 0xfd, 0x9a }; 13 | 14 | Assert.Equal("00ff01fe02fd9a", data.ToHex()); 15 | } 16 | 17 | [Fact] 18 | public void TestToHex__NullData() 19 | { 20 | ArgumentNullException ex = Assert.Throws(delegate 21 | { 22 | ByteArrayExtensions.ToHex(null); 23 | }); 24 | 25 | Assert.Equal("data", ex.ParamName); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ModuleManagerTests/Extensions/IBasicLoggerExtensionsTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using NSubstitute; 4 | using ModuleManager.Logging; 5 | using ModuleManager.Extensions; 6 | 7 | namespace ModuleManagerTests.Extensions 8 | { 9 | public class IBasicLoggerExtensionsTest 10 | { 11 | private readonly IBasicLogger logger = Substitute.For(); 12 | 13 | [Fact] 14 | public void TestInfo() 15 | { 16 | logger.Info("well hi there"); 17 | logger.AssertInfo("well hi there"); 18 | } 19 | 20 | [Fact] 21 | public void TestWarning() 22 | { 23 | logger.Warning("I'm warning you"); 24 | logger.AssertWarning("I'm warning you"); 25 | } 26 | 27 | [Fact] 28 | public void TestError() 29 | { 30 | logger.Error("You have made a grave mistake"); 31 | logger.AssertError("You have made a grave mistake"); 32 | } 33 | 34 | [Fact] 35 | public void TestException() 36 | { 37 | Exception ex = new Exception(); 38 | logger.Exception(ex); 39 | logger.AssertException(ex); 40 | } 41 | 42 | [Fact] 43 | public void TestException__Null() 44 | { 45 | ArgumentNullException ex = Assert.Throws(delegate 46 | { 47 | logger.Exception(null); 48 | }); 49 | 50 | Assert.Equal("exception", ex.ParamName); 51 | } 52 | 53 | [Fact] 54 | public void TestException__Message() 55 | { 56 | Exception ex = new Exception(); 57 | logger.Exception("a message", ex); 58 | logger.AssertException("a message", ex); 59 | } 60 | 61 | [Fact] 62 | public void TestException__Message__MessageNull() 63 | { 64 | ArgumentNullException ex = Assert.Throws(delegate 65 | { 66 | logger.Exception(null, new Exception()); 67 | }); 68 | 69 | Assert.Equal("message", ex.ParamName); 70 | } 71 | 72 | [Fact] 73 | public void TestException__Message__ExceptionNull() 74 | { 75 | ArgumentNullException ex = Assert.Throws(delegate 76 | { 77 | logger.Exception("a message", null); 78 | }); 79 | 80 | Assert.Equal("exception", ex.ParamName); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /ModuleManagerTests/Extensions/NodeStackExtensionsTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using ModuleManager.Collections; 4 | using ModuleManager.Extensions; 5 | 6 | namespace ModuleManagerTests.Extensions 7 | { 8 | public class NodeStackExtensionsTest 9 | { 10 | [Fact] 11 | public void TestGetPath() 12 | { 13 | ConfigNode node1 = new ConfigNode("NODE1"); 14 | ConfigNode node2 = new ConfigNode("NODE2"); 15 | ConfigNode node3 = new ConfigNode("NODE3"); 16 | 17 | ImmutableStack stack = new ImmutableStack(node1).Push(node2).Push(node3); 18 | 19 | Assert.Equal("NODE1/NODE2/NODE3", stack.GetPath()); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ModuleManagerTests/Extensions/StringExtensionsTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Xunit; 6 | using ModuleManager.Extensions; 7 | 8 | namespace ModuleManagerTests.Extensions 9 | { 10 | public class StringExtensionsTest 11 | { 12 | [Fact] 13 | public void TestIsBracketBalanced() 14 | { 15 | Assert.True("abc[def[ghi[jkl]mno[pqr]]stu]vwx".IsBracketBalanced()); 16 | } 17 | 18 | [Fact] 19 | public void TestIsBracketBalanced__NoBrackets() 20 | { 21 | Assert.True("she sells seashells by the seashore".IsBracketBalanced()); 22 | } 23 | 24 | [Fact] 25 | public void TestIsBracketBalanced__Unbalanced() 26 | { 27 | Assert.False("abc[def[ghi[jkl]mno[pqr]]stuvwx".IsBracketBalanced()); 28 | Assert.False("abcdef[ghi[jkl]mno[pqr]]stu]vwx".IsBracketBalanced()); 29 | } 30 | 31 | [Fact] 32 | public void TestIsBracketBalanced__BalancedButNegative() 33 | { 34 | Assert.False("abc]def[ghi".IsBracketBalanced()); 35 | } 36 | 37 | [Fact] 38 | public void TestRemoveWS() 39 | { 40 | Assert.Equal("abcdef", " abc \tdef\r\n\t ".RemoveWS()); 41 | } 42 | 43 | 44 | [InlineData("abc", "b", true, 1)] 45 | [InlineData("abc", "x", false, -1)] 46 | [Theory] 47 | public void TestContains(string str, string test, bool expectedResult, int expectedIndex) 48 | { 49 | bool result = str.Contains(test, out int index); 50 | Assert.Equal(expectedResult, result); 51 | Assert.Equal(expectedIndex, index); 52 | } 53 | 54 | [Fact] 55 | public void TestContains__NullStr() 56 | { 57 | string s = null; 58 | Assert.Throws(delegate 59 | { 60 | s.Contains("x", out int _x); 61 | }); 62 | } 63 | 64 | [Fact] 65 | public void TestContains__NullValue() 66 | { 67 | Assert.Throws(delegate 68 | { 69 | "abc".Contains(null, out int _x); 70 | }); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /ModuleManagerTests/Extensions/UrlDirExtensionsTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using TestUtils; 4 | using ModuleManager.Extensions; 5 | 6 | namespace ModuleManagerTests.Extensions 7 | { 8 | public class UrlDirExtensionsTest 9 | { 10 | 11 | [Fact] 12 | public void TestFind__IndirectChild() 13 | { 14 | UrlDir urlDir = UrlBuilder.CreateDir("abc"); 15 | UrlDir.UrlFile urlFile = UrlBuilder.CreateFile("def/ghi.cfg", urlDir); 16 | 17 | Assert.Equal(urlFile, urlDir.Find("def/ghi")); 18 | } 19 | 20 | [Fact] 21 | public void TestFind__DirectChild() 22 | { 23 | UrlDir urlDir = UrlBuilder.CreateDir("abc"); 24 | UrlDir.UrlFile urlFile = UrlBuilder.CreateFile("def.cfg", urlDir); 25 | 26 | Assert.Equal(urlFile, urlDir.Find("def")); 27 | } 28 | 29 | [Fact] 30 | public void TestFind__Extension() 31 | { 32 | UrlDir urlDir = UrlBuilder.CreateDir("abc"); 33 | UrlBuilder.CreateFile("def/ghi.yyy", urlDir); 34 | UrlDir.UrlFile urlFile = UrlBuilder.CreateFile("def/ghi.cfg", urlDir); 35 | UrlBuilder.CreateFile("def/ghi.zzz", urlDir); 36 | 37 | Assert.Equal(urlFile, urlDir.Find("def/ghi.cfg")); 38 | } 39 | 40 | [Fact] 41 | public void TestFind__NotFound() 42 | { 43 | UrlDir urlDir = UrlBuilder.CreateDir("abc"); 44 | UrlBuilder.CreateDir("def", urlDir); 45 | 46 | Assert.Null(urlDir.Find("def/ghi")); 47 | } 48 | 49 | [Fact] 50 | public void TestFind__Extension__NotFound() 51 | { 52 | UrlDir urlDir = UrlBuilder.CreateDir("abc"); 53 | UrlBuilder.CreateFile("def/ghi.yyy", urlDir); 54 | UrlBuilder.CreateFile("def/ghi.zzz", urlDir); 55 | 56 | Assert.Null(urlDir.Find("def/ghi.cfg")); 57 | } 58 | 59 | [Fact] 60 | public void TestFind__IntermediateDirectoryNotFound() 61 | { 62 | UrlDir urlDir = UrlBuilder.CreateDir("abc"); 63 | Assert.Null(urlDir.Find("def/ghi")); 64 | } 65 | 66 | [Fact] 67 | public void TestFind__UrlDirNull() 68 | { 69 | ArgumentNullException ex = Assert.Throws(delegate 70 | { 71 | UrlDirExtensions.Find(null, "abc"); 72 | }); 73 | 74 | Assert.Equal("urlDir", ex.ParamName); 75 | } 76 | 77 | [Fact] 78 | public void TestFind__UrlNull() 79 | { 80 | ArgumentNullException ex = Assert.Throws(delegate 81 | { 82 | UrlDirExtensions.Find(UrlBuilder.CreateDir("abc"), null); 83 | }); 84 | 85 | Assert.Equal("url", ex.ParamName); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /ModuleManagerTests/Extensions/UrlFileExtensionsTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using TestUtils; 4 | using ModuleManager.Extensions; 5 | 6 | namespace ModuleManagerTests.Extensions 7 | { 8 | public static class UrlFileExtensionsTest 9 | { 10 | [Fact] 11 | public static void TestGetUrlWithExtension() 12 | { 13 | UrlDir.UrlFile urlFile = UrlBuilder.CreateFile("abc/def/ghi.cfg"); 14 | Assert.Equal("abc/def/ghi.cfg", urlFile.GetUrlWithExtension()); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ModuleManagerTests/Logging/LogSplitterTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using NSubstitute; 4 | using UnityEngine; 5 | using ModuleManager.Logging; 6 | 7 | namespace ModuleManagerTests.Logging 8 | { 9 | public class LogSplitterTest 10 | { 11 | [Fact] 12 | public void TestConstructor__Logger1Null() 13 | { 14 | ArgumentNullException ex = Assert.Throws(delegate 15 | { 16 | new LogSplitter(null, Substitute.For()); 17 | }); 18 | 19 | Assert.Equal("logger1", ex.ParamName); 20 | } 21 | 22 | [Fact] 23 | public void TestConstructor__Logger2Null() 24 | { 25 | ArgumentNullException ex = Assert.Throws(delegate 26 | { 27 | new LogSplitter(Substitute.For(), null); 28 | }); 29 | 30 | Assert.Equal("logger2", ex.ParamName); 31 | } 32 | 33 | [Fact] 34 | public void TestLog() 35 | { 36 | IBasicLogger logger1 = Substitute.For(); 37 | IBasicLogger logger2 = Substitute.For(); 38 | LogSplitter logSplitter = new LogSplitter(logger1, logger2); 39 | ILogMessage message = Substitute.For(); 40 | logSplitter.Log(message); 41 | logger1.Received().Log(message); 42 | logger2.Received().Log(message); 43 | } 44 | 45 | [Fact] 46 | public void TestLog__MessageNull() 47 | { 48 | LogSplitter logSplitter = new LogSplitter(Substitute.For(), Substitute.For()); 49 | ArgumentNullException ex = Assert.Throws(delegate 50 | { 51 | logSplitter.Log(null); 52 | }); 53 | 54 | Assert.Equal("message", ex.ParamName); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /ModuleManagerTests/Logging/PrefixLoggerTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using NSubstitute; 4 | using UnityEngine; 5 | using ModuleManager.Logging; 6 | 7 | namespace ModuleManagerTests.Logging 8 | { 9 | public class PrefixLoggerTest 10 | { 11 | private readonly IBasicLogger innerLogger = Substitute.For(); 12 | private readonly PrefixLogger logger; 13 | 14 | public PrefixLoggerTest() 15 | { 16 | logger = new PrefixLogger("MyMod", innerLogger); 17 | } 18 | 19 | [Fact] 20 | public void TestConstructor__PrefixNull() 21 | { 22 | ArgumentNullException e = Assert.Throws(delegate 23 | { 24 | new PrefixLogger(null, innerLogger); 25 | }); 26 | 27 | Assert.Equal("prefix", e.ParamName); 28 | } 29 | 30 | [Fact] 31 | public void TestConstructor__PrefixBlank() 32 | { 33 | ArgumentNullException e = Assert.Throws(delegate 34 | { 35 | new PrefixLogger("", innerLogger); 36 | }); 37 | 38 | Assert.Equal("prefix", e.ParamName); 39 | } 40 | 41 | [Fact] 42 | public void TestConstructor__LoggerNull() 43 | { 44 | ArgumentNullException e = Assert.Throws(delegate 45 | { 46 | new PrefixLogger("blah", null); 47 | }); 48 | 49 | Assert.Equal("logger", e.ParamName); 50 | } 51 | 52 | [Fact] 53 | public void TestLog() 54 | { 55 | ILogMessage logMessage = Substitute.For(); 56 | logMessage.LogType.Returns(LogType.Log); 57 | logMessage.Message.Returns("well hi there"); 58 | logMessage.Timestamp.Returns(new DateTime(2000, 1, 1, 12, 34, 45, 678)); 59 | 60 | logger.Log(logMessage); 61 | 62 | innerLogger.Received().Log(Arg.Is(msg => 63 | msg.LogType == LogType.Log && 64 | msg.Timestamp == logMessage.Timestamp && 65 | msg.Message == "[MyMod] well hi there" 66 | )); 67 | } 68 | 69 | [Fact] 70 | public void TestLog__Null() 71 | { 72 | ArgumentNullException ex = Assert.Throws(delegate 73 | { 74 | logger.Log(null); 75 | }); 76 | 77 | Assert.Equal("message", ex.ParamName); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /ModuleManagerTests/Logging/QueueLoggerTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using NSubstitute; 4 | using ModuleManager.Collections; 5 | using ModuleManager.Logging; 6 | 7 | namespace ModuleManagerTests.Logging 8 | { 9 | public class QueueLoggerTest 10 | { 11 | private readonly IMessageQueue queue = Substitute.For>(); 12 | private readonly QueueLogger logger; 13 | 14 | public QueueLoggerTest() 15 | { 16 | logger = new QueueLogger(queue); 17 | } 18 | 19 | [Fact] 20 | public void TestConstructor__QueueNull() 21 | { 22 | ArgumentNullException ex = Assert.Throws(delegate 23 | { 24 | new QueueLogger(null); 25 | }); 26 | 27 | Assert.Equal("queue", ex.ParamName); 28 | } 29 | 30 | [Fact] 31 | public void TestLog() 32 | { 33 | ILogMessage message = Substitute.For(); 34 | logger.Log(message); 35 | queue.Received().Add(message); 36 | } 37 | 38 | [Fact] 39 | public void TestLog__MessageNull() 40 | { 41 | ArgumentNullException ex = Assert.Throws(delegate 42 | { 43 | logger.Log(null); 44 | }); 45 | 46 | Assert.Equal("message", ex.ParamName); 47 | } 48 | 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /ModuleManagerTests/Logging/StreamLoggerTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using Xunit; 4 | using NSubstitute; 5 | using ModuleManager.Logging; 6 | 7 | namespace ModuleManagerTests.Logging 8 | { 9 | public class StreamLoggerTest 10 | { 11 | [Fact] 12 | public void TestConstructor__StreamNull() 13 | { 14 | Assert.Throws(delegate 15 | { 16 | new StreamLogger(null); 17 | }); 18 | } 19 | 20 | [Fact] 21 | public void TestConstructor__CantWrite() 22 | { 23 | using (MemoryStream stream = new MemoryStream(new byte[0], false)) 24 | { 25 | Assert.Throws(delegate 26 | { 27 | new StreamLogger(stream); 28 | }); 29 | } 30 | } 31 | 32 | [Fact] 33 | public void TestLog__AlreadyDisposed() 34 | { 35 | using (MemoryStream stream = new MemoryStream(new byte[0], true)) 36 | { 37 | StreamLogger streamLogger = new StreamLogger(stream); 38 | streamLogger.Dispose(); 39 | 40 | InvalidOperationException ex = Assert.Throws(delegate 41 | { 42 | streamLogger.Log(Substitute.For()); 43 | }); 44 | 45 | Assert.Contains("Object has already been disposed", ex.Message); 46 | } 47 | } 48 | 49 | [Fact] 50 | public void TestLog() 51 | { 52 | ILogMessage message = Substitute.For(); 53 | message.ToLogString().Returns("[OMG wtf] bbq"); 54 | byte[] bytes = new byte[15]; 55 | using (MemoryStream stream = new MemoryStream(bytes, true)) 56 | { 57 | using (StreamLogger streamLogger = new StreamLogger(stream)) 58 | { 59 | streamLogger.Log(message); 60 | } 61 | } 62 | 63 | using (MemoryStream stream = new MemoryStream(bytes, false)) 64 | { 65 | using (StreamReader reader = new StreamReader(stream)) 66 | { 67 | string result = reader.ReadToEnd().Trim('\r', '\n', '\0'); 68 | Assert.Equal("[OMG wtf] bbq", result); 69 | } 70 | } 71 | } 72 | 73 | [Fact] 74 | public void TestLog__MessageNull() 75 | { 76 | using (MemoryStream stream = new MemoryStream(new byte[0], true)) 77 | { 78 | StreamLogger streamLogger = new StreamLogger(stream); 79 | 80 | ArgumentNullException ex = Assert.Throws(delegate 81 | { 82 | streamLogger.Log(null); 83 | }); 84 | 85 | Assert.Equal("message", ex.ParamName); 86 | } 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /ModuleManagerTests/Logging/UnityLoggerTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using NSubstitute; 4 | using UnityEngine; 5 | using ModuleManager.Extensions; 6 | using ModuleManager.Logging; 7 | 8 | namespace ModuleManagerTests.Logging 9 | { 10 | public class UnityLoggerTest 11 | { 12 | private readonly ILogger innerLogger = Substitute.For(); 13 | private readonly UnityLogger logger; 14 | 15 | public UnityLoggerTest() 16 | { 17 | logger = new UnityLogger(innerLogger); 18 | } 19 | 20 | [Fact] 21 | public void TestConstructor__LoggerNull() 22 | { 23 | ArgumentNullException e = Assert.Throws(delegate 24 | { 25 | new UnityLogger(null); 26 | }); 27 | 28 | Assert.Equal("logger", e.ParamName); 29 | } 30 | 31 | [Fact] 32 | public void TestLog__Info() 33 | { 34 | logger.Info("well hi there"); 35 | 36 | innerLogger.Received().Log(LogType.Log, "well hi there"); 37 | } 38 | 39 | [Fact] 40 | public void TestLog__Warning() 41 | { 42 | logger.Warning("I'm warning you"); 43 | 44 | innerLogger.Received().Log(LogType.Warning, "I'm warning you"); 45 | } 46 | 47 | [Fact] 48 | public void TestLog__MessageNull() 49 | { 50 | ArgumentNullException e = Assert.Throws(delegate 51 | { 52 | logger.Log(null); 53 | }); 54 | 55 | Assert.Equal("message", e.ParamName); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /ModuleManagerTests/LoggingAssertionHelpers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | using NSubstitute; 4 | using ModuleManager.Logging; 5 | 6 | namespace ModuleManagerTests 7 | { 8 | public static class LoggingAssertionHelpers 9 | { 10 | public static void AssertInfo(this IBasicLogger logger, string message) 11 | { 12 | if (logger == null) throw new ArgumentNullException(nameof(logger)); 13 | logger.Received().Log(Arg.Is(msg => msg.LogType == LogType.Log && msg.Message == message)); 14 | } 15 | 16 | public static void AssertNoInfo(this IBasicLogger logger) 17 | { 18 | if (logger == null) throw new ArgumentNullException(nameof(logger)); 19 | logger.DidNotReceive().Log(Arg.Is(msg => msg.LogType == LogType.Log)); 20 | } 21 | 22 | public static void AssertWarning(this IBasicLogger logger, string message) 23 | { 24 | if (logger == null) throw new ArgumentNullException(nameof(logger)); 25 | logger.Received().Log(Arg.Is(msg => msg.LogType == LogType.Warning && msg.Message == message)); 26 | } 27 | 28 | public static void AssertNoWarning(this IBasicLogger logger) 29 | { 30 | if (logger == null) throw new ArgumentNullException(nameof(logger)); 31 | logger.DidNotReceive().Log(Arg.Is(msg => msg.LogType == LogType.Warning)); 32 | } 33 | 34 | public static void AssertError(this IBasicLogger logger, string message) 35 | { 36 | if (logger == null) throw new ArgumentNullException(nameof(logger)); 37 | logger.Received().Log(Arg.Is(msg => msg.LogType == LogType.Error && msg.Message == message)); 38 | } 39 | 40 | public static void AssertNoError(this IBasicLogger logger) 41 | { 42 | if (logger == null) throw new ArgumentNullException(nameof(logger)); 43 | logger.DidNotReceive().Log(Arg.Is(msg => msg.LogType == LogType.Error)); 44 | } 45 | 46 | public static void AssertException(this IBasicLogger logger, string message, Exception exception) 47 | { 48 | if (logger == null) throw new ArgumentNullException(nameof(logger)); 49 | logger.Received().Log(Arg.Is(msg => msg.LogType == LogType.Exception && msg.Message == message + ": " + exception.ToString())); 50 | } 51 | 52 | public static void AssertException(this IBasicLogger logger, Exception exception) 53 | { 54 | if (logger == null) throw new ArgumentNullException(nameof(logger)); 55 | logger.Received().Log(Arg.Is(msg => msg.LogType == LogType.Exception && msg.Message == exception.ToString())); 56 | } 57 | 58 | public static void AssertNoException(this IBasicLogger logger) 59 | { 60 | if (logger == null) throw new ArgumentNullException(nameof(logger)); 61 | logger.DidNotReceive().Log(Arg.Is(msg => msg.LogType == LogType.Exception)); 62 | } 63 | 64 | public static void AssertNoLog(this IBasicLogger logger) 65 | { 66 | if (logger == null) throw new ArgumentNullException(nameof(logger)); 67 | logger.DidNotReceiveWithAnyArgs().Log(null); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /ModuleManagerTests/OperatorParserTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using ModuleManager; 4 | 5 | namespace ModuleManagerTests 6 | { 7 | public class OperatorParserTest 8 | { 9 | [Fact] 10 | public void TestParse__Null() 11 | { 12 | ArgumentNullException ex = Assert.Throws(delegate 13 | { 14 | OperatorParser.Parse(null, out string _); 15 | }); 16 | 17 | Assert.Equal("name", ex.ParamName); 18 | } 19 | 20 | [Fact] 21 | public void TestParse__Empty() 22 | { 23 | Operator op = OperatorParser.Parse("", out string result); 24 | 25 | Assert.Equal(Operator.Assign, op); 26 | Assert.Equal("", result); 27 | } 28 | 29 | [Fact] 30 | public void TestParse__Assign() 31 | { 32 | Operator op = OperatorParser.Parse("some_stuff,1[2, ]", out string result); 33 | 34 | Assert.Equal(Operator.Assign, op); 35 | Assert.Equal("some_stuff,1[2, ]", result); 36 | } 37 | 38 | [Fact] 39 | public void TestParse__Add() 40 | { 41 | Operator op = OperatorParser.Parse("some_stuff,1[2, ] +", out string result); 42 | 43 | Assert.Equal(Operator.Add, op); 44 | Assert.Equal("some_stuff,1[2, ]", result); 45 | } 46 | 47 | [Fact] 48 | public void TestParse__Subtract() 49 | { 50 | Operator op = OperatorParser.Parse("some_stuff,1[2, ] -", out string result); 51 | 52 | Assert.Equal(Operator.Subtract, op); 53 | Assert.Equal("some_stuff,1[2, ]", result); 54 | } 55 | 56 | [Fact] 57 | public void TestParse__Multiply() 58 | { 59 | Operator op = OperatorParser.Parse("some_stuff,1[2, ] *", out string result); 60 | 61 | Assert.Equal(Operator.Multiply, op); 62 | Assert.Equal("some_stuff,1[2, ]", result); 63 | } 64 | 65 | [Fact] 66 | public void TestParse__Divide() 67 | { 68 | Operator op = OperatorParser.Parse("some_stuff,1[2, ] /", out string result); 69 | 70 | Assert.Equal(Operator.Divide, op); 71 | Assert.Equal("some_stuff,1[2, ]", result); 72 | } 73 | 74 | [Fact] 75 | public void TestParse__Exponentiate() 76 | { 77 | Operator op = OperatorParser.Parse("some_stuff,1[2, ] !", out string result); 78 | 79 | Assert.Equal(Operator.Exponentiate, op); 80 | Assert.Equal("some_stuff,1[2, ]", result); 81 | } 82 | 83 | [Fact] 84 | public void TestParse__RegexReplace() 85 | { 86 | Operator op = OperatorParser.Parse("some_stuff,1[2, ] ^", out string result); 87 | 88 | Assert.Equal(Operator.RegexReplace, op); 89 | Assert.Equal("some_stuff,1[2, ]", result); 90 | } 91 | 92 | [Fact] 93 | public void TestParse__NoSpaceMeansNoOp() 94 | { 95 | Operator op = OperatorParser.Parse("some_stuff*", out string result); 96 | 97 | Assert.Equal(Operator.Assign, op); 98 | Assert.Equal("some_stuff*", result); 99 | } 100 | 101 | [Fact] 102 | public void TestParse__SingleCharacterNotOp() 103 | { 104 | Operator op = OperatorParser.Parse("*", out string result); 105 | 106 | Assert.Equal(Operator.Assign, op); 107 | Assert.Equal("*", result); 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /ModuleManagerTests/PassTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Xunit; 4 | using NSubstitute; 5 | using ModuleManager; 6 | using ModuleManager.Patches; 7 | 8 | namespace ModuleManagerTests 9 | { 10 | public class PassTest 11 | { 12 | [Fact] 13 | public void TestConstructor__NameNull() 14 | { 15 | ArgumentNullException ex = Assert.Throws(delegate 16 | { 17 | new Pass(null); 18 | }); 19 | 20 | Assert.Equal("name", ex.ParamName); 21 | } 22 | 23 | [Fact] 24 | public void TestConstructor__NameEmpty() 25 | { 26 | ArgumentException ex = Assert.Throws(delegate 27 | { 28 | new Pass(""); 29 | }); 30 | 31 | Assert.Contains("can't be empty", ex.Message); 32 | Assert.Equal("name", ex.ParamName); 33 | } 34 | 35 | [Fact] 36 | public void TestName() 37 | { 38 | Pass pass = new Pass(":NOTINAMILLIONYEARS"); 39 | 40 | Assert.Equal(":NOTINAMILLIONYEARS", pass.Name); 41 | } 42 | 43 | [Fact] 44 | public void Test__Add__Enumerator() 45 | { 46 | IPatch[] patches = 47 | { 48 | Substitute.For(), 49 | Substitute.For(), 50 | Substitute.For(), 51 | }; 52 | 53 | Pass pass = new Pass("blah") 54 | { 55 | patches[0], 56 | patches[1], 57 | patches[2], 58 | }; 59 | 60 | IPatch[] passPatches = pass.ToArray(); 61 | Assert.Equal(patches.Length, passPatches.Length); 62 | 63 | for (int i = 0; i < patches.Length; i++) 64 | { 65 | Assert.Same(patches[i], passPatches[i]); 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /ModuleManagerTests/Patches/PassSpecifiers/AfterPassSpecifierTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using NSubstitute; 4 | using TestUtils; 5 | using ModuleManager; 6 | using ModuleManager.Patches.PassSpecifiers; 7 | using ModuleManager.Progress; 8 | 9 | namespace ModuleManagerTests.Patches 10 | { 11 | public class AfterPassSpecifierTest 12 | { 13 | public readonly UrlDir.UrlConfig urlConfig = UrlBuilder.CreateConfig("abc/def", new ConfigNode("NODE")); 14 | public readonly INeedsChecker needsChecker = Substitute.For(); 15 | public readonly IPatchProgress progress = Substitute.For(); 16 | public readonly AfterPassSpecifier passSpecifier; 17 | 18 | public AfterPassSpecifierTest() 19 | { 20 | passSpecifier = new AfterPassSpecifier("mod1", urlConfig); 21 | } 22 | 23 | [Fact] 24 | public void TestConstructor__ModNull() 25 | { 26 | ArgumentNullException ex = Assert.Throws(delegate 27 | { 28 | new AfterPassSpecifier(null, urlConfig); 29 | }); 30 | 31 | Assert.Equal("mod", ex.ParamName); 32 | } 33 | 34 | [Fact] 35 | public void TestConstructor__ModEmpty() 36 | { 37 | ArgumentException ex = Assert.Throws(delegate 38 | { 39 | new AfterPassSpecifier("", urlConfig); 40 | }); 41 | 42 | Assert.Equal("mod", ex.ParamName); 43 | Assert.Contains("can't be empty", ex.Message); 44 | } 45 | 46 | [Fact] 47 | public void TestConstructor__UrlConfigNull() 48 | { 49 | ArgumentNullException ex = Assert.Throws(delegate 50 | { 51 | new AfterPassSpecifier("mod1", null); 52 | }); 53 | 54 | Assert.Equal("urlConfig", ex.ParamName); 55 | } 56 | 57 | [Fact] 58 | public void TestCheckNeeds__False() 59 | { 60 | needsChecker.CheckNeeds("mod1").Returns(false); 61 | Assert.False(passSpecifier.CheckNeeds(needsChecker, progress)); 62 | 63 | progress.Received().NeedsUnsatisfiedAfter(urlConfig); 64 | } 65 | 66 | [Fact] 67 | public void TestCheckNeeds__True() 68 | { 69 | needsChecker.CheckNeeds("mod1").Returns(true); 70 | Assert.True(passSpecifier.CheckNeeds(needsChecker, progress)); 71 | 72 | progress.DidNotReceiveWithAnyArgs().NeedsUnsatisfiedAfter(null); 73 | } 74 | 75 | [Fact] 76 | public void TestCheckNeeds__NeedsCheckerNull() 77 | { 78 | ArgumentNullException ex = Assert.Throws(delegate 79 | { 80 | passSpecifier.CheckNeeds(null, progress); 81 | }); 82 | 83 | Assert.Equal("needsChecker", ex.ParamName); 84 | 85 | progress.DidNotReceiveWithAnyArgs().NeedsUnsatisfiedAfter(null); 86 | } 87 | 88 | [Fact] 89 | public void TestCheckNeeds__ProgressNull() 90 | { 91 | ArgumentNullException ex = Assert.Throws(delegate 92 | { 93 | passSpecifier.CheckNeeds(needsChecker, null); 94 | }); 95 | 96 | Assert.Equal("progress", ex.ParamName); 97 | } 98 | 99 | [Fact] 100 | public void TestDescriptor() 101 | { 102 | Assert.Equal(":AFTER[MOD1]", passSpecifier.Descriptor); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /ModuleManagerTests/Patches/PassSpecifiers/BeforePassSpecifierTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using NSubstitute; 4 | using TestUtils; 5 | using ModuleManager; 6 | using ModuleManager.Patches.PassSpecifiers; 7 | using ModuleManager.Progress; 8 | 9 | namespace ModuleManagerTests.Patches.PassSpecifiers 10 | { 11 | public class BeforePassSpecifierTest 12 | { 13 | public readonly UrlDir.UrlConfig urlConfig = UrlBuilder.CreateConfig("abc/def", new ConfigNode("NODE")); 14 | public readonly INeedsChecker needsChecker = Substitute.For(); 15 | public readonly IPatchProgress progress = Substitute.For(); 16 | public readonly BeforePassSpecifier passSpecifier; 17 | 18 | public BeforePassSpecifierTest() 19 | { 20 | passSpecifier = new BeforePassSpecifier("mod1", urlConfig); 21 | } 22 | 23 | [Fact] 24 | public void TestConstructor__ModNull() 25 | { 26 | ArgumentNullException ex = Assert.Throws(delegate 27 | { 28 | new BeforePassSpecifier(null, urlConfig); 29 | }); 30 | 31 | Assert.Equal("mod", ex.ParamName); 32 | } 33 | 34 | [Fact] 35 | public void TestConstructor__ModEmpty() 36 | { 37 | ArgumentException ex = Assert.Throws(delegate 38 | { 39 | new BeforePassSpecifier("", urlConfig); 40 | }); 41 | 42 | Assert.Equal("mod", ex.ParamName); 43 | Assert.Contains("can't be empty", ex.Message); 44 | } 45 | 46 | [Fact] 47 | public void TestConstructor__UrlConfigNull() 48 | { 49 | ArgumentNullException ex = Assert.Throws(delegate 50 | { 51 | new BeforePassSpecifier("mod1", null); 52 | }); 53 | 54 | Assert.Equal("urlConfig", ex.ParamName); 55 | } 56 | 57 | [Fact] 58 | public void TestCheckNeeds__False() 59 | { 60 | needsChecker.CheckNeeds("mod1").Returns(false); 61 | Assert.False(passSpecifier.CheckNeeds(needsChecker, progress)); 62 | 63 | progress.Received().NeedsUnsatisfiedBefore(urlConfig); 64 | } 65 | 66 | [Fact] 67 | public void TestCheckNeeds__True() 68 | { 69 | needsChecker.CheckNeeds("mod1").Returns(true); 70 | Assert.True(passSpecifier.CheckNeeds(needsChecker, progress)); 71 | 72 | progress.DidNotReceiveWithAnyArgs().NeedsUnsatisfiedBefore(null); 73 | } 74 | 75 | [Fact] 76 | public void TestCheckNeeds__NeedsCheckerNull() 77 | { 78 | ArgumentNullException ex = Assert.Throws(delegate 79 | { 80 | passSpecifier.CheckNeeds(null, progress); 81 | }); 82 | 83 | Assert.Equal("needsChecker", ex.ParamName); 84 | 85 | progress.DidNotReceiveWithAnyArgs().NeedsUnsatisfiedBefore(null); 86 | } 87 | 88 | [Fact] 89 | public void TestCheckNeeds__ProgressNull() 90 | { 91 | ArgumentNullException ex = Assert.Throws(delegate 92 | { 93 | passSpecifier.CheckNeeds(needsChecker, null); 94 | }); 95 | 96 | Assert.Equal("progress", ex.ParamName); 97 | } 98 | 99 | [Fact] 100 | public void TestDescriptor() 101 | { 102 | Assert.Equal(":BEFORE[MOD1]", passSpecifier.Descriptor); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /ModuleManagerTests/Patches/PassSpecifiers/FinalPassSpecifierTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using NSubstitute; 4 | using ModuleManager; 5 | using ModuleManager.Patches.PassSpecifiers; 6 | using ModuleManager.Progress; 7 | 8 | namespace ModuleManagerTests.Patches 9 | { 10 | public class FinalPassSpecifierrTest 11 | { 12 | public readonly INeedsChecker needsChecker = Substitute.For(); 13 | public readonly IPatchProgress progress = Substitute.For(); 14 | private readonly FinalPassSpecifier passSpecifier = new FinalPassSpecifier(); 15 | 16 | [Fact] 17 | public void TestCheckNeeds() 18 | { 19 | Assert.True(passSpecifier.CheckNeeds(needsChecker, progress)); 20 | } 21 | 22 | [Fact] 23 | public void TestCheckNeeds__NeedsCheckerNull() 24 | { 25 | ArgumentNullException ex = Assert.Throws(delegate 26 | { 27 | passSpecifier.CheckNeeds(null, progress); 28 | }); 29 | 30 | Assert.Equal("needsChecker", ex.ParamName); 31 | 32 | progress.DidNotReceiveWithAnyArgs().NeedsUnsatisfiedAfter(null); 33 | } 34 | 35 | [Fact] 36 | public void TestCheckNeeds__ProgressNull() 37 | { 38 | ArgumentNullException ex = Assert.Throws(delegate 39 | { 40 | passSpecifier.CheckNeeds(needsChecker, null); 41 | }); 42 | 43 | Assert.Equal("progress", ex.ParamName); 44 | } 45 | 46 | [Fact] 47 | public void TestDescriptor() 48 | { 49 | Assert.Equal(":FINAL", passSpecifier.Descriptor); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ModuleManagerTests/Patches/PassSpecifiers/FirstPassSpecifierTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using NSubstitute; 4 | using ModuleManager; 5 | using ModuleManager.Patches.PassSpecifiers; 6 | using ModuleManager.Progress; 7 | 8 | namespace ModuleManagerTests.Patches 9 | { 10 | public class FirstPassSpecifierTest 11 | { 12 | public readonly INeedsChecker needsChecker = Substitute.For(); 13 | public readonly IPatchProgress progress = Substitute.For(); 14 | private readonly FirstPassSpecifier passSpecifier = new FirstPassSpecifier(); 15 | 16 | [Fact] 17 | public void TestCheckNeeds() 18 | { 19 | Assert.True(passSpecifier.CheckNeeds(needsChecker, progress)); 20 | } 21 | 22 | [Fact] 23 | public void TestCheckNeeds__NeedsCheckerNull() 24 | { 25 | ArgumentNullException ex = Assert.Throws(delegate 26 | { 27 | passSpecifier.CheckNeeds(null, progress); 28 | }); 29 | 30 | Assert.Equal("needsChecker", ex.ParamName); 31 | 32 | progress.DidNotReceiveWithAnyArgs().NeedsUnsatisfiedAfter(null); 33 | } 34 | 35 | [Fact] 36 | public void TestCheckNeeds__ProgressNull() 37 | { 38 | ArgumentNullException ex = Assert.Throws(delegate 39 | { 40 | passSpecifier.CheckNeeds(needsChecker, null); 41 | }); 42 | 43 | Assert.Equal("progress", ex.ParamName); 44 | } 45 | 46 | [Fact] 47 | public void TestDescriptor() 48 | { 49 | Assert.Equal(":FIRST", passSpecifier.Descriptor); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ModuleManagerTests/Patches/PassSpecifiers/ForPassSpecifierTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using NSubstitute; 4 | using TestUtils; 5 | using ModuleManager; 6 | using ModuleManager.Patches.PassSpecifiers; 7 | using ModuleManager.Progress; 8 | 9 | namespace ModuleManagerTests.Patches 10 | { 11 | public class ForPassSpecifierTest 12 | { 13 | public readonly UrlDir.UrlConfig urlConfig = UrlBuilder.CreateConfig("abc/def", new ConfigNode("NODE")); 14 | public readonly INeedsChecker needsChecker = Substitute.For(); 15 | public readonly IPatchProgress progress = Substitute.For(); 16 | public readonly ForPassSpecifier passSpecifier; 17 | 18 | public ForPassSpecifierTest() 19 | { 20 | passSpecifier = new ForPassSpecifier("mod1", urlConfig); 21 | } 22 | 23 | [Fact] 24 | public void TestConstructor__ModNull() 25 | { 26 | ArgumentNullException ex = Assert.Throws(delegate 27 | { 28 | new ForPassSpecifier(null, urlConfig); 29 | }); 30 | 31 | Assert.Equal("mod", ex.ParamName); 32 | } 33 | 34 | [Fact] 35 | public void TestConstructor__ModEmpty() 36 | { 37 | ArgumentException ex = Assert.Throws(delegate 38 | { 39 | new ForPassSpecifier("", urlConfig); 40 | }); 41 | 42 | Assert.Equal("mod", ex.ParamName); 43 | Assert.Contains("can't be empty", ex.Message); 44 | } 45 | 46 | [Fact] 47 | public void TestConstructor__UrlConfigNull() 48 | { 49 | ArgumentNullException ex = Assert.Throws(delegate 50 | { 51 | new ForPassSpecifier("mod1", null); 52 | }); 53 | 54 | Assert.Equal("urlConfig", ex.ParamName); 55 | } 56 | 57 | [Fact] 58 | public void TestCheckNeeds__False() 59 | { 60 | needsChecker.CheckNeeds("mod1").Returns(false); 61 | Assert.False(passSpecifier.CheckNeeds(needsChecker, progress)); 62 | 63 | progress.Received().NeedsUnsatisfiedFor(urlConfig); 64 | } 65 | 66 | [Fact] 67 | public void TestCheckNeeds__True() 68 | { 69 | needsChecker.CheckNeeds("mod1").Returns(true); 70 | Assert.True(passSpecifier.CheckNeeds(needsChecker, progress)); 71 | 72 | progress.DidNotReceiveWithAnyArgs().NeedsUnsatisfiedFor(null); 73 | } 74 | 75 | [Fact] 76 | public void TestCheckNeeds__NeedsCheckerNull() 77 | { 78 | ArgumentNullException ex = Assert.Throws(delegate 79 | { 80 | passSpecifier.CheckNeeds(null, progress); 81 | }); 82 | 83 | Assert.Equal("needsChecker", ex.ParamName); 84 | 85 | progress.DidNotReceiveWithAnyArgs().NeedsUnsatisfiedFor(null); 86 | } 87 | 88 | [Fact] 89 | public void TestCheckNeeds__ProgressNull() 90 | { 91 | ArgumentNullException ex = Assert.Throws(delegate 92 | { 93 | passSpecifier.CheckNeeds(needsChecker, null); 94 | }); 95 | 96 | Assert.Equal("progress", ex.ParamName); 97 | } 98 | 99 | [Fact] 100 | public void TestDescriptor() 101 | { 102 | Assert.Equal(":FOR[MOD1]", passSpecifier.Descriptor); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /ModuleManagerTests/Patches/PassSpecifiers/InsertPassSpecifierTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using NSubstitute; 4 | using ModuleManager; 5 | using ModuleManager.Patches.PassSpecifiers; 6 | using ModuleManager.Progress; 7 | 8 | namespace ModuleManagerTests.Patches 9 | { 10 | public class InsertPassSpecifierTest 11 | { 12 | public readonly INeedsChecker needsChecker = Substitute.For(); 13 | public readonly IPatchProgress progress = Substitute.For(); 14 | private readonly InsertPassSpecifier passSpecifier = new InsertPassSpecifier(); 15 | 16 | [Fact] 17 | public void TestCheckNeeds() 18 | { 19 | Assert.True(passSpecifier.CheckNeeds(needsChecker, progress)); 20 | } 21 | 22 | [Fact] 23 | public void TestCheckNeeds__NeedsCheckerNull() 24 | { 25 | ArgumentNullException ex = Assert.Throws(delegate 26 | { 27 | passSpecifier.CheckNeeds(null, progress); 28 | }); 29 | 30 | Assert.Equal("needsChecker", ex.ParamName); 31 | 32 | progress.DidNotReceiveWithAnyArgs().NeedsUnsatisfiedAfter(null); 33 | } 34 | 35 | [Fact] 36 | public void TestCheckNeeds__ProgressNull() 37 | { 38 | ArgumentNullException ex = Assert.Throws(delegate 39 | { 40 | passSpecifier.CheckNeeds(needsChecker, null); 41 | }); 42 | 43 | Assert.Equal("progress", ex.ParamName); 44 | } 45 | 46 | [Fact] 47 | public void TestDescriptor() 48 | { 49 | Assert.Equal(":INSERT (initial)", passSpecifier.Descriptor); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ModuleManagerTests/Patches/PassSpecifiers/LastPassSpecifierTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using NSubstitute; 4 | using ModuleManager; 5 | using ModuleManager.Patches.PassSpecifiers; 6 | using ModuleManager.Progress; 7 | 8 | namespace ModuleManagerTests.Patches 9 | { 10 | public class LastPassSpecifierTest 11 | { 12 | public readonly INeedsChecker needsChecker = Substitute.For(); 13 | public readonly IPatchProgress progress = Substitute.For(); 14 | public readonly LastPassSpecifier passSpecifier; 15 | 16 | public LastPassSpecifierTest() 17 | { 18 | passSpecifier = new LastPassSpecifier("mod1"); 19 | } 20 | 21 | [Fact] 22 | public void TestConstructor__ModNull() 23 | { 24 | ArgumentNullException ex = Assert.Throws(delegate 25 | { 26 | new LastPassSpecifier(null); 27 | }); 28 | 29 | Assert.Equal("mod", ex.ParamName); 30 | } 31 | 32 | [Fact] 33 | public void TestConstructor__ModEmpty() 34 | { 35 | ArgumentException ex = Assert.Throws(delegate 36 | { 37 | new LastPassSpecifier(""); 38 | }); 39 | 40 | Assert.Equal("mod", ex.ParamName); 41 | Assert.Contains("can't be empty", ex.Message); 42 | } 43 | 44 | [Fact] 45 | public void TestCheckNeeds() 46 | { 47 | passSpecifier.CheckNeeds(needsChecker, progress); 48 | 49 | needsChecker.DidNotReceiveWithAnyArgs().CheckNeeds(null); 50 | } 51 | 52 | [Fact] 53 | public void TestDescriptor() 54 | { 55 | Assert.Equal(":LAST[MOD1]", passSpecifier.Descriptor); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /ModuleManagerTests/Patches/PassSpecifiers/LegacyPassSpecifierTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using NSubstitute; 4 | using ModuleManager; 5 | using ModuleManager.Patches.PassSpecifiers; 6 | using ModuleManager.Progress; 7 | 8 | namespace ModuleManagerTests.Patches 9 | { 10 | public class LegacyPassSpecifierTest 11 | { 12 | public readonly INeedsChecker needsChecker = Substitute.For(); 13 | public readonly IPatchProgress progress = Substitute.For(); 14 | private readonly LegacyPassSpecifier passSpecifier = new LegacyPassSpecifier(); 15 | 16 | [Fact] 17 | public void TestCheckNeeds() 18 | { 19 | Assert.True(passSpecifier.CheckNeeds(needsChecker, progress)); 20 | } 21 | 22 | [Fact] 23 | public void TestCheckNeeds__NeedsCheckerNull() 24 | { 25 | ArgumentNullException ex = Assert.Throws(delegate 26 | { 27 | passSpecifier.CheckNeeds(null, progress); 28 | }); 29 | 30 | Assert.Equal("needsChecker", ex.ParamName); 31 | 32 | progress.DidNotReceiveWithAnyArgs().NeedsUnsatisfiedAfter(null); 33 | } 34 | 35 | [Fact] 36 | public void TestCheckNeeds__ProgressNull() 37 | { 38 | ArgumentNullException ex = Assert.Throws(delegate 39 | { 40 | passSpecifier.CheckNeeds(needsChecker, null); 41 | }); 42 | 43 | Assert.Equal("progress", ex.ParamName); 44 | } 45 | 46 | [Fact] 47 | public void TestDescriptor() 48 | { 49 | Assert.Equal(":LEGACY (default)", passSpecifier.Descriptor); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ModuleManagerTests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ModuleManagerTests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ModuleManagerTests")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("bc2a08c8-64ef-4823-a40b-8889c1ccfd75")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /ModuleManagerTests/ProtoUrlConfigTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using TestUtils; 4 | using ModuleManager; 5 | 6 | namespace ModuleManagerTests 7 | { 8 | public class ProtoUrlConfigTest 9 | { 10 | [Fact] 11 | public void TestContructor__UrlFileNull() 12 | { 13 | ArgumentNullException ex = Assert.Throws(delegate 14 | { 15 | new ProtoUrlConfig(null, new ConfigNode()); 16 | }); 17 | 18 | Assert.Equal("urlFile", ex.ParamName); 19 | } 20 | 21 | [Fact] 22 | public void TestContructor__NodeNull() 23 | { 24 | ArgumentNullException ex = Assert.Throws(delegate 25 | { 26 | new ProtoUrlConfig(UrlBuilder.CreateFile("foo/bar"), null); 27 | }); 28 | 29 | Assert.Equal("node", ex.ParamName); 30 | } 31 | 32 | [Fact] 33 | public void TestUrlFile() 34 | { 35 | UrlDir.UrlFile urlFile = UrlBuilder.CreateFile("abc/def.cfg"); 36 | ProtoUrlConfig protoUrlConfig = new ProtoUrlConfig(urlFile, new ConfigNode()); 37 | 38 | Assert.Same(urlFile, protoUrlConfig.UrlFile); 39 | } 40 | 41 | [Fact] 42 | public void TestNode() 43 | { 44 | ConfigNode node = new ConfigNode("NODE"); 45 | ProtoUrlConfig protoUrlConfig = new ProtoUrlConfig(UrlBuilder.CreateFile("foo/bar"), node); 46 | 47 | Assert.Same(node, protoUrlConfig.Node); 48 | } 49 | 50 | [Fact] 51 | public void TestFileUrl() 52 | { 53 | ProtoUrlConfig protoUrlConfig = new ProtoUrlConfig(UrlBuilder.CreateFile("abc/def.cfg"), new ConfigNode()); 54 | 55 | Assert.Equal("abc/def.cfg", protoUrlConfig.FileUrl); 56 | } 57 | 58 | [Fact] 59 | public void TestNodeType() 60 | { 61 | ProtoUrlConfig protoUrlConfig = new ProtoUrlConfig(UrlBuilder.CreateFile("abc/def"), new ConfigNode("SOME_NODE")); 62 | 63 | Assert.Equal("SOME_NODE", protoUrlConfig.NodeType); 64 | } 65 | 66 | [Fact] 67 | public void TestFullUrl() 68 | { 69 | ProtoUrlConfig protoUrlConfig = new ProtoUrlConfig(UrlBuilder.CreateFile("abc/def.cfg"), new ConfigNode("SOME_NODE")); 70 | 71 | Assert.Equal("abc/def.cfg/SOME_NODE", protoUrlConfig.FullUrl); 72 | } 73 | 74 | [Fact] 75 | public void TestFullUrl__NameValue() 76 | { 77 | ConfigNode node = new TestConfigNode("SOME_NODE") 78 | { 79 | { "name", "some_value" }, 80 | }; 81 | 82 | ProtoUrlConfig protoUrlConfig = new ProtoUrlConfig(UrlBuilder.CreateFile("abc/def.cfg"), node); 83 | 84 | Assert.Equal("abc/def.cfg/SOME_NODE[some_value]", protoUrlConfig.FullUrl); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /ModuleManagerTests/Tags/TagListTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using ModuleManager.Tags; 4 | 5 | namespace ModuleManagerTests.Tags 6 | { 7 | public class TagListTest 8 | { 9 | [Fact] 10 | public void TestPrimaryTag() 11 | { 12 | Tag primaryTag = new Tag("stuff", null, null); 13 | TagList tagList = new TagList(primaryTag, new Tag[0]); 14 | 15 | Assert.Equal(primaryTag, tagList.PrimaryTag); 16 | } 17 | 18 | [Fact] 19 | public void TestEnumeration() 20 | { 21 | Tag primaryTag = new Tag("stuff", null, null); 22 | Tag tag1 = new Tag("tag1", null, null); 23 | Tag tag2 = new Tag("tag2", null, null); 24 | 25 | Tag[] tags = new Tag[] { tag1, tag2 }; 26 | TagList tagList = new TagList(primaryTag, tags); 27 | 28 | tags[0] = new Tag("tag3", null, null); 29 | 30 | Assert.Equal(new[] { tag1, tag2 }, tagList); 31 | } 32 | 33 | [Fact] 34 | public void TestConstructor__TagsNull() 35 | { 36 | ArgumentNullException ex = Assert.Throws(delegate 37 | { 38 | new TagList(new Tag("blah", null, null), null); 39 | }); 40 | 41 | Assert.Equal("tags", ex.ParamName); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ModuleManagerTests/Tags/TagTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using ModuleManager.Tags; 4 | 5 | namespace ModuleManagerTests.Tags 6 | { 7 | public class TagTest 8 | { 9 | [Fact] 10 | public void Test__OnlyKey() 11 | { 12 | Tag tag = new Tag("key", null, null); 13 | 14 | Assert.Equal("key", tag.key); 15 | Assert.Null(tag.value); 16 | Assert.Null(tag.trailer); 17 | } 18 | 19 | [Fact] 20 | public void Test__KeyAndValue() 21 | { 22 | Tag tag = new Tag("key", "value", null); 23 | 24 | Assert.Equal("key", tag.key); 25 | Assert.Equal("value", tag.value); 26 | Assert.Null(tag.trailer); 27 | } 28 | 29 | [Fact] 30 | public void Test__KeyAndEmptyValue() 31 | { 32 | Tag tag = new Tag("key", "", null); 33 | 34 | Assert.Equal("key", tag.key); 35 | Assert.Equal("", tag.value); 36 | Assert.Null(tag.trailer); 37 | } 38 | 39 | [Fact] 40 | public void Test__KeyValueAndTrailer() 41 | { 42 | Tag tag = new Tag("key", "value", "trailer"); 43 | 44 | Assert.Equal("key", tag.key); 45 | Assert.Equal("value", tag.value); 46 | Assert.Equal("trailer", tag.trailer); 47 | } 48 | 49 | [Fact] 50 | public void Test__KeyEmptyValueAndTrailer() 51 | { 52 | Tag tag = new Tag("key", "", "trailer"); 53 | 54 | Assert.Equal("key", tag.key); 55 | Assert.Equal("", tag.value); 56 | Assert.Equal("trailer", tag.trailer); 57 | } 58 | 59 | [Fact] 60 | public void TestConstructor__KeyNull() 61 | { 62 | ArgumentNullException ex = Assert.Throws(delegate 63 | { 64 | new Tag(null, "value", "trailer"); 65 | }); 66 | 67 | Assert.Equal("key", ex.ParamName); 68 | } 69 | 70 | [Fact] 71 | public void TestConstructor__KeyEmpty() 72 | { 73 | ArgumentException ex = Assert.Throws(delegate 74 | { 75 | new Tag("", "value", "trailer"); 76 | }); 77 | 78 | Assert.Equal("key", ex.ParamName); 79 | Assert.Contains("can't be empty", ex.Message); 80 | } 81 | 82 | [Fact] 83 | public void TestConstructor__ValueNullButTrailerNotNull() 84 | { 85 | ArgumentException ex = Assert.Throws(delegate 86 | { 87 | new Tag("key", null, "trailer"); 88 | }); 89 | 90 | Assert.Contains("trailer must be null if value is null", ex.Message); 91 | } 92 | 93 | [Fact] 94 | public void TestConstructor__TrailerEmpty() 95 | { 96 | ArgumentException ex = Assert.Throws(delegate 97 | { 98 | new Tag("key", "value", ""); 99 | }); 100 | 101 | Assert.Equal("trailer", ex.ParamName); 102 | Assert.Contains("can't be empty (null allowed)", ex.Message); 103 | } 104 | 105 | [Fact] 106 | public void TestToString__Key() 107 | { 108 | Tag tag = new Tag("key", null, null); 109 | 110 | Assert.Equal("< 'key' >", tag.ToString()); 111 | } 112 | 113 | [Fact] 114 | public void TestToString__KeyAndValue() 115 | { 116 | Tag tag = new Tag("key", "value", null); 117 | 118 | Assert.Equal("< 'key' [ 'value' ] >", tag.ToString()); 119 | } 120 | 121 | [Fact] 122 | public void TestToString__KeyAndEmptyValue() 123 | { 124 | Tag tag = new Tag("key", "", null); 125 | 126 | Assert.Equal("< 'key' [ '' ] >", tag.ToString()); 127 | } 128 | 129 | [Fact] 130 | public void TestToString__KeyValueAndTrailer() 131 | { 132 | Tag tag = new Tag("key", "value", "trailer"); 133 | 134 | Assert.Equal("< 'key' [ 'value' ] 'trailer' >", tag.ToString()); 135 | } 136 | 137 | [Fact] 138 | public void TestToString__KeyEmptyValueAndTrailer() 139 | { 140 | Tag tag = new Tag("key", "", "trailer"); 141 | 142 | Assert.Equal("< 'key' [ '' ] 'trailer' >", tag.ToString()); 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /ModuleManagerTests/Threading/BackgroundTaskTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using ModuleManager.Threading; 4 | 5 | namespace ModuleManagerTests.Threading 6 | { 7 | public class BackgroundTaskTest 8 | { 9 | [Fact] 10 | public void Test__Start() 11 | { 12 | bool finish = false; 13 | void Run() 14 | { 15 | while (!finish) continue; 16 | } 17 | 18 | ITaskStatus status = BackgroundTask.Start(Run); 19 | 20 | Assert.True(status.IsRunning); 21 | Assert.False(status.IsFinished); 22 | Assert.False(status.IsExitedWithError); 23 | Assert.Null(status.Exception); 24 | 25 | finish = true; 26 | 27 | while (status.IsRunning) continue; 28 | 29 | Assert.False(status.IsRunning); 30 | Assert.True(status.IsFinished); 31 | Assert.False(status.IsExitedWithError); 32 | Assert.Null(status.Exception); 33 | } 34 | 35 | [Fact] 36 | public void Test__Start__Exception() 37 | { 38 | bool finish = false; 39 | Exception ex = new Exception(); 40 | void Run() 41 | { 42 | while (!finish) continue; 43 | throw ex; 44 | } 45 | 46 | ITaskStatus status = BackgroundTask.Start(Run); 47 | 48 | Assert.True(status.IsRunning); 49 | Assert.False(status.IsFinished); 50 | Assert.False(status.IsExitedWithError); 51 | Assert.Null(status.Exception); 52 | 53 | finish = true; 54 | 55 | while (status.IsRunning) continue; 56 | 57 | Assert.False(status.IsRunning); 58 | Assert.False(status.IsFinished); 59 | Assert.True(status.IsExitedWithError); 60 | Assert.Same(ex, status.Exception); 61 | } 62 | 63 | [Fact] 64 | public void Test__Run__ArgumentNull() 65 | { 66 | Assert.Throws(delegate 67 | { 68 | BackgroundTask.Start(null); 69 | }); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /ModuleManagerTests/Threading/TaskStatusTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using ModuleManager.Threading; 4 | 5 | namespace ModuleManagerTests.Threading 6 | { 7 | public class TaskStatusTest 8 | { 9 | [Fact] 10 | public void Test__Cosntructor() 11 | { 12 | TaskStatus status = new TaskStatus(); 13 | 14 | Assert.True(status.IsRunning); 15 | Assert.False(status.IsFinished); 16 | Assert.False(status.IsExitedWithError); 17 | Assert.Null(status.Exception); 18 | } 19 | 20 | [Fact] 21 | public void TestFinished() 22 | { 23 | TaskStatus status = new TaskStatus(); 24 | 25 | status.Finished(); 26 | 27 | Assert.False(status.IsRunning); 28 | Assert.True(status.IsFinished); 29 | Assert.False(status.IsExitedWithError); 30 | Assert.Null(status.Exception); 31 | } 32 | 33 | [Fact] 34 | public void TestError() 35 | { 36 | TaskStatus status = new TaskStatus(); 37 | Exception ex = new Exception(); 38 | 39 | status.Error(ex); 40 | 41 | Assert.False(status.IsRunning); 42 | Assert.False(status.IsFinished); 43 | Assert.True(status.IsExitedWithError); 44 | Assert.Same(ex, status.Exception); 45 | } 46 | 47 | [Fact] 48 | public void TestFinished__AlreadyFinished() 49 | { 50 | TaskStatus status = new TaskStatus(); 51 | 52 | status.Finished(); 53 | 54 | Assert.Throws(delegate 55 | { 56 | status.Finished(); 57 | }); 58 | 59 | Assert.False(status.IsRunning); 60 | Assert.True(status.IsFinished); 61 | Assert.False(status.IsExitedWithError); 62 | Assert.Null(status.Exception); 63 | } 64 | 65 | [Fact] 66 | public void TestFinished__AlreadyErrored() 67 | { 68 | TaskStatus status = new TaskStatus(); 69 | Exception ex = new Exception(); 70 | 71 | status.Error(ex); 72 | 73 | Assert.Throws(delegate 74 | { 75 | status.Finished(); 76 | }); 77 | 78 | Assert.False(status.IsRunning); 79 | Assert.False(status.IsFinished); 80 | Assert.True(status.IsExitedWithError); 81 | Assert.Same(ex, status.Exception); 82 | } 83 | 84 | [Fact] 85 | public void TestError__AlreadyFinished() 86 | { 87 | TaskStatus status = new TaskStatus(); 88 | 89 | status.Finished(); 90 | 91 | Assert.Throws(delegate 92 | { 93 | status.Error(new Exception()); 94 | }); 95 | 96 | Assert.False(status.IsRunning); 97 | Assert.True(status.IsFinished); 98 | Assert.False(status.IsExitedWithError); 99 | Assert.Null(status.Exception); 100 | } 101 | 102 | [Fact] 103 | public void TestError__AlreadyErrored() 104 | { 105 | TaskStatus status = new TaskStatus(); 106 | Exception ex = new Exception(); 107 | 108 | status.Error(ex); 109 | 110 | Assert.Throws(delegate 111 | { 112 | status.Error(new Exception()); 113 | }); 114 | 115 | Assert.False(status.IsRunning); 116 | Assert.False(status.IsFinished); 117 | Assert.True(status.IsExitedWithError); 118 | Assert.Same(ex, status.Exception); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /ModuleManagerTests/Utils/CounterTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Xunit; 6 | using ModuleManager.Utils; 7 | 8 | namespace ModuleManagerTests.Utils 9 | { 10 | public class CounterTest 11 | { 12 | [Fact] 13 | public void Test__Constructor() 14 | { 15 | Counter counter = new Counter(); 16 | 17 | Assert.Equal(0, counter.Value); 18 | } 19 | 20 | [Fact] 21 | public void TestIncrement() 22 | { 23 | Counter counter = new Counter(); 24 | 25 | Assert.Equal(0, counter.Value); 26 | 27 | counter.Increment(); 28 | 29 | Assert.Equal(1, counter.Value); 30 | 31 | counter.Increment(); 32 | 33 | Assert.Equal(2, counter.Value); 34 | } 35 | 36 | [Fact] 37 | public void TestToString() 38 | { 39 | Counter counter = new Counter(); 40 | 41 | Assert.Equal("0", counter.ToString()); 42 | 43 | counter.Increment(); 44 | 45 | Assert.Equal("1", counter.ToString()); 46 | 47 | counter.Increment(); 48 | 49 | Assert.Equal("2", counter.ToString()); 50 | } 51 | 52 | [Fact] 53 | public void Test__CastAsInt() 54 | { 55 | Counter counter = new Counter(); 56 | int i; 57 | 58 | i = counter; 59 | Assert.Equal(0, i); 60 | 61 | counter.Increment(); 62 | 63 | i = counter; 64 | Assert.Equal(1, i); 65 | 66 | counter.Increment(); 67 | 68 | i = counter; 69 | Assert.Equal(2, i); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /ModuleManagerTests/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ModuleManagerTests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ModuleManager 2 | ============= 3 | 4 | 5 | Original (c) from Ialdabaoth ( https://github.com/Ialdabaoth ) 6 | 7 | Modified by // Modifications by // Maintained by sarbian ( https://github.com/sarbian ) 8 | 9 | 10 | The original licence requirement was: 11 | 12 | --- 13 | 14 | under a CC share-alike license. Anyone is free to do anything they like with ModuleManager's source, with two caveats: 15 | 16 | 1. You credit me as the original creator that your code is based on 17 | 2. You make it ABSOLUTELY CLEAR that your code is not the original ModuleManager, and that any problems that people have with your fork should be taken up with YOU, not me. 18 | 19 | --- 20 | 21 | 22 | THIS IS NOT THE ORIGINAL MODULEMANAGER CODE. 23 | 24 | Do not bother Ialdabaoth about any problems with it. 25 | 26 | ## Dependencies 27 | 28 | - mono resgen2 29 | - Fedora: `sudo dnf install mono-devel` 30 | - Mono C# Compiler 31 | - Fedora: `sudo ln -s /usr/bin/mcs /usr/bin/gmcs` 32 | 33 | -------------------------------------------------------------------------------- /TestUtils/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("TestUtils")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("TestUtils")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("20eaafe6-510d-4374-8d2f-6b52d0178e85")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /TestUtils/TestConfigNode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | 4 | namespace TestUtils 5 | { 6 | public class TestConfigNode : ConfigNode, IEnumerable 7 | { 8 | public TestConfigNode() : base() { } 9 | public TestConfigNode(string name) : base(name) { } 10 | 11 | public void Add(string name, string value) => Add(new Value(name, value)); 12 | public void Add(Value value) => values.Add(value); 13 | public void Add(string name, ConfigNode node) => AddNode(name, node); 14 | public void Add(ConfigNode node) => AddNode(node); 15 | 16 | public IEnumerator GetEnumerator() => throw new NotImplementedException(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /TestUtils/TestUtils.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {20EAAFE6-510D-4374-8D2F-6B52D0178E85} 8 | Library 9 | Properties 10 | TestUtils 11 | TestUtils 12 | v4.7.1 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | false 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | false 34 | 35 | 36 | 8.0 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /TestUtilsTests/DummyTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | 4 | namespace TestUtilsTests 5 | { 6 | public class DummyTest 7 | { 8 | [Fact] 9 | public void PassingTest() 10 | { 11 | Assert.True(true); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /TestUtilsTests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("TestUtilsTests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("TestUtilsTests")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("e695c11f-4217-4014-9b51-7232a654c205")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /TestUtilsTests/TestConfigNodeTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using TestUtils; 4 | 5 | namespace TestUtilsTests 6 | { 7 | public class TestConfigNodeTest 8 | { 9 | [Fact] 10 | public void TestTestConfigNode() 11 | { 12 | ConfigNode node = new TestConfigNode("NODE") 13 | { 14 | { "value1", "something" }, 15 | { "value2", "something else" }, 16 | { "multiple", "first" }, 17 | { "multiple", "second" }, 18 | new ConfigNode.Value("foo", "bar"), 19 | { "weird_values", "some\r\n\tstuff" }, 20 | { "NODE_1", new TestConfigNode 21 | { 22 | { "name", "something" }, 23 | { "stuff", "something else" }, 24 | } 25 | }, 26 | new TestConfigNode("MULTIPLE") 27 | { 28 | { "value3", "blah" }, 29 | { "value4", "bleh" }, 30 | }, 31 | new TestConfigNode("MULTIPLE") 32 | { 33 | { "value3", "blih" }, 34 | { "value4", "bloh" }, 35 | }, 36 | }; 37 | 38 | Assert.Equal(6, node.values.Count); 39 | AssertValue("value1", "something", node.values[0]); 40 | AssertValue("value2", "something else", node.values[1]); 41 | AssertValue("multiple", "first", node.values[2]); 42 | AssertValue("multiple", "second", node.values[3]); 43 | AssertValue("foo", "bar", node.values[4]); 44 | AssertValue("weird_values", "some\r\n\tstuff", node.values[5]); 45 | 46 | Assert.Equal(3, node.nodes.Count); 47 | ConfigNode innerNode1 = node.GetNode("NODE_1"); 48 | Assert.NotNull(innerNode1); 49 | 50 | Assert.Equal("NODE_1", node.nodes[0].name); 51 | Assert.Equal(2, node.nodes[0].values.Count); 52 | AssertValue("name", "something", node.nodes[0].values[0]); 53 | AssertValue("stuff", "something else", node.nodes[0].values[1]); 54 | Assert.Empty(node.nodes[0].nodes); 55 | 56 | Assert.Equal("MULTIPLE", node.nodes[1].name); 57 | Assert.Equal(2, node.nodes[1].values.Count); 58 | AssertValue("value3", "blah", node.nodes[1].values[0]); 59 | AssertValue("value4", "bleh", node.nodes[1].values[1]); 60 | Assert.Empty(node.nodes[1].nodes); 61 | 62 | Assert.Equal("MULTIPLE", node.nodes[2].name); 63 | Assert.Equal(2, node.nodes[2].values.Count); 64 | AssertValue("value3", "blih", node.nodes[2].values[0]); 65 | AssertValue("value4", "bloh", node.nodes[2].values[1]); 66 | Assert.Empty(node.nodes[2].nodes); 67 | } 68 | 69 | private void AssertValue(string name, string value, ConfigNode.Value nodeValue) 70 | { 71 | Assert.Equal(name, nodeValue.name); 72 | Assert.Equal(value, nodeValue.value); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /TestUtilsTests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Tests/AltEdits.cfg: -------------------------------------------------------------------------------- 1 | 2 | MMTEST 3 | { 4 | name = altEdits 5 | MODULE 6 | { 7 | name = module1 8 | } 9 | MODULE 10 | { 11 | name = module2 12 | } 13 | } 14 | 15 | // Adds value to module2 16 | @MMTEST[altEdits] 17 | { 18 | @MODULE[module2] 19 | { 20 | addedValue = added 21 | +copyNothing = novalue 22 | } 23 | +MODULE[module1] 24 | { 25 | @name = moduleCopy 26 | } 27 | -MODULE[module1] { } 28 | } 29 | 30 | MMTEST_EXPECT 31 | { 32 | MMTEST 33 | { 34 | name = altEdits 35 | MODULE 36 | { 37 | name = module2 38 | addedValue = added 39 | } 40 | MODULE 41 | { 42 | name = moduleCopy 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /Tests/BadlyFormed.cfg: -------------------------------------------------------------------------------- 1 | 2 | MMTEST 3 | { 4 | name = badFormat 5 | MODULE 6 | { 7 | name = module1 8 | } 9 | // Brackets without name 10 | //MODULE 11 | { 12 | name = module2 13 | } 14 | testval = testthing 15 | { 16 | // name without value (ignored) 17 | subval 18 | } 19 | double_brace 20 | { 21 | { 22 | one = 1 23 | } 24 | { 25 | two = 2 26 | } 27 | } 28 | } 29 | 30 | // Adds value to module2 31 | @MMTEST[badFormat] 32 | { 33 | @MODULE[module1] 34 | { 35 | addedValue = added 36 | } 37 | @[module2] 38 | { 39 | key = Key 40 | } 41 | } 42 | 43 | 44 | MMTEST_EXPECT 45 | { 46 | MMTEST 47 | { 48 | name = badFormat 49 | testval = testthing 50 | MODULE 51 | { 52 | name = module1 53 | addedValue = added 54 | } 55 | 56 | { 57 | name = module2 58 | key = Key 59 | } 60 | 61 | { 62 | } 63 | 64 | double_brace 65 | { 66 | { 67 | one = 1 68 | } 69 | { 70 | two = 2 71 | } 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /Tests/NameLessNode.cfg: -------------------------------------------------------------------------------- 1 | MMTEST 2 | { 3 | name = NameLessNode 4 | } 5 | 6 | @MMTEST[NameLessNode] 7 | { 8 | // Insert a name Less node 9 | %MODEL { 10 | %key = value 11 | } 12 | } 13 | 14 | MMTEST_EXPECT 15 | { 16 | MMTEST 17 | { 18 | name = NameLessNode 19 | MODEL 20 | { 21 | key = value 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /Tests/Needs.cfg: -------------------------------------------------------------------------------- 1 | 2 | MMTEST 3 | { 4 | name = testNeeds 5 | MODULE 6 | { 7 | name = module1 8 | } 9 | MODULE 10 | { 11 | name = module2 12 | } 13 | } 14 | 15 | // Adds value to module2 16 | @MMTEST[testNeeds]:NEEDS[!ModuleManager] 17 | { 18 | @MODULE[module2] 19 | { 20 | addedValue = added 21 | } 22 | } 23 | 24 | @MMTEST[testNeeds]:NEEDS[ModuleManager] 25 | { 26 | @MODULE[module1] 27 | { 28 | addedValue = added 29 | } 30 | } 31 | 32 | MMTEST_EXPECT 33 | { 34 | MMTEST 35 | { 36 | name = testNeeds 37 | MODULE 38 | { 39 | name = module1 40 | addedValue = added 41 | } 42 | MODULE 43 | { 44 | name = module2 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /Tests/NodeCopy.cfg: -------------------------------------------------------------------------------- 1 | 2 | MMTEST 3 | { 4 | name = nodeCopy 5 | MODULE 6 | { 7 | name = module1 8 | } 9 | } 10 | 11 | // Adds value to module2 12 | @MMTEST[nodeCopy] 13 | { 14 | // Copy module 1 15 | +MODULE 16 | { 17 | @name = module2 18 | } 19 | // Copy new module 2 20 | +MODULE[module2],0 21 | { 22 | @name = module3 23 | } 24 | // Copy with wildcard 25 | +MODULE,* 26 | { 27 | // $ matches the end - good for adding suffixes 28 | @name ^= :$:duplicate: 29 | tag = duplicate 30 | } 31 | } 32 | 33 | MMTEST_EXPECT 34 | { 35 | MMTEST 36 | { 37 | name = nodeCopy 38 | MODULE 39 | { 40 | name = module1 41 | } 42 | MODULE 43 | { 44 | name = module2 45 | } 46 | MODULE 47 | { 48 | name = module3 49 | } 50 | MODULE 51 | { 52 | name = module1duplicate 53 | tag = duplicate 54 | } 55 | MODULE 56 | { 57 | name = module2duplicate 58 | tag = duplicate 59 | } 60 | MODULE 61 | { 62 | name = module3duplicate 63 | tag = duplicate 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /Tests/NodeCreate.cfg: -------------------------------------------------------------------------------- 1 | MMTEST 2 | { 3 | name = CreateNode 4 | NODE 5 | { 6 | name = TestNode1 7 | 8 | value = test1 9 | } 10 | } 11 | 12 | @MMTEST[CreateNode] 13 | { 14 | &NODE[TestNode1] 15 | { 16 | %value = test2 17 | } 18 | &NODE[TestNode2] 19 | { 20 | %value = test3 21 | } 22 | } 23 | 24 | MMTEST_EXPECT 25 | { 26 | MMTEST 27 | { 28 | name = CreateNode 29 | NODE 30 | { 31 | name = TestNode1 32 | value = test1 33 | } 34 | NODE 35 | { 36 | name = TestNode2 37 | value = test3 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /Tests/NodeDelete.cfg: -------------------------------------------------------------------------------- 1 | 2 | MMTEST 3 | { 4 | name = nodeDelete 5 | MODULE 6 | { 7 | name = module1 8 | } 9 | MODULE 10 | { 11 | name = module2 12 | key = copy0 13 | } 14 | MODULE 15 | { 16 | name = module2 17 | key = copy1 18 | } 19 | MODULE 20 | { 21 | name = module2 22 | key = copy2 23 | } 24 | MODULE 25 | { 26 | name = module2 27 | key = copy3 28 | } 29 | MODULE 30 | { 31 | name = module3 32 | } 33 | MODULE 34 | { 35 | name = module4 36 | } 37 | MODULE 38 | { 39 | name = tailEnd 40 | } 41 | } 42 | 43 | // Adds value to module2 44 | @MMTEST[nodeDelete] 45 | { 46 | // Delete the first copy 47 | -MODULE[module2] {} 48 | // Indexed delete 49 | -MODULE,2 { } 50 | // Indexed delete from end 51 | -MODULE,-2 { } 52 | // Indexed delete off end 53 | -MODULE,9999 { } 54 | } 55 | 56 | MMTEST_EXPECT 57 | { 58 | MMTEST 59 | { 60 | name = nodeDelete 61 | MODULE 62 | { 63 | name = module1 64 | } 65 | MODULE 66 | { 67 | name = module2 68 | key = copy1 69 | } 70 | MODULE 71 | { 72 | name = module2 73 | key = copy3 74 | } 75 | MODULE 76 | { 77 | name = module3 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /Tests/NodeEdit.cfg: -------------------------------------------------------------------------------- 1 | 2 | MMTEST 3 | { 4 | name = nodeEdit 5 | MODULE 6 | { 7 | name = module1 8 | } 9 | MODULE 10 | { 11 | name = module2 12 | key = firstCopy 13 | } 14 | MODULE 15 | { 16 | name = module2 17 | key = secondCopy 18 | } 19 | } 20 | 21 | // Adds value to module2 22 | @MMTEST[nodeEdit] 23 | { 24 | // edit by name 25 | @MODULE[module2] 26 | { 27 | addTo = firstCopy 28 | } 29 | // edit by index 30 | @MODULE,1 31 | { 32 | addTo = module2.1 33 | } 34 | // edit by name and index 35 | @MODULE[module2],1 36 | { 37 | addTo = module2.2 38 | } 39 | // edit by wildcard and index (off end) 40 | @MODULE[*2],5 41 | { 42 | addTo = module2.2again 43 | } 44 | } 45 | 46 | MMTEST_EXPECT 47 | { 48 | MMTEST 49 | { 50 | name = nodeEdit 51 | MODULE 52 | { 53 | name = module1 54 | } 55 | MODULE 56 | { 57 | name = module2 58 | key = firstCopy 59 | addTo = firstCopy 60 | addTo = module2.1 61 | } 62 | MODULE 63 | { 64 | name = module2 65 | key = secondCopy 66 | addTo = module2.2 67 | addTo = module2.2again 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /Tests/NodeEditWildcard.cfg: -------------------------------------------------------------------------------- 1 | 2 | MMTEST 3 | { 4 | name = nodeEditWildcard 5 | MODULE 6 | { 7 | name = module1 8 | } 9 | MODULE 10 | { 11 | name = module2 12 | key = firstCopy 13 | } 14 | MODULE 15 | { 16 | name = module2 17 | key = secondCopy 18 | } 19 | } 20 | 21 | // Adds value to module2 22 | @MMTEST[nodeEditWildcard] 23 | { 24 | // edit by name 25 | @MODULE[module*],* 26 | { 27 | addTo = allName 28 | } 29 | // edit by index 30 | @MODULE,* 31 | { 32 | addTo = allIndex 33 | } 34 | // edit by name and index 35 | @MODULE[module2],* 36 | { 37 | addTo = module2 38 | } 39 | } 40 | 41 | MMTEST_EXPECT 42 | { 43 | MMTEST 44 | { 45 | name = nodeEditWildcard 46 | MODULE 47 | { 48 | name = module1 49 | addTo = allName 50 | addTo = allIndex 51 | } 52 | MODULE 53 | { 54 | name = module2 55 | key = firstCopy 56 | addTo = allName 57 | addTo = allIndex 58 | addTo = module2 59 | } 60 | MODULE 61 | { 62 | name = module2 63 | key = secondCopy 64 | addTo = allName 65 | addTo = allIndex 66 | addTo = module2 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /Tests/NodeHas.cfg: -------------------------------------------------------------------------------- 1 | 2 | MMTEST 3 | { 4 | name = nodeHas 5 | MODULE 6 | { 7 | name = module1 8 | } 9 | MODULE 10 | { 11 | name = module2 12 | key = firstCopy 13 | value = 5 14 | } 15 | MODULE 16 | { 17 | name = module2 18 | key = secondCopy 19 | value = 2 20 | } 21 | } 22 | 23 | // Adds value to module2 24 | @MMTEST[nodeHas] 25 | { 26 | 27 | @MODULE[module2]:HAS[#key[secondCopy]] 28 | { 29 | addTo = secondCopy 30 | } 31 | 32 | @MODULE:HAS[#value[<5]] 33 | { 34 | addTo = secondCopy.value 35 | } 36 | 37 | @MODULE:HAS[#value[>1]] 38 | { 39 | addTo = firstCopy.secondCopy 40 | } 41 | } 42 | 43 | MMTEST_EXPECT 44 | { 45 | MMTEST 46 | { 47 | name = nodeHas 48 | MODULE 49 | { 50 | name = module1 51 | } 52 | MODULE 53 | { 54 | name = module2 55 | key = firstCopy 56 | value = 5 57 | addTo = firstCopy.secondCopy 58 | } 59 | MODULE 60 | { 61 | name = module2 62 | key = secondCopy 63 | value = 2 64 | addTo = secondCopy 65 | addTo = secondCopy.value 66 | addTo = firstCopy.secondCopy 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /Tests/NodeInsert.cfg: -------------------------------------------------------------------------------- 1 | 2 | MMTEST 3 | { 4 | name = nodeInsert 5 | MODULE 6 | { 7 | name = module1 8 | } 9 | } 10 | 11 | // Adds value to module2 12 | @MMTEST[nodeInsert] 13 | { 14 | // Added at end 15 | MODULE 16 | { 17 | name = module2 18 | } 19 | // Insert at start 20 | MODULE,0 21 | { 22 | name = module0 23 | } 24 | // Insert off the end 25 | MODULE,9999 26 | { 27 | name = module999 28 | } 29 | 30 | } 31 | 32 | MMTEST_EXPECT 33 | { 34 | MMTEST 35 | { 36 | name = nodeInsert 37 | MODULE 38 | { 39 | name = module0 40 | } 41 | MODULE 42 | { 43 | name = module1 44 | } 45 | MODULE 46 | { 47 | name = module2 48 | } 49 | MODULE 50 | { 51 | name = module999 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /Tests/NodeReplace.cfg: -------------------------------------------------------------------------------- 1 | 2 | MMTEST 3 | { 4 | name = nodeReplace 5 | MODULE 6 | { 7 | name = module1 8 | } 9 | MODULE 10 | { 11 | name = module2 12 | key = firstCopy 13 | } 14 | MODULE 15 | { 16 | name = module2 17 | key = secondCopy 18 | } 19 | } 20 | 21 | // Adds value to module2 22 | @MMTEST[nodeReplace] 23 | { 24 | // replace by name 25 | %MODULE[module2] 26 | { 27 | %name = module2replaced 28 | %key = replaceFirstCopy 29 | } 30 | // replace by index 31 | %MODULE,0 32 | { 33 | %name = module1 34 | %addTo = replacedModule1 35 | } 36 | // Replace non-existent 37 | %MODULE[module3] 38 | { 39 | %name = module3 40 | @wontFind = value 41 | } 42 | } 43 | 44 | MMTEST_EXPECT 45 | { 46 | MMTEST 47 | { 48 | name = nodeReplace 49 | MODULE 50 | { 51 | name = module1 52 | addTo = replacedModule1 53 | } 54 | MODULE 55 | { 56 | name = module2replaced 57 | key = replaceFirstCopy 58 | } 59 | MODULE 60 | { 61 | name = module2 62 | key = secondCopy 63 | } 64 | MODULE 65 | { 66 | name = module3 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /Tests/ValueCopy.cfg: -------------------------------------------------------------------------------- 1 | 2 | MMTEST 3 | { 4 | name = valueCopy 5 | MODULE 6 | { 7 | name = module1 8 | multiVal = one 9 | multiVal = two 10 | numeric = 0 11 | } 12 | } 13 | 14 | // Adds value to module2 15 | @MMTEST[valueCopy] 16 | { 17 | // Copy new module 2 18 | @MODULE[module1] 19 | { 20 | // Unindexed 21 | +multiVal = three 22 | // regexp with index 23 | +multiVal,1 ^= :$:duplicate: 24 | // Arithmetic 25 | +numeric += 5 26 | } 27 | } 28 | 29 | MMTEST_EXPECT 30 | { 31 | MMTEST 32 | { 33 | name = valueCopy 34 | MODULE 35 | { 36 | name = module1 37 | multiVal = one 38 | multiVal = two 39 | numeric = 0 40 | multiVal = three 41 | multiVal = twoduplicate 42 | numeric = 5 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /Tests/ValueCreate.cfg: -------------------------------------------------------------------------------- 1 | MMTEST 2 | { 3 | name = valueCreate 4 | value1 = test1 5 | value2 = test2 6 | NODE 7 | { 8 | name = testNode 9 | 10 | value3 = test3 11 | value4 = test4 12 | } 13 | } 14 | 15 | @MMTEST[valueCreate] 16 | { 17 | &value1 = test5 18 | &value2 = #$NODE[testNode]/value3$ 19 | &value5 = test6 20 | &value6 = #$NODE[testNode]/value4$ 21 | } 22 | 23 | @MMTEST[valueCreate] 24 | { 25 | !NODE[testNode] {} 26 | } 27 | 28 | MMTEST_EXPECT 29 | { 30 | MMTEST 31 | { 32 | name = valueCreate 33 | value1 = test1 34 | value2 = test2 35 | value5 = test6 36 | value6 = test4 37 | } 38 | } -------------------------------------------------------------------------------- /Tests/ValueDelete.cfg: -------------------------------------------------------------------------------- 1 | 2 | MMTEST 3 | { 4 | name = valueDelete 5 | MODULE 6 | { 7 | name = module1 8 | multiVal = one 9 | multiVal = two 10 | multiVal2 = one 11 | multiVal2 = two 12 | numeric = 0 13 | } 14 | } 15 | 16 | // Adds value to module2 17 | @MMTEST[valueDelete] 18 | { 19 | // Copy new module 2 20 | @MODULE[module1] 21 | { 22 | // Unindexed (remove all) 23 | -multiVal = dummy 24 | // Indexed 25 | -multiVal2,0 = dummy 26 | // Wildcard 27 | -num*ic = dummy 28 | } 29 | } 30 | 31 | MMTEST_EXPECT 32 | { 33 | MMTEST 34 | { 35 | name = valueDelete 36 | MODULE 37 | { 38 | name = module1 39 | multiVal2 = two 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /Tests/ValueEdit.cfg: -------------------------------------------------------------------------------- 1 | 2 | MMTEST 3 | { 4 | name = valueEdit 5 | MODULE 6 | { 7 | name = module1 8 | multiVal = one 9 | multiVal = two 10 | numeric = 0 11 | } 12 | } 13 | 14 | // Adds value to module2 15 | @MMTEST[valueEdit] 16 | { 17 | // Copy new module 2 18 | @MODULE[module1] 19 | { 20 | // Unindexed 21 | @name = module2 22 | // Unindexed for multi - defaults to zero 23 | @multiVal = oneEdit 24 | // regexp with index 25 | @multiVal,1 ^= :tw:mo: 26 | // Arithmetic 27 | @numeric += 5 28 | @numeric *= 20 29 | } 30 | } 31 | 32 | MMTEST_EXPECT 33 | { 34 | MMTEST 35 | { 36 | name = valueEdit 37 | MODULE 38 | { 39 | name = module2 40 | multiVal = oneEdit 41 | multiVal = moo 42 | numeric = 100 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /Tests/ValueEmpty.cfg: -------------------------------------------------------------------------------- 1 | 2 | MMTEST 3 | { 4 | name = valueEmpty 5 | MODULE 6 | { 7 | name = module1 8 | emptyVal = notEmptyYet 9 | } 10 | } 11 | 12 | // Changes value to empty 13 | @MMTEST[valueEmpty] 14 | { 15 | @MODULE[module1] 16 | { 17 | // Set to empty 18 | @emptyVal = 19 | } 20 | } 21 | 22 | MMTEST_EXPECT 23 | { 24 | MMTEST 25 | { 26 | name = valueEmpty 27 | MODULE 28 | { 29 | name = module1 30 | emptyVal = 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /Tests/ValueInsert.cfg: -------------------------------------------------------------------------------- 1 | 2 | MMTEST 3 | { 4 | name = valueInsert 5 | MODULE 6 | { 7 | name = module1 8 | multiVal = one 9 | multiVal = two 10 | numeric = 0 11 | } 12 | } 13 | 14 | // Adds value to module2 15 | @MMTEST[valueInsert] 16 | { 17 | // Copy new module 2 18 | @MODULE[module1] 19 | { 20 | // Unindexed 21 | multiVal = three 22 | // Indexed 23 | multiVal,0 = zero 24 | // Indexed off end 25 | multiVal,999 = four 26 | } 27 | } 28 | 29 | MMTEST_EXPECT 30 | { 31 | MMTEST 32 | { 33 | name = valueInsert 34 | MODULE 35 | { 36 | name = module1 37 | numeric = 0 38 | multiVal = zero 39 | multiVal = one 40 | multiVal = two 41 | multiVal = three 42 | multiVal = four 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /Tests/ValueReplace.cfg: -------------------------------------------------------------------------------- 1 | 2 | MMTEST 3 | { 4 | name = valueReplace 5 | MODULE 6 | { 7 | name = module1 8 | multiVal = one 9 | multiVal = two 10 | numeric = 0 11 | } 12 | } 13 | 14 | // Adds value to module2 15 | @MMTEST[valueReplace] 16 | { 17 | // Copy new module 2 18 | @MODULE[module1] 19 | { 20 | // Replace has by nature pretty limited capabilities 21 | // value present 22 | %multiVal = replaced 23 | // Value not present 24 | %hedgehog = spiky 25 | } 26 | } 27 | 28 | MMTEST_EXPECT 29 | { 30 | MMTEST 31 | { 32 | name = valueReplace 33 | MODULE 34 | { 35 | name = module1 36 | numeric = 0 37 | multiVal = replaced 38 | hedgehog = spiky 39 | } 40 | } 41 | } --------------------------------------------------------------------------------