LSL Language Test

From Second Life Wiki
Revision as of 11:17, 25 January 2008 by Babbage Linden (talk | contribs)
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
  • Tests all LSL language features
  • To run, rez a prim, add this script, then touch the prim
  • On successful completion, says "Ran N tests in 0.5 seconds with 0 failures"
  • For more verbose reporting, uncomment llSay call in testPassed
integer gTestsPassed = 0;
integer gTestsFailed = 0;

testPassed(string description, string actual, string expected)
{
    ++gTestsPassed;
    //llSay(0, description);
}

testFailed(string description, string actual, string expected)
{
    ++gTestsFailed;
    llSay(0, "FAILED!: " + description + " (" + actual + " expected " + expected + ")");
}

ensureTrue(string description, integer actual)
{
    if(actual)
    {
        testPassed(description, (string) actual, (string) TRUE);
    }
    else
    {
        testFailed(description, (string) actual, (string) TRUE);
    }
}

ensureFalse(string description, integer actual)
{
    if(actual)
    {
        testFailed(description, (string) actual, (string) FALSE);
    }
    else
    {
        testPassed(description, (string) actual, (string) FALSE);
    }
}

ensureIntegerEqual(string description, integer actual, integer expected)
{
    if(actual == expected)
    {
        testPassed(description, (string) actual, (string) expected);
    }
    else
    {
        testFailed(description, (string) actual, (string) expected);
    }
}

integer floatEqual(float actual, float expected)
{
    float error = llFabs(expected - actual);
    float epsilon = 0.001;
    if(error > epsilon)
    {
        llSay(0,"error " + (string)error);
        return FALSE;
    }
    return TRUE;
}

ensureFloatEqual(string description, float actual, float expected)
{
    if(floatEqual(actual, expected))
    {
        testPassed(description, (string) actual, (string) expected);
    }
    else
    {
        testFailed(description, (string) actual, (string) expected);
    }
}

ensureStringEqual(string description, string actual, string expected)
{
    if(actual == expected)
    {
        testPassed(description, (string) actual, (string) expected);
    }
    else
    {
        testFailed(description, (string) actual, (string) expected);
    }
}

ensureVectorEqual(string description, vector actual, vector expected)
{
    if(floatEqual(actual.x, expected.x) &&
        floatEqual(actual.y, expected.y) &&
        floatEqual(actual.z, expected.z))
    {
        testPassed(description, (string) actual, (string) expected);
    }
    else
    {
        testFailed(description, (string) actual, (string) expected);
    }
}

ensureRotationEqual(string description, rotation actual, rotation expected)
{
    if(floatEqual(actual.x, expected.x) &&
        floatEqual(actual.y, expected.y) &&
        floatEqual(actual.z, expected.z) &&
        floatEqual(actual.s, expected.s))
    {
        testPassed(description, (string) actual, (string) expected);
    }
    else
    {
        testFailed(description, (string) actual, (string) expected);
    }
}

ensureListEqual(string description, list actual, list expected)
{
    if(actual == expected)
    {
        testPassed(description, (string) actual, (string) expected);
    }
    else
    {
        testFailed(description, (string) actual, (string) expected);
    }
}

integer gInteger = 5;
float gFloat = 1.5;
string gString = "foo";
vector gVector = <1, 2, 3>;
rotation gRot = <1, 2, 3, 4>;
list gList = [1, 2, 3];

integer testReturn()
{
    return 1;
}

integer testParameters(integer param)
{
    param = param + 1;
    return param;
}

integer testRecursion(integer param)
{
    if(param <= 0)
    {
        return 0;
    }
    else
    {
        return testRecursion(param - 1);
    }
}

string testExpressionLists(list l)
{
    return "foo" + (string)l;
}

tests()
{
    // truth
    ensureIntegerEqual("TRUE", TRUE, TRUE);
    ensureIntegerEqual("FALSE", FALSE, FALSE);
    
    // equality
    ensureIntegerEqual("(TRUE == TRUE)", (TRUE == TRUE), TRUE);
    ensureIntegerEqual("(TRUE == FALSE)", (TRUE == FALSE), FALSE);
    ensureIntegerEqual("(TRUE == FALSE)", (TRUE == FALSE), FALSE);
    ensureIntegerEqual("(FALSE == FALSE)", (FALSE == FALSE), TRUE);
    
    // inequality
    ensureIntegerEqual("(TRUE != TRUE)", (TRUE != TRUE), FALSE);
    ensureIntegerEqual("(TRUE != FALSE)", (TRUE != FALSE), TRUE);
    ensureIntegerEqual("(TRUE != FALSE)", (TRUE != FALSE), TRUE);
    ensureIntegerEqual("(FALSE != FALSE)", (FALSE != FALSE), FALSE);
    
    // and
    ensureIntegerEqual("(TRUE && TRUE)", (TRUE && TRUE), TRUE);
    ensureIntegerEqual("(TRUE && FALSE)", (TRUE && FALSE), FALSE);
    ensureIntegerEqual("(FALSE && TRUE)", (FALSE && TRUE), FALSE);
    ensureIntegerEqual("(FALSE && FALSE)", (FALSE && FALSE), FALSE);
    
    // or
    ensureIntegerEqual("(TRUE || TRUE)", (TRUE || TRUE), TRUE);
    ensureIntegerEqual("(TRUE || FALSE)", (TRUE || FALSE), TRUE);
    ensureIntegerEqual("(FALSE || TRUE)", (FALSE || TRUE), TRUE);
    ensureIntegerEqual("(FALSE || FALSE)", (FALSE || FALSE), FALSE);
    
    // not
    ensureIntegerEqual("(! TRUE)", (! TRUE), FALSE);
    ensureIntegerEqual("(! FALSE)", (! FALSE), TRUE);
    
    // greater than
    ensureIntegerEqual("(1 > 0)", (1 > 0), TRUE);
    ensureIntegerEqual("(0 > 1)", (0 > 1), FALSE);
    ensureIntegerEqual("(1 > 1)", (1 > 1), FALSE);
    
    // less than
    ensureIntegerEqual("(0 < 1)", (0 < 1), TRUE);
    ensureIntegerEqual("(1 < 0)", (1 < 0), FALSE);
    ensureIntegerEqual("(1 < 1)", (1 < 1), FALSE);
    
    // greater than or equal
    ensureIntegerEqual("(1 >= 0)", (1 >= 0), TRUE);
    ensureIntegerEqual("(0 >= 1)", (0 >= 1), FALSE);
    ensureIntegerEqual("(1 >= 1)", (1 >= 1), TRUE);
    
    // tess than or equal
    ensureIntegerEqual("(0 <= 1)", (0 <= 1), TRUE);
    ensureIntegerEqual("(1 <= 0)", (1 <= 0), FALSE);
    ensureIntegerEqual("(1 <= 1)", (1 <= 1), TRUE);
    
    // bitwise and
    ensureIntegerEqual("(10 & 25)", (10 & 25), 8);
    
    // bitwise or
    ensureIntegerEqual("(10 | 25)", (10 | 25), 27);
    
    // bitwise not
    ensureIntegerEqual("~10", ~10, -11);
    
    // xor
    ensureIntegerEqual("(10 ^ 25)", (10 ^ 25), 19);
    
    // right shift
    ensureIntegerEqual("(523 >> 2)", (523 >> 2), 130);
    
    // left shift
    ensureIntegerEqual("(523 << 2)", (523 << 2), 2092);
    
    // addition
    ensureIntegerEqual("(1 + 1)", (1 + 1), 2);
    ensureFloatEqual("(1 + 1.1)", (1 + 1.1), 2.1);
    ensureFloatEqual("(1.1 + 1)", (1.1 + 1), 2.1);
    ensureFloatEqual("(1.1 + 1.1)", (1.1 + 1.1), 2.2);
    ensureStringEqual("\"foo\" + \"bar\"", "foo" + "bar", "foobar");
    ensureVectorEqual("(<1.1, 2.2, 3.3> + <4.4, 5.5, 6.6>)", (<1.1, 2.2, 3.3> + <4.4, 5.5, 6.6>), <5.5, 7.7, 9.9>);
    ensureRotationEqual("(<1.1, 2.2, 3.3, 4.4> + <4.4, 5.5, 6.6, 3.3>)", (<1.1, 2.2, 3.3, 4.4> + <4.4, 5.5, 6.6, 3.3>), <5.5, 7.7, 9.9, 7.7>);
    ensureListEqual("([1] + 2)", ([1] + 2), [1,2]);
    ensureListEqual("([] + 1.5)", ([] + 1.5), [1.5]);
    ensureListEqual("([\"foo\"] + \"bar\")", (["foo"] + "bar"), ["foo", "bar"]);
    ensureListEqual("([] + <1,2,3>)", ([] + <1,2,3>), [<1,2,3>]);
    ensureListEqual("([] + <1,2,3,4>)", ([] + <1,2,3,4>), [<1,2,3,4>]);
    
    ensureListEqual("(1 + [2])", (1 + [2]), [1,2]);
    ensureListEqual("(1.0 + [2])", (1.0 + [2]), [1.0,2]);
    ensureListEqual("(1 + [2])", ("one" + [2]), ["one",2]);
    ensureListEqual("(<1.0,1.0,1.0,1.0> + [2])", (<1.0,1.0,1.0,1.0> + [2]), [<1.0,1.0,1.0,1.0>,2]);
    ensureListEqual("(<1.0,1.0,1.0> + [2])", (<1.0,1.0,1.0> + [2]), [<1.0,1.0,1.0>,2]);


    // subtraction
    ensureIntegerEqual("(1 - 1)", (1 - 1), 0);
    ensureFloatEqual("(1 - 0.5)", (1 - 0.5), 0.5);
    ensureFloatEqual("(1.5 - 1)", (1.5 - 1), 0.5);
    ensureFloatEqual("(2.2 - 1.1)", (2.2 - 1.1), 1.1);
    ensureVectorEqual("(<1.5, 2.5, 3.5> - <4.5, 5.5, 6.5>)", (<1.5, 2.5, 3.5> - <4.5, 5.5, 6.5>), <-3.0, -3.0, -3.0>);
    ensureRotationEqual("(<1.5, 2.5, 3.5, 4.5> - <4.5, 5.5, 6.5, 7.5>)", (<1.5, 2.5, 3.5, 4.5> - <4.5, 5.5, 6.5, 7.5>), <-3.0, -3.0, -3.0, -3.0>);

    // multiplication
    ensureIntegerEqual("(2 * 3)", (2 * 3), 6);
    ensureFloatEqual("(2 * 3.5)", (2 * 3.5), 7.0);
    ensureFloatEqual("(2.5 * 3)", (2.5 * 3), 7.5);
    ensureFloatEqual("(2.5 * 3.5)", (2.5 * 3.5), 8.75);
    ensureVectorEqual("(<1.1, 2.2, 3.3> * 2)", (<1.1, 2.2, 3.3> * 2), <2.2, 4.4, 6.6>);
    ensureVectorEqual("(2 * <1.1, 2.2, 3.3>)", (<1.1, 2.2, 3.3> * 2), (2 * <1.1, 2.2, 3.3>));
    ensureVectorEqual("(<2.2, 4.4, 6.6> * 2.0)", (<2.2, 4.4, 6.6> * 2.0), <4.4, 8.8, 13.2>);
    ensureVectorEqual("(2.0 * <2.2, 4.4, 6.6>)", (<2.2, 4.4, 6.6> * 2.0), (2.0 * <2.2, 4.4, 6.6>));
    ensureFloatEqual("<1,3,-5> * <4,-2,-1>", <1,3,-5> * <4,-2,-1>, 3.0);
    ensureVectorEqual("<-1,0,0> * <0, 0, 0.707, 0.707>", <-1,0,0> * <0, 0, 0.707, 0.707>, <0,-1, 0>);
    ensureRotationEqual("(<1.0, 2.0, 3.0, 4.0> * <5.0, 6.0, 7.0, 8.0>)", (<1.0, 2.0, 3.0, 4.0> * <5.0, 6.0, 7.0, 8.0>), <32.0, 32.0, 56.0, -6.0>);
    

    // division
    ensureIntegerEqual("(2 / 2)", (2 / 2), 1);
    ensureFloatEqual("(2.2 / 2)", (2.2 / 2), 1.1);
    ensureFloatEqual("(3 / 1.5)", (3 / 1.5), 2.0);
    ensureFloatEqual("(2.2 / 2.0)", (2.2 / 2.0), 1.1);
    ensureVectorEqual("(<1.0, 2.0, 3.0> / 2)", (<1.0, 2.0, 3.0> / 2), <0.5, 1.0, 1.5>);
    ensureVectorEqual("(<3.0, 6.0, 9.0> / 1.5)", (<3.0, 6.0, 9.0> / 1.5), <2.0, 4.0, 6.0>);
    ensureVectorEqual("<-1,0,0> / <0, 0, 0.707, 0.707>", <-1,0,0> / <0, 0, 0.707, 0.707>, <0,1, 0>);
    ensureRotationEqual("(<1.0, 2.0, 3.0, 4.0> / <5.0, 6.0, 7.0, 8.0>)", (<1.0, 2.0, 3.0, 4.0> / <5.0, 6.0, 7.0, 8.0>), <-16.0, 0.0, -8.0, 70.0>);

    // modulo
    ensureIntegerEqual("(3 % 1)", (3 % 1), 0);
    ensureVectorEqual("(<1.0, 2.0, 3.0> % <4.0, 5.0, 6.0>)", (<1.0, 2.0, 3.0> % <4.0, 5.0, 6.0>), <-3.0, 6.0, -3.0>);
    
    // assignment
    integer i = 1;
    ensureIntegerEqual("i = 1;", i, 1);
    
    // addition assignment
    i = 1;
    i += 1;
    ensureIntegerEqual("i = 1; i += 1;", i, 2);
    
    // subtraction assignment
    i = 1;
    i -= 1;
    ensureIntegerEqual("i = 1; i -= 1;", i, 0);
    
    // multiplication assignment
    i = 2;
    i *= 2;
    ensureIntegerEqual("i = 2; i *= 2;", i, 4);
    
    // division assignment
    i = 2;
    i /= 2;
    ensureIntegerEqual("i = 2; i /= 2;", i, 1);
    
    // modulo assignment
    i = 3;
    i %= 1;
    ensureIntegerEqual("i = 3; i %= 1;", i, 0);
    
    // post increment.
    i = 1;
    ensureIntegerEqual("i = 1; (i == 2) && (i++ == 1)", (i == 2) && (i++ == 1), TRUE);
        
    // pre increment.
    i = 1;
    ensureIntegerEqual("i = 1; (i == 2) && (++i == 2)", (i == 2) && (++i == 2), TRUE);
        
    // post decrement.
    i = 2;
    ensureIntegerEqual("i = 2; (i == 1) && (i-- == 2)", (i == 1) && (i-- == 2), TRUE);
        
    // pre decrement.
    i = 2;
    ensureIntegerEqual("i = 2; (i == 1) && (--i == 1)", (i == 1) && (--i == 1), TRUE);
    i = 2; --i;
    ensureIntegerEqual("i = 2; --i", i, 1);
    
    // casting
    ensureFloatEqual("((float)2)", ((float)2), 2.0);
    ensureStringEqual("((string)2)", ((string)2), "2");
    ensureIntegerEqual("((integer) 1.5)", ((integer) 1.5), 1);
    ensureStringEqual("((string) 1.5)", ((string) 1.5), "1.500000");
    ensureIntegerEqual("((integer) \"0xF\")", ((integer) "0xF"), 15);
    ensureIntegerEqual("((integer) \"2\")", ((integer) "2"), 2);
    ensureFloatEqual("((float) \"1.5\")", ((float) "1.5"), 1.5);
    ensureVectorEqual("((vector) \"<1,2,3>\")", ((vector) "<1,2,3>"), <1,2,3>);
    ensureRotationEqual("((quaternion) \"<1,2,3,4>\")", ((quaternion) "<1,2,3,4>"), <1,2,3,4>);
    ensureStringEqual("((string) <1,2,3>)", ((string) <1,2,3>), "<1.00000, 2.00000, 3.00000>");
    ensureStringEqual("((string) <1,2,3,4>)", ((string) <1,2,3,4>), "<1.00000, 2.00000, 3.00000, 4.00000>");
    ensureStringEqual("((string) [1,2.5,<1,2,3>])", ((string) [1,2.5,<1,2,3>]), "12.500000<1.000000, 2.000000, 3.000000>");
    
    // while
    i = 0;
    while(i < 10) ++i;
    ensureIntegerEqual("i = 0; while(i < 10) ++i", i, 10);
    
    // do while
    i = 0;
    do {++i;} while(i < 10);
    ensureIntegerEqual("i = 0; do {++i;} while(i < 10);", i, 10);
    
    // for
    for(i = 0; i < 10; ++i);
    ensureIntegerEqual("for(i = 0; i < 10; ++i);", i, 10);
    
    // jump
    i = 1;
    jump SkipAssign;
    llSetText("Error", <1,0,0>, 1); // Inserting this library call causes a save point to be added.
    i = 2;
    @SkipAssign;
    ensureIntegerEqual("i = 1; jump SkipAssign; i = 2; @SkipAssign;", i, 1);
    
    // return
    ensureIntegerEqual("testReturn()", testReturn(), 1);
    
    // parameters
    ensureIntegerEqual("testParameters(1)", testParameters(1), 2);
    
    // variable parameters
    i = 1;
    ensureIntegerEqual("i = 1; testParameters(i)", testParameters(i), 2);
    
    // recursion
    ensureIntegerEqual("testRecursion(10)", testRecursion(10), 0);
    
    // globals
    ensureIntegerEqual("gInteger", gInteger, 5);
    ensureFloatEqual("gFloat", gFloat, 1.5);
    ensureStringEqual("gString", gString, "foo");
    ensureVectorEqual("gVector", gVector, <1, 2, 3>);
    ensureRotationEqual("gRot", gRot, <1, 2, 3, 4>);
    ensureListEqual("gList", gList, [1, 2, 3]);
    
    // global assignment
    gInteger = 1;
    ensureIntegerEqual("gInteger = 1", gInteger, 1);
    
    gFloat = 0.5;
    ensureFloatEqual("gFloat = 0.5", gFloat, 0.5);
    
    gString = "bar";
    ensureStringEqual("gString = \"bar\"", gString, "bar");
    
    gVector = <3,3,3>;
    ensureVectorEqual("gVector = <3,3,3>", gVector, <3,3,3>);
    
    gRot = <3,3,3,3>;
    ensureRotationEqual("gRot = <3,3,3,3>", gRot, <3,3,3,3>);

    gList = [4,5,6];
    ensureListEqual("gList = [4,5,6]", gList, [4,5,6]);
    

    // vector accessor
    vector v;
    v.x = 3;
    ensureFloatEqual("v.x", v.x, 3);
    
    // rotation accessor
    rotation q;
    q.s = 5;
    ensureFloatEqual("q.s", q.s, 5);
    
    // global vector accessor
    gVector.y = 17.5;
    ensureFloatEqual("gVector.y = 17.5", gVector.y, 17.5);
    
    // global rotation accessor
    gRot.z = 19.5;
    ensureFloatEqual("gRot.z = 19.5", gRot.z, 19.5);
    
    // list equality
    list l = (list) 5;
    list l2 = (list) 5;
    ensureListEqual("list l = (list) 5; list l2 = (list) 5", l, l2);
    ensureListEqual("list l = (list) 5", l, [5]);
    ensureListEqual("[1.5, 6, <1,2,3>, <1,2,3,4>]", [1.5, 6, <1,2,3>, <1,2,3,4>], [1.5, 6, <1,2,3>, <1,2,3,4>]);

    // String escaping
    ensureIntegerEqual("llStringLength(\"\\\") == 1", llStringLength("\\"), 1);
    ensureIntegerEqual("llStringLength(\"\\t\") == 4", llStringLength("\t"), 4);
    ensureIntegerEqual("llStringLength(\"\\n\") == 1", llStringLength("\n"), 1);
    ensureIntegerEqual("llStringLength(\"\\\"\") == 1", llStringLength("\""), 1);

    // Nested expression lists
    ensureStringEqual("testExpressionLists([testExpressionLists([]), \"bar\"]) == \"foofoobar\"", testExpressionLists([testExpressionLists([]), "bar"]), "foofoobar");
}

default
{
    touch_start(integer total_number)
    {
        llResetTime();
        tests();
        llSay(0, "Ran " + (string)(gTestsPassed + gTestsFailed) + " tests in " + (string) llGetTime() + " seconds with " + (string)gTestsFailed + " failures");
        
        // reset globals  
        gInteger = 5;
        gFloat = 1.5;
        gString = "foo";
        gVector = <1, 2, 3>;
        gRot = <1, 2, 3, 4>;
        gList = [1, 2, 3];
        gTestsPassed = 0;
        gTestsFailed = 0;
    }
}