| using System; | |
| using System.Text; | |
| using System.Collections.Generic; | |
| using System.Linq; | |
| using Microsoft.VisualStudio.TestTools.UnitTesting; | |
| using OfficeOpenXml.FormulaParsing.ExpressionGraph.CompileStrategy; | |
| using Rhino.Mocks; | |
| using OfficeOpenXml.FormulaParsing.ExpressionGraph; | |
| using OfficeOpenXml.FormulaParsing.LexicalAnalysis; | |
| using OfficeOpenXml.FormulaParsing.Excel.Operators; | |
| using OfficeOpenXml.FormulaParsing; | |
| namespace EPPlusTest.FormulaParsing.ExpressionGraph | |
| { | |
| [TestClass] | |
| public class ExpressionGraphBuilderTests | |
| { | |
| private IExpressionGraphBuilder _graphBuilder; | |
| private ExcelDataProvider _excelDataProvider; | |
| [TestInitialize] | |
| public void Setup() | |
| { | |
| _excelDataProvider = MockRepository.GenerateStub<ExcelDataProvider>(); | |
| var parsingContext = ParsingContext.Create(); | |
| _graphBuilder = new ExpressionGraphBuilder(_excelDataProvider, parsingContext); | |
| } | |
| [TestCleanup] | |
| public void Cleanup() | |
| { | |
| } | |
| [TestMethod] | |
| public void BuildShouldNotUseStringIdentifyersWhenBuildingStringExpression() | |
| { | |
| var tokens = new List<Token> | |
| { | |
| new Token("'", TokenType.String), | |
| new Token("abc", TokenType.StringContent), | |
| new Token("'", TokenType.String) | |
| }; | |
| var result = _graphBuilder.Build(tokens); | |
| Assert.AreEqual(1, result.Expressions.Count()); | |
| } | |
| [TestMethod] | |
| public void BuildShouldNotEvaluateExpressionsWithinAString() | |
| { | |
| var tokens = new List<Token> | |
| { | |
| new Token("'", TokenType.String), | |
| new Token("1 + 2", TokenType.StringContent), | |
| new Token("'", TokenType.String) | |
| }; | |
| var result = _graphBuilder.Build(tokens); | |
| Assert.AreEqual("1 + 2", result.Expressions.First().Compile().Result); | |
| } | |
| [TestMethod] | |
| public void BuildShouldSetOperatorOnGroupExpressionCorrectly() | |
| { | |
| var tokens = new List<Token> | |
| { | |
| new Token("(", TokenType.OpeningParenthesis), | |
| new Token("2", TokenType.Integer), | |
| new Token("+", TokenType.Operator), | |
| new Token("4", TokenType.Integer), | |
| new Token(")", TokenType.ClosingParenthesis), | |
| new Token("*", TokenType.Operator), | |
| new Token("2", TokenType.Integer) | |
| }; | |
| var result = _graphBuilder.Build(tokens); | |
| Assert.AreEqual(Operator.Multiply.Operator, result.Expressions.First().Operator.Operator); | |
| } | |
| [TestMethod] | |
| public void BuildShouldSetChildrenOnGroupExpression() | |
| { | |
| var tokens = new List<Token> | |
| { | |
| new Token("(", TokenType.OpeningParenthesis), | |
| new Token("2", TokenType.Integer), | |
| new Token("+", TokenType.Operator), | |
| new Token("4", TokenType.Integer), | |
| new Token(")", TokenType.ClosingParenthesis), | |
| new Token("*", TokenType.Operator), | |
| new Token("2", TokenType.Integer) | |
| }; | |
| var result = _graphBuilder.Build(tokens); | |
| Assert.IsInstanceOfType(result.Expressions.First(), typeof(GroupExpression)); | |
| Assert.AreEqual(2, result.Expressions.First().Children.Count()); | |
| } | |
| [TestMethod] | |
| public void BuildShouldSetNextOnGroupedExpression() | |
| { | |
| var tokens = new List<Token> | |
| { | |
| new Token("(", TokenType.OpeningParenthesis), | |
| new Token("2", TokenType.Integer), | |
| new Token("+", TokenType.Operator), | |
| new Token("4", TokenType.Integer), | |
| new Token(")", TokenType.ClosingParenthesis), | |
| new Token("*", TokenType.Operator), | |
| new Token("2", TokenType.Integer) | |
| }; | |
| var result = _graphBuilder.Build(tokens); | |
| Assert.IsNotNull(result.Expressions.First().Next); | |
| Assert.IsInstanceOfType(result.Expressions.First().Next, typeof(IntegerExpression)); | |
| } | |
| [TestMethod] | |
| public void BuildShouldBuildFunctionExpressionIfFirstTokenIsFunction() | |
| { | |
| var tokens = new List<Token> | |
| { | |
| new Token("CStr", TokenType.Function), | |
| new Token("(", TokenType.OpeningParenthesis), | |
| new Token("2", TokenType.Integer), | |
| new Token(")", TokenType.ClosingParenthesis), | |
| }; | |
| var result = _graphBuilder.Build(tokens); | |
| Assert.AreEqual(1, result.Expressions.Count()); | |
| Assert.IsInstanceOfType(result.Expressions.First(), typeof(FunctionExpression)); | |
| } | |
| [TestMethod] | |
| public void BuildShouldSetChildrenOnFunctionExpression() | |
| { | |
| var tokens = new List<Token> | |
| { | |
| new Token("CStr", TokenType.Function), | |
| new Token("(", TokenType.OpeningParenthesis), | |
| new Token("2", TokenType.Integer), | |
| new Token(")", TokenType.ClosingParenthesis) | |
| }; | |
| var result = _graphBuilder.Build(tokens); | |
| Assert.AreEqual(1, result.Expressions.First().Children.Count()); | |
| Assert.IsInstanceOfType(result.Expressions.First().Children.First(), typeof(GroupExpression)); | |
| Assert.IsInstanceOfType(result.Expressions.First().Children.First().Children.First(), typeof(IntegerExpression)); | |
| Assert.AreEqual(2d, result.Expressions.First().Children.First().Compile().Result); | |
| } | |
| [TestMethod] | |
| public void BuildShouldAddOperatorToFunctionExpression() | |
| { | |
| var tokens = new List<Token> | |
| { | |
| new Token("CStr", TokenType.Function), | |
| new Token("(", TokenType.OpeningParenthesis), | |
| new Token("2", TokenType.Integer), | |
| new Token(")", TokenType.ClosingParenthesis), | |
| new Token("&", TokenType.Operator), | |
| new Token("A", TokenType.StringContent) | |
| }; | |
| var result = _graphBuilder.Build(tokens); | |
| Assert.AreEqual(1, result.Expressions.First().Children.Count()); | |
| Assert.AreEqual(2, result.Expressions.Count()); | |
| } | |
| [TestMethod] | |
| public void BuildShouldAddCommaSeparatedFunctionArgumentsAsChildrenToFunctionExpression() | |
| { | |
| var tokens = new List<Token> | |
| { | |
| new Token("Text", TokenType.Function), | |
| new Token("(", TokenType.OpeningParenthesis), | |
| new Token("2", TokenType.Integer), | |
| new Token(",", TokenType.Comma), | |
| new Token("3", TokenType.Integer), | |
| new Token(")", TokenType.ClosingParenthesis), | |
| new Token("&", TokenType.Operator), | |
| new Token("A", TokenType.StringContent) | |
| }; | |
| var result = _graphBuilder.Build(tokens); | |
| Assert.AreEqual(2, result.Expressions.First().Children.Count()); | |
| } | |
| [TestMethod] | |
| public void BuildShouldCreateASingleExpressionOutOfANegatorAndANumericToken() | |
| { | |
| var tokens = new List<Token> | |
| { | |
| new Token("-", TokenType.Negator), | |
| new Token("2", TokenType.Integer), | |
| }; | |
| var result = _graphBuilder.Build(tokens); | |
| Assert.AreEqual(1, result.Expressions.Count()); | |
| Assert.AreEqual(-2d, result.Expressions.First().Compile().Result); | |
| } | |
| [TestMethod] | |
| public void BuildShouldHandleEnumerableTokens() | |
| { | |
| var tokens = new List<Token> | |
| { | |
| new Token("Text", TokenType.Function), | |
| new Token("(", TokenType.OpeningParenthesis), | |
| new Token("{", TokenType.OpeningEnumerable), | |
| new Token("2", TokenType.Integer), | |
| new Token(",", TokenType.Comma), | |
| new Token("3", TokenType.Integer), | |
| new Token("}", TokenType.ClosingEnumerable), | |
| new Token(")", TokenType.ClosingParenthesis) | |
| }; | |
| var result = _graphBuilder.Build(tokens); | |
| var funcArgExpression = result.Expressions.First().Children.First(); | |
| Assert.IsInstanceOfType(funcArgExpression, typeof(FunctionArgumentExpression)); | |
| var enumerableExpression = funcArgExpression.Children.First(); | |
| Assert.IsInstanceOfType(enumerableExpression, typeof(EnumerableExpression)); | |
| Assert.AreEqual(2, enumerableExpression.Children.Count(), "Enumerable.Count was not 2"); | |
| } | |
| [TestMethod] | |
| public void ShouldHandleInnerFunctionCall2() | |
| { | |
| var ctx = ParsingContext.Create(); | |
| const string formula = "IF(3>2;\"Yes\";\"No\")"; | |
| var tokenizer = new SourceCodeTokenizer(ctx.Configuration.FunctionRepository, ctx.NameValueProvider); | |
| var tokens = tokenizer.Tokenize(formula); | |
| var expression = _graphBuilder.Build(tokens); | |
| Assert.AreEqual(1, expression.Expressions.Count()); | |
| var compiler = new ExpressionCompiler(new ExpressionConverter(), new CompileStrategyFactory()); | |
| var result = compiler.Compile(expression.Expressions); | |
| Assert.AreEqual("Yes", result.Result); | |
| } | |
| [TestMethod] | |
| public void ShouldHandleInnerFunctionCall3() | |
| { | |
| var ctx = ParsingContext.Create(); | |
| const string formula = "IF(I10>=0;IF(O10>I10;((O10-I10)*$B10)/$C$27;IF(O10<0;(O10*$B10)/$C$27;\"\"));IF(O10<0;((O10-I10)*$B10)/$C$27;IF(O10>0;(O10*$B10)/$C$27;)))"; | |
| var tokenizer = new SourceCodeTokenizer(ctx.Configuration.FunctionRepository, ctx.NameValueProvider); | |
| var tokens = tokenizer.Tokenize(formula); | |
| var expression = _graphBuilder.Build(tokens); | |
| Assert.AreEqual(1, expression.Expressions.Count()); | |
| var exp1 = expression.Expressions.First(); | |
| Assert.AreEqual(3, exp1.Children.Count()); | |
| } | |
| [TestMethod] | |
| public void RemoveDuplicateOperators1() | |
| { | |
| var ctx = ParsingContext.Create(); | |
| const string formula = "++1--2++-3+-1----3-+2"; | |
| var tokenizer = new SourceCodeTokenizer(ctx.Configuration.FunctionRepository, ctx.NameValueProvider); | |
| var tokens = tokenizer.Tokenize(formula).ToList(); | |
| var expression = _graphBuilder.Build(tokens); | |
| Assert.AreEqual(11, tokens.Count()); | |
| Assert.AreEqual("+", tokens[1].Value); | |
| Assert.AreEqual("-", tokens[3].Value); | |
| Assert.AreEqual("-", tokens[5].Value); | |
| Assert.AreEqual("+", tokens[7].Value); | |
| Assert.AreEqual("-", tokens[9].Value); | |
| } | |
| [TestMethod] | |
| public void RemoveDuplicateOperators2() | |
| { | |
| var ctx = ParsingContext.Create(); | |
| const string formula = "++-1--(---2)++-3+-1----3-+2"; | |
| var tokenizer = new SourceCodeTokenizer(ctx.Configuration.FunctionRepository, ctx.NameValueProvider); | |
| var tokens = tokenizer.Tokenize(formula).ToList(); | |
| } | |
| } | |
| } |