Large config file upgrade - backend rewrite; config file change monitoring; setting changed events propagate from ConfigFile; added unit tests and Test configuration

This commit is contained in:
ManlyMarco committed 2019-08-11 23:22:46 +02:00
1 parent ad1c9bd7e1
commit 65177c419e
13 files changed
+916 -85

No files matched your search

+123
View File
@@ -27,44 +27,167 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoMod.RuntimeDetour", "su
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoMod.Utils", "submodules\MonoMod\MonoMod.Utils\MonoMod.Utils.csproj", "{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BepInExTests", "BepInExTests\BepInExTests.csproj", "{E7CD429A-D057-48E3-8C51-E5C934E8E07B}"
ProjectSection(ProjectDependencies) = postProject
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9} = {4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|Test = Debug|Test
Legacy|Any CPU = Legacy|Any CPU
Legacy|Test = Legacy|Test
Release|Any CPU = Release|Any CPU
Release|Test = Release|Test
v2018|Any CPU = v2018|Any CPU
v2018|Test = v2018|Test
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Debug|Any CPU.ActiveCfg = v2018|Any CPU
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Debug|Any CPU.Build.0 = v2018|Any CPU
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Debug|Test.ActiveCfg = v2018|x86
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Debug|Test.Build.0 = v2018|x86
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Legacy|Any CPU.ActiveCfg = Legacy|Any CPU
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Legacy|Any CPU.Build.0 = Legacy|Any CPU
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Legacy|Test.ActiveCfg = Legacy|Any CPU
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Legacy|Test.Build.0 = Legacy|Any CPU
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Release|Any CPU.ActiveCfg = v2018|Any CPU
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Release|Any CPU.Build.0 = v2018|Any CPU
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Release|Test.ActiveCfg = v2018|Any CPU
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Release|Test.Build.0 = v2018|Any CPU
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.v2018|Any CPU.ActiveCfg = v2018|Any CPU
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.v2018|Any CPU.Build.0 = v2018|Any CPU
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.v2018|Test.ActiveCfg = v2018|Any CPU
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.v2018|Test.Build.0 = v2018|Any CPU
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Debug|Any CPU.ActiveCfg = v2018|Any CPU
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Debug|Any CPU.Build.0 = v2018|Any CPU
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Debug|Test.ActiveCfg = v2018|Any CPU
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Debug|Test.Build.0 = v2018|Any CPU
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Legacy|Any CPU.ActiveCfg = Legacy|Any CPU
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Legacy|Any CPU.Build.0 = Legacy|Any CPU
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Legacy|Test.ActiveCfg = Legacy|Any CPU
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Legacy|Test.Build.0 = Legacy|Any CPU
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Release|Any CPU.ActiveCfg = v2018|Any CPU
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Release|Any CPU.Build.0 = v2018|Any CPU
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Release|Test.ActiveCfg = v2018|Any CPU
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Release|Test.Build.0 = v2018|Any CPU
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.v2018|Any CPU.ActiveCfg = v2018|Any CPU
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.v2018|Any CPU.Build.0 = v2018|Any CPU
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.v2018|Test.ActiveCfg = v2018|Any CPU
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.v2018|Test.Build.0 = v2018|Any CPU
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Debug|Any CPU.ActiveCfg = Release|Any CPU
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Debug|Any CPU.Build.0 = Release|Any CPU
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Debug|Test.ActiveCfg = Release|Any CPU
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Debug|Test.Build.0 = Release|Any CPU
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Legacy|Any CPU.ActiveCfg = Release|Any CPU
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Legacy|Any CPU.Build.0 = Release|Any CPU
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Legacy|Test.ActiveCfg = Release|Any CPU
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Legacy|Test.Build.0 = Release|Any CPU
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Release|Any CPU.Build.0 = Release|Any CPU
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Release|Test.ActiveCfg = Release|Any CPU
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Release|Test.Build.0 = Release|Any CPU
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.v2018|Any CPU.ActiveCfg = Release|Any CPU
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.v2018|Any CPU.Build.0 = Release|Any CPU
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.v2018|Test.ActiveCfg = Release|Any CPU
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.v2018|Test.Build.0 = Release|Any CPU
{54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Debug|Test.ActiveCfg = Debug|Any CPU
{54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Debug|Test.Build.0 = Debug|Any CPU
{54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Legacy|Any CPU.ActiveCfg = Release|Any CPU
{54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Legacy|Any CPU.Build.0 = Release|Any CPU
{54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Legacy|Test.ActiveCfg = Release|Any CPU
{54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Legacy|Test.Build.0 = Release|Any CPU
{54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Release|Any CPU.Build.0 = Release|Any CPU
{54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Release|Test.ActiveCfg = Release|Any CPU
{54161CFE-FF42-4DDE-B161-3A49545DB5CD}.Release|Test.Build.0 = Release|Any CPU
{54161CFE-FF42-4DDE-B161-3A49545DB5CD}.v2018|Any CPU.ActiveCfg = Release|Any CPU
{54161CFE-FF42-4DDE-B161-3A49545DB5CD}.v2018|Any CPU.Build.0 = Release|Any CPU
{54161CFE-FF42-4DDE-B161-3A49545DB5CD}.v2018|Test.ActiveCfg = Release|Any CPU
{54161CFE-FF42-4DDE-B161-3A49545DB5CD}.v2018|Test.Build.0 = Release|Any CPU
{A15D6EE6-F954-415B-8605-8A8470CC87DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A15D6EE6-F954-415B-8605-8A8470CC87DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A15D6EE6-F954-415B-8605-8A8470CC87DC}.Debug|Test.ActiveCfg = Debug|Any CPU
{A15D6EE6-F954-415B-8605-8A8470CC87DC}.Debug|Test.Build.0 = Debug|Any CPU
{A15D6EE6-F954-415B-8605-8A8470CC87DC}.Legacy|Any CPU.ActiveCfg = Release|Any CPU
{A15D6EE6-F954-415B-8605-8A8470CC87DC}.Legacy|Any CPU.Build.0 = Release|Any CPU
{A15D6EE6-F954-415B-8605-8A8470CC87DC}.Legacy|Test.ActiveCfg = Release|Any CPU
{A15D6EE6-F954-415B-8605-8A8470CC87DC}.Legacy|Test.Build.0 = Release|Any CPU
{A15D6EE6-F954-415B-8605-8A8470CC87DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A15D6EE6-F954-415B-8605-8A8470CC87DC}.Release|Any CPU.Build.0 = Release|Any CPU
{A15D6EE6-F954-415B-8605-8A8470CC87DC}.Release|Test.ActiveCfg = Release|Any CPU
{A15D6EE6-F954-415B-8605-8A8470CC87DC}.Release|Test.Build.0 = Release|Any CPU
{A15D6EE6-F954-415B-8605-8A8470CC87DC}.v2018|Any CPU.ActiveCfg = Release|Any CPU
{A15D6EE6-F954-415B-8605-8A8470CC87DC}.v2018|Any CPU.Build.0 = Release|Any CPU
{A15D6EE6-F954-415B-8605-8A8470CC87DC}.v2018|Test.ActiveCfg = Release|Any CPU
{A15D6EE6-F954-415B-8605-8A8470CC87DC}.v2018|Test.Build.0 = Release|Any CPU
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Debug|Any CPU.ActiveCfg = v2018|Any CPU
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Debug|Any CPU.Build.0 = v2018|Any CPU
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Debug|Test.ActiveCfg = v2018|Any CPU
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Debug|Test.Build.0 = v2018|Any CPU
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Legacy|Any CPU.ActiveCfg = Legacy|Any CPU
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Legacy|Any CPU.Build.0 = Legacy|Any CPU
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Legacy|Test.ActiveCfg = Legacy|Any CPU
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Legacy|Test.Build.0 = Legacy|Any CPU
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Release|Any CPU.ActiveCfg = v2018|Any CPU
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Release|Any CPU.Build.0 = v2018|Any CPU
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Release|Test.ActiveCfg = v2018|Any CPU
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.Release|Test.Build.0 = v2018|Any CPU
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.v2018|Any CPU.ActiveCfg = v2018|Any CPU
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.v2018|Any CPU.Build.0 = v2018|Any CPU
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.v2018|Test.ActiveCfg = v2018|Any CPU
{F7ABBE07-C02F-4F7C-BF6E-C6656BF588CA}.v2018|Test.Build.0 = v2018|Any CPU
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.Debug|Test.ActiveCfg = Debug|Any CPU
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.Debug|Test.Build.0 = Debug|Any CPU
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.Legacy|Any CPU.ActiveCfg = Release|Any CPU
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.Legacy|Any CPU.Build.0 = Release|Any CPU
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.Legacy|Test.ActiveCfg = Release|Any CPU
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.Legacy|Test.Build.0 = Release|Any CPU
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.Release|Any CPU.Build.0 = Release|Any CPU
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.Release|Test.ActiveCfg = Release|Any CPU
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.Release|Test.Build.0 = Release|Any CPU
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.v2018|Any CPU.ActiveCfg = Release|Any CPU
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.v2018|Any CPU.Build.0 = Release|Any CPU
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.v2018|Test.ActiveCfg = Release|Any CPU
{D0C584C0-81D7-486E-B70E-D7F9256E0909}.v2018|Test.Build.0 = Release|Any CPU
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Debug|Test.ActiveCfg = Debug|Any CPU
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Debug|Test.Build.0 = Debug|Any CPU
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Legacy|Any CPU.ActiveCfg = Release|Any CPU
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Legacy|Any CPU.Build.0 = Release|Any CPU
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Legacy|Test.ActiveCfg = Release|Any CPU
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Legacy|Test.Build.0 = Release|Any CPU
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Release|Any CPU.Build.0 = Release|Any CPU
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Release|Test.ActiveCfg = Release|Any CPU
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.Release|Test.Build.0 = Release|Any CPU
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.v2018|Any CPU.ActiveCfg = Release|Any CPU
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.v2018|Any CPU.Build.0 = Release|Any CPU
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.v2018|Test.ActiveCfg = Release|Any CPU
{1839CFE2-3DB0-45A8-B03D-9AA797479A3A}.v2018|Test.Build.0 = Release|Any CPU
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Debug|Test.ActiveCfg = Debug|x86
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Debug|Test.Build.0 = Debug|x86
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Legacy|Any CPU.ActiveCfg = Release|Any CPU
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Legacy|Any CPU.Build.0 = Release|Any CPU
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Legacy|Test.ActiveCfg = Release|Any CPU
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Legacy|Test.Build.0 = Release|Any CPU
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Release|Any CPU.Build.0 = Release|Any CPU
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Release|Test.ActiveCfg = Release|Any CPU
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.Release|Test.Build.0 = Release|Any CPU
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.v2018|Any CPU.ActiveCfg = Release|Any CPU
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.v2018|Any CPU.Build.0 = Release|Any CPU
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.v2018|Test.ActiveCfg = Release|Any CPU
{E7CD429A-D057-48E3-8C51-E5C934E8E07B}.v2018|Test.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
+33
View File
@@ -33,11 +33,41 @@
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<DocumentationFile>..\bin\BepInEx.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Legacy|x86'">
<OutputPath>bin\x86\Legacy\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<Optimize>false</Optimize>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'v2018|x86'">
<OutputPath>bin\x86\v2018\</OutputPath>
<DefineConstants>DEBUG;TRACE;UNITY_2018</DefineConstants>
<Optimize>false</Optimize>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<DebugType>full</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<Reference Include="Mono.Cecil, Version=0.10.3.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
<HintPath>..\packages\Mono.Cecil.0.10.3\lib\net35\Mono.Cecil.dll</HintPath>
</Reference>
<Reference Include="Mono.Cecil.Mdb, Version=0.10.3.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
<HintPath>..\packages\Mono.Cecil.0.10.3\lib\net35\Mono.Cecil.Mdb.dll</HintPath>
</Reference>
<Reference Include="Mono.Cecil.Pdb, Version=0.10.3.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
<HintPath>..\packages\Mono.Cecil.0.10.3\lib\net35\Mono.Cecil.Pdb.dll</HintPath>
</Reference>
<Reference Include="Mono.Cecil.Rocks, Version=0.10.3.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
<HintPath>..\packages\Mono.Cecil.0.10.3\lib\net35\Mono.Cecil.Rocks.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="UnityEngine">
<HintPath>..\lib\UnityEngine.dll</HintPath>
@@ -46,8 +76,11 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Configuration\ConfigDefinition.cs" />
<Compile Include="Configuration\ConfigDescription.cs" />
<Compile Include="Configuration\ConfigEntry.cs" />
<Compile Include="Configuration\ConfigFile.cs" />
<Compile Include="Configuration\ConfigWrapper.cs" />
<Compile Include="Configuration\SettingChangedEventArgs.cs" />
<Compile Include="Configuration\TomlTypeConverter.cs" />
<Compile Include="Contract\Attributes.cs" />
<Compile Include="ConsoleUtil\ConsoleEncoding\ConsoleEncoding.Buffers.cs" />
+6 -22
View File
@@ -2,27 +2,6 @@
namespace BepInEx.Configuration
{
public class ConfigDescription
{
public ConfigDescription(string description, Type settingType, object defaultValue)
{
Description = description ?? throw new ArgumentNullException(nameof(description));
SettingType = settingType ?? throw new ArgumentNullException(nameof(settingType));
DefaultValue = defaultValue;
if(defaultValue == null && settingType.IsByRef)
throw new ArgumentException("defaultValue is null while settingType is a value type");
if(defaultValue != null && !settingType.IsInstanceOfType(defaultValue))
throw new ArgumentException("defaultValue can not be assigned to type " + settingType.Name);
}
public string Description { get; }
public Type SettingType { get; }
public object DefaultValue { get; }
//todo value range
}
public class ConfigDefinition : IEquatable<ConfigDefinition>
{
public string Section { get; }
@@ -39,7 +18,7 @@ namespace BepInEx.Configuration
{
if (other == null) return false;
return string.Equals(Key, other.Key)
&& string.Equals(Section, other.Section);
&& string.Equals(Section, other.Section);
}
public override bool Equals(object obj)
@@ -67,5 +46,10 @@ namespace BepInEx.Configuration
public static bool operator !=(ConfigDefinition left, ConfigDefinition right)
=> !Equals(left, right);
public override string ToString()
{
return Section + " / " + Key;
}
}
}
@@ -0,0 +1,21 @@
using System;
namespace BepInEx.Configuration
{
public class ConfigDescription
{
public ConfigDescription(string description)
{
Description = description ?? throw new ArgumentNullException(nameof(description));
}
public string Description { get; }
//todo value range
public string ToSerializedString()
{
return $"# {Description.Replace("\n", "\n# ")}";
}
}
}
+182
View File
@@ -0,0 +1,182 @@
using System;
using System.IO;
using System.Linq;
using BepInEx.Logging;
namespace BepInEx.Configuration
{
public sealed class ConfigEntry
{
internal ConfigEntry(ConfigFile configFile, ConfigDefinition definition, Type settingType, object defaultValue) : this(configFile, definition)
{
SetTypeAndDefaultValue(settingType, defaultValue, true);
}
internal ConfigEntry(ConfigFile configFile, ConfigDefinition definition)
{
ConfigFile = configFile ?? throw new ArgumentNullException(nameof(configFile));
Definition = definition ?? throw new ArgumentNullException(nameof(definition));
}
internal void SetTypeAndDefaultValue(Type settingType, object defaultValue, bool uniqueDefaultValue)
{
if (settingType == null) throw new ArgumentNullException(nameof(settingType));
if (settingType == SettingType)
{
if (uniqueDefaultValue)
DefaultValue = defaultValue;
return;
}
if (SettingType != null)
{
throw new ArgumentException($"Tried to define setting \"{Definition}\" as type {settingType.Name} " +
$"while it was already defined as type {SettingType.Name}. Use the same " +
$"Type for all Wrappers of a single setting.");
}
if (defaultValue == null && settingType.IsValueType)
throw new ArgumentException("defaultValue is null while settingType is a value type");
if (defaultValue != null && !settingType.IsInstanceOfType(defaultValue))
throw new ArgumentException("defaultValue can not be assigned to type " + settingType.Name);
SettingType = settingType;
DefaultValue = defaultValue;
}
private object _convertedValue;
private string _serializedValue;
public ConfigFile ConfigFile { get; }
public ConfigDefinition Definition { get; }
public ConfigDescription Description { get; internal set; }
public Type SettingType { get; private set; }
public object DefaultValue { get; private set; }
/// <summary>
/// Is the type of this setting defined, and by extension can <see cref="Value"/> of this setting be accessed.
/// Setting is defined when any <see cref="ConfigWrapper{T}"/> objects reference it.
/// </summary>
public bool IsDefined => SettingType != null;
/// <summary>
/// Can't be used when <see cref="IsDefined"/> is false.
/// </summary>
public object Value
{
get
{
ProcessSerializedValue();
return _convertedValue;
}
set => SetValue(value, true, this);
}
internal void SetValue(object newValue, bool fireEvent, object sender)
{
bool wasChanged = ProcessSerializedValue();
wasChanged = wasChanged || !Equals(newValue, _convertedValue);
if (wasChanged)
{
_convertedValue = newValue;
if (fireEvent)
OnSettingChanged(sender);
}
}
public string GetSerializedValue()
{
if (_serializedValue != null)
return _serializedValue;
if (!IsDefined)
return null;
return TomlTypeConverter.ConvertToString(Value, SettingType);
}
public void SetSerializedValue(string newValue, bool fireEvent, object sender)
{
string current = GetSerializedValue();
if (string.Equals(current, newValue)) return;
_serializedValue = newValue;
if (!IsDefined) return;
if (ProcessSerializedValue())
{
if (fireEvent)
OnSettingChanged(sender);
}
}
private bool ProcessSerializedValue()
{
if (!IsDefined)
throw new InvalidOperationException("Can't get the value before the SettingType is specified");
if (_serializedValue != null)
{
string value = _serializedValue;
_serializedValue = null;
if (value != "")
{
try
{
var newValue = TomlTypeConverter.ConvertToValue(value, SettingType);
if (!Equals(newValue, _convertedValue))
{
_convertedValue = newValue;
return true;
}
return false;
}
catch (Exception e)
{
Logger.Log(LogLevel.Warning, $"Config value of setting \"{Definition}\" could not be " +
$"parsed and will be ignored. Reason: {e.Message}; Value: {value}");
}
}
}
if (_convertedValue == null && DefaultValue != null)
{
_convertedValue = DefaultValue;
return true;
}
return false;
}
private void OnSettingChanged(object sender)
{
ConfigFile.OnSettingChanged(sender, this);
}
public void WriteDescription(StreamWriter writer)
{
if (Description != null)
writer.WriteLine(Description.ToSerializedString());
if (SettingType != null)
{
writer.WriteLine("# Setting type: " + SettingType.Name);
writer.WriteLine("# Default value: " + DefaultValue);
// todo acceptable values
if (SettingType.IsEnum && SettingType.GetCustomAttributes(typeof(FlagsAttribute), true).Any())
writer.WriteLine("# Multiple values can be set at the same time by separating them with , (e.g. Debug, Warning)");
}
}
}
}
+175 -40
View File
@@ -3,7 +3,8 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Text;
using BepInEx.Logging;
namespace BepInEx.Configuration
{
@@ -12,28 +13,30 @@ namespace BepInEx.Configuration
/// </summary>
public class ConfigFile
{
private static readonly Regex sanitizeKeyRegex = new Regex(@"[^a-zA-Z0-9\-\.]+");
// Need to be lazy evaluated to not cause problems for unit tests
private static ConfigFile _coreConfig;
internal static ConfigFile CoreConfig => _coreConfig ?? (_coreConfig = new ConfigFile(Paths.BepInExConfigPath, true));
internal static ConfigFile CoreConfig { get; } = new ConfigFile(Paths.BepInExConfigPath, true);
protected Dictionary<ConfigDefinition, ConfigEntry> Entries { get; } = new Dictionary<ConfigDefinition, ConfigEntry>();
protected internal Dictionary<ConfigDefinition, string> Cache { get; } = new Dictionary<ConfigDefinition, string>();
[Obsolete("Use ConfigEntries instead")]
public ReadOnlyCollection<ConfigDefinition> ConfigDefinitions => Entries.Keys.ToList().AsReadOnly();
public ReadOnlyCollection<ConfigDefinition> ConfigDefinitions => Cache.Keys.ToList().AsReadOnly();
/// <summary>
/// An event that is fired every time the config is reloaded.
/// </summary>
public event EventHandler ConfigReloaded;
public ReadOnlyCollection<ConfigEntry> ConfigEntries => Entries.Values.ToList().AsReadOnly();
public string ConfigFilePath { get; }
/// <summary>
/// If enabled, writes the config to disk every time a value is set.
/// If enabled, writes the config to disk every time a value is set. If disabled, you have to manually save or the changes will be lost!
/// </summary>
public bool SaveOnConfigSet { get; set; } = true;
public ConfigFile(string configPath, bool saveOnInit)
{
if (configPath == null) throw new ArgumentNullException(nameof(configPath));
configPath = Path.GetFullPath(configPath);
ConfigFilePath = configPath;
if (File.Exists(ConfigFilePath))
@@ -44,9 +47,13 @@ namespace BepInEx.Configuration
{
Save();
}
StartWatching();
}
private object _ioLock = new object();
#region Save/Load
private readonly object _ioLock = new object();
/// <summary>
/// Reloads the config from disk. Unsaved changes are lost.
@@ -55,9 +62,7 @@ namespace BepInEx.Configuration
{
lock (_ioLock)
{
Dictionary<ConfigDefinition, string> descriptions = Cache.ToDictionary(x => x.Key, x => x.Key.Description);
string currentSection = "";
string currentSection = string.Empty;
foreach (string rawLine in File.ReadAllLines(ConfigFilePath))
{
@@ -81,14 +86,18 @@ namespace BepInEx.Configuration
var definition = new ConfigDefinition(currentSection, currentKey);
if (descriptions.ContainsKey(definition))
definition.Description = descriptions[definition];
Entries.TryGetValue(definition, out ConfigEntry entry);
if (entry == null)
{
entry = new ConfigEntry(this, definition);
Entries[definition] = entry;
}
Cache[definition] = currentValue;
entry.SetSerializedValue(currentValue, true, this);
}
ConfigReloaded?.Invoke(this, EventArgs.Empty);
}
OnConfigReloaded();
}
/// <summary>
@@ -98,51 +107,177 @@ namespace BepInEx.Configuration
{
lock (_ioLock)
{
if (!Directory.Exists(Paths.ConfigPath))
Directory.CreateDirectory(Paths.ConfigPath);
StopWatching();
using (StreamWriter writer = new StreamWriter(File.Create(ConfigFilePath), System.Text.Encoding.UTF8))
foreach (var sectionKv in Cache.GroupBy(x => x.Key.Section).OrderBy(x => x.Key))
string directoryName = Path.GetDirectoryName(ConfigFilePath);
if (directoryName != null) Directory.CreateDirectory(directoryName);
using (var writer = new StreamWriter(File.Create(ConfigFilePath), Encoding.UTF8))
{
foreach (var sectionKv in Entries.GroupBy(x => x.Key.Section).OrderBy(x => x.Key))
{
// Section heading
writer.WriteLine($"[{sectionKv.Key}]");
foreach (var entryKv in sectionKv)
foreach (var configEntry in sectionKv.Select(x => x.Value))
{
writer.WriteLine();
if (!string.IsNullOrEmpty(entryKv.Key.Description))
writer.WriteLine($"# {entryKv.Key.Description.Replace("\n", "\n# ")}");
configEntry.WriteDescription(writer);
writer.WriteLine($"{entryKv.Key.Key} = {entryKv.Value}");
writer.WriteLine($"{configEntry.Definition.Key} = {configEntry.GetSerializedValue()}");
}
writer.WriteLine();
}
}
StartWatching();
}
}
public ConfigWrapper<T> Wrap<T>(ConfigDefinition configDefinition, T defaultValue = default(T))
#endregion
#region Wraps
public ConfigWrapper<T> Wrap<T>(ConfigDefinition configDefinition, T defaultValue, ConfigDescription configDescription = null)
{
if (!Cache.ContainsKey(configDefinition))
if (!TomlTypeConverter.CanConvert(typeof(T)))
throw new ArgumentException($"Type {typeof(T)} is not supported by the config system. Supported types: {string.Join(", ", TomlTypeConverter.GetSupportedTypes().Select(x => x.Name).ToArray())}");
Entries.TryGetValue(configDefinition, out var entry);
if (entry == null)
{
Cache.Add(configDefinition, TomlTypeConverter.ConvertToString(defaultValue));
Save();
entry = new ConfigEntry(this, configDefinition, typeof(T), defaultValue);
Entries[configDefinition] = entry;
}
else
{
var original = Cache.Keys.First(x => x.Equals(configDefinition));
entry.SetTypeAndDefaultValue(typeof(T), defaultValue, !Equals(defaultValue, default(T)));
}
if (original.Description != configDefinition.Description)
if (configDescription != null)
{
if (entry.Description != null)
Logger.Log(LogLevel.Warning, $"Tried to add configDescription to setting {configDefinition} when it already had one defined. Only add configDescription once or a random one will be used.");
entry.Description = configDescription;
}
return new ConfigWrapper<T>(entry);
}
[Obsolete("Use other Wrap overloads instead")]
public ConfigWrapper<T> Wrap<T>(string section, string key, string description = null, T defaultValue = default(T))
=> Wrap(new ConfigDefinition(section, key), defaultValue, string.IsNullOrEmpty(description) ? null : new ConfigDescription(description));
public ConfigWrapper<T> Wrap<T>(string section, string key, T defaultValue, ConfigDescription configDescription = null)
=> Wrap(new ConfigDefinition(section, key), defaultValue, configDescription);
#endregion
#region Events
/// <summary>
/// An event that is fired every time the config is reloaded.
/// </summary>
public event EventHandler ConfigReloaded;
/// <summary>
/// Fired when one of the settings is changed.
/// </summary>
public event EventHandler<SettingChangedEventArgs> SettingChanged;
protected internal void OnSettingChanged(object sender, ConfigEntry changedEntry)
{
if (changedEntry == null) throw new ArgumentNullException(nameof(changedEntry));
if (SettingChanged != null)
{
var args = new SettingChangedEventArgs(changedEntry);
foreach (var callback in SettingChanged.GetInvocationList().Cast<EventHandler<SettingChangedEventArgs>>())
{
original.Description = configDefinition.Description;
Save();
try
{
callback(sender, args);
}
catch (Exception e)
{
Logger.Log(LogLevel.Error, e);
}
}
}
return new ConfigWrapper<T>(this, configDefinition);
// todo better way to prevent write loop? maybe do some caching?
if (sender != this && SaveOnConfigSet)
Save();
}
public ConfigWrapper<T> Wrap<T>(string section, string key, string description = null, T defaultValue = default(T))
=> Wrap<T>(new ConfigDefinition(section, key, description), defaultValue);
protected void OnConfigReloaded()
{
if (ConfigReloaded != null)
{
foreach (var callback in ConfigReloaded.GetInvocationList().Cast<EventHandler>())
{
try
{
callback(this, EventArgs.Empty);
}
catch (Exception e)
{
Logger.Log(LogLevel.Error, e);
}
}
}
}
#endregion
#region File watcher
private FileSystemWatcher _watcher;
/// <summary>
/// Start watching the config file on disk for changes.
/// </summary>
public void StartWatching()
{
lock (_ioLock)
{
if (_watcher != null) return;
_watcher = new FileSystemWatcher
{
Path = Path.GetDirectoryName(ConfigFilePath) ?? throw new ArgumentException("Invalid config path"),
Filter = Path.GetFileName(ConfigFilePath),
IncludeSubdirectories = false,
NotifyFilter = NotifyFilters.LastWrite,
EnableRaisingEvents = true
};
_watcher.Changed += (sender, args) => Reload();
}
}
/// <summary>
/// Stop watching the config file on disk for changes.
/// </summary>
public void StopWatching()
{
lock (_ioLock)
{
_watcher?.Dispose();
_watcher = null;
}
}
~ConfigFile()
{
StopWatching();
}
#endregion
}
}
}
+12 -18
View File
@@ -2,11 +2,12 @@
namespace BepInEx.Configuration
{
public class ConfigWrapper<T>
public sealed class ConfigWrapper<T>
{
public ConfigDefinition Definition { get; protected set; }
public ConfigEntry ConfigEntry { get; }
public ConfigFile ConfigFile { get; protected set; }
public ConfigDefinition Definition => ConfigEntry.Definition;
public ConfigFile ConfigFile => ConfigEntry.ConfigFile;
/// <summary>
/// Fired when the setting is changed. Does not detect changes made outside from this object.
@@ -15,25 +16,18 @@ namespace BepInEx.Configuration
public T Value
{
get => TomlTypeConverter.ConvertToValue<T>(ConfigFile.Cache[Definition]);
set
{
ConfigFile.Cache[Definition] = TomlTypeConverter.ConvertToString(value);
if (ConfigFile.SaveOnConfigSet)
ConfigFile.Save();
SettingChanged?.Invoke(this, EventArgs.Empty);
}
get => (T)ConfigEntry.Value;
set => ConfigEntry.SetValue(value, true, this);
}
public ConfigWrapper(ConfigFile configFile, ConfigDefinition definition)
internal ConfigWrapper(ConfigEntry configEntry)
{
if (!TomlTypeConverter.TypeConverters.ContainsKey(typeof(T)))
throw new ArgumentException("Unsupported config wrapper type");
ConfigEntry = configEntry ?? throw new ArgumentNullException(nameof(configEntry));
ConfigFile = configFile;
Definition = definition;
configEntry.ConfigFile.SettingChanged += (sender, args) =>
{
if (args.ChangedSetting == configEntry) SettingChanged?.Invoke(sender, args);
};
}
}
}
@@ -0,0 +1,14 @@
using System;
namespace BepInEx.Configuration
{
public sealed class SettingChangedEventArgs : EventArgs
{
public SettingChangedEventArgs(ConfigEntry changedSetting)
{
ChangedSetting = changedSetting;
}
public ConfigEntry ChangedSetting { get; }
}
}
+6 -5
View File
@@ -98,10 +98,8 @@ namespace BepInEx.Configuration
},
};
public static string ConvertToString<T>(T value)
public static string ConvertToString(object value, Type valueType)
{
var valueType = typeof(T);
var conv = GetConverter(valueType);
if (conv == null)
throw new InvalidOperationException($"Cannot convert from type {valueType}");
@@ -111,13 +109,16 @@ namespace BepInEx.Configuration
public static T ConvertToValue<T>(string value)
{
var valueType = typeof(T);
return (T)ConvertToValue(value, typeof(T));
}
public static object ConvertToValue(string value, Type valueType)
{
var conv = GetConverter(valueType);
if (conv == null)
throw new InvalidOperationException($"Cannot convert to type {valueType}");
return (T)conv.ConvertToObject(value, valueType);
return conv.ConvertToObject(value, valueType);
}
private static TypeConverter GetConverter(Type valueType)
+131
View File
@@ -0,0 +1,131 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{E7CD429A-D057-48E3-8C51-E5C934E8E07B}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>BepInExTests</RootNamespace>
<AssemblyName>BepInExTests</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
<IsCodedUITest>False</IsCodedUITest>
<TestProjectType>UnitTest</TestProjectType>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<TargetFrameworkProfile />
<FileUpgradeFlags>
</FileUpgradeFlags>
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<OldToolsVersion>15.0</OldToolsVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
</Reference>
<Reference Include="System" />
</ItemGroup>
<Choose>
<When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'">
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
</ItemGroup>
</When>
<Otherwise />
</Choose>
<ItemGroup>
<Compile Include="Configuration\ConfigFileTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BepInEx\BepInEx.csproj">
<Project>{4ffba620-f5ed-47f9-b90c-dad1316fd9b9}</Project>
<Name>BepInEx</Name>
</ProjectReference>
</ItemGroup>
<Choose>
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.CodedUITestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Extension, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITesting, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
</ItemGroup>
</When>
</Choose>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.props'))" />
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.targets'))" />
</Target>
<Import Project="..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.1.3.2\build\net45\MSTest.TestAdapter.targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
@@ -0,0 +1,172 @@
using System;
using System.Collections.Concurrent;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.IO;
using System.Linq;
using System.Threading;
namespace BepInEx.Configuration.Tests
{
[TestClass]
public class ConfigFileTests
{
private static ConcurrentBag<ConfigFile> _toRemove;
[ClassInitialize]
public static void Init(TestContext context)
{
_toRemove = new ConcurrentBag<ConfigFile>();
}
[ClassCleanup]
public static void Cleanup()
{
foreach (var configFile in _toRemove)
{
configFile.StopWatching();
File.Delete(configFile.ConfigFilePath);
}
}
private static ConfigFile MakeConfig()
{
string configPath = Path.GetTempFileName();
if (configPath == null) throw new InvalidOperationException("Wtf");
var config = new ConfigFile(configPath, true);
_toRemove.Add(config);
return config;
}
[TestMethod]
public void SaveTest()
{
MakeConfig().Save();
}
[TestMethod]
public void SaveTestValueChange()
{
var c = MakeConfig();
var w = c.Wrap("Cat", "Key", 0, new ConfigDescription("Test"));
var lines = File.ReadAllLines(c.ConfigFilePath);
Assert.AreEqual(0, lines.Count(x => x.Equals("[Cat]")));
Assert.AreEqual(0, lines.Count(x => x.Equals("# Test")));
Assert.AreEqual(0, lines.Count(x => x.Equals("Key = 0")));
c.Save();
lines = File.ReadAllLines(c.ConfigFilePath);
Assert.AreEqual(1, lines.Count(x => x.Equals("[Cat]")));
Assert.AreEqual(1, lines.Count(x => x.Equals("# Test")));
Assert.AreEqual(1, lines.Count(x => x.Equals("Key = 0")));
w.Value = 69;
lines = File.ReadAllLines(c.ConfigFilePath);
Assert.AreEqual(1, lines.Count(x => x.Equals("[Cat]")));
Assert.AreEqual(1, lines.Count(x => x.Equals("# Test")));
Assert.AreEqual(1, lines.Count(x => x.Equals("Key = 69")));
}
[TestMethod]
public void AutoSaveTest()
{
var c = MakeConfig();
c.Wrap("Cat", "Key", 0, new ConfigDescription("Test"));
var eventFired = new AutoResetEvent(false);
c.ConfigReloaded += (sender, args) => eventFired.Set();
c.Save();
Assert.IsFalse(eventFired.WaitOne(200));
}
[TestMethod]
public void ReadTest()
{
var c = MakeConfig();
File.WriteAllText(c.ConfigFilePath, "[Cat]\n# Test\nKey=1\n");
c.Reload();
var w = c.Wrap("Cat", "Key", 0, new ConfigDescription("Test"));
Assert.AreEqual(w.Value, 1);
var w2 = c.Wrap("Cat", "Key2", 0, new ConfigDescription("Test"));
Assert.AreEqual(w2.Value, 0);
}
[TestMethod]
public void ReadTest2()
{
var c = MakeConfig();
var w = c.Wrap("Cat", "Key", 0, new ConfigDescription("Test"));
Assert.AreEqual(w.Value, 0);
File.WriteAllText(c.ConfigFilePath, "[Cat]\n# Test\nKey = 1 \n");
c.Reload();
Assert.AreEqual(w.Value, 1);
}
[TestMethod]
public void FileWatchTest()
{
var c = MakeConfig();
var w = c.Wrap("Cat", "Key", 0, new ConfigDescription("Test"));
Assert.AreEqual(w.Value, 0);
var eventFired = new AutoResetEvent(false);
w.SettingChanged += (sender, args) => eventFired.Set();
File.WriteAllText(c.ConfigFilePath, "[Cat]\n# Test\nKey = 1 \n");
Assert.IsTrue(eventFired.WaitOne(500));
Assert.AreEqual(w.Value, 1);
}
[TestMethod]
public void FileWatchTestNoSelfReload()
{
var c = MakeConfig();
var eventFired = new AutoResetEvent(false);
c.ConfigReloaded += (sender, args) => eventFired.Set();
c.Save();
Assert.IsFalse(eventFired.WaitOne(200));
}
[TestMethod]
public void EventTestWrapper()
{
var c = MakeConfig();
var w = c.Wrap("Cat", "Key", 0, new ConfigDescription("Test"));
File.WriteAllText(c.ConfigFilePath, "[Cat]\n# Test\nKey=1\n");
var eventFired = false;
w.SettingChanged += (sender, args) => eventFired = true;
c.Reload();
Assert.IsTrue(eventFired);
}
[TestMethod]
public void EventTestReload()
{
var c = MakeConfig();
var eventFired = false;
var w = c.Wrap("Cat", "Key", 0, new ConfigDescription("Test"));
w.SettingChanged += (sender, args) => eventFired = true;
Assert.IsFalse(eventFired);
File.WriteAllText(c.ConfigFilePath, "[Cat]\n# Test\nKey=1\n");
c.Reload();
Assert.IsTrue(eventFired);
}
}
}
+36
View File
@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("BepInExTests")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("BepInExTests")]
[assembly: AssemblyCopyright("Copyright © 2019")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("e7cd429a-d057-48e3-8c51-e5c934e8e07b")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
+5
View File
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="MSTest.TestAdapter" version="1.3.2" targetFramework="net45" />
<package id="MSTest.TestFramework" version="1.3.2" targetFramework="net45" />
</packages>