fix bugs, get more bugs...
This commit is contained in:
parent
c3776a0ba3
commit
32ecee29a4
@ -1 +1 @@
|
||||
Subproject commit a18167f13f14d45bff57f8d88cea1e9060d3fa60
|
||||
Subproject commit a40a8b3685a377ab183eb0c906acf8c806b4e215
|
||||
9
mocha-dotnet-new
Executable file
9
mocha-dotnet-new
Executable file
@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
# mocha-dotnet-new
|
||||
# creates a new DotNet project and adds all the references needed to use
|
||||
# Mocha from within .NET
|
||||
|
||||
dotnet new console
|
||||
dotnet add reference /usr/lib/mocha/dotnet/Mocha.Core.dll
|
||||
dotnet add reference /usr/lib/mocha/dotnet/MBS.Core.dll
|
||||
@ -64,20 +64,8 @@ public abstract class Oms
|
||||
{
|
||||
InitializeInternal();
|
||||
|
||||
AccessModifier.Private = new AccessModifier(GetInstance(KnownInstanceGuids.AccessModifiers.Private));
|
||||
AccessModifier.Protected = new AccessModifier(GetInstance(KnownInstanceGuids.AccessModifiers.Protected));
|
||||
AccessModifier.Public = new AccessModifier(GetInstance(KnownInstanceGuids.AccessModifiers.Public));
|
||||
AccessModifier.RootA2 = new AccessModifier(GetInstance(KnownInstanceGuids.AccessModifiers.RootA2));
|
||||
UpdateSyntacticSugar();
|
||||
|
||||
PropertyInfo[] pis = typeof(RelationalOperator).GetProperties(BindingFlags.Static | BindingFlags.Public);
|
||||
foreach (PropertyInfo pi in pis)
|
||||
{
|
||||
PropertyInfo pi2 = typeof(KnownInstanceGuids.RelationalOperators).GetProperty(pi.Name, BindingFlags.Public | BindingFlags.Static);
|
||||
Guid guid = (Guid)pi2.GetValue(null);
|
||||
|
||||
pi.SetValue(null, new RelationalOperator(GetInstance(guid)));
|
||||
}
|
||||
|
||||
RegisterSystemRoutine(KnownInstanceGuids.SystemAttributeRoutines.GetRuntimeVersion, new SystemAttributeRoutine<string>(this, KnownInstanceGuids.SystemAttributeRoutines.GetRuntimeVersion, delegate (Oms oms, OmsContext context)
|
||||
{
|
||||
return RuntimeVersion.ToString();
|
||||
@ -141,12 +129,45 @@ public abstract class Oms
|
||||
|
||||
public TenantHandle CurrentTenant { get; private set; }
|
||||
|
||||
protected abstract bool SelectTenantInternal(TenantHandle tenant);
|
||||
private void UpdateSyntacticSugar()
|
||||
{
|
||||
if (CurrentTenant != TenantHandle.Empty)
|
||||
{
|
||||
// now that tenant is selected, update the syntactic sugar properties
|
||||
// !!! THIS IS PROBABLY NOT THREAD-SAFE. BE VERY VERY CAREFUL !!!
|
||||
UpdateSyntacticSugar<AccessModifier>(typeof(KnownInstanceGuids.AccessModifiers));
|
||||
UpdateSyntacticSugar<RelationalOperator>(typeof(KnownInstanceGuids.RelationalOperators));
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSyntacticSugar<T>(Type sourceType) where T : ConcreteInstanceWrapper
|
||||
{
|
||||
Type destinationType = typeof(T);
|
||||
PropertyInfo[] pis = destinationType.GetProperties(BindingFlags.Static | BindingFlags.Public);
|
||||
foreach (PropertyInfo pi in pis)
|
||||
{
|
||||
PropertyInfo pi2 = sourceType.GetProperty(pi.Name, BindingFlags.Public | BindingFlags.Static);
|
||||
Guid guid = (Guid)pi2.GetValue(null);
|
||||
|
||||
if (TryGetInstance(guid, out InstanceHandle ih))
|
||||
{
|
||||
object objT = destinationType.Assembly.CreateInstance(destinationType.FullName, false, BindingFlags.CreateInstance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new object[] { ih }, null, null);
|
||||
pi.SetValue(null, objT);
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract bool SelectTenantInternal(TenantHandle tenant);
|
||||
public void SelectTenant(TenantHandle tenant)
|
||||
{
|
||||
if (SelectTenantInternal(tenant))
|
||||
{
|
||||
CurrentTenant = tenant;
|
||||
|
||||
UpdateSyntacticSugar();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1116,6 +1137,11 @@ public abstract class Oms
|
||||
{
|
||||
AddLibraryReferenceInternal(library);
|
||||
}
|
||||
protected abstract void RemoveLibraryReferenceInternal(LibraryHandle library);
|
||||
public void RemoveLibraryReference(LibraryHandle library)
|
||||
{
|
||||
RemoveLibraryReferenceInternal(library);
|
||||
}
|
||||
|
||||
private Dictionary<string, LibraryHandle> _libraries = new Dictionary<string, LibraryHandle>();
|
||||
/// <summary>
|
||||
@ -1249,6 +1275,20 @@ public abstract class Oms
|
||||
return lh;
|
||||
}
|
||||
|
||||
private Dictionary<LibraryHandle, string> _libraryFileNames = new Dictionary<LibraryHandle, string>();
|
||||
|
||||
public LibraryHandle LoadLibrary(Type type, string manifestResourceStreamName)
|
||||
{
|
||||
Stream? st = type.Assembly.GetManifestResourceStream(manifestResourceStreamName);
|
||||
if (st != null)
|
||||
{
|
||||
LibraryHandle lh = LoadLibrary(st);
|
||||
_libraryFileNames[lh] = manifestResourceStreamName;
|
||||
return lh;
|
||||
}
|
||||
throw new KeyNotFoundException(String.Format("manifest resource stream named '{0}' not found", manifestResourceStreamName));
|
||||
}
|
||||
|
||||
protected abstract void InitializeLibraryInternal(LibraryHandle lh, Library lib);
|
||||
|
||||
public string? GetTenantName(TenantHandle tenant)
|
||||
|
||||
@ -34,6 +34,9 @@ public class MemoryOms : Oms
|
||||
private Dictionary<InstanceHandle, Dictionary<InstanceHandle, List<AttributeValue>>> _Attributes = new Dictionary<InstanceHandle, Dictionary<InstanceHandle, List<AttributeValue>>>();
|
||||
private Dictionary<InstanceHandle, Dictionary<InstanceHandle, List<RelationshipValue>>> _Relationships = new Dictionary<InstanceHandle, Dictionary<InstanceHandle, List<RelationshipValue>>>();
|
||||
|
||||
// private Dictionary<Guid, LibraryInst_g> _libraryInst_gs = new Dictionary<Guid, LibraryInst_g>();
|
||||
// private Dictionary<InstanceHandle, LibraryInst_g> _libraryInst_is = new Dictionary<InstanceHandle, LibraryInst_g>();
|
||||
|
||||
public Guid GetGlobalIdentifier(InstanceHandle instance)
|
||||
{
|
||||
if (_guidsByInstance.ContainsKey(instance))
|
||||
@ -240,6 +243,16 @@ public class MemoryOms : Oms
|
||||
_libraryReferences[_CurrentTenant].Add(library);
|
||||
}
|
||||
}
|
||||
protected override void RemoveLibraryReferenceInternal(LibraryHandle library)
|
||||
{
|
||||
if (_libraryReferences.ContainsKey(_CurrentTenant))
|
||||
{
|
||||
if (_libraryReferences[_CurrentTenant].Contains(library))
|
||||
{
|
||||
_libraryReferences[_CurrentTenant].Remove(library);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override Guid GetGlobalIdentifierInternal(InstanceHandle instance)
|
||||
{
|
||||
@ -249,10 +262,19 @@ public class MemoryOms : Oms
|
||||
Guid gid = _CurrentTenantData.GetGlobalIdentifier(instance);
|
||||
if (gid == Guid.Empty)
|
||||
{
|
||||
if (_libraryInst_is.ContainsKey(instance))
|
||||
if (_libraryReferences.ContainsKey(_CurrentTenant))
|
||||
{
|
||||
gid = _libraryInst_is[instance].GlobalIdentifier;
|
||||
foreach (LibraryHandle lh in _libraryReferences[_CurrentTenant])
|
||||
{
|
||||
gid = _libraries[lh].GetGlobalIdentifier(instance);
|
||||
if (gid != Guid.Empty)
|
||||
return gid;
|
||||
}
|
||||
}
|
||||
// if (_libraryInst_is.ContainsKey(instance))
|
||||
// {
|
||||
// gid = _libraryInst_is[instance].GlobalIdentifier;
|
||||
// }
|
||||
}
|
||||
return gid;
|
||||
}
|
||||
@ -298,9 +320,9 @@ public class MemoryOms : Oms
|
||||
{
|
||||
InstanceHandle ih = lib.CreateInstance(inst.InstanceGuid);
|
||||
|
||||
LibraryInst_g lig = new LibraryInst_g() { GlobalIdentifier = inst.InstanceGuid, InstanceHandle = ih, SourceLibrary = lh };
|
||||
_libraryInst_is[ih] = lig;
|
||||
_libraryInst_gs[inst.InstanceGuid] = lig;
|
||||
// LibraryInst_g lig = new LibraryInst_g() { GlobalIdentifier = inst.InstanceGuid, InstanceHandle = ih, SourceLibrary = lh };
|
||||
// _libraryInst_is[ih] = lig;
|
||||
// _libraryInst_gs[inst.InstanceGuid] = lig;
|
||||
}
|
||||
foreach (LibraryAttribute att in data.Attributes)
|
||||
{
|
||||
@ -328,8 +350,6 @@ public class MemoryOms : Oms
|
||||
public InstanceHandle InstanceHandle;
|
||||
public Guid GlobalIdentifier;
|
||||
}
|
||||
private Dictionary<Guid, LibraryInst_g> _libraryInst_gs = new Dictionary<Guid, LibraryInst_g>();
|
||||
private Dictionary<InstanceHandle, LibraryInst_g> _libraryInst_is = new Dictionary<InstanceHandle, LibraryInst_g>();
|
||||
|
||||
protected override bool TryGetInstanceInternal(Guid globalIdentifier, out InstanceHandle ih)
|
||||
{
|
||||
@ -339,27 +359,30 @@ public class MemoryOms : Oms
|
||||
bool v = _CurrentTenantData.TryGetInstance(globalIdentifier, out ih);
|
||||
if (!v)
|
||||
{
|
||||
if (_libraryInst_gs.ContainsKey(globalIdentifier))
|
||||
{
|
||||
ih = _libraryInst_gs[globalIdentifier].InstanceHandle;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (LibraryHandle lh in _libraryReferences[_CurrentTenant])
|
||||
// if (_libraryInst_gs.ContainsKey(globalIdentifier))
|
||||
// {
|
||||
// ih = _libraryInst_gs[globalIdentifier].InstanceHandle;
|
||||
// return true;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
if (_libraryReferences.ContainsKey(_CurrentTenant))
|
||||
{
|
||||
if (_libraries.ContainsKey(lh))
|
||||
foreach (LibraryHandle lh in _libraryReferences[_CurrentTenant])
|
||||
{
|
||||
if (_libraries[lh].TryGetInstance(globalIdentifier, out ih))
|
||||
if (_libraries.ContainsKey(lh))
|
||||
{
|
||||
LibraryInst_g g = new LibraryInst_g() { InstanceHandle = ih, SourceLibrary = lh, GlobalIdentifier = globalIdentifier };
|
||||
_libraryInst_gs[globalIdentifier] = g;
|
||||
_libraryInst_is[ih] = g;
|
||||
return true;
|
||||
if (_libraries[lh].TryGetInstance(globalIdentifier, out ih))
|
||||
{
|
||||
// LibraryInst_g g = new LibraryInst_g() { InstanceHandle = ih, SourceLibrary = lh, GlobalIdentifier = globalIdentifier };
|
||||
// _libraries[lh]._libraryInst_gs[globalIdentifier] = g;
|
||||
// _libraries[lh]._libraryInst_is[ih] = g;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// }
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
@ -142,9 +142,11 @@ public class McxMiniLibraryPlugin : LibraryPlugin
|
||||
|
||||
Guid sourceInstanceGuid = guids[sourceInstanceIndex];
|
||||
Guid relationshipInstanceGuid = guids[relationshipInstanceIndex];
|
||||
Guid targetInstanceGuid = guids[targetInstanceIndex];
|
||||
|
||||
library.Relationships.Add(new LibraryRelationship(sourceInstanceGuid, relationshipInstanceGuid, targetInstanceGuid));
|
||||
if (targetInstanceIndex >= 0 && targetInstanceIndex < guids.Count)
|
||||
{
|
||||
Guid targetInstanceGuid = guids[targetInstanceIndex];
|
||||
library.Relationships.Add(new LibraryRelationship(sourceInstanceGuid, relationshipInstanceGuid, targetInstanceGuid));
|
||||
}
|
||||
}
|
||||
|
||||
r.BaseStream.Seek(stringTableSection.Offset, SeekOrigin.Begin);
|
||||
|
||||
@ -131,4 +131,23 @@ public class BasicTests : OmsTestsBase
|
||||
Oms.SetAttributeValue(irTestClassInstance, irTestTextAttribute, irTestClass);
|
||||
}, Throws.ArgumentException, "Assigning something other than a System.String value to a Text Attribute MUST throw an ArgumentException.");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TextAttribute_GetInstance_returns_same_InstanceHandle_before_and_after_LoadLibrary()
|
||||
{
|
||||
// remove the Web reference just for this test
|
||||
// Oms.RemoveLibraryReference(lhWeb);
|
||||
|
||||
InstanceHandle ihTextAttribute = Oms.GetInstance(KnownInstanceGuids.Classes.TextAttribute);
|
||||
|
||||
// load the other library, but do not reference it yet
|
||||
LibraryHandle lhWeb = Oms.LoadLibrary(typeof(LibraryLoaderTests), "Mocha.Core.Tests.Resources.net.alcetech.Mocha.Web.mcl");
|
||||
|
||||
// add back the reference to the other library
|
||||
// Oms.AddLibraryReference(lhWeb);
|
||||
|
||||
InstanceHandle ihTextAttribute2 = Oms.GetInstance(KnownInstanceGuids.Classes.TextAttribute);
|
||||
Assert.That(ihTextAttribute2, Is.EqualTo(ihTextAttribute));
|
||||
}
|
||||
|
||||
}
|
||||
@ -8,19 +8,10 @@ public class EmbeddedMiniOms : MemoryOms
|
||||
{
|
||||
base.InitializeInternal();
|
||||
|
||||
LibraryHandle lh = LoadLibrary(typeof(EmbeddedMiniOms), "Mocha.Core.Tests.Resources.net.alcetech.Mocha.System.mcl");
|
||||
|
||||
TenantHandle th = CreateTenant("super");
|
||||
SelectTenant(th);
|
||||
|
||||
System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly();
|
||||
Stream? stream = asm.GetManifestResourceStream("Mocha.Core.Tests.Resources.net.alcetech.Mocha.System.mcl");
|
||||
if (stream != null)
|
||||
{
|
||||
LibraryHandle lh = LoadLibrary(stream);
|
||||
this.AddLibraryReference(lh);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Manifest resource stream not found!");
|
||||
}
|
||||
AddLibraryReference(lh);
|
||||
}
|
||||
}
|
||||
56
mocha-dotnet/tests/Mocha.Core.Tests/InheritanceTests.cs
Normal file
56
mocha-dotnet/tests/Mocha.Core.Tests/InheritanceTests.cs
Normal file
@ -0,0 +1,56 @@
|
||||
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// This file is part of Mocha.NET.
|
||||
//
|
||||
// Mocha.NET is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Mocha.NET is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
using Mocha.Core.OmsImplementations.Mini;
|
||||
|
||||
namespace Mocha.Core.Tests;
|
||||
|
||||
[TestFixture]
|
||||
public class InheritanceTests : OmsTestsBase
|
||||
{
|
||||
[Test]
|
||||
public void TextAttribute_ParentClass_is_Class()
|
||||
{
|
||||
InstanceHandle ihTextAttribute = Oms.GetInstance(KnownInstanceGuids.Classes.TextAttribute);
|
||||
InstanceHandle ihClass = Oms.GetInstance(KnownInstanceGuids.Classes.Class);
|
||||
|
||||
InstanceHandle ihParentClass = Oms.GetParentClass(ihTextAttribute);
|
||||
Assert.That(ihParentClass, Is.EqualTo(ihClass));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TextAttribute_ParentClass_is_Class_when_multiple_libraries_are_referenced()
|
||||
{
|
||||
// add a reference to the other library
|
||||
LibraryHandle lhWeb = Oms.LoadLibrary(typeof(LibraryLoaderTests), "Mocha.Core.Tests.Resources.net.alcetech.Mocha.Web.mcl");
|
||||
Oms.AddLibraryReference(lhWeb);
|
||||
|
||||
// ??? this is weird. the moment we add that library reference, this suddenly breaks
|
||||
// 2024-10-09 08:24 beckermj - this is why we have this unit test
|
||||
// the reason is that the inst for guid {c2f36542...} is NOT the same instance handle as the originally registered one
|
||||
// we need to fix GetInstance so that it returns the same InstanceHandle for the same GUID
|
||||
// ... or maybe get rid of InstanceHandles entirely
|
||||
|
||||
InstanceHandle ihTextAttribute = Oms.GetInstance(KnownInstanceGuids.Classes.TextAttribute);
|
||||
InstanceHandle ihClass = Oms.GetInstance(KnownInstanceGuids.Classes.Class);
|
||||
|
||||
InstanceHandle ihParentClass = Oms.GetParentClass(ihTextAttribute);
|
||||
Assert.That(ihParentClass, Is.EqualTo(ihClass));
|
||||
}
|
||||
}
|
||||
84
mocha-dotnet/tests/Mocha.Core.Tests/LibraryLoaderTests.cs
Normal file
84
mocha-dotnet/tests/Mocha.Core.Tests/LibraryLoaderTests.cs
Normal file
@ -0,0 +1,84 @@
|
||||
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// This file is part of Mocha.NET.
|
||||
//
|
||||
// Mocha.NET is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Mocha.NET is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
using Mocha.Core.OmsImplementations.Mini;
|
||||
|
||||
namespace Mocha.Core.Tests;
|
||||
|
||||
[TestFixture]
|
||||
public class LibraryLoaderTests : OmsTestsBase
|
||||
{
|
||||
private LibraryHandle lhWeb;
|
||||
|
||||
protected override void AfterSetup()
|
||||
{
|
||||
base.AfterSetup();
|
||||
|
||||
// add a reference to the other library
|
||||
lhWeb = Oms.LoadLibrary(typeof(LibraryLoaderTests), "Mocha.Core.Tests.Resources.net.alcetech.Mocha.Web.mcl");
|
||||
Oms.AddLibraryReference(lhWeb);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TextAttribute_GetInstance_returns_same_InstanceHandle_before_and_after_AddLibraryReference()
|
||||
{
|
||||
// remove the Web reference just for this test
|
||||
Oms.RemoveLibraryReference(lhWeb);
|
||||
|
||||
InstanceHandle ihTextAttribute = Oms.GetInstance(KnownInstanceGuids.Classes.TextAttribute);
|
||||
|
||||
// add back the reference to the other library
|
||||
Oms.AddLibraryReference(lhWeb);
|
||||
|
||||
InstanceHandle ihTextAttribute2 = Oms.GetInstance(KnownInstanceGuids.Classes.TextAttribute);
|
||||
Assert.That(ihTextAttribute, Is.EqualTo(ihTextAttribute2));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Class_referenced_from_One_library_Accessible_from_Another()
|
||||
{
|
||||
// First we make sure that Class (defined in Mocha.System, which we are known to have) loads
|
||||
InstanceHandle ihClass = Oms.GetInstance(KnownInstanceGuids.Classes.Class);
|
||||
Assert.That(ihClass, Is.Not.EqualTo(InstanceHandle.Empty));
|
||||
|
||||
// Now we try loading Mocha.Web::`Route`, if this fails, we fail our test
|
||||
bool succeeded = Oms.TryGetInstance(KnownInstanceGuids.Classes.Route, out InstanceHandle ihRoute);
|
||||
Assert.That(succeeded, Is.True);
|
||||
|
||||
Assert.That(ihRoute, Is.Not.EqualTo(InstanceHandle.Empty));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Parent_Class_referenced_from_One_library_Accessible_from_Another()
|
||||
{
|
||||
// First we make sure that Class (defined in Mocha.System, which we are known to have) loads
|
||||
InstanceHandle ihClass = Oms.GetInstance(KnownInstanceGuids.Classes.Class);
|
||||
Assert.That(ihClass, Is.Not.EqualTo(InstanceHandle.Empty));
|
||||
|
||||
// Now we try loading Mocha.Web::`Route`, if this fails, we fail our test
|
||||
bool succeeded = Oms.TryGetInstance(KnownInstanceGuids.Classes.Route, out InstanceHandle ihRoute);
|
||||
Assert.That(succeeded, Is.True);
|
||||
|
||||
Assert.That(ihRoute, Is.Not.EqualTo(InstanceHandle.Empty));
|
||||
|
||||
// `Route` parent class should be `Class`
|
||||
InstanceHandle ihPClass = Oms.GetParentClass(ihRoute);
|
||||
Assert.That(ihPClass, Is.EqualTo(ihClass));
|
||||
}
|
||||
}
|
||||
@ -44,6 +44,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources\net.alcetech.Mocha.System.mcl" />
|
||||
<EmbeddedResource Include="Resources\net.alcetech.Mocha.Web.mcl" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@ -0,0 +1 @@
|
||||
../../../../mocha-common/mocha-common/output/net.alcetech.Mocha.Web.mcl
|
||||
Loading…
x
Reference in New Issue
Block a user