// Tests illegal UTF-8 sequences

const Cc = Components.Constructor;
const Ci = Components.interfaces;
const Cu = Components.utils;

Cu.import("resource://gre/modules/Services.jsm");

const tests = [
{ inStrings: ["%80",                 // Illegal or incomplete sequences
              "%8f",
              "%90",
              "%9f",
              "%a0",
              "%bf",
              "%c0",
              "%c1",
              "%c2",
              "%df",
              "%e0",
              "%e0%a0",
              "%e0%bf",
              "%ed%80",
              "%ed%9f",
              "%ef",
              "%ef%bf",
              "%f0",
              "%f0%90",
              "%f0%90%80",
              "%f0%90%bf",
              "%f0%bf",
              "%f0%bf%80",
              "%f0%bf%bf",
              "%f4",
              "%f4%80",
              "%f4%80%80",
              "%f4%80%bf",
              "%f4%8f",
              "%f4%8f%80",
              "%f4%8f%bf",
              "%f5",
              "%f7",
              "%f8",
              "%fb",
              "%fc",
              "%fd"],
  expected: "ABC\ufffdXYZ" },

{ inStrings: ["%c0%af",              // Illegal bytes in 2-octet
              "%c1%af"],             //  sequences
  expected: "ABC\ufffd\ufffdXYZ" },

{ inStrings: ["%e0%80%80",           // Illegal bytes in 3-octet
              "%e0%80%af",           //  sequences
              "%e0%9f%bf",
                                     // long surrogates
              "%ed%a0%80",           // D800
              "%ed%ad%bf",           // DB7F
              "%ed%ae%80",           // DB80
              "%ed%af%bf",           // DBFF
              "%ed%b0%80",           // DC00
              "%ed%be%80",           // DF80
              "%ed%bf%bf"],          // DFFF
  expected: "ABC\ufffd\ufffd\ufffdXYZ" },

{ inStrings: ["%f0%80%80%80",        // Illegal bytes in 4-octet
              "%f0%80%80%af",        //  sequences
              "%f0%8f%bf%bf",
              "%f4%90%80%80",
              "%f4%bf%bf%bf",
              "%f5%80%80%80",
              "%f7%bf%bf%bf"],
  expected: "ABC\ufffd\ufffd\ufffd\ufffdXYZ" },

{ inStrings: ["%f8%80%80%80%80",     // Illegal bytes in 5-octet
              "%f8%80%80%80%af",     //  sequences
              "%fb%bf%bf%bf%bf"],
  expected: "ABC\ufffd\ufffd\ufffd\ufffd\ufffdXYZ" },

                                     // Surrogate pairs
{ inStrings: ["%ed%a0%80%ed%b0%80",  // D800 DC00
              "%ed%a0%80%ed%bf%bf",  // D800 DFFF
              "%ed%ad%bf%ed%b0%80",  // DB7F DC00
              "%ed%ad%bf%ed%bf%bf",  // DB7F DFFF
              "%ed%ae%80%ed%b0%80",  // DB80 DC00
              "%ed%ae%80%ed%bf%bf",  // DB80 DFFF
              "%ed%af%bf%ed%b0%80",  // DBFF DC00
              "%ed%ad%bf%ed%bf%bf",  // DBFF DFFF
              "%fc%80%80%80%80%80",  // Illegal bytes in 6-octet
              "%fc%80%80%80%80%af",  //  sequences
              "%fd%bf%bf%bf%bf%bf"],
  expected: "ABC\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdXYZ" },
];


function testCaseInputStream(inStr, expected)
{
  var dataURI = "data:text/plain; charset=UTF-8,ABC" + inStr + "XYZ"
  dump(inStr + "==>");

  var IOService = Cc("@mozilla.org/network/io-service;1",
		     "nsIIOService");
  var ConverterInputStream =
      Cc("@mozilla.org/intl/converter-input-stream;1",
	 "nsIConverterInputStream",
	 "init");

  var ios = new IOService();
  var channel = ios.newChannel2(dataURI,
                                "",
                                null,
                                null,      // aLoadingNode
                                Services.scriptSecurityManager.getSystemPrincipal(),
                                null,      // aTriggeringPrincipal
                                Ci.nsILoadInfo.SEC_NORMAL,
                                Ci.nsIContentPolicy.TYPE_OTHER);
  var testInputStream = channel.open();
  var testConverter = new ConverterInputStream(testInputStream,
					       "UTF-8",
					       16,
					       0xFFFD);

  if (!(testConverter instanceof Ci.nsIUnicharLineInputStream))
      throw "not line input stream";

  var outStr = "";
  var more;
  do {
      // read the line and check for eof
      var line = {};
      more = testConverter.readLine(line);
      outStr += line.value;
  } while (more);

  dump(outStr + "; expected=" + expected + "\n");
  do_check_eq(outStr, expected);
  do_check_eq(outStr.length, expected.length);
}

function run_test() {
  for (var t of tests) {
    for (var inStr of t.inStrings) {
      testCaseInputStream(inStr, t.expected);
    }
  }
}
