Skip to content

Commit

Permalink
Implementation of custom-scalar and applicable to variable
Browse files Browse the repository at this point in the history
  • Loading branch information
Cédric L. Charlier committed Nov 4, 2019
1 parent 0ada10a commit ea0e7ad
Show file tree
Hide file tree
Showing 22 changed files with 315 additions and 26 deletions.
Expand Up @@ -9,7 +9,7 @@
using System.Text;
using System.Threading.Tasks;

namespace NBi.Core.Assemblies.Decoration
namespace NBi.Core.Assemblies
{
public class AbstractCustomFactory<T> where T:class
{
Expand All @@ -28,7 +28,7 @@ protected T Instantiate(ICustomArgs args)
catch (TypeNotExistingException)
{ throw new NBiException($"The assembly '{assembly.FullName}' doesn't contain any type named '{args.TypeName}'. This type was describe in the test as a {CustomKind}."); }
catch (TypeNotImplementingInterfaceException)
{ throw new NBiException($"The type '{typeName}' of the assembly '{assembly.FullName}' is not implementing the interface '{typeof(ICustomCommand).Name}' but is used as a {CustomKind}."); }
{ throw new NBiException($"The type '{typeName}' of the assembly '{assembly.FullName}' is not implementing the interface '{typeof(T).Name}' but is used as a {CustomKind}."); }
catch (NoConstructorFoundException)
{ throw new NBiException($"The type '{typeName}' of the assembly '{assembly.FullName}' has no constructor matching with the {parameters.Count()} parameter{(parameters.Count() > 1 ? "s" : string.Empty)} that {(parameters.Count() > 1 ? "were" : "was")} provided."); }
}
Expand Down Expand Up @@ -60,7 +60,7 @@ protected internal Type GetType(Assembly assembly, string typeName)
}

protected internal IReadOnlyDictionary<string, object> GetParameters(IReadOnlyDictionary<string, IScalarResolver> parameters)
=> parameters?.Select(x => new KeyValuePair<string, object>(x.Key, x.Value.Execute()))
=> parameters?.Select(x => new { x.Key, Value = x.Value.Execute() })
.ToDictionary(x => x.Key, y => y.Value);

protected internal T Instantiate(Type customCommandType, IReadOnlyDictionary<string, object> parameters)
Expand Down
Expand Up @@ -6,7 +6,7 @@
using System.Text;
using System.Threading.Tasks;

namespace NBi.Core.Assemblies.Decoration
namespace NBi.Core.Assemblies
{
public interface ICustomArgs
{
Expand Down
6 changes: 4 additions & 2 deletions NBi.Core/NBi.Core.csproj
Expand Up @@ -134,13 +134,13 @@
<Compile Include="Api\Rest\SegmentRest.cs" />
<Compile Include="Api\Rest\RestEngine.cs" />
<Compile Include="Api\Rest\ParameterRest.cs" />
<Compile Include="Assemblies\Decoration\AbstractCustomFactory.cs" />
<Compile Include="Assemblies\AbstractCustomFactory.cs" />
<Compile Include="Assemblies\Decoration\CustomCommand.cs" />
<Compile Include="Assemblies\Decoration\CustomCondition.cs" />
<Compile Include="Assemblies\Decoration\CustomCommandFactory.cs" />
<Compile Include="Assemblies\Decoration\CustomConditionFactory.cs" />
<Compile Include="Assemblies\Decoration\ICustomCommandArgs.cs" />
<Compile Include="Assemblies\Decoration\ICustomArgs.cs" />
<Compile Include="Assemblies\ICustomArgs.cs" />
<Compile Include="Assemblies\Decoration\ICustomConditionArgs.cs" />
<Compile Include="AttributeExtensions.cs" />
<Compile Include="Calculation\BaseRankingFilter.cs" />
Expand Down Expand Up @@ -671,6 +671,8 @@
<Compile Include="Properties\ProjectAssemblyInfo.cs" />
<Compile Include="Scalar\Resolver\ContextScalarResolverArgs.cs" />
<Compile Include="Scalar\Resolver\ContextScalarResolver.cs" />
<Compile Include="Scalar\Resolver\CustomScalarResolverArgs.cs" />
<Compile Include="Scalar\Resolver\CustomScalarResolver.cs" />
<Compile Include="Scalar\Resolver\NCalcScalarResolver.cs" />
<Compile Include="Scalar\Resolver\FormatScalarResolverArgs.cs" />
<Compile Include="Scalar\Resolver\EnvironmentScalarResolverArgs.cs" />
Expand Down
31 changes: 31 additions & 0 deletions NBi.Core/Scalar/Resolver/CustomScalarResolver.cs
@@ -0,0 +1,31 @@
using Microsoft.CSharp;
using NBi.Core.Assemblies;
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace NBi.Core.Scalar.Resolver
{
class CustomScalarResolver<T> : AbstractCustomFactory<IScalarResolver>, IScalarResolver<T>
{
private CustomScalarResolverArgs Args {get;}

public CustomScalarResolver(CustomScalarResolverArgs args)
=> Args = args;

protected override string CustomKind => "custom evaluation of a scalar";

public T Execute()
{
var instance = Instantiate(Args);
var value = instance.Execute();
return (T)Convert.ChangeType(value, typeof(T));
}

object IResolver.Execute() => Execute();
}
}
25 changes: 25 additions & 0 deletions NBi.Core/Scalar/Resolver/CustomScalarResolverArgs.cs
@@ -0,0 +1,25 @@
using NBi.Core.Assemblies;
using NBi.Core.Query;
using NBi.Core.Query.Resolver;
using NBi.Core.ResultSet.Resolver;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NBi.Core.Scalar.Resolver
{
public class CustomScalarResolverArgs : IScalarResolverArgs, ICustomArgs
{
public IScalarResolver<string> AssemblyPath { get; }

public IScalarResolver<string> TypeName { get; }

public IReadOnlyDictionary<string, IScalarResolver> Parameters { get; }

public CustomScalarResolverArgs(IScalarResolver<string> assemblyPath, IScalarResolver<string> typeName, IDictionary<string, IScalarResolver> parameters)
=> (AssemblyPath, TypeName, Parameters) = (assemblyPath, typeName, new ReadOnlyDictionary<string, IScalarResolver>(parameters));
}
}
2 changes: 2 additions & 0 deletions NBi.Core/Scalar/Resolver/ScalarResolverFactory.cs
Expand Up @@ -56,6 +56,8 @@ public IScalarResolver<T> Instantiate<T>(IScalarResolverArgs args)
return new NCalcScalarResolver<T>(x);
case EnvironmentScalarResolverArgs x:
return new EnvironmentScalarResolver<T>(x);
case CustomScalarResolverArgs x:
return new CustomScalarResolver<T>(x);
case FormatScalarResolverArgs x:
return typeof(T) == typeof(string) ? (IScalarResolver<T>)new FormatScalarResolver(x, serviceLocator) : throw new ArgumentException("You cannot instantiate a FormatScalarResolver that is not a string.");
case FunctionScalarResolverArgs x:
Expand Down
2 changes: 2 additions & 0 deletions NBi.NUnit.Runtime/TestSuite.cs
Expand Up @@ -302,6 +302,8 @@ public IEnumerable<TestCaseData> GetTestCases()
}
else if (variable.Environment != null)
builder.Setup(variable.Environment);
else if (variable.Custom != null)
builder.Setup(variable.Custom);
builder.Build();
var args = builder.GetArgs();

Expand Down
10 changes: 10 additions & 0 deletions NBi.NUnit/Builder/Helper/ScalarResolverArgsBuilder.cs
Expand Up @@ -11,6 +11,7 @@
using NBi.Xml.Settings;
using NBi.Xml.Systems;
using NBi.Xml.Variables;
using NBi.Xml.Variables.Custom;
using System;
using System.Collections.Generic;
using System.IO;
Expand Down Expand Up @@ -71,6 +72,15 @@ public void Build()
case EnvironmentXml obj:
args = new EnvironmentScalarResolverArgs(obj.Name);
break;
case CustomScalarXml obj:
var helper = new ScalarHelper(ServiceLocator, Context);
args = new CustomScalarResolverArgs(
helper.InstantiateResolver<string>(obj.AssemblyPath),
helper.InstantiateResolver<string>(obj.TypeName),
obj.Parameters.Select(x => new { x.Name, ScalarResolver = (IScalarResolver)helper.InstantiateResolver<string>(x.StringValue)})
.ToDictionary(x => x.Name, y => y.ScalarResolver)
);
break;
default:
var factory = new ScalarResolverArgsFactory(ServiceLocator, Context);
args = factory.Instantiate(obj as string);
Expand Down
4 changes: 4 additions & 0 deletions NBi.Testing.Core/NBi.Testing.Core.csproj
Expand Up @@ -260,6 +260,9 @@
<Compile Include="Transformation\Transformer\Native\PathTest.cs" />
<Compile Include="Transformation\Transformer\Native\TextTest.cs" />
<Compile Include="Transformation\Transformer\NCalcTransformerTest.cs" />
<Compile Include="Scalar\Resolver\CustomScalarResolverTest.cs" />
<Compile Include="Scalar\Resolver\Resources\MyCustomClassWithParams.cs" />
<Compile Include="Scalar\Resolver\Resources\MyCustomClass.cs" />
<Compile Include="Variable\InstanceFactoryTest.cs" />
<Compile Include="Variable\VariableFactoryTest.cs" />
<Compile Include="Hierarchical\Xml\XPathEngineTest.cs" />
Expand Down Expand Up @@ -328,6 +331,7 @@
<ItemGroup>
<EmbeddedResource Include="Hierarchical\Json\Resources\PurchaseOrders.json" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
Expand Down
56 changes: 56 additions & 0 deletions NBi.Testing.Core/Scalar/Resolver/CustomScalarResolverTest.cs
@@ -0,0 +1,56 @@
using Moq;
using NBi.Core.Scalar.Resolver;
using NBi.Testing.Core.Scalar.Resolver.Resources;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NBi.Testing.Core.Scalar.Resolver
{
public class CustomScalarResolverTest
{
[Test]
public void Execute_TypeWithoutParam_CorrectEvaluation()
{
var args = new CustomScalarResolverArgs
(
new LiteralScalarResolver<string>(GetType().Assembly.Location),
new LiteralScalarResolver<string>($"{typeof(MyCustomClass).Namespace}.{typeof(MyCustomClass).Name}"),
new Dictionary<string, IScalarResolver>()
);
var resolver = new CustomScalarResolver<string>(args);
Assert.That(resolver.Execute(), Is.EqualTo("myValue"));
}

[Test]
public void Execute_TypeWithParam_CorrectEvaluation()
{
var args = new CustomScalarResolverArgs
(
new LiteralScalarResolver<string>(GetType().Assembly.Location),
new LiteralScalarResolver<string>($"{typeof(MyCustomClassWithParams).Namespace}.{typeof(MyCustomClassWithParams).Name}"),
new Dictionary<string, IScalarResolver>() { { "foo", new LiteralScalarResolver<int>(5) }, { "bar", new LiteralScalarResolver<DateTime>(new DateTime(2019, 1, 1)) } }
);
var resolver = new CustomScalarResolver<DateTime>(args);
var output = resolver.Execute();
Assert.That(output, Is.EqualTo(new DateTime(2019,1,6)));
}

//[Test]
//public void Execute_TwoCalls_OneExecution()
//{
// var factory = new CustomScalarFactory();
// var mock = new Mock<ICustomScalarEvaluation>();
// mock.SetupSequence(x => x.Execute()).Returns(true).Returns(false);

// var customEvaluation = factory.Instantiate(mock.Object);
// Assert.That(customEvaluation.Execute(), Is.True);
// Assert.That(customEvaluation.Execute(), Is.True);
// mock.Verify(x => x.Execute(), Times.Once);
//}
}
}
14 changes: 14 additions & 0 deletions NBi.Testing.Core/Scalar/Resolver/Resources/MyCustomClass.cs
@@ -0,0 +1,14 @@
using NBi.Core.Scalar.Resolver;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NBi.Testing.Core.Scalar.Resolver.Resources
{
public class MyCustomClass : IScalarResolver
{
public object Execute() => "myValue";
}
}
@@ -0,0 +1,20 @@
using NBi.Core.Scalar.Resolver;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NBi.Testing.Core.Scalar.Resolver.Resources
{
public class MyCustomClassWithParams : IScalarResolver
{
private int Foo { get; }
private DateTime Bar { get; }

public MyCustomClassWithParams(DateTime bar, int foo)
=> (Bar, Foo) = (bar, foo);

public object Execute() => Bar.AddDays(Foo);
}
}
42 changes: 42 additions & 0 deletions NBi.Testing.Xml/Variables/GlobalVariableXmlTest.cs
Expand Up @@ -10,6 +10,7 @@
using System.IO;
using System.Diagnostics;
using NBi.Xml.Items;
using NBi.Xml.Variables.Custom;

namespace NBi.Testing.Xml.Unit.Variables
{
Expand Down Expand Up @@ -161,5 +162,46 @@ public void Serialize_NoVariable_NothingSerialized()
Assert.That(content, Does.Not.Contain("<variables"));
Assert.That(content, Does.Not.Contain("<variable"));
}

[Test]
public void Serialize_CustomEvaluation_CustomElement()
{
var testSuiteXml = new TestSuiteXml()
{
Variables = new List<GlobalVariableXml>
{
new GlobalVariableXml
{
Name= "myVar",
Custom = new CustomScalarXml
{
AssemblyPath = "AssemblyPath\\myAssembly.dll",
TypeName = "@VarType",
Parameters = new List<CustomScalarParameterXml>
{
new CustomScalarParameterXml{Name="myParam", StringValue="@VarParam"}
}
}
}
}
};

var serializer = new XmlSerializer(typeof(TestSuiteXml));
using (var stream = new MemoryStream())
using (var writer = new StreamWriter(stream, Encoding.UTF8))
{
serializer.Serialize(writer, testSuiteXml);
var content = Encoding.UTF8.GetString(stream.ToArray());

Debug.WriteLine(content);

Assert.That(content, Does.Contain("<variable name=\"myVar\""));
Assert.That(content, Does.Contain("<custom"));
Assert.That(content, Does.Contain("assembly-path=\"AssemblyPath\\myAssembly.dll\""));
Assert.That(content, Does.Contain("type=\"@VarType\""));
Assert.That(content, Does.Contain("<parameter name=\"myParam\">"));
Assert.That(content, Does.Contain("@VarParam"));
}
}
}
}
20 changes: 20 additions & 0 deletions NBi.Testing/Acceptance/Resources/CustomVariableDaysBetween.cs
@@ -0,0 +1,20 @@
using NBi.Core.Scalar.Resolver;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NBi.Testing.Acceptance.Resources
{
public class CustomVariableDaysBetween : IScalarResolver
{
private DateTime From { get; }
private DateTime To { get; }

public CustomVariableDaysBetween(DateTime from, DateTime to)
=> (From, To) = (from, to);

public object Execute() => To.Subtract(From).TotalDays;
}
}
28 changes: 28 additions & 0 deletions NBi.Testing/Acceptance/Resources/Positive/Variable.nbits
Expand Up @@ -23,6 +23,12 @@
select cast(RAND() as varchar(40))
</query-scalar>
</variable>
<variable name="daysBetween">
<custom assembly-path="NBi.Testing.dll" type="CustomVariableDaysBetween">
<parameter name="from">@januarySecond</parameter>
<parameter name="to">@prevcommon</parameter>
</custom>
</variable>
</variables>
<test name="'Reseller Order Count' by year before 2006 (csv)" uid="0001">
<system-under-test>
Expand Down Expand Up @@ -114,4 +120,26 @@
</equal-to>
</assert>
</test>
<test name="Custom variable using variables as parameters" uid="0100">
<system-under-test>
<result-set>
<query>
<parameter name="var">@daysBetween</parameter>
select @var;
</query>
</result-set>
</system-under-test>
<assert>
<all-rows>
<combination operator="and">
<predicate operand="#0">
<more-than>360</more-than>
</predicate>
<predicate operand="#0">
<less-than>370</less-than>
</predicate>
</combination>
</all-rows>
</assert>
</test>
</testSuite>
1 change: 1 addition & 0 deletions NBi.Testing/NBi.Testing.csproj
Expand Up @@ -135,6 +135,7 @@
<Compile Include="Acceptance\Resources\CustomCommand.cs" />
<Compile Include="Acceptance\Resources\CustomConditionFalse.cs" />
<Compile Include="Acceptance\Resources\CustomConditionTrue.cs" />
<Compile Include="Acceptance\Resources\CustomVariableDaysBetween.cs" />
<Compile Include="Acceptance\Resources\TsvReader.cs" />
<Compile Include="Acceptance\RuntimeOverrider.cs" />
<Compile Include="Acceptance\TestSuiteOverrider.cs" />
Expand Down

0 comments on commit ea0e7ad

Please sign in to comment.