public void InvariantCultureTest()
        {
            // The StandardEnvironment must always use InvariantCulture
            // (de-DE uses "," as the decimal separator). Because CONCAT
            // converts its arguments to string, it's a good test case:

            var env = new StandardEnvironment();

            var fconcat = GetFunction(env, "CONCAT");

            var cc = Thread.CurrentThread.CurrentCulture;

            Thread.CurrentThread.CurrentCulture = new CultureInfo("de-DE");

            try
            {
                Assert.AreEqual("1,2", (1.2).ToString(CultureInfo.CurrentCulture));

                Assert.AreEqual("1.2", env.Invoke(fconcat, Args(1.2)));
                Assert.AreEqual("1.23.4", env.Invoke(fconcat, Args(1.2, 3.4)));
                Assert.AreEqual("1.23.45.67.8", env.Invoke(fconcat, Args(1.2, 3.4, 5.6, 7.8)));
            }
            finally
            {
                Thread.CurrentThread.CurrentCulture = cc;                 // restore
            }
        }
        public void CanRegisterExtraFunctions()
        {
            var extra = new ExtraFunctionality(2.0);

            var env = new StandardEnvironment();

            // Register functions (static):
            env.Register("Pi", ExtraFunctionality.Pi);
            env.Register <double, double>("Square", ExtraFunctionality.Square);

            // Register methods (need object state):
            env.Register("Circumference", extra.Circumference, extra);
            env.Register <double, double>("Volume", extra.Volume, extra);
            env.Register <object[], double>("Volume", extra.Volume, extra);            // variadic

            object r1 = env.Invoke(GetFunction(env, "Pi"));

            Assert.AreEqual(Math.PI, r1);

            object r2 = env.Invoke(GetFunction(env, "Square"), 2.0);

            Assert.AreEqual(4.0, r2);

            object r3 = env.Invoke(GetFunction(env, "Circumference"));

            Assert.AreEqual(extra.Radius * Math.PI * 2, r3);

            object r4 = env.Invoke(GetFunction(env, "Volume"), 3.0);

            Assert.AreEqual(extra.Radius * extra.Radius * Math.PI * 3.0, r4);

            object r5 = env.Invoke(GetFunction(env, "Volume"), 1.0, 2.0, 3.0);             // variadic

            Assert.AreEqual(extra.Radius * extra.Radius * Math.PI * 6.0, r5);
        }
        public void CanAbsFunction()
        {
            var env = new StandardEnvironment();

            var fabs = GetFunction(env, "ABS");

            Assert.IsNull(env.Invoke(fabs, new object[] { null }));
            Assert.AreEqual(13.5, env.Invoke(fabs, Args(13.5)));
            Assert.AreEqual(7.0, env.Invoke(fabs, Args(-7.0)));
        }
        public void CanLengthFunction()
        {
            var env = new StandardEnvironment();

            var flength = GetFunction(env, "LENGTH");

            Assert.AreEqual(3, env.Invoke(flength, Args("abc")));
            Assert.AreEqual(0, env.Invoke(flength, Args("")));
            Assert.IsNull(env.Invoke(flength, Args(null)));
        }
        public void CanCeilFunction()
        {
            var env = new StandardEnvironment();

            var fceil = GetFunction(env, "CEIL");

            Assert.IsNull(env.Invoke(fceil, new object[] { null }));
            Assert.AreEqual(14.0, env.Invoke(fceil, Args(13.5)));
            Assert.AreEqual(-7.0, env.Invoke(fceil, Args(-7.1)));
            Assert.AreEqual(0, env.Invoke(fceil, Args(-0.99)));
        }
        public void CanFloorFunction()
        {
            var env = new StandardEnvironment();

            var ffloor = GetFunction(env, "FLOOR");

            Assert.IsNull(env.Invoke(ffloor, new object[] { null }));
            Assert.AreEqual(13.0, env.Invoke(ffloor, Args(13.5)));
            Assert.AreEqual(-8.0, env.Invoke(ffloor, Args(-7.1)));
            Assert.AreEqual(0, env.Invoke(ffloor, Args(0.99)));
        }
        public void CanRandPickFunction()
        {
            var env = new StandardEnvironment();

            var frandpick = GetFunction(env, "RANDPICK");

            env.RandomSeed = 1234;             // repeatable randomness

            Assert.IsNull(env.Invoke(frandpick, Args()));
            Assert.AreEqual("one", env.Invoke(frandpick, Args("one")));
            Assert.AreEqual("foo", env.Invoke(frandpick, Args("foo", "bar")));
            Assert.AreEqual(4, env.Invoke(frandpick, Args(1, 2, 3, 3, 3, 4)));
        }
        public void CanRPadFunction()
        {
            var env = new StandardEnvironment();

            var frpad = GetFunction(env, "RPAD");

            Assert.AreEqual("abc   ", env.Invoke(frpad, Args("abc", 6)));
            Assert.AreEqual("123***", env.Invoke(frpad, Args(123, 6, "*")));

            // 1st arg is null-contagious, 2nd & 3rd args have defaults
            Assert.IsNull(env.Invoke(frpad, Args(null, null)));
            Assert.IsNull(env.Invoke(frpad, Args(null, 5)));
            Assert.AreEqual("x", env.Invoke(frpad, Args("x", null)));
        }
        public void CanLcaseFunction()
        {
            var env = new StandardEnvironment();

            var flcase = GetFunction(env, "LCASE");

            Assert.AreEqual("hello world", env.Invoke(flcase, Args("Hello World")));
            Assert.IsNull(env.Invoke(flcase, Args(null)));

            Assert.Catch <EvaluationException>(() => env.Invoke(flcase, Args()));
            Assert.Catch <EvaluationException>(() => env.Invoke(flcase, Args("one", "two")));
            var ex = Assert.Catch(() => env.Invoke(flcase, Args(123)));

            Console.WriteLine(@"Expected exception: {0}", ex.Message);
        }
        public void CanRegexReplaceFunction()
        {
            var env = new StandardEnvironment();

            var fregex = GetFunction(env, "REGEX");

            Assert.IsNull(env.Invoke(fregex, Args("pattern", "text", null)));
            Assert.IsNull(env.Invoke(fregex, Args("pattern", null, "replacement")));
            Assert.IsNull(env.Invoke(fregex, Args(null, "text", "replacement")));

            Assert.AreEqual("a_b_c", env.Invoke(fregex, Args("(\\w)(?=.)", "abc", "$1_")));

            var ex = Assert.Catch <EvaluationException>(() => env.Invoke(fregex, Args("this", "is", "too", "much")));

            Console.WriteLine(@"Expected exception: {0}", ex.Message);
        }
        public void CanTruncFunction()
        {
            var env = new StandardEnvironment();

            var ftrunc = GetFunction(env, "TRUNC");

            Assert.IsNull(env.Invoke(ftrunc, new object[] { null }));
            Assert.AreEqual(13.0, env.Invoke(ftrunc, Args(13.5)));
            Assert.AreEqual(-7.0, env.Invoke(ftrunc, Args(-7.1)));
            Assert.AreEqual(0, env.Invoke(ftrunc, Args(0.99)));

            // The argument to TRUNC is required:
            var ex = Assert.Catch <EvaluationException>(() => env.Invoke(ftrunc));

            Console.WriteLine(@"Expected exception: {0}", ex.Message);
        }
        public void CanMaxFunction()
        {
            var env = new StandardEnvironment();

            var fmax = GetFunction(env, "MAX");

            Assert.IsNull(env.Invoke(fmax, new object[] { null }));
            Assert.AreEqual("one", env.Invoke(fmax, Args("one")));
            Assert.AreEqual("two", env.Invoke(fmax, Args("one", "two", "three")));

            // The StandardEnvironment's MAX function defines
            // false < true < numbers < strings and treats null
            // as uncomparable (in which case it returns null):

            Assert.AreEqual("foo", env.Invoke(fmax, Args("foo", 123.45, true, "bar", -7, false)));
            Assert.AreEqual(null, env.Invoke(fmax, Args("foo", 123.45, true, "bar", -7, false, null)));             // "foo"
        }
        public void CanWhenFunction()
        {
            var env = new StandardEnvironment();

            var fwhen = GetFunction(env, "WHEN");

            Assert.IsNull(env.Invoke(fwhen, Args()));
            Assert.AreEqual(123, env.Invoke(fwhen, Args(123)));
            Assert.AreEqual(null, env.Invoke(fwhen, Args(false, "foo")));
            Assert.AreEqual("foo", env.Invoke(fwhen, Args(true, "foo")));
            Assert.AreEqual("nix", env.Invoke(fwhen, Args(false, "foo", "nix")));
            Assert.AreEqual("foo", env.Invoke(fwhen, Args(true, "foo", "nix")));
            Assert.AreEqual("bar", env.Invoke(fwhen, Args(false, "foo", true, "bar")));
            Assert.AreEqual("foo", env.Invoke(fwhen, Args(true, "foo", true, "bar")));
            Assert.AreEqual(-1, env.Invoke(fwhen, Args(false, 1, false, 2, false, 3, -1)));
        }
        public void CanRegexMatchFunction()
        {
            var env = new StandardEnvironment();

            var fregex = GetFunction(env, "REGEX");

            Assert.IsNull(env.Invoke(fregex, Args(null, null)));
            Assert.IsNull(env.Invoke(fregex, Args("pattern", null)));
            Assert.IsNull(env.Invoke(fregex, Args(null, "text")));

            Assert.AreEqual(true, env.Invoke(fregex, Args("bazaa?r", "What a bazaar!")));
            Assert.AreEqual(true, env.Invoke(fregex, Args("bazaa?r", "What a bazar!")));
            Assert.AreEqual(false, env.Invoke(fregex, Args("bazaa?r", "What a baz!")));

            var ex = Assert.Catch <EvaluationException>(() => env.Invoke(fregex, Args("something missing")));

            Console.WriteLine(@"Expected exception: {0}", ex.Message);
        }
        public void CanRandFunction()
        {
            var env = new StandardEnvironment();

            const double epsilon = 0.0000000001;

            var frand = GetFunction(env, "RAND");

            env.RandomSeed = 1234;                                                            // repeatable randomness

            Assert.AreEqual(0.39908097935797693, (double)env.Invoke(frand, Args()), epsilon); // RAND/0
            Assert.AreEqual(8, env.Invoke(frand, Args(10)));
            Assert.AreEqual(7, env.Invoke(frand, Args(5, 12)));

            Assert.IsNull(env.Invoke(frand, Args(null)));
            Assert.IsNull(env.Invoke(frand, Args(null, 12)));
            Assert.IsNull(env.Invoke(frand, Args(7, null)));
            Assert.IsNull(env.Invoke(frand, Args(null, null)));

            var ex = Assert.Catch <EvaluationException>(() => env.Invoke(frand, Args(1, 2, 3)));

            Console.WriteLine(@"Expected exception: {0}", ex.Message);
        }
        public void CanTrimFunction()
        {
            var env = new StandardEnvironment();

            var ftrim = GetFunction(env, "TRIM");

            Assert.AreEqual(".abc-", env.Invoke(ftrim, Args(" .abc-\t  ")));
            Assert.AreEqual("abc", env.Invoke(ftrim, Args(" .abc-\t  ", " \t.-")));

            // TRIM: 1st arg is null-contagious, 2nd arg defaults to white space
            Assert.IsNull(env.Invoke(ftrim, Args(null)));
            Assert.AreEqual("x", env.Invoke(ftrim, Args(" x ", null)));
            Assert.IsNull(env.Invoke(ftrim, Args(null, null)));

            var ex1 = Assert.Catch <EvaluationException>(() => env.Invoke(ftrim, Args()));

            Console.WriteLine(@"Expected exception: {0}", ex1.Message);
            var ex2 = Assert.Catch <EvaluationException>(() => env.Invoke(ftrim, "foo", "bar", "bang"));

            Console.WriteLine(@"Expected exception: {0}", ex2.Message);
        }
        public void CanDecodeFunction()
        {
            var env = new StandardEnvironment();

            var fdecode = GetFunction(env, "DECODE");

            Assert.AreEqual(12, env.Invoke(fdecode, Args(12)));
            Assert.AreEqual(13, env.Invoke(fdecode, Args(12, 13)));
            Assert.AreEqual(null, env.Invoke(fdecode, Args(12, 8, 16)));
            Assert.AreEqual(24, env.Invoke(fdecode, Args(12, 12, 24)));
            Assert.AreEqual(25, env.Invoke(fdecode, Args(12, 8, 16, 25)));

            // DECODE considers two nulls to be equal (emulate Oracle behaviour):
            Assert.IsNull(env.Invoke(fdecode, Args(null)));
            Assert.IsNull(env.Invoke(fdecode, Args(null, null)));
            Assert.AreEqual("deflt", env.Invoke(fdecode, Args(null, "deflt")));
            Assert.AreEqual("deflt", env.Invoke(fdecode, Args(null, "s1", "r1", "s2", "r2", "deflt")));
            Assert.AreEqual("r2", env.Invoke(fdecode, Args(null, "s1", "r1", null, "r2", "deflt")));
            Assert.IsNull(env.Invoke(fdecode, Args(null, "s1", "r1", "s2", "r2")));
            Assert.IsNull(env.Invoke(fdecode, Args(null, "s1", "r1", "s2", "r2", null)));
            Assert.IsNull(env.Invoke(fdecode, Args("s2", "s1", null, "s2", null, "deflt")));

            // DECODE: equate (int) 1 and (double/float) 1.0 etc.
            Assert.AreEqual("one", env.Invoke(fdecode, Args(1.0, 1, "one", "oops")));
        }
        public void CanConcatFunction()
        {
            var env = new StandardEnvironment();

            var fconcat = GetFunction(env, "CONCAT");

            Assert.AreEqual("x", env.Invoke(fconcat, Args("x")));
            Assert.AreEqual("xy", env.Invoke(fconcat, Args("x", "y")));
            Assert.AreEqual("xyz", env.Invoke(fconcat, Args("x", "y", "z")));

            Assert.AreEqual("", env.Invoke(fconcat, Args()));
            Assert.AreEqual("", env.Invoke(fconcat, Args(null)));

            Assert.AreEqual("123", env.Invoke(fconcat, Args(123)));
            Assert.AreEqual("x3", env.Invoke(fconcat, Args(null, "x", null, 3.0, null)));

            // CONCAT: null on input results in empty string on output!
            Assert.AreEqual("", env.Invoke(fconcat, Args()));
            Assert.AreEqual("", env.Invoke(fconcat, Args(null)));
            Assert.AreEqual("", env.Invoke(fconcat, Args(null, null)));
            Assert.AreEqual("", env.Invoke(fconcat, Args(null, null, null)));
        }
        public void CanSubstrFunction()
        {
            var env = new StandardEnvironment();

            var fsubstr = GetFunction(env, "SUBSTR");

            Assert.AreEqual("cde", env.Invoke(fsubstr, Args("abcdefg", 2, 3)));
            Assert.AreEqual("cdefg", env.Invoke(fsubstr, Args("abcdefg", 2)));
            Assert.AreEqual("cdefg", env.Invoke(fsubstr, Args("abcdefg", 2, 99)));
            Assert.AreEqual("", env.Invoke(fsubstr, Args("abcdefg", -7, 3)));
            Assert.AreEqual("a", env.Invoke(fsubstr, Args("abcdefg", -7, 8)));
            Assert.AreEqual("abcdefgh", env.Invoke(fsubstr, Args("abcdefgh", -7)));
            Assert.AreEqual("", env.Invoke(fsubstr, Args("abcdefg", 9, 3)));

            // SUBSTR: the 1st and 2nd arg are null-contagious; the 3rd defaults
            Assert.IsNull(env.Invoke(fsubstr, Args(null, null, null)));
            Assert.IsNull(env.Invoke(fsubstr, Args(null, null)));
            Assert.IsNull(env.Invoke(fsubstr, Args(null, 2, 3)));
            Assert.IsNull(env.Invoke(fsubstr, Args(null, 2)));
            Assert.AreEqual("b", env.Invoke(fsubstr, Args("abc", 1, 1)));
            Assert.AreEqual("bc", env.Invoke(fsubstr, Args("abc", 1, null)));
            Assert.IsNull(env.Invoke(fsubstr, Args("abc", null, 1)));
            Assert.IsNull(env.Invoke(fsubstr, Args("abc", null, null)));
            Assert.IsNull(env.Invoke(fsubstr, Args("abc", null)));
        }
        public void CanRoundFunction()
        {
            var env = new StandardEnvironment();

            var fround = GetFunction(env, "ROUND");

            Assert.IsNull(env.Invoke(fround, Args(null)));
            Assert.AreEqual(13.0, env.Invoke(fround, Args(13.4)));
            Assert.AreEqual(14.0, env.Invoke(fround, Args(13.5)));
            Assert.AreEqual(14.0, env.Invoke(fround, Args(13.6)));
            Assert.AreEqual(-7.0, env.Invoke(fround, Args(-7.1)));
            Assert.AreEqual(1.0, env.Invoke(fround, Args(0.99)));

            Assert.IsNull(env.Invoke(fround, Args(null, null)));
            Assert.AreEqual(12.345, env.Invoke(fround, Args(12.345, 3)));
            Assert.AreEqual(12.34, env.Invoke(fround, Args(12.345, 2)));
            Assert.AreEqual(12.3, env.Invoke(fround, Args(12.345, 1)));
            Assert.AreEqual(12.0, env.Invoke(fround, Args(12.345, 0)));
            Assert.AreEqual(12.0, env.Invoke(fround, Args(12.345, null)));
        }