Difference between revisions of "Json usage in LSL/TestScript"

From Second Life Wiki
Jump to navigation Jump to search
m (readabiliteeeh++)
m (→‎Full Test Script: readabiliteeh++)
Line 165: Line 165:
== Full Test Script ==
== Full Test Script ==
<lsl>
<lsl>
// This test script verifies expected behavior or LSL JSON functions
// This is just the framework for creating new tests for LSL JSON functions without the tests.
 
 
integer tests;
integer total_number_of_tests;
integer fails;
integer total_number_of_fails;
 
 
string typeName(string type)
string typeName(string type_flag)
{
{
     if (type == JSON_NUMBER) return "JSON_NUMBER";
     if           (type_flag == JSON_INVALID) return "JSON_INVALID";
     if (type == JSON_INVALID) return "JSON_INVALID";
 
     if (type == JSON_OBJECT) return "JSON_OBJECT";
    /* else */ if (type_flag == JSON_OBJECT) return "JSON_OBJECT";
     if (type == JSON_ARRAY) return "JSON_ARRAY";
     /* else */ if (type_flag == JSON_ARRAY)   return "JSON_ARRAY";
     if (type == JSON_TRUE) return "JSON_TRUE";
 
     if (type == JSON_FALSE) return "JSON_FALSE";
     /* else */ if (type_flag == JSON_NUMBER) return "JSON_NUMBER";
     if (type == JSON_STRING) return "JSON_STRING";
     /* else */ if (type_flag == JSON_STRING) return "JSON_STRING";
     if (type == JSON_NULL) return "JSON_NULL";
     /* else */ if (type_flag == JSON_NULL)   return "JSON_NULL";
     return type;
 
     /* else */ if (type_flag == JSON_TRUE)   return "JSON_TRUE";
     /* else */ if (type_flag == JSON_FALSE)   return "JSON_FALSE";
 
     /* else */ if (type_flag == JSON_DELETE) return "JSON_DELETE";
 
     /* else ...... type_flag unknown */      return type_flag;
}
}
 
 
string listEntryTypeName(integer type)
string listEntryTypeName(integer type)
{
{
     list list_type_names = ["TYPE_INVALID", "TYPE_INTEGER", "TYPE_FLOAT", "TYPE_STRING", "TYPE_KEY", "TYPE_VECTOR", "TYPE_ROTATION"];
     list list_type_names = [
     if(type >= llGetListLength(list_type_names)) return "UNKNOWN_LIST_TYPE";
        "TYPE_INVALID",
     return llList2String(list_type_names, type);
        "TYPE_INTEGER",
        "TYPE_FLOAT",
        "TYPE_STRING",
        "TYPE_KEY",
        "TYPE_VECTOR",
        "TYPE_ROTATION"
    ];
 
     integer length = llGetListLength(list_type_names);
 
    if (length <= type) return "UNKNOWN_LIST_TYPE";
     /* else */          return llList2String(list_type_names, type);
}
}
 
 
integer max(integer x, integer y) {
integer max(integer x, integer y)
     if( y > x ) return y;
{
     return x;
     if (x < y) return y;
     /* else */ return x;
}
}
 
 
 
integer verify(string message, string result, string expected)
integer verify(string message, string result, string expected)
{
{
     ++tests;
     ++total_number_of_tests;
     if (expected != result)
 
     if (result != expected)
     {
     {
         llOwnerSay("FAIL test " + message  + ", expected: " + typeName(expected) + ", result: " + typeName(result));
         ++total_number_of_fails;
        ++fails;
 
         return 0;
        publish("TEST FAILED"
            + "\nexpected: " + typeName(expected)
            + "\nresult: " + typeName(result)
            + "\nJSON string value: " + message
            + "\n ");
 
         return FALSE;
     }
     }
     return 1;
     /* else */
        return TRUE;
}
}
 
 
verify_list(string message, list result, list expected)
verify_list(string message, list result, list expected)
{
{
     // First verify that the list lengths match before looking at the data types
     integer length_result  = llGetListLength(result);
     verify(message + ": list length", (string)llGetListLength(result), (string)llGetListLength(expected));
    integer length_expected = llGetListLength(expected);
 
 
  // the CSV string comparison is probably enough to verify that the values stored are correct, but I want to check data type too.
// first verify that the list lengths match before looking at the data types
  integer i = max(llGetListLength(expected), llGetListLength(result));
 
  while(--i >= 0)
     verify(message + ": list length",
  {
        (string)length_result,
         verify(message + ": on list entry type comparison on index " + (string)i, listEntryTypeName(llGetListEntryType(result, i)),
        (string)length_expected);
             listEntryTypeName(llGetListEntryType(expected, i)));
 
         verify(message + ": on list value comparison on index " + (string)i, typeName(llList2String(result, i)), typeName(llList2String(expected, i)));
// the CSV string comparison is probably enough to verify that the values stored are correct
//  but I want to check data type too
 
    integer index = max(length_result, length_expected);
    while (--index >= 0)
    {
         verify(message + ": on list entry type comparison on index " + (string)index,
            listEntryTypeName(llGetListEntryType(result, index)),
             listEntryTypeName(llGetListEntryType(expected, index)));
 
         verify(message + ": on list value comparison on index " + (string)index,
            typeName(llList2String(result, index)),
            typeName(llList2String(expected, index)));
     }
     }
     verify(message + ": llList2CSV comparison",llList2CSV(result),llList2CSV(expected));
 
     verify(message + ": llList2CSV comparison",
        llList2CSV(result),
        llList2CSV(expected));
}
 
run_tests()
{
    publish("JSON TESTS: started"
        + "\n ");
 
    total_number_of_tests = 0;
    total_number_of_fails = 0;
 
    test_types();
    test_get_value();
    test_set_value();
    test_json_to_list();
    test_list_to_json();
    test_strings_with_escaped_chars();
    test_jira_fixes();
 
    publish("JSON TESTS: done\n"
        + (string)total_number_of_fails + " of "
        + (string)total_number_of_tests + " tests failed."
        + "\n ");
}
}
    
 
publish(string message)
{
//  if you want to keep it private
//
    llOwnerSay(message);
 
//  if you want to make it public
//
//  llSay(PUBLIC_CHANNEL, message);
 
//  if you want to reach the owner wherever she/he is as fast as you can
//
//  key   owner    = llGetOwner();
//  vector ownerSize = llGetAgentSize(owner);
//  if (ownerSize != ZERO_VECTOR) llRegionSayTo(owner, PUBLIC_CHANNEL, message);
//  else                          llInstantMessage(owner, message);
}
 
test_types()
test_types()
{
{
Line 235: Line 315:
     verify("Type of number",llJsonValueType("-123.4e-5",[]),JSON_NUMBER);
     verify("Type of number",llJsonValueType("-123.4e-5",[]),JSON_NUMBER);
     verify("Type of object",llJsonValueType("{\"a\":\"b\"}",[]),JSON_OBJECT);
     verify("Type of object",llJsonValueType("{\"a\":\"b\"}",[]),JSON_OBJECT);
    // Expected to be OBJECT, since we don't do deep validation on input
// Expected to be OBJECT, since we don't do deep validation on input
    //verify("Type of object, invalid/unquoted key",llJsonValueType("{a:\"b\"}",[]),JSON_INVALID);
// verify("Type of object, invalid/unquoted key",llJsonValueType("{a:\"b\"}",[]),JSON_INVALID);
    // Expected to be OBJECT, since we don't do deep validation on input
// Expected to be OBJECT, since we don't do deep validation on input
    //verify("Type of object, invalid/unquoted value",llJsonValueType("{\"a\":b}",[]),JSON_INVALID);
// verify("Type of object, invalid/unquoted value",llJsonValueType("{\"a\":b}",[]),JSON_INVALID);
     verify("Type of array",llJsonValueType("[1,2,3]",[]),JSON_ARRAY);
     verify("Type of array",llJsonValueType("[1,2,3]",[]),JSON_ARRAY);
     verify("Type of array padded front",llJsonValueType(" [1,2,3]",[]),JSON_ARRAY);
     verify("Type of array padded front",llJsonValueType(" [1,2,3]",[]),JSON_ARRAY);
Line 246: Line 326:
     verify("Type of false",llJsonValueType("false",[]),JSON_FALSE);
     verify("Type of false",llJsonValueType("false",[]),JSON_FALSE);
     verify("Type of null",llJsonValueType("null",[]),JSON_NULL);
     verify("Type of null",llJsonValueType("null",[]),JSON_NULL);
 
 
    // test traversal of llJsonValueType
// test traversal of llJsonValueType
     string json = "[[1,2,3],{\"a\":3,\"b\":[true,\"test\",6],\"c\":\"true\",\"d\":false}]";
     string json = "[[1,2,3],{\"a\":3,\"b\":[true,\"test\",6],\"c\":\"true\",\"d\":false}]";
     verify("Type of [0]",llJsonValueType(json,[0]),JSON_ARRAY);
     verify("Type of [0]",llJsonValueType(json,[0]),JSON_ARRAY);
Line 263: Line 343:
     verify("Type of [1,\"b\",3] (invalid index at level 2)",llJsonValueType(json,[1,"b",3]),JSON_INVALID);
     verify("Type of [1,\"b\",3] (invalid index at level 2)",llJsonValueType(json,[1,"b",3]),JSON_INVALID);
     verify("Type of [1,\"b\",2,0,1] (invalid index at level 3) MAINT-2670",llJsonValueType(json,[1,"b",2, 0, 1]),JSON_INVALID);
     verify("Type of [1,\"b\",2,0,1] (invalid index at level 3) MAINT-2670",llJsonValueType(json,[1,"b",2, 0, 1]),JSON_INVALID);
 
 
    // some invalid cases where keys are uncoded
// some invalid cases where keys are uncoded
     json = "[[1,2,3],{a:3,b:[true,\"test\",6],c:\"true\",\"d\":false}]";
     json = "[[1,2,3],{a:3,b:[true,\"test\",6],c:\"true\",\"d\":false}]";
     verify("Type of [1,\"a\"] where key is unquoted",llJsonValueType(json,[1,"a"]),JSON_INVALID);
     verify("Type of [1,\"a\"] where key is unquoted",llJsonValueType(json,[1,"a"]),JSON_INVALID);
Line 270: Line 350:
     verify("Type of [1,\"c\"] where key is unquoted",llJsonValueType(json,[1,"c"]),JSON_INVALID);
     verify("Type of [1,\"c\"] where key is unquoted",llJsonValueType(json,[1,"c"]),JSON_INVALID);
}
}
 
 
test_get_value()
test_get_value()
{
{
Line 288: Line 368:
     verify("llJsonGetValue [0,\"f\"] (look for missing object within array, depth=1) MAINT-2671",llJsonGetValue(json,[0,"f"]),JSON_INVALID);
     verify("llJsonGetValue [0,\"f\"] (look for missing object within array, depth=1) MAINT-2671",llJsonGetValue(json,[0,"f"]),JSON_INVALID);
     verify("llJsonGetValue [1,2] (specify index within object - disallowed)",llJsonGetValue(json,[1,2]),JSON_INVALID);
     verify("llJsonGetValue [1,2] (specify index within object - disallowed)",llJsonGetValue(json,[1,2]),JSON_INVALID);
 
 
    // invalid input json - missing quotes around 'a' and 'test'
// invalid input json - missing quotes around 'a' and 'test'
     json = "[[1,2,3,4.0],{a:3,\"b\":[true,test,6]}]";
     json = "[[1,2,3,4.0],{a:3,\"b\":[true,test,6]}]";
     verify("llJsonGetValue [1,\"b\",1], unquoted/invalid string value",llJsonGetValue(json,[1,"b",1]),JSON_INVALID);
     verify("llJsonGetValue [1,\"b\",1], unquoted/invalid string value",llJsonGetValue(json,[1,"b",1]),JSON_INVALID);
     verify("llJsonGetValue [1,\"a\"], unquoted/invalid string for key",llJsonGetValue(json,[1,"a"]),JSON_INVALID);
     verify("llJsonGetValue [1,\"a\"], unquoted/invalid string for key",llJsonGetValue(json,[1,"a"]),JSON_INVALID);
}
}
 
 
test_set_value()
test_set_value()
{
{
    // Test building from scratch
// Test building from scratch
     string json;
     string json;
     json = llJsonSetValue(json,[0,0],(string)1);
     json = llJsonSetValue(json,[0,0],(string)1);
Line 313: Line 393:
     json = llJsonSetValue(json,[1,"b",2],"6");
     json = llJsonSetValue(json,[1,"b",2],"6");
     verify("llJsonSetValue completed json",json,"[[1,2,3],{\"a\":3,\"b\":[true,\"test\",6]}]");
     verify("llJsonSetValue completed json",json,"[[1,2,3],{\"a\":3,\"b\":[true,\"test\",6]}]");
 
 
    // Test replacing
// Test replacing
     json = llJsonSetValue(json,[1,"b",1],"foo");
     json = llJsonSetValue(json,[1,"b",1],"foo");
     verify("llJsonSetValue completed json",json,"[[1,2,3],{\"a\":3,\"b\":[true,\"foo\",6]}]");
     verify("llJsonSetValue completed json",json,"[[1,2,3],{\"a\":3,\"b\":[true,\"foo\",6]}]");
Line 323: Line 403:
     json = llJsonSetValue(json,[1,0,0],JSON_FALSE);
     json = llJsonSetValue(json,[1,0,0],JSON_FALSE);
     verify("llJsonSetValue completed json",json,"[[1,2,3],[[false]]]");
     verify("llJsonSetValue completed json",json,"[[1,2,3],[[false]]]");
 
 
    // Test appending
// Test appending
     json = llJsonSetValue("[[1,2,3],{\"a\":3,\"b\":[true,\"test\",6]}]",[0,JSON_APPEND], "4.0");
     json = llJsonSetValue("[[1,2,3],{\"a\":3,\"b\":[true,\"test\",6]}]",[0,JSON_APPEND], "4.0");
     verify("llJsonSetValue append to first array",json,"[[1,2,3,4.0],{\"a\":3,\"b\":[true,\"test\",6]}]");
     verify("llJsonSetValue append to first array",json,"[[1,2,3,4.0],{\"a\":3,\"b\":[true,\"test\",6]}]");
Line 337: Line 417:
     json = llJsonSetValue("[]",[0], "\"alone\"");
     json = llJsonSetValue("[]",[0], "\"alone\"");
     verify("llJsonSetValue append to empty array at first index (MAINT-2684)",json,"[\"alone\"]");
     verify("llJsonSetValue append to empty array at first index (MAINT-2684)",json,"[\"alone\"]");
     
 
    // Test deleting  
// Test deleting
     json = "[[1,2,3],{\"a\":3,\"b\":[true,\"test\",6,null]}]";
     json = "[[1,2,3],{\"a\":3,\"b\":[true,\"test\",6,null]}]";
     json = llJsonSetValue(json,[1,"b",1],JSON_DELETE);
     json = llJsonSetValue(json,[1,"b",1],JSON_DELETE);
Line 358: Line 438:
     json = llJsonSetValue(json,[0],JSON_DELETE);
     json = llJsonSetValue(json,[0],JSON_DELETE);
     verify("llJsonSetValue deleting array within array",json,"[]");
     verify("llJsonSetValue deleting array within array",json,"[]");
     
 
    // Test failures in deleting
// Test failures in deleting
     json = "[[1,2,3],{\"a\":3,\"b\":[true,\"test\",6,null]}]";
     json = "[[1,2,3],{\"a\":3,\"b\":[true,\"test\",6,null]}]";
     verify("llJsonSetValue deleting undefined key-value in object",llJsonSetValue(json,[1,"d"],JSON_DELETE),JSON_INVALID);
     verify("llJsonSetValue deleting undefined key-value in object",llJsonSetValue(json,[1,"d"],JSON_DELETE),JSON_INVALID);
Line 365: Line 445:
     verify("llJsonSetValue deleting depth within object that doesn't exist",llJsonSetValue(json,[1,"a","unicorn"],JSON_DELETE),JSON_INVALID);
     verify("llJsonSetValue deleting depth within object that doesn't exist",llJsonSetValue(json,[1,"a","unicorn"],JSON_DELETE),JSON_INVALID);
     verify("llJsonSetValue deleting depth within array that doesn't exist",llJsonSetValue(json,[0,1,1],JSON_DELETE),JSON_INVALID);
     verify("llJsonSetValue deleting depth within array that doesn't exist",llJsonSetValue(json,[0,1,1],JSON_DELETE),JSON_INVALID);
 
 
    // this is the only failure mode that should exist.
// this is the only failure mode that should exist.
     json = "[[1,2,3],{\"a\":3,\"b\":[true,\"foo\",6]}]";
     json = "[[1,2,3],{\"a\":3,\"b\":[true,\"foo\",6]}]";
     json = llJsonSetValue(json,[3],JSON_FALSE);
     json = llJsonSetValue(json,[3],JSON_FALSE);
     verify("llJsonSetValue fail to insert data into invalid array index (MAINT-2675)",json,JSON_INVALID);
     verify("llJsonSetValue fail to insert data into invalid array index (MAINT-2675)",json,JSON_INVALID);
     
 
}
}
     
 
     
 
 
 
 
 
test_json_to_list()
test_json_to_list()
{
{
Line 399: Line 479:
     verify_list("llJson2List, malformed input",n,["[malformed}"]);
     verify_list("llJson2List, malformed input",n,["[malformed}"]);
}
}
 
 
test_list_to_json()
test_list_to_json()
{
{
    // test objects
// test objects
     string json = llList2Json(JSON_OBJECT,["a",1,"b",2.5,"c","test","d","true","e","[1,2,3]"]);
     string json = llList2Json(JSON_OBJECT,["a",1,"b",2.5,"c","test","d","true","e","[1,2,3]"]);
     verify("llList2Json, json object",json,"{\"a\":1,\"b\":2.500000,\"c\":\"test\",\"d\":true,\"e\":[1,2,3]}");
     verify("llList2Json, json object",json,"{\"a\":1,\"b\":2.500000,\"c\":\"test\",\"d\":true,\"e\":[1,2,3]}");
 
 
    // test arrays
// test arrays
     json = llList2Json(JSON_ARRAY,[1,2.5,"test","true","[1,2,3]"]);
     json = llList2Json(JSON_ARRAY,[1,2.5,"test","true","[1,2,3]"]);
     verify("llList2Json, json array",json,"[1,2.500000,\"test\",true,[1,2,3]]");
     verify("llList2Json, json array",json,"[1,2.500000,\"test\",true,[1,2,3]]");
 
 
    // test arrays
// test arrays
     json = llList2Json(JSON_ARRAY,[1,2.5,"test",JSON_TRUE,"[1,2,3]"]);
     json = llList2Json(JSON_ARRAY,[1,2.5,"test",JSON_TRUE,"[1,2,3]"]);
     verify("llList2Json, json array, alternative true representation",json,"[1,2.500000,\"test\",true,[1,2,3]]");
     verify("llList2Json, json array, alternative true representation",json,"[1,2.500000,\"test\",true,[1,2,3]]");
 
 
    // test objects, with empty input
// test objects, with empty input
     json = llList2Json(JSON_OBJECT,[]);
     json = llList2Json(JSON_OBJECT,[]);
     verify("llList2Json, json object with empty input (MAINT-2681)",json,"{}");
     verify("llList2Json, json object with empty input (MAINT-2681)",json,"{}");
 
 
    // test arrays, with empty input
// test arrays, with empty input
     json = llList2Json(JSON_ARRAY,[]);
     json = llList2Json(JSON_ARRAY,[]);
     verify("llList2Json, json array with empty input (MAINT-2681)",json,"[]");
     verify("llList2Json, json array with empty input (MAINT-2681)",json,"[]");
 
 
    // test objects which are truncated
// test objects which are truncated
     json = llList2Json(JSON_OBJECT,["a",1,"b",2.5,"c","test","d","true","e"]);
     json = llList2Json(JSON_OBJECT,["a",1,"b",2.5,"c","test","d","true","e"]);
     verify("llList2Json, json object, truncated",json,JSON_INVALID);
     verify("llList2Json, json object, truncated",json,JSON_INVALID);
 
 
    // test objects which has a non-string identifier somewhere
// test objects which has a non-string identifier somewhere
     json = llList2Json(JSON_OBJECT,["a",1,TRUE,2.5,"c","test","d","true","e"]);
     json = llList2Json(JSON_OBJECT,["a",1,TRUE,2.5,"c","test","d","true","e"]);
     verify("llList2Json, json object, non-string in one of the first stride values",json,JSON_INVALID);
     verify("llList2Json, json object, non-string in one of the first stride values",json,JSON_INVALID);
 
 
    // test invalid type
// test invalid type
     json = llList2Json("foo",["a",1,"b",2.5,"c","test","d","true","e","[1,2,3]"]);
     json = llList2Json("foo",["a",1,"b",2.5,"c","test","d","true","e","[1,2,3]"]);
     verify("llList2Json, json invalid type",json,JSON_INVALID);
     verify("llList2Json, json invalid type",json,JSON_INVALID);
}
}
 
 
test_strings_with_escaped_chars()
test_strings_with_escaped_chars()
{
{
Line 440: Line 520:
         "funky\"string", "funky\\\"string", "quote in middle",
         "funky\"string", "funky\\\"string", "quote in middle",
         "funkystr\"ing", "funkystr\\\"ing", "quote in middle, other position",
         "funkystr\"ing", "funkystr\\\"ing", "quote in middle, other position",
        // note that we have to double-up backslashes to assign them to strings..
//     note that we have to double-up backslashes to assign them to strings..
         "funky\\string", "funky\\\\string", "backslashes in middle",
         "funky\\string", "funky\\\\string", "backslashes in middle",
         "\\funkystring", "\\\\funkystring", "backslashes at beginning",
         "\\funkystring", "\\\\funkystring", "backslashes at beginning",
         "funky\nstring", "funky\\nstring", "newline in string",
         "funky\nstring", "funky\\nstring", "newline in string",
         "funky/string", "funky\\/string", "forward slash in string",
         "funky/string", "funky\\/string", "forward slash in string",
        // TAB (\t) fails, because it seems that LSL automatically converts any tab into 4 consecutive spaces.
//     TAB (\t) fails, because it seems that LSL automatically converts any tab into 4 consecutive spaces.
        // * "funky\tstring", "funky\\tstring", "tab in string",
//     * "funky\tstring", "funky\\tstring", "tab in string",
        // these cases fail; it seems that LSL doesn't support these characters, and strips the '\'
//     these cases fail; it seems that LSL doesn't support these characters, and strips the '\'
        // * "funky\bstring", "funky\\\bstring", "backspace in middle",
//     * "funky\bstring", "funky\\\bstring", "backspace in middle",
        // * "funky\fstring", "funky\\\fstring", "form feed in middle",
//     * "funky\fstring", "funky\\\fstring", "form feed in middle",
        // * "funky\rstring", "funky\\rstring", "carriage return in string",
//     * "funky\rstring", "funky\\rstring", "carriage return in string",
        // note that the following case can't be supported, since strings starting with \" can't be escaped
//     note that the following case can't be supported, since strings starting with \" can't be escaped
        // * "\"funkystring", "\\\"funkystring", "quote in beginning",
//     * "\"funkystring", "\\\"funkystring", "quote in beginning",
         "vanilla string", "vanilla string", "nothing that needs to be escaped.."
         "vanilla string", "vanilla string", "nothing that needs to be escaped.."
     ];
     ];
Line 461: Line 541:
         string funky_string_escaped = llList2String(escaped_pairs, i+1);
         string funky_string_escaped = llList2String(escaped_pairs, i+1);
         string escaped_desc = " '" + llList2String(escaped_pairs, i+2) + "'";
         string escaped_desc = " '" + llList2String(escaped_pairs, i+2) + "'";
 
 
         verify("Type of string with escaped char (for MAINT-2698),"+escaped_desc,llJsonValueType("\""+funky_string_escaped+"\"",[]),JSON_STRING);
         verify("Type of string with escaped char (for MAINT-2698),"+escaped_desc,llJsonValueType("\""+funky_string_escaped+"\"",[]),JSON_STRING);
 
 
         string json = "[[1,2,3,4.0],{\""+funky_string_escaped+"\":3,\"b\":\""+funky_string_escaped+"value"+"\"}]";
         string json = "[[1,2,3,4.0],{\""+funky_string_escaped+"\":3,\"b\":\""+funky_string_escaped+"value"+"\"}]";
         verify("llJsonGetValue [1,\""+funky_string_escaped+"\"] (for MAINT-2698),"+escaped_desc,
         verify("llJsonGetValue [1,\""+funky_string_escaped+"\"] (for MAINT-2698),"+escaped_desc,
             llJsonGetValue(json,[1,funky_string]),"3");
             llJsonGetValue(json,[1,funky_string]),"3");
         verify("llJsonGetValue [1,\"b\"] (for MAINT-2698),"+escaped_desc,llJsonGetValue(json,[1,"b"]),funky_string+"value");
         verify("llJsonGetValue [1,\"b\"] (for MAINT-2698),"+escaped_desc,llJsonGetValue(json,[1,"b"]),funky_string+"value");
 
 
        //llSay(0, "DEBUG: '" + llEscapeURL(json) + "' is input for test " + escaped_desc);
//     llSay(PUBLIC_CHANNEL, "DEBUG: '" + llEscapeURL(json) + "' is input for test " + escaped_desc);
         json = llJsonSetValue(json,[0],funky_string);
         json = llJsonSetValue(json,[0],funky_string);
         verify("llJsonSetValue with escaped string as value (for MAINT-2698),"+escaped_desc,json,
         verify("llJsonSetValue with escaped string as value (for MAINT-2698),"+escaped_desc,json,
             "[\""+funky_string_escaped+"\",{\""+funky_string_escaped+"\":3,\"b\":\""+funky_string_escaped+"value"+"\"}]");
             "[\""+funky_string_escaped+"\",{\""+funky_string_escaped+"\":3,\"b\":\""+funky_string_escaped+"value"+"\"}]");
 
 
         json = llJsonSetValue(json,[0],funky_string);
         json = llJsonSetValue(json,[0],funky_string);
         verify("llJsonSetValue with escaped string as value (for MAINT-2698),"+escaped_desc,json,
         verify("llJsonSetValue with escaped string as value (for MAINT-2698),"+escaped_desc,json,
             "[\""+funky_string_escaped+"\",{\""+funky_string_escaped+"\":3,\"b\":\""+funky_string_escaped+"value"+"\"}]");
             "[\""+funky_string_escaped+"\",{\""+funky_string_escaped+"\":3,\"b\":\""+funky_string_escaped+"value"+"\"}]");
 
 
         json = llJsonSetValue(json,[0,funky_string], funky_string+"value");
         json = llJsonSetValue(json,[0,funky_string], funky_string+"value");
         verify("llJsonSetValue with escaped string as key's value (for MAINT-2698),"+escaped_desc,json,
         verify("llJsonSetValue with escaped string as key's value (for MAINT-2698),"+escaped_desc,json,
             "[{\""+funky_string_escaped+"\":\""+funky_string_escaped+"value\"},{\""+funky_string_escaped+"\":3,\"b\":\""+funky_string_escaped+"value"+"\"}]");
             "[{\""+funky_string_escaped+"\":\""+funky_string_escaped+"value\"},{\""+funky_string_escaped+"\":3,\"b\":\""+funky_string_escaped+"value"+"\"}]");
 
 
         list l = llJson2List(json);
         list l = llJson2List(json);
         verify_list("llJson2List extracting object containing escaped string (for MAINT-2698),"+escaped_desc, l,
         verify_list("llJson2List extracting object containing escaped string (for MAINT-2698),"+escaped_desc, l,
Line 487: Line 567:
         list n = llJson2List(llList2String(l, 0));
         list n = llJson2List(llList2String(l, 0));
         verify_list("llJson2List extracting escaped strings (for MAINT-2698),"+escaped_desc, n,
         verify_list("llJson2List extracting escaped strings (for MAINT-2698),"+escaped_desc, n,
             [funky_string,funky_string+"value"]);
             [funky_string,funky_string+"value"]);  
 
 
         json = llList2Json(JSON_ARRAY,n);
         json = llList2Json(JSON_ARRAY,n);
         verify("llList2Json from escaped-containing string to array (for MAINT-2698),"+escaped_desc,json,
         verify("llList2Json from escaped-containing string to array (for MAINT-2698),"+escaped_desc,json,
             "[\""+funky_string_escaped+"\",\""+funky_string_escaped+"value\"]");
             "[\""+funky_string_escaped+"\",\""+funky_string_escaped+"value\"]");
 
 
         json = llList2Json(JSON_OBJECT,n);
         json = llList2Json(JSON_OBJECT,n);
         verify("llList2Json from escaped-containing string to object (for MAINT-2698),"+escaped_desc,json,
         verify("llList2Json from escaped-containing string to object (for MAINT-2698),"+escaped_desc,json,
Line 498: Line 578:
     }
     }
}
}
 
 
maint3070()
maint3070()
{
{
Line 505: Line 585:
     verify("Set value 'messag\[e\]'", llJsonSetValue("",["toast"],"messag\[e\]"), "{\"toast\":\"messag[e]\"}");
     verify("Set value 'messag\[e\]'", llJsonSetValue("",["toast"],"messag\[e\]"), "{\"toast\":\"messag[e]\"}");
}
}
 
 
maint3053()
maint3053()
{
{
Line 515: Line 595:
     verify("llJsonSetValue(jT1,[0, 1],\"t\")",llJsonSetValue(jT1,[0, 1],"t"),JSON_INVALID);
     verify("llJsonSetValue(jT1,[0, 1],\"t\")",llJsonSetValue(jT1,[0, 1],"t"),JSON_INVALID);
     verify("llJsonSetValue(jT1,[0, 1, 2, \"t\", 75],\"t\")",llJsonSetValue(jT1,[0, 1, 2, "t", 75],"t"),JSON_INVALID);
     verify("llJsonSetValue(jT1,[0, 1, 2, \"t\", 75],\"t\")",llJsonSetValue(jT1,[0, 1, 2, "t", 75],"t"),JSON_INVALID);
 
 
     string jT2 = "[ [\"A\", \"B\", \"C\"], 2]";
     string jT2 = "[ [\"A\", \"B\", \"C\"], 2]";
     verify("llJsonSetValue(jT2,[0, 3],\"t\")",llJsonSetValue(jT2,[0, 3],"t"),"[[\"A\",\"B\",\"C\",\"t\"],2]");
     verify("llJsonSetValue(jT2,[0, 3],\"t\")",llJsonSetValue(jT2,[0, 3],"t"),"[[\"A\",\"B\",\"C\",\"t\"],2]");
Line 521: Line 601:
     verify("llJsonSetValue(jT2,[0, 1, 0],\"t\")",llJsonSetValue(jT2,[0, 1, 0],"t"),"[[\"A\",[\"t\"],\"C\"],2]");
     verify("llJsonSetValue(jT2,[0, 1, 0],\"t\")",llJsonSetValue(jT2,[0, 1, 0],"t"),"[[\"A\",[\"t\"],\"C\"],2]");
     verify("llJsonSetValue(jT2,[0, 1, 1],\"t\")",llJsonSetValue(jT2,[0, 1, 1],"t"),JSON_INVALID);
     verify("llJsonSetValue(jT2,[0, 1, 1],\"t\")",llJsonSetValue(jT2,[0, 1, 1],"t"),JSON_INVALID);
 
 
     string jT3 = "{\"1\":2}";
     string jT3 = "{\"1\":2}";
     verify("llJsonSetValue(jT3,[\"1\"],\"t\")",llJsonSetValue(jT3,["1"],"t"),"{\"1\":\"t\"}");
     verify("llJsonSetValue(jT3,[\"1\"],\"t\")",llJsonSetValue(jT3,["1"],"t"),"{\"1\":\"t\"}");
     verify("llJsonSetValue(jT3,[\"1\",0],\"t\")",llJsonSetValue(jT3,["1",0],"t"),"{\"1\":[\"t\"]}");
     verify("llJsonSetValue(jT3,[\"1\",0],\"t\")",llJsonSetValue(jT3,["1",0],"t"),"{\"1\":[\"t\"]}");
     verify("llJsonSetValue(jT3,[\"1\",1],\"t\")",llJsonSetValue(jT3,["1",1],"t"),JSON_INVALID);
     verify("llJsonSetValue(jT3,[\"1\",1],\"t\")",llJsonSetValue(jT3,["1",1],"t"),JSON_INVALID);
 
 
     string jGood = "[null, 2]";
     string jGood = "[null, 2]";
     verify("llJsonValueType(jGood, [0])",llJsonValueType(jGood, [0]),JSON_NULL);
     verify("llJsonValueType(jGood, [0])",llJsonValueType(jGood, [0]),JSON_NULL);
     verify("llJsonValueType(jGood, [0, 0])",llJsonValueType(jGood, [0, 0]),JSON_INVALID);
     verify("llJsonValueType(jGood, [0, 0])",llJsonValueType(jGood, [0, 0]),JSON_INVALID);
 
 
     string jBad = "[, 2]";
     string jBad = "[, 2]";
     verify("llJsonValueType(jBad,[0])",llJsonValueType(jBad,[0]),JSON_INVALID);
     verify("llJsonValueType(jBad,[0])",llJsonValueType(jBad,[0]),JSON_INVALID);
Line 536: Line 616:
     verify("llJsonGetValue(jBad,[1, 0, 2, \"t\", 75])",llJsonGetValue(jBad,[1, 0, 2, "t", 75]),JSON_INVALID);
     verify("llJsonGetValue(jBad,[1, 0, 2, \"t\", 75])",llJsonGetValue(jBad,[1, 0, 2, "t", 75]),JSON_INVALID);
}
}
 
 
maint3081()
maint3081()
{
{
     verify("llJsonSetValue blank string",llJsonSetValue("",["test"],""),"{\"test\":\"\"}");
     verify("llJsonSetValue blank string",llJsonSetValue("",["test"],""),"{\"test\":\"\"}");
     verify("llJsonSetValue JSON_NULL",llJsonSetValue("",["test"],JSON_NULL),"{\"test\":null}");    
     verify("llJsonSetValue JSON_NULL",llJsonSetValue("",["test"],JSON_NULL),"{\"test\":null}");
     verify("llJsonGetValue blank string",llJsonGetValue("{\"test\":\"\"}",["test"]),"");
     verify("llJsonGetValue blank string",llJsonGetValue("{\"test\":\"\"}",["test"]),"");
     verify("llJsonGetValue JSON_NULL",llJsonGetValue("{\"test\":null}",["test"]),JSON_NULL);
     verify("llJsonGetValue JSON_NULL",llJsonGetValue("{\"test\":null}",["test"]),JSON_NULL);
Line 546: Line 626:
     verify("Identity (set->get) JSON_NULL",llJsonGetValue(llJsonSetValue("",["test"],JSON_NULL),["test"]),JSON_NULL);
     verify("Identity (set->get) JSON_NULL",llJsonGetValue(llJsonSetValue("",["test"],JSON_NULL),["test"]),JSON_NULL);
}
}
 
 
test_jira_fixes()
test_jira_fixes()
{
{
Line 553: Line 633:
     maint3081();
     maint3081();
}
}
 
 
default
default
{
{
    on_rez(integer start_param)
    {
        llResetScript();
    }
    changed(integer change)
    {
        if (change & (CHANGED_OWNER | CHANGED_INVENTORY))
        {
            llResetScript();
        }
    }
     state_entry()
     state_entry()
     {
     {
         llOwnerSay("Starting tests.");
         run_tests();
        test_types();
        test_get_value();
        test_set_value();
        test_json_to_list();
        test_list_to_json();
        test_strings_with_escaped_chars();
        test_jira_fixes();
        llOwnerSay("Tests complete. Ran " + (string)tests + " tests with " + (string)fails + " failures.");
     }
     }
 
 
     touch_start(integer n)
     touch_start(integer num_detected)
     {
     {
         llOwnerSay("Starting tests.");
         run_tests();
        tests = 0;
        fails = 0;
        test_types();
        test_get_value();
        test_set_value();
        test_json_to_list();
        test_list_to_json();
        test_strings_with_escaped_chars();
        test_jira_fixes();
        llOwnerSay("Tests complete. Ran " + (string)tests + " tests with " + (string)fails + " failures.");
     }
     }
}
}
</lsl>
</lsl>

Revision as of 05:29, 5 October 2013

JSON Test Framework

<lsl> // This is just the framework for creating new tests for LSL JSON functions without the tests.

integer total_number_of_tests; integer total_number_of_fails;

string typeName(string type_flag) {

   if            (type_flag == JSON_INVALID) return "JSON_INVALID";
   /* else */ if (type_flag == JSON_OBJECT)  return "JSON_OBJECT";
   /* else */ if (type_flag == JSON_ARRAY)   return "JSON_ARRAY";
   /* else */ if (type_flag == JSON_NUMBER)  return "JSON_NUMBER";
   /* else */ if (type_flag == JSON_STRING)  return "JSON_STRING";
   /* else */ if (type_flag == JSON_NULL)    return "JSON_NULL";
   /* else */ if (type_flag == JSON_TRUE)    return "JSON_TRUE";
   /* else */ if (type_flag == JSON_FALSE)   return "JSON_FALSE";
   /* else */ if (type_flag == JSON_DELETE)  return "JSON_DELETE";
   /* else ...... type_flag unknown */       return type_flag;

}

string listEntryTypeName(integer type) {

   list list_type_names = [
       "TYPE_INVALID",
       "TYPE_INTEGER",
       "TYPE_FLOAT",
       "TYPE_STRING",
       "TYPE_KEY",
       "TYPE_VECTOR",
       "TYPE_ROTATION"
   ];
   integer length = llGetListLength(list_type_names);
   if (length <= type) return "UNKNOWN_LIST_TYPE";
   /* else */          return llList2String(list_type_names, type);

}

integer max(integer x, integer y) {

   if (x < y) return y;
   /* else */ return x;

}

integer verify(string message, string result, string expected) {

   ++total_number_of_tests;
   if (result != expected)
   {
       ++total_number_of_fails;
       publish("TEST FAILED"
           + "\nexpected: " + typeName(expected)
           + "\nresult: " + typeName(result)
           + "\nJSON string value: " + message
           + "\n ");
       return FALSE;
   }
   /* else */
       return TRUE;

}

verify_list(string message, list result, list expected) {

   integer length_result   = llGetListLength(result);
   integer length_expected = llGetListLength(expected);

// first verify that the list lengths match before looking at the data types

   verify(message + ": list length",
       (string)length_result,
       (string)length_expected);

// the CSV string comparison is probably enough to verify that the values stored are correct // but I want to check data type too

   integer index = max(length_result, length_expected);
   while (--index >= 0)
   {
       verify(message + ": on list entry type comparison on index " + (string)index,
           listEntryTypeName(llGetListEntryType(result, index)),
           listEntryTypeName(llGetListEntryType(expected, index)));
       verify(message + ": on list value comparison on index " + (string)index,
           typeName(llList2String(result, index)),
           typeName(llList2String(expected, index)));
   }
   verify(message + ": llList2CSV comparison",
       llList2CSV(result),
       llList2CSV(expected));

}

run_tests() {

   publish("JSON TESTS: started"
       + "\n ");
   total_number_of_tests = 0;
   total_number_of_fails = 0;

// Add new tests here. // ... // ... // ...

   publish("JSON TESTS: done\n"
       + (string)total_number_of_fails + " of "
       + (string)total_number_of_tests + " tests failed."
       + "\n ");

}

publish(string message) { // if you want to keep it private //

   llOwnerSay(message);

// if you want to make it public // // llSay(PUBLIC_CHANNEL, message);

// if you want to reach the owner wherever she/he is as fast as you can // // key owner = llGetOwner(); // vector ownerSize = llGetAgentSize(owner); // if (ownerSize != ZERO_VECTOR) llRegionSayTo(owner, PUBLIC_CHANNEL, message); // else llInstantMessage(owner, message); }

default {

   on_rez(integer start_param)
   {
       llResetScript();
   }
   changed(integer change)
   {
       if (change & (CHANGED_OWNER | CHANGED_INVENTORY))
       {
           llResetScript();
       }
   }
   state_entry()
   {
       run_tests();
   }
   touch_start(integer num_detected)
   {
       run_tests();
   }

} </lsl>

Full Test Script

<lsl> // This is just the framework for creating new tests for LSL JSON functions without the tests.

integer total_number_of_tests; integer total_number_of_fails;

string typeName(string type_flag) {

   if            (type_flag == JSON_INVALID) return "JSON_INVALID";
   /* else */ if (type_flag == JSON_OBJECT)  return "JSON_OBJECT";
   /* else */ if (type_flag == JSON_ARRAY)   return "JSON_ARRAY";
   /* else */ if (type_flag == JSON_NUMBER)  return "JSON_NUMBER";
   /* else */ if (type_flag == JSON_STRING)  return "JSON_STRING";
   /* else */ if (type_flag == JSON_NULL)    return "JSON_NULL";
   /* else */ if (type_flag == JSON_TRUE)    return "JSON_TRUE";
   /* else */ if (type_flag == JSON_FALSE)   return "JSON_FALSE";
   /* else */ if (type_flag == JSON_DELETE)  return "JSON_DELETE";
   /* else ...... type_flag unknown */       return type_flag;

}

string listEntryTypeName(integer type) {

   list list_type_names = [
       "TYPE_INVALID",
       "TYPE_INTEGER",
       "TYPE_FLOAT",
       "TYPE_STRING",
       "TYPE_KEY",
       "TYPE_VECTOR",
       "TYPE_ROTATION"
   ];
   integer length = llGetListLength(list_type_names);
   if (length <= type) return "UNKNOWN_LIST_TYPE";
   /* else */          return llList2String(list_type_names, type);

}

integer max(integer x, integer y) {

   if (x < y) return y;
   /* else */ return x;

}

integer verify(string message, string result, string expected) {

   ++total_number_of_tests;
   if (result != expected)
   {
       ++total_number_of_fails;
       publish("TEST FAILED"
           + "\nexpected: " + typeName(expected)
           + "\nresult: " + typeName(result)
           + "\nJSON string value: " + message
           + "\n ");
       return FALSE;
   }
   /* else */
       return TRUE;

}

verify_list(string message, list result, list expected) {

   integer length_result   = llGetListLength(result);
   integer length_expected = llGetListLength(expected);

// first verify that the list lengths match before looking at the data types

   verify(message + ": list length",
       (string)length_result,
       (string)length_expected);

// the CSV string comparison is probably enough to verify that the values stored are correct // but I want to check data type too

   integer index = max(length_result, length_expected);
   while (--index >= 0)
   {
       verify(message + ": on list entry type comparison on index " + (string)index,
           listEntryTypeName(llGetListEntryType(result, index)),
           listEntryTypeName(llGetListEntryType(expected, index)));
       verify(message + ": on list value comparison on index " + (string)index,
           typeName(llList2String(result, index)),
           typeName(llList2String(expected, index)));
   }
   verify(message + ": llList2CSV comparison",
       llList2CSV(result),
       llList2CSV(expected));

}

run_tests() {

   publish("JSON TESTS: started"
       + "\n ");
   total_number_of_tests = 0;
   total_number_of_fails = 0;
   test_types();
   test_get_value();
   test_set_value();
   test_json_to_list();
   test_list_to_json();
   test_strings_with_escaped_chars();
   test_jira_fixes();
   publish("JSON TESTS: done\n"
       + (string)total_number_of_fails + " of "
       + (string)total_number_of_tests + " tests failed."
       + "\n ");

}

publish(string message) { // if you want to keep it private //

   llOwnerSay(message);

// if you want to make it public // // llSay(PUBLIC_CHANNEL, message);

// if you want to reach the owner wherever she/he is as fast as you can // // key owner = llGetOwner(); // vector ownerSize = llGetAgentSize(owner); // if (ownerSize != ZERO_VECTOR) llRegionSayTo(owner, PUBLIC_CHANNEL, message); // else llInstantMessage(owner, message); }

test_types() {

   verify("Type of string",llJsonValueType("\"test\"",[]),JSON_STRING);
   verify("Type of string, unquoted",llJsonValueType("test",[]),JSON_INVALID);
   verify("Type of invalid",llJsonValueType("test",[]),JSON_INVALID);
   verify("Type of integer",llJsonValueType((string)12,[]),JSON_NUMBER);
   verify("Type of float",llJsonValueType((string)12.3,[]),JSON_NUMBER);
   verify("Type of Inf (is unsupported by JSON standard)",llJsonValueType("Inf",[]),JSON_INVALID);
   verify("Type of NaN (is unsupported by JSON standard)",llJsonValueType("NaN",[]),JSON_INVALID);
   verify("Type of number",llJsonValueType("-123.4e-5",[]),JSON_NUMBER);
   verify("Type of object",llJsonValueType("{\"a\":\"b\"}",[]),JSON_OBJECT);

// Expected to be OBJECT, since we don't do deep validation on input // verify("Type of object, invalid/unquoted key",llJsonValueType("{a:\"b\"}",[]),JSON_INVALID); // Expected to be OBJECT, since we don't do deep validation on input // verify("Type of object, invalid/unquoted value",llJsonValueType("{\"a\":b}",[]),JSON_INVALID);

   verify("Type of array",llJsonValueType("[1,2,3]",[]),JSON_ARRAY);
   verify("Type of array padded front",llJsonValueType(" [1,2,3]",[]),JSON_ARRAY);
   verify("Type of array padded back",llJsonValueType("[1,2,3] ",[]),JSON_ARRAY);
   verify("Type of array padded",llJsonValueType(" [1,2,3] ",[]),JSON_ARRAY);
   verify("Type of true",llJsonValueType("true",[]),JSON_TRUE);
   verify("Type of false",llJsonValueType("false",[]),JSON_FALSE);
   verify("Type of null",llJsonValueType("null",[]),JSON_NULL);

// test traversal of llJsonValueType

   string json = "[[1,2,3],{\"a\":3,\"b\":[true,\"test\",6],\"c\":\"true\",\"d\":false}]";
   verify("Type of [0]",llJsonValueType(json,[0]),JSON_ARRAY);
   verify("Type of [0,1]",llJsonValueType(json,[0,1]),JSON_NUMBER);
   verify("Type of [1]",llJsonValueType(json,[1]),JSON_OBJECT);
   verify("Type of [1,\"b\"]",llJsonValueType(json,[1,"b"]),JSON_ARRAY);
   verify("Type of [1,\"b\",0]",llJsonValueType(json,[1,"b",0]),JSON_TRUE);
   verify("Type of [1,\"b\",1]",llJsonValueType(json,[1,"b",1]),JSON_STRING);
   verify("Type of [1,\"b\",2]",llJsonValueType(json,[1,"b",2]),JSON_NUMBER);
   verify("Type of [1,\"c\"]",llJsonValueType(json,[1,"c"]),JSON_STRING);
   verify("Type of [1,\"d\"]",llJsonValueType(json,[1,"d"]),JSON_FALSE);
   verify("Type of [3] (invalid index at level 0)",llJsonValueType(json,[3]),JSON_INVALID);
   verify("Type of [-1] (invalid index at level 0)",llJsonValueType(json,[-1]),JSON_INVALID);
   verify("Type of [1,\"c\",3] (invalid index at level 1), MAINT-2670",llJsonValueType(json,[1,"c",3]),JSON_INVALID);
   verify("Type of [1,\"b\",3] (invalid index at level 2)",llJsonValueType(json,[1,"b",3]),JSON_INVALID);
   verify("Type of [1,\"b\",2,0,1] (invalid index at level 3) MAINT-2670",llJsonValueType(json,[1,"b",2, 0, 1]),JSON_INVALID);

// some invalid cases where keys are uncoded

   json = "[[1,2,3],{a:3,b:[true,\"test\",6],c:\"true\",\"d\":false}]";
   verify("Type of [1,\"a\"] where key is unquoted",llJsonValueType(json,[1,"a"]),JSON_INVALID);
   verify("Type of [1,\"b\"] where key is unquoted",llJsonValueType(json,[1,"b"]),JSON_INVALID);
   verify("Type of [1,\"c\"] where key is unquoted",llJsonValueType(json,[1,"c"]),JSON_INVALID);

}

test_get_value() {

   string json = "[[1,2,3,4.0],{\"a\":3,\"b\":[true,\"test\",6]}]";
   verify("llJsonGetValue [0]",llJsonGetValue(json,[0]),"[1,2,3,4.0]");
   verify("llJsonGetValue [0,1]",llJsonGetValue(json,[0,1]),"2");
   verify("llJsonGetValue [1]",llJsonGetValue(json,[1]),"{\"a\":3,\"b\":[true,\"test\",6]}");
   verify("llJsonGetValue [1,\"b\"]",llJsonGetValue(json,[1,"b"]),"[true,\"test\",6]");
   verify("llJsonGetValue [1,\"b\",0]",llJsonGetValue(json,[1,"b",0]),JSON_TRUE);
   verify("llJsonGetValue [1,\"b\",1]",llJsonGetValue(json,[1,"b",1]),"test");
   verify("llJsonGetValue [1,\"b\",2]",llJsonGetValue(json,[1,"b",2]),"6");
   verify("llJsonGetValue [0,3]",llJsonGetValue(json,[0,3]), "4.0");
   verify("llJsonGetValue [2] (invalid index at level 0)",llJsonGetValue(json,[2]),JSON_INVALID);
   verify("llJsonGetValue [-1] (invalid index at level 0)",llJsonGetValue(json,[-1]),JSON_INVALID);
   verify("llJsonGetValue [0,4] (invalid index within array)",llJsonGetValue(json,[0,4]),JSON_INVALID);
   verify("llJsonGetValue [\"f\"] (look for missing object within array, depth=0) MAINT-2671",llJsonGetValue(json,["f"]),JSON_INVALID);
   verify("llJsonGetValue [0,\"f\"] (look for missing object within array, depth=1) MAINT-2671",llJsonGetValue(json,[0,"f"]),JSON_INVALID);
   verify("llJsonGetValue [1,2] (specify index within object - disallowed)",llJsonGetValue(json,[1,2]),JSON_INVALID);

// invalid input json - missing quotes around 'a' and 'test'

   json = "[[1,2,3,4.0],{a:3,\"b\":[true,test,6]}]";
   verify("llJsonGetValue [1,\"b\",1], unquoted/invalid string value",llJsonGetValue(json,[1,"b",1]),JSON_INVALID);
   verify("llJsonGetValue [1,\"a\"], unquoted/invalid string for key",llJsonGetValue(json,[1,"a"]),JSON_INVALID);

}

test_set_value() { // Test building from scratch

   string json;
   json = llJsonSetValue(json,[0,0],(string)1);
   verify("llJsonSetValue build json",json,"1");
   json = llJsonSetValue(json,[0,1],(string)2);
   verify("llJsonSetValue build json",json,"1,2");
   json = llJsonSetValue(json,[0,2],(string)3);
   verify("llJsonSetValue build json",json,"1,2,3");
   json = llJsonSetValue(json,[1,"a"],(string)3);
   verify("llJsonSetValue build json",json,"[[1,2,3],{\"a\":3}]");
   json = llJsonSetValue(json,[1,"b",0],JSON_TRUE);
   verify("llJsonSetValue build json",json,"[[1,2,3],{\"a\":3,\"b\":[true]}]");
   json = llJsonSetValue(json,[1,"b",1],"test");
   verify("llJsonSetValue build json",json,"[[1,2,3],{\"a\":3,\"b\":[true,\"test\"]}]");
   json = llJsonSetValue(json,[1,"b",2],"6");
   verify("llJsonSetValue completed json",json,"[[1,2,3],{\"a\":3,\"b\":[true,\"test\",6]}]");

// Test replacing

   json = llJsonSetValue(json,[1,"b",1],"foo");
   verify("llJsonSetValue completed json",json,"[[1,2,3],{\"a\":3,\"b\":[true,\"foo\",6]}]");
   json = llJsonSetValue(json,[1,"b"],JSON_TRUE);
   verify("llJsonSetValue completed json, true",json,"[[1,2,3],{\"a\":3,\"b\":true}]");
   json = llJsonSetValue(json,[1,"b"],"true");
   verify("llJsonSetValue completed json, alt true",json,"[[1,2,3],{\"a\":3,\"b\":true}]");
   json = llJsonSetValue(json,[1,0,0],JSON_FALSE);
   verify("llJsonSetValue completed json",json,"[[1,2,3],false]");

// Test appending

   json = llJsonSetValue("[[1,2,3],{\"a\":3,\"b\":[true,\"test\",6]}]",[0,JSON_APPEND], "4.0");
   verify("llJsonSetValue append to first array",json,"[[1,2,3,4.0],{\"a\":3,\"b\":[true,\"test\",6]}]");
   json = llJsonSetValue(json,[1,"b",JSON_APPEND], "5.0");
   verify("llJsonSetValue append to array withhin object",json,"[[1,2,3,4.0],{\"a\":3,\"b\":[true,\"test\",6,5.0]}]");
   json = llJsonSetValue(json,[JSON_APPEND], "6.0");
   verify("llJsonSetValue append to outer array",json,"[[1,2,3,4.0],{\"a\":3,\"b\":[true,\"test\",6,5.0]},6.0]");
   json = llJsonSetValue("[]",[JSON_APPEND], "\"alone\"");
   verify("llJsonSetValue append to empty array (MAINT-2684)",json,"[\"alone\"]");
   json = llJsonSetValue("[]",[1], "\"alone\"");
   verify("llJsonSetValue append to empty array at invalid index (MAINT-2684)",json,JSON_INVALID);
   json = llJsonSetValue("[]",[0], "\"alone\"");
   verify("llJsonSetValue append to empty array at first index (MAINT-2684)",json,"[\"alone\"]");

// Test deleting

   json = "[[1,2,3],{\"a\":3,\"b\":[true,\"test\",6,null]}]";
   json = llJsonSetValue(json,[1,"b",1],JSON_DELETE);
   verify("llJsonSetValue deleting string in middle of array",json,"[[1,2,3],{\"a\":3,\"b\":[true,6,null]}]");
   json = llJsonSetValue(json,[1,"b",2],JSON_DELETE);
   verify("llJsonSetValue deleting null at end of array",json,"[[1,2,3],{\"a\":3,\"b\":[true,6]}]");
   json = llJsonSetValue(json,[1,"b"],JSON_DELETE);
   verify("llJsonSetValue deleting key-value",json,"[[1,2,3],{\"a\":3}]");
   json = llJsonSetValue(json,[1],JSON_DELETE);
   verify("llJsonSetValue deleting object in array",json,"1,2,3");
   json = "[[1,2,3],4]";
   json = llJsonSetValue(json,[0],JSON_DELETE);
   verify("llJsonSetValue deleting array (which is first index in array)",json,"[4]");
   json = llJsonSetValue(json,[0],JSON_DELETE);
   verify("llJsonSetValue deleting last element in array",json,"[]");
   json = "1";
   json = llJsonSetValue(json,[0,0],JSON_DELETE);
   verify("llJsonSetValue deleting last element in array",json,"[[]]");
   json = llJsonSetValue(json,[0],JSON_DELETE);
   verify("llJsonSetValue deleting array within array",json,"[]");

// Test failures in deleting

   json = "[[1,2,3],{\"a\":3,\"b\":[true,\"test\",6,null]}]";
   verify("llJsonSetValue deleting undefined key-value in object",llJsonSetValue(json,[1,"d"],JSON_DELETE),JSON_INVALID);
   verify("llJsonSetValue deleting out-of-range index in array",llJsonSetValue(json,[2],JSON_DELETE),JSON_INVALID);
   verify("llJsonSetValue deleting depth within object that doesn't exist",llJsonSetValue(json,[1,"a","unicorn"],JSON_DELETE),JSON_INVALID);
   verify("llJsonSetValue deleting depth within array that doesn't exist",llJsonSetValue(json,[0,1,1],JSON_DELETE),JSON_INVALID);

// this is the only failure mode that should exist.

   json = "[[1,2,3],{\"a\":3,\"b\":[true,\"foo\",6]}]";
   json = llJsonSetValue(json,[3],JSON_FALSE);
   verify("llJsonSetValue fail to insert data into invalid array index (MAINT-2675)",json,JSON_INVALID);

}



test_json_to_list() {

   list l = llJson2List("[[1,2,3],{\"a\":3,\"b\":[true,\"test\",6]}]");
   verify_list("llJson2List first",l,["[1,2,3]","{\"a\":3,\"b\":[true,\"test\",6]}"]);
   list n = llJson2List(llList2String(l,0));
   verify_list("llJson2List l,0",n,[1,2,3]);
   n = llJson2List(llList2String(l,1));
   verify_list("llJson2List l,0",n,["a",3,"b","[true,\"test\",6]"]);
   n = llJson2List(llList2String(n,3));
   verify_list("llJson2List l,0",n,[JSON_TRUE, "test", 6]);
   n = llJson2List(llList2String(n,1));
   verify_list("llJson2List l,0",n,["test"]);
   n = llJson2List("");
   verify_list("Empty JSON string becomes empty list",n,[]);
   n = llJson2List("[]");
   verify_list("Empty JSON array becomes empty list (MAINT-2678)",n,[]);
   n = llJson2List("{}");
   verify_list("Empty JSON object becomes empty list (MAINT-2678)",n,[]);
   n = llJson2List("Non-JSON string, with comma");
   verify_list("llJson2List for non-JSON string is stored as a single object",n,["Non-JSON string, with comma"]);
   n = llJson2List("[malformed}");
   verify_list("llJson2List, malformed input",n,["[malformed}"]);

}

test_list_to_json() { // test objects

   string json = llList2Json(JSON_OBJECT,["a",1,"b",2.5,"c","test","d","true","e","[1,2,3]"]);
   verify("llList2Json, json object",json,"{\"a\":1,\"b\":2.500000,\"c\":\"test\",\"d\":true,\"e\":[1,2,3]}");

// test arrays

   json = llList2Json(JSON_ARRAY,[1,2.5,"test","true","[1,2,3]"]);
   verify("llList2Json, json array",json,"[1,2.500000,\"test\",true,[1,2,3]]");

// test arrays

   json = llList2Json(JSON_ARRAY,[1,2.5,"test",JSON_TRUE,"[1,2,3]"]);
   verify("llList2Json, json array, alternative true representation",json,"[1,2.500000,\"test\",true,[1,2,3]]");

// test objects, with empty input

   json = llList2Json(JSON_OBJECT,[]);
   verify("llList2Json, json object with empty input (MAINT-2681)",json,"{}");

// test arrays, with empty input

   json = llList2Json(JSON_ARRAY,[]);
   verify("llList2Json, json array with empty input (MAINT-2681)",json,"[]");

// test objects which are truncated

   json = llList2Json(JSON_OBJECT,["a",1,"b",2.5,"c","test","d","true","e"]);
   verify("llList2Json, json object, truncated",json,JSON_INVALID);

// test objects which has a non-string identifier somewhere

   json = llList2Json(JSON_OBJECT,["a",1,TRUE,2.5,"c","test","d","true","e"]);
   verify("llList2Json, json object, non-string in one of the first stride values",json,JSON_INVALID);

// test invalid type

   json = llList2Json("foo",["a",1,"b",2.5,"c","test","d","true","e","[1,2,3]"]);
   verify("llList2Json, json invalid type",json,JSON_INVALID);

}

test_strings_with_escaped_chars() {

   list escaped_pairs = [
       "funky\"string", "funky\\\"string", "quote in middle",
       "funkystr\"ing", "funkystr\\\"ing", "quote in middle, other position",

// note that we have to double-up backslashes to assign them to strings..

       "funky\\string", "funky\\\\string", "backslashes in middle",
       "\\funkystring", "\\\\funkystring", "backslashes at beginning",
       "funky\nstring", "funky\\nstring", "newline in string",
       "funky/string", "funky\\/string", "forward slash in string",

// TAB (\t) fails, because it seems that LSL automatically converts any tab into 4 consecutive spaces. // * "funky\tstring", "funky\\tstring", "tab in string", // these cases fail; it seems that LSL doesn't support these characters, and strips the '\' // * "funky\bstring", "funky\\\bstring", "backspace in middle", // * "funky\fstring", "funky\\\fstring", "form feed in middle", // * "funky\rstring", "funky\\rstring", "carriage return in string", // note that the following case can't be supported, since strings starting with \" can't be escaped // * "\"funkystring", "\\\"funkystring", "quote in beginning",

       "vanilla string", "vanilla string", "nothing that needs to be escaped.."
   ];
   integer i;
   for(i=0; i < llGetListLength(escaped_pairs); i+=3)
   {
       string funky_string = llList2String(escaped_pairs, i);
       string funky_string_escaped = llList2String(escaped_pairs, i+1);
       string escaped_desc = " '" + llList2String(escaped_pairs, i+2) + "'";
       verify("Type of string with escaped char (for MAINT-2698),"+escaped_desc,llJsonValueType("\""+funky_string_escaped+"\"",[]),JSON_STRING);
       string json = "[[1,2,3,4.0],{\""+funky_string_escaped+"\":3,\"b\":\""+funky_string_escaped+"value"+"\"}]";
       verify("llJsonGetValue [1,\""+funky_string_escaped+"\"] (for MAINT-2698),"+escaped_desc,
           llJsonGetValue(json,[1,funky_string]),"3");
       verify("llJsonGetValue [1,\"b\"] (for MAINT-2698),"+escaped_desc,llJsonGetValue(json,[1,"b"]),funky_string+"value");

// llSay(PUBLIC_CHANNEL, "DEBUG: '" + llEscapeURL(json) + "' is input for test " + escaped_desc);

       json = llJsonSetValue(json,[0],funky_string);
       verify("llJsonSetValue with escaped string as value (for MAINT-2698),"+escaped_desc,json,
           "[\""+funky_string_escaped+"\",{\""+funky_string_escaped+"\":3,\"b\":\""+funky_string_escaped+"value"+"\"}]");
       json = llJsonSetValue(json,[0],funky_string);
       verify("llJsonSetValue with escaped string as value (for MAINT-2698),"+escaped_desc,json,
           "[\""+funky_string_escaped+"\",{\""+funky_string_escaped+"\":3,\"b\":\""+funky_string_escaped+"value"+"\"}]");
       json = llJsonSetValue(json,[0,funky_string], funky_string+"value");
       verify("llJsonSetValue with escaped string as key's value (for MAINT-2698),"+escaped_desc,json,
           "[{\""+funky_string_escaped+"\":\""+funky_string_escaped+"value\"},{\""+funky_string_escaped+"\":3,\"b\":\""+funky_string_escaped+"value"+"\"}]");
       list l = llJson2List(json);
       verify_list("llJson2List extracting object containing escaped string (for MAINT-2698),"+escaped_desc, l,
           ["{\""+funky_string_escaped+"\":\""+funky_string_escaped+"value\"}","{\""+funky_string_escaped+"\":3,\"b\":\""+funky_string_escaped+"value"+"\"}"]);
       list n = llJson2List(llList2String(l, 0));
       verify_list("llJson2List extracting escaped strings (for MAINT-2698),"+escaped_desc, n,
           [funky_string,funky_string+"value"]); 
       json = llList2Json(JSON_ARRAY,n);
       verify("llList2Json from escaped-containing string to array (for MAINT-2698),"+escaped_desc,json,
           "[\""+funky_string_escaped+"\",\""+funky_string_escaped+"value\"]");
       json = llList2Json(JSON_OBJECT,n);
       verify("llList2Json from escaped-containing string to object (for MAINT-2698),"+escaped_desc,json,
           "{\""+funky_string_escaped+"\":\""+funky_string_escaped+"value\"}");
   }

}

maint3070() {

   verify("Set value 'messa[g]e'", llJsonSetValue("",["toast"],"messa[g]e"), "{\"toast\":\"messa[g]e\"}");
   verify("Set value 'messag[e]'", llJsonSetValue("",["toast"],"messag[e]"), "{\"toast\":\"messag[e]\"}");
   verify("Set value 'messag\[e\]'", llJsonSetValue("",["toast"],"messag\[e\]"), "{\"toast\":\"messag[e]\"}");

}

maint3053() {

   string jT1 = "[1, 2]"; // A JSON array
   verify("llJsonSetValue(jT1,[2],\"t\")",llJsonSetValue(jT1,[2],"t"),"[1,2,\"t\"]");
   verify("llJsonSetValue(jT1,[3],\"t\")",llJsonSetValue(jT1,[3],"t"),JSON_INVALID);
   verify("llJsonSetValue(jT1,[0, 0],\"t\")",llJsonSetValue(jT1,[0, 0],"t"),"[[\"t\"],2]");
   verify("llJsonSetValue(jT1,[0, 0, 2, \"t\", 75],\"t\")",llJsonSetValue(jT1,[0, 0, 2, "t", 75],"t"),JSON_INVALID);
   verify("llJsonSetValue(jT1,[0, 1],\"t\")",llJsonSetValue(jT1,[0, 1],"t"),JSON_INVALID);
   verify("llJsonSetValue(jT1,[0, 1, 2, \"t\", 75],\"t\")",llJsonSetValue(jT1,[0, 1, 2, "t", 75],"t"),JSON_INVALID);
   string jT2 = "[ [\"A\", \"B\", \"C\"], 2]";
   verify("llJsonSetValue(jT2,[0, 3],\"t\")",llJsonSetValue(jT2,[0, 3],"t"),"[[\"A\",\"B\",\"C\",\"t\"],2]");
   verify("llJsonSetValue(jT2,[0, 4],\"t\")",llJsonSetValue(jT2,[0, 4],"t"),JSON_INVALID);
   verify("llJsonSetValue(jT2,[0, 1, 0],\"t\")",llJsonSetValue(jT2,[0, 1, 0],"t"),"[[\"A\",[\"t\"],\"C\"],2]");
   verify("llJsonSetValue(jT2,[0, 1, 1],\"t\")",llJsonSetValue(jT2,[0, 1, 1],"t"),JSON_INVALID);
   string jT3 = "{\"1\":2}";
   verify("llJsonSetValue(jT3,[\"1\"],\"t\")",llJsonSetValue(jT3,["1"],"t"),"{\"1\":\"t\"}");
   verify("llJsonSetValue(jT3,[\"1\",0],\"t\")",llJsonSetValue(jT3,["1",0],"t"),"{\"1\":[\"t\"]}");
   verify("llJsonSetValue(jT3,[\"1\",1],\"t\")",llJsonSetValue(jT3,["1",1],"t"),JSON_INVALID);
   string jGood = "[null, 2]";
   verify("llJsonValueType(jGood, [0])",llJsonValueType(jGood, [0]),JSON_NULL);
   verify("llJsonValueType(jGood, [0, 0])",llJsonValueType(jGood, [0, 0]),JSON_INVALID);
   string jBad = "[, 2]";
   verify("llJsonValueType(jBad,[0])",llJsonValueType(jBad,[0]),JSON_INVALID);
   verify("llJsonValueType(jBad,[0, 0, 2, \"t\", 75])",llJsonValueType(jBad,[0, 0, 2, "t", 75]),JSON_INVALID);
   verify("llJsonGetValue(jBad,[1, 0, 2, \"t\", 75])",llJsonGetValue(jBad,[1, 0, 2, "t", 75]),JSON_INVALID);

}

maint3081() {

   verify("llJsonSetValue blank string",llJsonSetValue("",["test"],""),"{\"test\":\"\"}");
   verify("llJsonSetValue JSON_NULL",llJsonSetValue("",["test"],JSON_NULL),"{\"test\":null}");  
   verify("llJsonGetValue blank string",llJsonGetValue("{\"test\":\"\"}",["test"]),"");
   verify("llJsonGetValue JSON_NULL",llJsonGetValue("{\"test\":null}",["test"]),JSON_NULL);
   verify("Identity (set->get) blank string",llJsonGetValue(llJsonSetValue("",["test"],""),["test"]),"");
   verify("Identity (set->get) JSON_NULL",llJsonGetValue(llJsonSetValue("",["test"],JSON_NULL),["test"]),JSON_NULL);

}

test_jira_fixes() {

   maint3070();
   maint3053();
   maint3081();

}

default {

   on_rez(integer start_param)
   {
       llResetScript();
   }
   changed(integer change)
   {
       if (change & (CHANGED_OWNER | CHANGED_INVENTORY))
       {
           llResetScript();
       }
   }
   state_entry()
   {
       run_tests();
   }
   touch_start(integer num_detected)
   {
       run_tests();
   }

} </lsl>