Imports NUnit.Framework
Imports ciloci.Flee
<TestFixture()> _
Public Class ExpressionTestFixture
Private Const COMMENT_CHAR As Char = "'"c
Private Const SEPARATOR_CHAR As Char = ";"c
Private MyTypeTesterMap As IDictionary
Private MyExpressionOwner As ExpressionOwner
Private MyTestImports As ImportsCollection
Public Sub New()
MyTypeTesterMap = New Hashtable()
MyTypeTesterMap.Add(GetType(Byte), GetType(ByteExpressionTester))
MyTypeTesterMap.Add(GetType(SByte), GetType(SByteExpressionTester))
MyTypeTesterMap.Add(GetType(Short), GetType(ShortExpressionTester))
MyTypeTesterMap.Add(GetType(UShort), GetType(UShortExpressionTester))
MyTypeTesterMap.Add(GetType(Integer), GetType(IntegerExpressionTester))
MyTypeTesterMap.Add(GetType(UInteger), GetType(UnsignedIntegerExpressionTester))
MyTypeTesterMap.Add(GetType(Int64), GetType(Int64ExpressionTester))
MyTypeTesterMap.Add(GetType(UInt64), GetType(UInt64ExpressionTester))
MyTypeTesterMap.Add(GetType(Double), GetType(DoubleExpressionTester))
MyTypeTesterMap.Add(GetType(Single), GetType(SingleExpressionTester))
MyTypeTesterMap.Add(GetType(String), GetType(StringExpressionTester))
MyTypeTesterMap.Add(GetType(Boolean), GetType(BooleanExpressionTester))
MyTypeTesterMap.Add(GetType(Char), GetType(CharExpressionTester))
MyTypeTesterMap.Add(GetType(Decimal), GetType(DecimalExpressionTester))
MyTypeTesterMap.Add(GetType(Object), GetType(ObjectExpressionTester))
MyExpressionOwner = New ExpressionOwner
MyTestImports = New ImportsCollection()
MyTestImports.AddNamespace("System")
MyTestImports.AllowGlobalImport = True
End Sub
<Test()> _
Public Sub TestValidExpressions()
Me.ProcessScriptTests("ValidExpressions.txt", AddressOf DoTestValidExpressions)
End Sub
<Test()> _
Public Sub TestInvalidExpressions()
Me.ProcessScriptTests("InvalidExpressions.txt", AddressOf DoTestInvalidExpressions)
End Sub
<Test()> _
Public Sub TestValidCasts()
Me.ProcessScriptTests("ValidCasts.txt", AddressOf DoTestValidExpressions)
End Sub
<Test()> _
Public Sub TestCheckedExpressions()
Me.ProcessScriptTests("CheckedTests.txt", AddressOf DoTestCheckedExpressions)
End Sub
<Test()> _
Public Sub TestStringNewlineEscape()
Dim e As New Expression("""a\r\nb""", MyExpressionOwner)
Dim s As String = DirectCast(e.Evaluator, ExpressionEvaluator(Of String))()
Dim expected As String = String.Format("a{0}b", ControlChars.CrLf)
Assert.AreEqual(expected, s)
End Sub
<Test()> _
Public Sub TestSerialize()
Dim ms As New System.IO.MemoryStream()
Dim bf As New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter()
Dim owner As New SerializableExpressionOwner()
Dim options As New ExpressionOptions()
options.Imports.AddType(GetType(Math))
options.Imports.ImportBuiltinTypes = True
Dim e As New Expression("sqrt(byte.maxvalue + 1)", owner, options)
bf.Serialize(ms, e)
ms.Seek(0, IO.SeekOrigin.Begin)
Dim e2 As Expression = bf.Deserialize(ms)
Dim d As ExpressionEvaluator(Of Double) = e2.Evaluator
Dim result As Double = d()
Assert.AreEqual(16.0, result)
End Sub
<Test()> _
Public Sub TestMultiTreaded()
' Test that we can parse and evaluate from multiple threads
Dim t1 As New System.Threading.Thread(AddressOf ThreadRun)
t1.Name = "Thread1"
Dim t2 As New System.Threading.Thread(AddressOf ThreadRun)
t2.Name = "Thread2"
Dim e As New Expression("1+1*200", MyExpressionOwner)
t1.Start(e)
t2.Start(e)
t1.Join()
t2.Join()
End Sub
Private Sub ThreadRun(ByVal o As Object)
' Test parse
For i As Integer = 0 To 10 - 1
Dim e As New Expression("1+1*200", MyExpressionOwner)
Next
' Test evaluate
Dim evaluator As ExpressionEvaluator(Of Integer) = DirectCast(o, Expression).Evaluator
For i As Integer = 0 To 10 - 1
evaluator()
Next
End Sub
<Test()> _
Public Sub TestBuiltinsImport()
' Test our builtin type importing
Dim options As New ExpressionOptions()
options.Imports.ImportBuiltinTypes = True
Dim e As New Expression("long.maxvalue", MyExpressionOwner, options)
Dim evaluator As ExpressionEvaluator(Of Long) = e.Evaluator
Assert.AreEqual(Long.MaxValue, evaluator())
options.Imports.ImportBuiltinTypes = False
Me.AssertCompileException("long.maxvalue", GetType(Long), options.Imports)
End Sub
<Test()> _
Public Sub TestBoxing()
' Test that we box properly
Dim options As New ExpressionOptions()
options.ResultType = GetType(Object)
Dim e1 As New Expression("1000", MyExpressionOwner, options)
Dim d1 As ExpressionEvaluator(Of Object) = e1.Evaluator
Dim o As Object = d1()
Assert.IsInstanceOfType(GetType(Int32), o)
options.ResultType = GetType(ValueType)
Dim e2 As New Expression("12.34", MyExpressionOwner, options)
Dim d2 As ExpressionEvaluator(Of ValueType) = e2.Evaluator
Dim vt As ValueType = d2()
Assert.IsInstanceOfType(GetType(Double), vt)
End Sub
<Test()> _
Public Sub TestImports()
Dim e As Expression
Dim imps As ImportsCollection
Dim del As ExpressionEvaluator(Of Double)
imps = New ImportsCollection()
' Import math type directly
imps.AddType(GetType(Math))
Dim options As New ExpressionOptions()
options.Imports = imps
' Should be able to see PI without qualification
e = New Expression("pi", MyExpressionOwner, options)
del = e.Evaluator
Assert.AreEqual(Math.PI, del())
imps = New ImportsCollection()
' Import system namespace
imps.AddNamespace("System")
options.Imports = imps
' Should be able to see pi by qualifying with Math
e = New Expression("Math.pi", MyExpressionOwner, options)
del = e.Evaluator
Assert.AreEqual(Math.PI, del())
imps = New ImportsCollection()
' Import root namespace
imps.AllowGlobalImport = True
options.Imports = imps
' Should see pi by fully qualified name
e = New Expression("System.Math.pi", MyExpressionOwner, options)
del = e.Evaluator
Assert.AreEqual(Math.PI, del())
' Import nothing
imps = New ImportsCollection()
options.Imports = imps
' Should not be able to see PI
Me.AssertCompileException("pi", GetType(Double), imps)
Me.AssertCompileException("Math.pi", GetType(Double), imps)
Me.AssertCompileException("system.math.pi", GetType(Double), imps)
End Sub
<Test()> _
Public Sub TestStringEquality()
' Test our string equality
Dim options As New ExpressionOptions()
' Should be equal
Dim e As New Expression("""abc"" = ""abc""", MyExpressionOwner, options)
Dim evaluator As ExpressionEvaluator(Of Boolean) = e.Evaluator
Assert.IsTrue(evaluator())
' Should not be equal
e = New Expression("""ABC"" = ""abc""", MyExpressionOwner, options)
evaluator = e.Evaluator
Assert.IsFalse(evaluator())
' Should be not equal
e = New Expression("""ABC"" <> ""abc""", MyExpressionOwner, options)
evaluator = e.Evaluator
Assert.IsTrue(evaluator())
' Change string compare type
options.StringComparison = StringComparison.OrdinalIgnoreCase
' Should be equal
e = New Expression("""ABC"" = ""abc""", MyExpressionOwner, options)
evaluator = e.Evaluator
Assert.IsTrue(evaluator())
' Should also be equal
e = New Expression("""ABC"" <> ""abc""", MyExpressionOwner, options)
evaluator = e.Evaluator
Assert.IsFalse(evaluator())
' Should also be not equal
e = New Expression("""A"" <> ""z""", MyExpressionOwner, options)
evaluator = e.Evaluator
Assert.IsTrue(evaluator())
End Sub
<Test()> _
Public Sub TestDynamicExpressionOwner()
Me.TestStronglyTypedDynamicExpressionOwner()
Me.TestObjectDynamicExpressionOwner()
End Sub
Private Sub TestStronglyTypedDynamicExpressionOwner()
Dim owner As New DynamicExpressionOwner(Of Integer)
owner.Variables("a") = 100
owner.Variables("b") = -100
Dim e As New Expression("a+b", owner)
Dim evaluator As ExpressionEvaluator(Of Integer) = e.Evaluator
Dim result As Integer = evaluator()
Assert.AreEqual(result, 100 + -100)
owner.Variables("B") = 1000
result = evaluator()
Assert.AreEqual(result, 100 + 1000)
End Sub
Private Sub TestObjectDynamicExpressionOwner()
Dim owner As New DynamicExpressionOwner(Of Object)
owner.Variables("a") = 100
owner.Variables("b") = 2.25
Dim e As New Expression("a+b", owner)
Dim evaluator As ExpressionEvaluator(Of Double) = e.Evaluator
Dim result As Double = evaluator()
Assert.AreEqual(result, 100 + 2.25)
owner.Variables("B") = 1000.25
result = evaluator()
Assert.AreEqual(result, 100 + 1000.25)
owner.Variables("string") = "String!"
e = New Expression("string", owner)
Dim evaluator2 As ExpressionEvaluator(Of String) = e.Evaluator
Assert.AreEqual(evaluator2(), "String!")
End Sub
Private Sub DoTestValidExpressions(ByVal line As String)
Dim arr As String() = line.Split(SEPARATOR_CHAR)
Dim typeName As String = String.Concat("System.", arr(0))
Dim expressionType As Type = Type.GetType(typeName, True, True)
Dim options As New ExpressionOptions()
options.ResultType = expressionType
options.Imports = MyTestImports
Dim tester As ExpressionTester = Activator.CreateInstance(MyTypeTesterMap.Item(expressionType))
Dim e As New Expression(arr(1), MyExpressionOwner, options)
tester.DoTest(e, arr(2))
End Sub
Private Sub DoTestInvalidExpressions(ByVal line As String)
Dim arr As String() = line.Split(SEPARATOR_CHAR)
Dim expressionType As Type = Type.GetType(arr(0), True, True)
Me.AssertCompileException(arr(1), expressionType, MyTestImports)
End Sub
Private Sub AssertCompileException(ByVal expression As String, ByVal returnType As Type, ByVal imps As ImportsCollection)
Try
Dim e As Expression = Me.CreateExpression(expression, returnType)
Assert.Fail()
Catch ex As ExpressionCompileException
End Try
End Sub
Private Sub DoTestCheckedExpressions(ByVal line As String)
Dim arr As String() = line.Split(SEPARATOR_CHAR)
Dim expression As String = arr(0)
Dim checked As Boolean = Boolean.Parse(arr(1))
Dim shouldOverflow As Boolean = Boolean.Parse(arr(2))
Dim options As New ExpressionOptions()
options.Imports = MyTestImports
options.ResultType = GetType(Object)
options.Checked = checked
Try
Dim e As New Expression(expression, MyExpressionOwner, options)
Dim d As ExpressionEvaluator(Of Object) = e.Evaluator
d()
Assert.IsFalse(shouldOverflow)
Catch ex As OverflowException
Assert.IsTrue(shouldOverflow)
End Try
End Sub
Private Function CreateExpression(ByVal expression As String, ByVal resultType As Type) As Expression
Dim options As New ExpressionOptions()
options.ResultType = resultType
Return New Expression(expression, MyExpressionOwner, options)
End Function
Protected Sub ProcessScriptTests(ByVal scriptFileName As String, ByVal processor As LineProcessor)
Console.WriteLine("Testing: {0}", scriptFileName)
Dim scriptPath As String = System.IO.Path.Combine("../../TestScripts", scriptFileName)
Dim instream As New System.IO.FileStream(scriptPath, IO.FileMode.Open, IO.FileAccess.Read)
Dim sr As New System.IO.StreamReader(instream)
Try
Me.ProcessLines(sr, processor)
Finally
instream.Close()
End Try
End Sub
Private Sub ProcessLines(ByVal sr As System.IO.StreamReader, ByVal processor As LineProcessor)
While sr.Peek() <> -1
Dim line As String = sr.ReadLine()
If line.StartsWith(COMMENT_CHAR) = False Then
Try
processor(line)
Catch ex As Exception
Console.WriteLine("Failed line: " & line)
Throw
End Try
End If
End While
End Sub
End Class