|  | 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(); | 
|  | } | 
|  | } | 
|  | } |