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

From Second Life Wiki
Jump to navigation Jump to search
(Add tests for JSON_DELETE)
m (readabiliteeeh++)
Line 1: Line 1:
== JSON Test Framework ==
== JSON Test Framework ==
<lsl>
<lsl>
// This is just the framework for creating new tests for LSL JSON functions without the tests.
// This is just the framework for creating new tests for LSL JSON functions without the tests.
integer tests;
 
integer fails;
integer total_number_of_tests;
   
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()
run_tests()
{
{
     // Add new tests here.
     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
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();
         run_tests();
        llOwnerSay("Tests complete. Ran " + (string)tests + " tests with " + (string)fails + " failures.");
     }
     }
         
 
     touch_start(integer n)
     touch_start(integer num_detected)
     {
     {
        llOwnerSay("Starting tests.");
        tests = 0;
        fails = 0;
         run_tests();
         run_tests();
        llOwnerSay("Tests complete. Ran " + (string)tests + " tests with " + (string)fails + " failures.");
     }
     }
}
}

Revision as of 05:16, 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 test script verifies expected behavior or LSL JSON functions

integer tests; integer fails;

string typeName(string type) {

   if (type == JSON_NUMBER) return "JSON_NUMBER";
   if (type == JSON_INVALID) return "JSON_INVALID";
   if (type == JSON_OBJECT) return "JSON_OBJECT";
   if (type == JSON_ARRAY) return "JSON_ARRAY";
   if (type == JSON_TRUE) return "JSON_TRUE";
   if (type == JSON_FALSE) return "JSON_FALSE";
   if (type == JSON_STRING) return "JSON_STRING";
   if (type == JSON_NULL) return "JSON_NULL";
   return type;

}

string listEntryTypeName(integer type) {

   list list_type_names = ["TYPE_INVALID", "TYPE_INTEGER", "TYPE_FLOAT", "TYPE_STRING", "TYPE_KEY", "TYPE_VECTOR", "TYPE_ROTATION"];
   if(type >= llGetListLength(list_type_names)) return "UNKNOWN_LIST_TYPE";
   return llList2String(list_type_names, type);

}

integer max(integer x, integer y) {

   if( y > x ) return y;
   return x;

}


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

   ++tests;
   if (expected != result)
   {
       llOwnerSay("FAIL test " + message  + ", expected: " + typeName(expected) + ", result: " + typeName(result));
       ++fails;
       return 0;
   }
   return 1;

}

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

   // First verify that the list lengths match before looking at the data types
   verify(message + ": list length", (string)llGetListLength(result), (string)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.
  integer i = max(llGetListLength(expected), llGetListLength(result));
  while(--i >= 0)
  {
       verify(message + ": on list entry type comparison on index " + (string)i, listEntryTypeName(llGetListEntryType(result, i)),
           listEntryTypeName(llGetListEntryType(expected, i)));
       verify(message + ": on list value comparison on index " + (string)i, typeName(llList2String(result, i)), typeName(llList2String(expected, i)));
   }
   verify(message + ": llList2CSV comparison",llList2CSV(result),llList2CSV(expected));

}

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(0, "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 {

   state_entry()
   {
       llOwnerSay("Starting 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)
   {
       llOwnerSay("Starting 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>