#!/usr/bin/env python
# coding=utf-8
# Copyright 2011 Google Inc.
#
# 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.

"""Tests for pfif_validator.py"""

import unittest
from StringIO import StringIO

import os
import sys
from pfif_validator import PfifValidator
import pfif_validator # to test main
import datetime
import utils
from utils import Message
import tests.pfif_xml as PfifXml

class ValidatorTests(unittest.TestCase):
  """Tests each validation function in pfif_validator.py"""

  EXPIRED_TIME = datetime.datetime(1999, 3, 1)

  PRINT_VALIDATOR_OUTPUT = True

  # Set Up

  def setUp(self): # pylint: disable=C0103
    """Some of the tests will run code that prints stuff out.  This prevents it
    from printing next to the clean dots from the unit tests."""
    if not ValidatorTests.PRINT_VALIDATOR_OUTPUT:
      sys.stdout = open(os.devnull, "w")

  @staticmethod
  def set_up_validator(xml):
    """Creates a PFIF validator from XML"""
    pfif_file = StringIO(xml)
    return PfifValidator(pfif_file)

  # printing

  def test_printing(self):
    """Tests that each of the printing options in set_printing_options changes
    the behavior of print_errors"""

    # set up the messages to be printed; the XML file here will not be used for
    # any tests.  It's just to get the validator initialized properly.
    validator = self.set_up_validator(PfifXml.XML_11_SMALL)
    lines = []
    for i in range(1, 12):
      lines.append('ZZZ ' + str(i))
    messages = []
    messages.append(Message("Message 1", is_error=True, xml_line_number=11,
                            xml_text="Text", person_record_id="Person",
                            note_record_id="Note"))
    messages.append(Message("Message 2", is_error=False))
    messages.append(Message("Message 3"))

    # With no errors or warnings, nothing should print
    output = validator.validator_messages_to_str(messages, show_errors=False,
                                       show_warnings=False)
    self.assertEqual(len(output), 0)

    # with only errors on, only errors should print
    output = validator.validator_messages_to_str(messages, show_warnings=False,
                                       show_line_numbers=False,
                                       show_record_ids=False,
                                       show_xml_text=False,
                                       show_full_line=False)
    self.assertNotEqual(output.find("Message 1"), -1)
    self.assertEqual(output.find("Message 2"), -1)
    # the default value of is_error should be True, so Message 3 should print
    self.assertNotEqual(output.find("Message 3"), -1)

    # with warnings on, warnings should print
    output = validator.validator_messages_to_str(
        messages, show_line_numbers=False, show_record_ids=False,
        show_xml_text=False, show_full_line=False)
    self.assertNotEqual(output.find("Message 2"), -1)

    # line numbers, xml text, and record IDs should not print with them off and
    # should print with them on
    self.assertEqual(output.find("11"), -1)
    output = validator.validator_messages_to_str(
        messages, show_line_numbers=True, show_record_ids=False,
        show_xml_text=False, show_full_line=False)
    self.assertNotEqual(output.find("11"), -1)

    self.assertEqual(output.find("Text"), -1)
    output = validator.validator_messages_to_str(
        messages, show_record_ids=False, show_xml_text=True,
        show_full_line=False)
    self.assertNotEqual(output.find("Text"), -1)

    self.assertEqual(output.find("Person"), -1)
    self.assertEqual(output.find("Note"), -1)
    output = validator.validator_messages_to_str(
        messages, show_record_ids=True, show_full_line=False)
    self.assertNotEqual(output.find("Person"), -1)
    self.assertNotEqual(output.find("Note"), -1)

    self.assertEqual(output.find("ZZZ 11"), -1)
    output = validator.validator_messages_to_str(
        messages, show_full_line=True, xml_lines=lines)
    self.assertNotEqual(output.find("ZZZ 11"), -1)

    # is_html should output a div somewhere
    self.assertEqual(output.find("div"), -1)
    output = validator.validator_messages_to_str(
        messages, is_html=True, xml_lines=lines)
    self.assertNotEqual(output.find("div"), -1)

  # validate_root_has_child

  def test_root_has_child(self):
    """validate_root_has_child should return an empty list if the root node has
    at least one child"""
    validator = self.set_up_validator(PfifXml.XML_11_SMALL)
    self.assertEqual(len(validator.validate_root_has_child()), 0)

  def test_root_lacks_child(self):
    """validate_root_has_child should return a list with a message if the root
    node does not have at least one child"""
    validator = self.set_up_validator(PfifXml.XML_ROOT_LACKS_CHILD)
    self.assertNotEqual(len(validator.validate_root_has_child()), 0)

  # validate_root_has_mandatory_children

  def test_root_has_mandatory_children(self):
    """validate_root_has_mandatory_children should return an empty list if one
    of the children is a person"""
    validator = self.set_up_validator(PfifXml.XML_11_SMALL)
    self.assertEqual(len(validator.validate_root_has_mandatory_children()), 0)

  def test_root_lacks_mandatory_children(self):
    """validate_root_has_mandatory_children should return a list with a message
    if the only children are not notes or persons"""
    validator = self.set_up_validator(PfifXml.XML_ROOT_HAS_BAD_CHILD)
    self.assertNotEqual(
        len(validator.validate_root_has_mandatory_children()), 0)

  def test_root_has_note_child_11(self):
    """validate_root_has_mandatory_children should return a list with a message
    if the only children are notes and the version is 1.1"""
    validator = self.set_up_validator(PfifXml.XML_TOP_LEVEL_NOTE_11)
    self.assertNotEqual(
        len(validator.validate_root_has_mandatory_children()), 0)

  def test_root_has_note_child_12(self):
    """validate_root_has_mandatory_children should return an empty list if the
    only children are notes and the version is greater than 1.1"""
    validator = self.set_up_validator(PfifXml.XML_TOP_LEVEL_NOTE_12)
    self.assertEqual(len(validator.validate_root_has_mandatory_children()), 0)

  # validate_has_mandatory_children

  def test_note_has_mandatory_children(self):
    """validate_has_mandatory_children should return an empty list if it is
    given notes with all mandatory children"""
    validator = self.set_up_validator(PfifXml.XML_NOTES_WITH_CHILDREN)
    self.assertEqual(len(validator.validate_note_has_mandatory_children()), 0)

  def test_note_has_no_mandatory_children(self):
    """validate_has_mandatory_children should return a list with nine missing
    children when given one child of a person with no children and one top level
    note (which also must have a person_record_id) with no children."""
    validator = self.set_up_validator(PfifXml.XML_NOTES_NO_CHILDREN)
    self.assertEqual(len(validator.validate_note_has_mandatory_children()), 9)

  def test_person_has_mandatory_children_11(self):
    """validate_has_mandatory_children should return an empty list if it is
    given a version 1.1 person with all mandatory children"""
    validator = self.set_up_validator(PfifXml.XML_PERSON_WITH_CHILDREN_11)
    self.assertEqual(len(validator.validate_person_has_mandatory_children()), 0)

  def test_person_has_mandatory_children_13(self):
    """validate_has_mandatory_children should return an empty list if it is
    given a version 1.3 person with all mandatory children"""
    validator = self.set_up_validator(PfifXml.XML_PERSON_WITH_CHILDREN_13)
    self.assertEqual(len(validator.validate_person_has_mandatory_children()), 0)

  def test_person_has_no_mandatory_children_11(self):
    """validate_has_mandatory_children should return a list with three missing
    children when given a version 1.1 person with no children"""
    validator = self.set_up_validator(PfifXml.XML_11_SMALL)
    self.assertEqual(len(validator.validate_person_has_mandatory_children()), 3)

  def test_person_has_no_mandatory_children_13(self):
    """validate_has_mandatory_children should return a list with three missing
    children when given a version 1.3 person with no children"""
    validator = self.set_up_validator(PfifXml.XML_PERSON_NO_CHILDREN_13)
    self.assertEqual(len(validator.validate_person_has_mandatory_children()), 3)

  # validate_fields_have_correct_format

  def test_no_fields_exist(self):
    """validate_fields_have_correct_format should return an empty list when
    passed a tree with no subelements of person or note because no nodes are
    improperly formatted."""
    validator = self.set_up_validator(PfifXml.XML_PERSON_NO_CHILDREN_13)
    self.assertEqual(len(validator.validate_fields_have_correct_format()), 0)