// Copyright 2012 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

goog.provide('goog.labs.format.csvTest');

goog.require('goog.labs.format.csv');
goog.require('goog.labs.format.csv.ParseError');
goog.require('goog.object');
goog.require('goog.testing.asserts');
goog.require('goog.testing.jsunit');

goog.setTestOnly('goog.labs.format.csvTest');


function testGoldenPath() {
  assertObjectEquals(
      [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']],
      goog.labs.format.csv.parse('a,b,c\nd,e,f\ng,h,i\n'));
  assertObjectEquals(
      [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']],
      goog.labs.format.csv.parse('a,b,c\r\nd,e,f\r\ng,h,i\r\n'));
}

function testGoldenPathWithSpaceAsCustomDelimiter() {
  assertObjectEquals(
      [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']],
      goog.labs.format.csv.parse('a b c\nd e f\ng h i\n', undefined, ' '));
  assertObjectEquals(
      [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']],
      goog.labs.format.csv.parse(
          'a b c\r\nd e f\r\ng h i\r\n', undefined, ' '));
}

function testGoldenPathWithEmptyStringAsCustomDelimiterUsesComma() {
  assertObjectEquals(
      [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']],
      goog.labs.format.csv.parse('a,b,c\nd,e,f\ng,h,i\n', undefined, ''));
  assertObjectEquals(
      [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']],
      goog.labs.format.csv.parse('a,b,c\r\nd,e,f\r\ng,h,i\r\n', undefined, ''));
}

function testBadDelimitersNewLine() {
  var e = assertThrows(function() {
    goog.labs.format.csv.parse('a,b,c\r\nd,e,f\r\ng,h,i\r\n', undefined, '\n');
  });
  assertEquals(
      'Assertion failed: Cannot use newline or carriage return has delimiter.',
      e.message);
}

function testBadDelimitersCarriageReturn() {
  var e = assertThrows(function() {
    goog.labs.format.csv.parse('a,b,c\r\nd,e,f\r\ng,h,i\r\n', undefined, '\r');
  });
  assertEquals(
      'Assertion failed: Cannot use newline or carriage return has delimiter.',
      e.message);
}

function testBadDelimitersTwoCharacter() {
  var e = assertThrows(function() {
    goog.labs.format.csv.parse('a,b,c\r\nd,e,f\r\ng,h,i\r\n', undefined, 'aa');
  });
  assertEquals(
      'Assertion failed: Delimiter must be a single character.', e.message);
}

function testNoCrlfAtEnd() {
  assertObjectEquals(
      [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']],
      goog.labs.format.csv.parse('a,b,c\nd,e,f\ng,h,i'));
}

function testQuotes() {
  assertObjectEquals(
      [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']],
      goog.labs.format.csv.parse('a,"b",c\n"d","e","f"\ng,h,"i"'));
  assertObjectEquals(
      [['a', 'b, as in boy', 'c'], ['d', 'e', 'f']],
      goog.labs.format.csv.parse('a,"b, as in boy",c\n"d","e","f"\n'));
  // Make sure empty elements after quoted elements are parsed.
  assertObjectEquals(
      [['a', 'b, as in boy', ''], ['d', 'e', 'f']],
      goog.labs.format.csv.parse('a,"b, as in boy",\nd,e,f\n'));
}

function testEmbeddedCrlfs() {
  assertObjectEquals(
      [['a', 'b\nball', 'c'], ['d\nd', 'e', 'f'], ['g', 'h', 'i']],
      goog.labs.format.csv.parse('a,"b\nball",c\n"d\nd","e","f"\ng,h,"i"\n'));
}

function testEmbeddedQuotes() {
  assertObjectEquals(
      [['a', '"b"', 'Jonathan "Smokey" Feinberg'], ['d', 'e', 'f']],
      goog.labs.format.csv.parse(
          'a,"""b""","Jonathan ""Smokey"" Feinberg"\nd,e,f\r\n'));
}

function testUnclosedQuote() {
  var e =
      assertThrows(function() { goog.labs.format.csv.parse('a,"b,c\nd,e,f'); });

  assertTrue(e instanceof goog.labs.format.csv.ParseError);
  assertEquals(2, e.position.line);
  assertEquals(5, e.position.column);
  assertEquals(
      'Unexpected end of text after open quote at line 2 column 5\n' +
          'd,e,f\n' +
          '    ^',
      e.message);
}

function testQuotesInUnquotedField() {
  var e = assertThrows(function() {
    goog.labs.format.csv.parse('a,b "and" b,c\nd,e,f');
  });

  assertTrue(e instanceof goog.labs.format.csv.ParseError);

  assertEquals(1, e.position.line);
  assertEquals(5, e.position.column);

  assertEquals(
      'Unexpected quote mark at line 1 column 5\n' +
          'a,b "and" b,c\n' +
          '    ^',
      e.message);
}

function testGarbageOutsideQuotes() {
  var e = assertThrows(function() {
    goog.labs.format.csv.parse('a,"b",c\nd,"e"oops,f');
  });

  assertTrue(e instanceof goog.labs.format.csv.ParseError);
  assertEquals(2, e.position.line);
  assertEquals(6, e.position.column);
  assertEquals(
      'Unexpected character "o" after quote mark at line 2 column 6\n' +
          'd,"e"oops,f\n' +
          '     ^',
      e.message);
}

function testEmptyRecords() {
  assertObjectEquals(
      [['a', '', 'c'], ['d', 'e', ''], ['', '', '']],
      goog.labs.format.csv.parse('a,,c\r\nd,e,\n,,'));
}

function testEmptyRecordsWithColonCustomDelimiter() {
  assertObjectEquals(
      [['a', '', 'c'], ['d', 'e', ''], ['', '', '']],
      goog.labs.format.csv.parse('a::c\r\nd:e:\n::', undefined, ':'));
}

function testIgnoringErrors() {
  // The results of these tests are not defined by the RFC. They
  // generally strive to be "reasonable" while keeping the code simple.

  // Quotes inside field
  assertObjectEquals(
      [['Hello "World"!', 'b'], ['c', 'd']],
      goog.labs.format.csv.parse('Hello "World"!,b\nc,d', true));

  // Missing closing quote
  assertObjectEquals(
      [['Hello', 'World!']], goog.labs.format.csv.parse('Hello,"World!', true));

  // Broken use of quotes in quoted field
  assertObjectEquals(
      [['a', '"Hello"World!"']],
      goog.labs.format.csv.parse('a,"Hello"World!"', true));

  // All of the above. A real mess.
  assertObjectEquals(
      [['This" is', '"very\n\tvery"broken"', ' indeed!']],
      goog.labs.format.csv.parse(
          'This" is,"very\n\tvery"broken"," indeed!', true));
}

function testIgnoringErrorsTrailingTabs() {
  assertObjectEquals(
      [['"a\tb"\t'], ['c,d']],
      goog.labs.format.csv.parse('"a\tb"\t\n"c,d"', true));
}

function testFindLineInfo() {
  var testString = 'abc\ndef\rghi';
  var info = goog.labs.format.csv.ParseError.findLineInfo_(testString, 4);

  assertEquals(4, info.line.startLineIndex);
  assertEquals(7, info.line.endContentIndex);
  assertEquals(8, info.line.endLineIndex);

  assertEquals(1, info.lineIndex);
}

function testGetLineDebugString() {
  var str = 'abcdefghijklmnop';
  var index = str.indexOf('j');
  var column = index + 1;
  assertEquals(
      goog.labs.format.csv.ParseError.getLineDebugString_(str, column),
      'abcdefghijklmnop\n' +
          '         ^');
}

function testIsCharacterString() {
  assertTrue(goog.labs.format.csv.isCharacterString_('a'));
  assertTrue(goog.labs.format.csv.isCharacterString_('\n'));
  assertTrue(goog.labs.format.csv.isCharacterString_(' '));

  assertFalse(goog.labs.format.csv.isCharacterString_(null));
  assertFalse(goog.labs.format.csv.isCharacterString_('  '));
  assertFalse(goog.labs.format.csv.isCharacterString_(''));
  assertFalse(goog.labs.format.csv.isCharacterString_('aa'));
}


function testAssertToken() {
  goog.labs.format.csv.assertToken_('a');

  goog.object.forEach(goog.labs.format.csv.SENTINELS_, function(value) {
    goog.labs.format.csv.assertToken_(value);
  });

  assertThrows(function() { goog.labs.format.csv.assertToken_('aa'); });

  assertThrows(function() { goog.labs.format.csv.assertToken_(''); });

  assertThrows(function() { goog.labs.format.csv.assertToken_({}); });
}
