# -*- coding: utf-8 -*-

__author__ = 'isparks'

import unittest

from rwslib.builders.metadata import *
from rwslib.builders.constants import DataType, LogicalRecordPositionType, StepType
from rwslib.builders.clinicaldata import ItemData

from ..common import obj_to_doc

# Metadata object tests


class TestTranslatedText(unittest.TestCase):
    def test_builder(self):
        """XML produced"""
        tested = TranslatedText('A test', lang='en')
        doc = obj_to_doc(tested)
        self.assertEqual(doc.tag, "TranslatedText")
        self.assertEqual("en", doc.attrib['xml:lang'])
        self.assertEqual("A test", doc.text)

    def test_builder_no_lang(self):
        """XML produced when no lang is provided"""
        tested = TranslatedText('A test')
        doc = obj_to_doc(tested)
        self.assertEqual(doc.tag, "TranslatedText")
        self.assertEqual("", doc.get('xml:lang', ''))
        self.assertEqual("A test", doc.text)

    def test_accepts_no_children(self):
        with self.assertRaises(ValueError):
            TranslatedText('test') << object()


class TestSymbols(unittest.TestCase):
    def test_can_only_receive_translated_text(self):
        with self.assertRaises(ValueError):
            Symbol() << object()

    def test_translated_text_received(self):
        tested = Symbol()
        tested << TranslatedText('en', 'Test string')
        self.assertEqual(1, len(tested.translations))

    def test_builder(self):
        """XML produced"""
        tested = Symbol()(TranslatedText('en', 'Test string'))
        doc = obj_to_doc(tested)
        self.assertEqual(doc.tag, "Symbol")
        self.assertEqual(list(doc)[0].tag, "TranslatedText")


class TestMeasurementUnits(unittest.TestCase):
    def test_basic(self):
        tested = MeasurementUnit('MU_OID', 'MU_NAME')
        self.assertEqual(1, tested.constant_a)
        self.assertEqual(1, tested.constant_b)
        self.assertEqual(0, tested.constant_c)
        self.assertEqual(0, tested.constant_k)
        self.assertEqual(0, len(tested.symbols))

    def test_kg(self):
        tested = MeasurementUnit("MSU00001", "KG", unit_dictionary_name='UN1', standard_unit=True)
        doc = obj_to_doc(tested)
        self.assertEqual(doc.attrib['mdsol:StandardUnit'], "Yes")

    def test_can_only_receive_symbol(self):
        with self.assertRaises(ValueError):
            MeasurementUnit('MU_OID', 'MU_NAME') << object()

    def test_symbol_received(self):
        tested = MeasurementUnit('MU_OID', 'MU_NAME')
        tested << Symbol()
        self.assertEqual(1, len(tested.symbols))

    def test_builder(self):
        """XML produced"""
        tested = MeasurementUnit('MU_OID', 'MU_NAME')(Symbol())
        doc = obj_to_doc(tested)
        self.assertEqual(doc.tag, "MeasurementUnit")
        self.assertEqual(list(doc)[0].tag, "Symbol")


class TestBasicDefinitions(unittest.TestCase):
    def test_basic(self):
        tested = BasicDefinitions()
        self.assertEqual(0, len(tested.measurement_units))

    def test_mus_onlu(self):
        tested = BasicDefinitions()
        with self.assertRaises(ValueError):
            tested << object()

    def test_mus(self):
        tested = BasicDefinitions()
        tested << MeasurementUnit("MU_OID", "MUNAME")
        self.assertEqual(1, len(tested.measurement_units))

    def test_builder(self):
        """XML produced"""
        tested = BasicDefinitions()(MeasurementUnit("MU_OID", "MUNAME"))
        doc = obj_to_doc(tested)
        self.assertEqual(doc.tag, "BasicDefinitions")
        self.assertEqual(list(doc)[0].tag, "MeasurementUnit")


class TestGlobalVariables(unittest.TestCase):
    """Test Global Variables class"""

    def test_basic(self):
        tested = GlobalVariables('project_name', 'name', 'description')
        self.assertEqual('project_name', tested.protocol_name)
        self.assertEqual('name', tested.name)
        self.assertEqual('description', tested.description)

    def test_defaults(self):
        tested = GlobalVariables('project_name')
        self.assertEqual('project_name', tested.protocol_name)
        self.assertEqual('project_name', tested.name)  # Defaults to protocol_name
        self.assertEqual('', tested.description)  # Defaults to empty string

    def test_no_children(self):
        """GlobalVariables accepts no children"""
        with self.assertRaises(ValueError):
            GlobalVariables('TEST') << object()

    def test_builder(self):
        """XML produced"""
        tested = GlobalVariables('project_name', description="A description")
        doc = obj_to_doc(tested)
        self.assertEqual(doc.tag, "GlobalVariables")
        self.assertEqual(list(doc)[0].tag, "StudyName")
        self.assertEqual("project_name", list(doc)[0].text)
        self.assertEqual(list(doc)[1].tag, "StudyDescription")
        self.assertEqual("A description", list(doc)[1].text)
        self.assertEqual(list(doc)[2].tag, "ProtocolName")
        self.assertEqual("project_name", list(doc)[2].text)


class TestStudyEventRef(unittest.TestCase):
    def test_cannot_accept_child(self):
        with self.assertRaises(ValueError):
            StudyEventRef("OID", 2, False) << object()

    def test_optional_order_number(self):
        ser = StudyEventRef("OID")
        doc = obj_to_doc(ser)
        self.assertIsNone(doc.get('OrderNumber'))
        self.assertEqual("No", doc.get('Mandatory'))

    def test_zero_order_number(self):
        """Not that it's entirely sensible, but it's cleaner"""
        ser = StudyEventRef("OID", order_number=0)
        doc = obj_to_doc(ser)
        self.assertEqual("0", str(doc.get('OrderNumber')))

    def test_mandatory_study_event_ref(self):
        ser = StudyEventRef("OID", mandatory=True)
        doc = obj_to_doc(ser)
        self.assertIsNone(doc.get('OrderNumber'))
        self.assertEqual("Yes", doc.get('Mandatory'))


class TestProtocol(unittest.TestCase):
    def test_can_accept_studyeventref_child(self):
        se = StudyEventRef('OID', 2, True)
        tested = Protocol()(se)
        self.assertEqual(se, tested.study_event_refs[0])

    def test_cannot_accept_non_study_eventref_child(self):
        with self.assertRaises(ValueError):
            Protocol() << object()

    def test_builder(self):
        """XML produced"""
        doc = obj_to_doc(Protocol()(StudyEventRef("OID", 1, True)))
        self.assertEqual(doc.tag, "Protocol")
        self.assertEqual(list(doc)[0].tag, "StudyEventRef")


class TestFormRef(unittest.TestCase):
    def test_cannot_accept_any_child(self):
        with self.assertRaises(ValueError):
            FormRef('OID', 1, False) << object()


class TestStudyEventDef(unittest.TestCase):
    def test_cannot_accept_non_formref_child(self):
        with self.assertRaises(ValueError):
            StudyEventDef("OID", "Name", False, StudyEventDef.SCHEDULED) << object()

    def test_can_accept_formref_child(self):
        tested = StudyEventDef("OID", "Name", False, StudyEventDef.SCHEDULED)(FormRef("FORMOID", 1, False))
        self.assertEqual(1, len(tested.formrefs))

    def test_builder(self):
        tested = StudyEventDef("OID", "Name", False,
                               StudyEventDef.SCHEDULED,
                               category="Test Cat",
                               access_days=1,
                               start_win_days=2,
                               target_days=3,
                               end_win_days=4,
                               overdue_days=5,
                               close_days=6
                               )
        tested << FormRef("FORMOID", 1, False)

        doc = obj_to_doc(tested)
        self.assertEqual(doc.tag, "StudyEventDef")
        self.assertEqual("OID", doc.attrib['OID'])
        self.assertEqual("Name", doc.attrib['Name'])
        self.assertEqual("Scheduled", doc.attrib['Type'])
        self.assertEqual("Test Cat", doc.attrib['Category'])
        self.assertEqual("1", doc.attrib['mdsol:AccessDays'])
        self.assertEqual("2", doc.attrib['mdsol:StartWinDays'])
        self.assertEqual("3", doc.attrib['mdsol:TargetDays'])
        self.assertEqual("4", doc.attrib['mdsol:EndWinDays'])
        self.assertEqual("5", doc.attrib['mdsol:OverDueDays'])
        self.assertEqual("6", doc.attrib['mdsol:CloseDays'])
        self.assertEqual("FormRef", list(doc)[0].tag)


class TestItemGroupRef(unittest.TestCase):
    def test_accepts_no_children(self):
        with self.assertRaises(ValueError):
            ItemGroupRef("ItemGroup1", 1) << object()

    def test_optional_order_number(self):
        igr = ItemGroupRef("ItemGroup1")
        doc = obj_to_doc(igr)
        self.assertEqual("ItemGroup1", doc.attrib['ItemGroupOID'])
        self.assertNotIn('OrderNumber', doc.attrib)

    def test_builder(self):
        tested = ItemGroupRef("ItemGroup1", 1, mandatory=True)
        doc = obj_to_doc(tested)
        self.assertEqual(doc.tag, "ItemGroupRef")
        self.assertEqual("ItemGroup1", doc.attrib['ItemGroupOID'])
        self.assertEqual("1", doc.attrib['OrderNumber'])
        self.assertEqual("Yes", doc.attrib['Mandatory'])


class TestMdsolHelpTexts(unittest.TestCase):
    def test_build(self):
        tested = MdsolHelpText("en", "This is a help text")
        doc = obj_to_doc(tested)
        self.assertEqual(doc.tag, "mdsol:HelpText")
        self.assertEqual("en", doc.attrib['xml:lang'])
        self.assertEqual("This is a help text", doc.text)

    def test_accepts_no_children(self):
        with self.assertRaises(ValueError):
            MdsolHelpText("en", "Content") << object()


class TestMdsolViewRestriction(unittest.TestCase):
    def test_accepts_no_children(self):
        with self.assertRaises(ValueError):
            MdsolViewRestriction("CRA") << object()

    def test_build(self):
        tested = MdsolViewRestriction("DM")
        doc = obj_to_doc(tested)
        self.assertEqual(doc.tag, "mdsol:ViewRestriction")
        self.assertEqual("DM", doc.text)


class TestMdsolEntryRestriction(unittest.TestCase):
    def test_accepts_no_children(self):
        with self.assertRaises(ValueError):
            MdsolEntryRestriction("CRA") << object()

    def test_build(self):
        tested = MdsolEntryRestriction("DM")
        doc = obj_to_doc(tested)
        self.assertEqual(doc.tag, "mdsol:EntryRestriction")
        self.assertEqual("DM", doc.text)


class TestMdsolLabelRef(unittest.TestCase):
    def test_accepts_no_children(self):
        with self.assertRaises(ValueError):
            MdsolLabelRef("LABEL1", 1) << object()

    def test_build(self):
        tested = MdsolLabelRef("OID1", 1)

        doc = obj_to_doc(tested)
        self.assertEqual(doc.tag, "mdsol:LabelRef")
        self.assertEqual("OID1", doc.attrib['LabelOID'])
        self.assertEqual("1", doc.attrib['OrderNumber'])


class TestMdsolAttribute(unittest.TestCase):
    def test_accepts_no_children(self):
        with self.assertRaises(ValueError):
            MdsolAttribute("Namespace", "Name", "Value") << object()


class TestItemRef(unittest.TestCase):
    def test_accepts_no_children(self):
        with self.assertRaises(ValueError):
            ItemRef("OID", 1) << object()

    def test_build(self):
        tested = ItemRef("OID1", 1,
                         key_sequence=3,
                         imputation_method_oid='IMPUTE1',
                         role="AROLE",
                         role_codelist_oid='ROLEX')

        tested << MdsolAttribute("Namespace", "Name", "Value")
        tested << MdsolAttribute("Namespace2", "Name2", "Value2")

        doc = obj_to_doc(tested)
        self.assertEqual(doc.tag, "ItemRef")
        self.assertEqual("OID1", doc.attrib['ItemOID'])
        self.assertEqual("1", doc.attrib['OrderNumber'])
        self.assertEqual("3", doc.attrib['KeySequence'])
        self.assertEqual("IMPUTE1", doc.attrib['ImputationMethodOID'])
        self.assertEqual("AROLE", doc.attrib['Role'])
        self.assertEqual("ROLEX", doc.attrib['RoleCodeListOID'])
        self.assertEqual("mdsol:Attribute", list(doc)[0].tag)
        self.assertEqual("mdsol:Attribute", list(doc)[1].tag)


class TestQuestion(unittest.TestCase):
    def test_cannot_accept_non_translation_child(self):
        with self.assertRaises(ValueError):
            Question() << object()

    def test_can_accept_translation_child(self):
        tested = Question()(TranslatedText("How do you feel today?"))
        self.assertEqual(1, len(tested.translations))

    def test_build(self):
        tested = Question()(TranslatedText('How are you feeling?'))
        doc = obj_to_doc(tested)
        self.assertEqual(doc.tag, "Question")
        self.assertEqual("TranslatedText", list(doc)[0].tag)


class TestMeasurementUnitRef(unittest.TestCase):
    def test_cannot_accept_child(self):
        with self.assertRaises(ValueError):
            MeasurementUnitRef("KG") << object()

    def test_build(self):
        tested = MeasurementUnitRef("KG")
        doc = obj_to_doc(tested)
        self.assertEqual(doc.tag, "MeasurementUnitRef")
        self.assertEqual("KG", doc.attrib['MeasurementUnitOID'])


class TestCodeListRef(unittest.TestCase):
    def test_cannot_accept_child(self):
        with self.assertRaises(ValueError):
            CodeListRef("SEV") << object()

    def test_build(self):
        tested = CodeListRef("SEVERITY")
        doc = obj_to_doc(tested)
        self.assertEqual(doc.tag, "CodeListRef")
        self.assertEqual("SEVERITY", doc.attrib['CodeListOID'])


class TestMdsolReviewGroup(unittest.TestCase):
    def test_accepts_no_children(self):
        with self.assertRaises(ValueError):
            MdsolReviewGroup("DM") << object()


    def test_build(self):
        doc = obj_to_doc(MdsolReviewGroup("CRA"))
        self.assertEqual(doc.tag, "mdsol:ReviewGroup")
        self.assertEqual("CRA", doc.text)


class TestMdsolLabelDef(unittest.TestCase):
    def setUp(self):
        self.tested = MdsolLabelDef("L_AGE", "Age Label", field_number=4)

    def test_accepts_no_strange_children(self):
        with self.assertRaises(ValueError):
            self.tested << object()

    def test_accepts_view_restriction(self):
        self.tested << MdsolViewRestriction("DM")
        self.assertEqual(1, len(self.tested.view_restrictions))

    def test_accepts_translated_text(self):
        tt = TranslatedText("Hello")
        self.tested << tt
        self.assertEqual(tt, self.tested.translations[0])

    def test_build(self):
        self.tested << TranslatedText("Please answer all questions")
        self.tested << MdsolViewRestriction("DM")

        doc = obj_to_doc(self.tested)
        self.assertEqual(doc.tag, "mdsol:LabelDef")
        self.assertEqual("L_AGE", doc.attrib['OID'])
        self.assertEqual("Age Label", doc.attrib['Name'])
        self.assertEqual("4", doc.attrib['FieldNumber'])
        self.assertEqual("TranslatedText", list(doc)[0].tag)
        self.assertEqual("mdsol:ViewRestriction", list(doc)[1].tag)


class TestCheckValue(unittest.TestCase):
    def test_accepts_no_children(self):
        with self.assertRaises(ValueError):
            CheckValue("Test") << object()

    def test_build(self):
        doc = obj_to_doc(CheckValue(99))
        self.assertEqual(doc.tag, "CheckValue")
        self.assertEqual("99", doc.text)


class TestRangeCheck(unittest.TestCase):
    def test_accepts_no_strange_children(self):
        with self.assertRaises(ValueError):
            RangeCheck(comparator=RangeCheckComparatorType.GreaterThanEqualTo,
                       soft_hard=RangeCheckType.Soft) << object()

    def test_accepts_no_strange_soft_hard(self):
        with self.assertRaises(AttributeError):
            RangeCheck(comparator=RangeCheckComparatorType.GreaterThanEqualTo, soft_hard="Blash")

    def test_accepts_no_strange_comparator(self):
        with self.assertRaises(AttributeError):
            RangeCheck(comparator="EQ", soft_hard="Blash")

    def test_accepts_checkvalue(self):
        tested = RangeCheck(comparator=RangeCheckComparatorType.LessThanEqualTo, soft_hard=RangeCheckType.Soft)
        cv = CheckValue(0)
        tested << cv
        self.assertEqual(cv, tested.check_value)

    def test_accepts_measurement_unit_ref(self):
        tested = RangeCheck(comparator=RangeCheckComparatorType.GreaterThanEqualTo, soft_hard=RangeCheckType.Soft)
        mr = MeasurementUnitRef('kg')
        tested << mr
        self.assertEqual(mr, tested.measurement_unit_ref)

    def test_build(self):
        self.tested = RangeCheck(comparator=RangeCheckComparatorType.GreaterThanEqualTo, soft_hard=RangeCheckType.Soft)
        self.tested << CheckValue(0)
        self.tested << MeasurementUnitRef('kg')

        doc = obj_to_doc(self.tested)
        self.assertEqual("RangeCheck", doc.tag)
        self.assertEqual("Soft", doc.attrib['SoftHard'])
        self.assertEqual("GE", doc.attrib['Comparator'])
        self.assertEqual("CheckValue", list(doc)[0].tag)
        self.assertEqual("MeasurementUnitRef", list(doc)[1].tag)


class TestMdsolHeaderText(unittest.TestCase):
    def test_lang_default(self):
        tested = MdsolHeaderText("Content", "en")
        self.assertEqual("en", tested.lang)
        doc = obj_to_doc(tested)
        self.assertEqual("mdsol:HeaderText", doc.tag)
        self.assertEqual("Content", doc.text)
        self.assertEqual("en", doc.attrib['xml:lang'])


class TestItemDef(unittest.TestCase):
    def setUp(self):
        self.tested = ItemDef("I_AGE", "Age", DataType.Integer, 3,
                              significant_digits=3,
                              sas_field_name='SAGE',
                              sds_var_name='SVARNAME',
                              sas_format="3.0",
                              sas_label='AGE_YRS',
                              source_document_verify=True,
                              query_future_date=False,
                              visible=True,
                              translation_required=True,
                              query_non_conformance=True,
                              other_visits=False,
                              can_set_item_group_date=False,
                              can_set_form_date=False,
                              can_set_study_event_date=False,
                              can_set_subject_date=False,
                              visual_verify=True,
                              does_not_break_signature=True,
                              acceptable_file_extensions='jpg',
                              control_type=ControlType.Text,
                              variable_oid='SOMETHING_DIFFERENT',
                              default_value=99,
                              origin='An origin',
                              comment='A comment',
                              date_time_format='mmm yy dd',
                              field_number='10'
                              )

    def test_accepts_no_strange_children(self):
        with self.assertRaises(ValueError):
            self.tested << object()

    def test_invalid_datatype(self):
        with self.assertRaises(AttributeError):
            ItemDef("TEST", "My Test", "TOTALLY_WRONG_DATATYPE", 10)

    def test_invalid_controltype(self):
        with self.assertRaises(AttributeError):
            ItemDef("TEST", "My Test", DataType.Text, 10, control_type="TOTALLY_WRONG_CONTROLTYPE")

    def test_invalid_integer_missing_length(self):
        """Test that integer type raises error if no length set"""
        with self.assertRaises(AttributeError):
            ItemDef("TEST", "My Test", DataType.Integer)

    def test_invalid_text_missing_length(self):
        """Test that text type raises error if no length set"""
        with self.assertRaises(AttributeError):
            ItemDef("TEST", "My Test", DataType.Text)

    def test_valid_date_missing_length(self):
        """Test that dates are defaulted to length of their format"""
        id = ItemDef("TEST", "My Test", DataType.Date, date_time_format="mmm dd yy")
        self.assertEqual(9, id.length)

    def test_accepts_mdsolhelp(self):
        self.tested << MdsolHelpText("en", "Content of help")
        self.assertEqual(1, len(self.tested.help_texts))

    def test_accepts_view_restriction(self):
        self.tested << MdsolViewRestriction("DM")
        self.assertEqual(1, len(self.tested.view_restrictions))

    def test_accepts_header_text(self):
        ht = MdsolHeaderText("KILOs")
        self.tested << ht
        self.assertEqual(ht, self.tested.header_text)

    def test_does_not_accept_two_header_texts(self):
        h1 = MdsolHeaderText("One")
        h2 = MdsolHeaderText("Two")
        self.tested << h1
        with self.assertRaises(ValueError):
            self.tested << h2

    def test_accepts_measurement_unit_ref(self):
        mu = MeasurementUnitRef("KG")
        self.tested << mu
        self.assertEqual(1, len(self.tested.measurement_unit_refs))

    def test_accepts_question(self):
        q = Question()
        self.tested << q
        self.assertEqual(q, self.tested.question)

    def test_does_not_accept_two_questions(self):
        q1 = Question()
        q2 = Question()
        self.tested << q1
        with self.assertRaises(ValueError):
            self.tested << q2

    def test_accepts_codelistref(self):
        cl = CodeListRef("SEVERITY")
        self.tested << cl
        self.assertEqual(cl, self.tested.codelistref)

    def test_does_not_accept_two_codelists(self):
        cl1 = CodeListRef("SEVERITY")
        cl2 = CodeListRef("STATUS")
        self.tested << cl1
        with self.assertRaises(ValueError):
            self.tested << cl2

    def test_accepts_entry_restriction(self):
        self.tested << MdsolEntryRestriction("CRA")
        self.assertEqual(1, len(self.tested.entry_restrictions))

    def test_accepts_review_group(self):
        self.tested << MdsolReviewGroup("CRA")
        self.assertEqual(1, len(self.tested.review_groups))

    def test_accepts_range_check(self):
        self.tested << RangeCheck(RangeCheckComparatorType.LessThanEqualTo, RangeCheckType.Soft)
        self.assertEqual(1, len(self.tested.range_checks))

    def test_build(self):
        self.tested << Question()(TranslatedText("How do you feel today?"))
        self.tested << CodeListRef("SCALE_1")
        self.tested << MeasurementUnitRef("Years")
        self.tested << MdsolHelpText("en", "Content of help")
        self.tested << MdsolViewRestriction("DM")
        self.tested << MdsolEntryRestriction("CRA")
        self.tested << MdsolHeaderText("YRS")
        self.tested << MdsolReviewGroup("CRA")
        self.tested << RangeCheck(RangeCheckComparatorType.LessThanEqualTo, RangeCheckType.Soft)

        doc = obj_to_doc(self.tested)
        self.assertEqual(doc.tag, "ItemDef")
        self.assertEqual("I_AGE", doc.attrib['OID'])
        self.assertEqual("Age", doc.attrib['Name'])
        self.assertEqual("Yes", doc.attrib['mdsol:Active'])
        self.assertEqual("integer", doc.attrib['DataType'])
        self.assertEqual("3", doc.attrib['Length'])
        self.assertEqual("Text", doc.attrib['mdsol:ControlType'])
        self.assertEqual("3", doc.attrib['SignificantDigits'])
        self.assertEqual("SAGE", doc.attrib['SASFieldName'])
        self.assertEqual("SVARNAME", doc.attrib['SDSVarName'])
        self.assertEqual("AGE_YRS", doc.attrib['mdsol:SASLabel'])
        self.assertEqual("3.0", doc.attrib['mdsol:SASFormat'])
        self.assertEqual("A comment", doc.attrib['Comment'])
        self.assertEqual("An origin", doc.attrib['Origin'])
        self.assertEqual("No", doc.attrib['mdsol:QueryFutureDate'])
        self.assertEqual("Yes", doc.attrib['mdsol:Visible'])
        self.assertEqual("Yes", doc.attrib['mdsol:TranslationRequired'])
        self.assertEqual("Yes", doc.attrib['mdsol:SourceDocument'])
        self.assertEqual("No", doc.attrib['mdsol:OtherVisits'])
        self.assertEqual("Yes", doc.attrib['mdsol:SourceDocument'])
        self.assertEqual("Yes", doc.attrib['mdsol:QueryNonConformance'])
        self.assertEqual("No", doc.attrib['mdsol:CanSetItemGroupDate'])
        self.assertEqual("No", doc.attrib['mdsol:CanSetFormDate'])
        self.assertEqual("No", doc.attrib['mdsol:CanSetStudyEventDate'])
        self.assertEqual("No", doc.attrib['mdsol:CanSetSubjectDate'])
        self.assertEqual("Yes", doc.attrib['mdsol:VisualVerify'])
        self.assertEqual("Yes", doc.attrib['mdsol:DoesNotBreakSignature'])
        self.assertEqual("SOMETHING_DIFFERENT", doc.attrib['mdsol:VariableOID'])
        self.assertEqual("jpg", doc.attrib['mdsol:AcceptableFileExtensions'])
        self.assertEqual("99", doc.attrib['mdsol:DefaultValue'])
        self.assertEqual("mmm yy dd", doc.attrib['mdsol:DateTimeFormat'])
        self.assertEqual("10", doc.attrib['mdsol:FieldNumber'])

        self.assertEqual("Question", list(doc)[0].tag)
        self.assertEqual("CodeListRef", list(doc)[1].tag)
        self.assertEqual("MeasurementUnitRef", list(doc)[2].tag)
        self.assertEqual("RangeCheck", list(doc)[3].tag)
        self.assertEqual("mdsol:HeaderText", list(doc)[4].tag)
        self.assertEqual("mdsol:ViewRestriction", list(doc)[5].tag)
        self.assertEqual("mdsol:EntryRestriction", list(doc)[6].tag)
        self.assertEqual("mdsol:HelpText", list(doc)[7].tag)
        self.assertEqual("mdsol:ReviewGroup", list(doc)[8].tag)


class TestItemGroupDef(unittest.TestCase):
    def test_does_notaccept_non_itemref_child(self):
        with self.assertRaises(ValueError):
            ItemGroupDef("DM", "Demog") << object()

    def test_accepts_itemref_child(self):
        tested = ItemGroupDef("DM", "Demog")
        tested << ItemRef("OID1", "1")
        tested << MdsolLabelRef("LABEL1", 2)

        self.assertEqual(1, len(tested.item_refs))
        self.assertEqual(1, len(tested.label_refs))

    def test_build(self):
        tested = ItemGroupDef("DM", "Demography",
                              repeating=True,
                              is_reference_data=True,
                              sas_dataset_name='DMSAS',
                              domain='TESTDOMAIN',
                              origin='TESTORIGIN',
                              role='TESTROLE',
                              purpose='TESTPURPOSE',
                              comment='A comment')

        tested << ItemRef("OID1", 1)
        tested << MdsolLabelRef("LABEL1", 2)

        doc = obj_to_doc(tested)
        self.assertEqual(doc.tag, "ItemGroupDef")
        self.assertEqual("DM", doc.attrib['OID'])
        self.assertEqual("Yes", doc.attrib['Repeating'])
        self.assertEqual("Yes", doc.attrib['IsReferenceData'])
        self.assertEqual("Demography", doc.attrib['Name'])
        self.assertEqual("DMSAS", doc.attrib['SASDatasetName'])
        self.assertEqual("TESTDOMAIN", doc.attrib['Domain'])
        self.assertEqual("TESTORIGIN", doc.attrib['Origin'])
        self.assertEqual("TESTROLE", doc.attrib['Role'])
        self.assertEqual("TESTPURPOSE", doc.attrib['Purpose'])
        self.assertEqual("A comment", doc.attrib['Comment'])
        self.assertEqual("ItemRef", list(doc)[0].tag)
        self.assertEqual("mdsol:LabelRef", list(doc)[1].tag)


class TestFormDef(unittest.TestCase):
    def test_only_accept_itemgroup_ref(self):
        with self.assertRaises(ValueError):
            FormDef("VS", "Vital Signs") << object()

    def test_accept_itemgroup_ref(self):
        tested = FormDef("DM", "Demog", order_number=1)(ItemGroupRef("ItemGroup1", 1))
        self.assertEqual(1, len(tested.itemgroup_refs))

    def test_builder(self):
        tested = FormDef("DM", "Demog", repeating=True,
                         order_number=2,
                         link_form_oid='FRM1',
                         link_study_event_oid='EVT1')

        tested << ItemGroupRef("ItemGroup1", 1)
        tested << MdsolHelpText("en", "This is a help text")
        tested << MdsolViewRestriction("DM")
        tested << MdsolEntryRestriction("CRA")

        doc = obj_to_doc(tested)
        self.assertEqual(doc.tag, "FormDef")
        self.assertEqual("DM", doc.attrib['OID'])
        self.assertEqual("Demog", doc.attrib['Name'])
        self.assertEqual("Yes", doc.attrib['Repeating'])
        self.assertEqual("2", doc.attrib['mdsol:OrderNumber'])
        # Would not see LinkFormOID and LinkStudyEventOID together, they are mutually exclusive. Just for coverage.
        self.assertEqual("FRM1", doc.attrib['mdsol:LinkFormOID'])
        self.assertEqual("EVT1", doc.attrib['mdsol:LinkStudyEventOID'])
        self.assertEqual("ItemGroupRef", list(doc)[0].tag)
        self.assertEqual("mdsol:HelpText", list(doc)[1].tag)
        self.assertEqual("mdsol:ViewRestriction", list(doc)[2].tag)
        self.assertEqual("mdsol:EntryRestriction", list(doc)[3].tag)


class TestDecode(unittest.TestCase):
    def test_cannot_accept_non_translated_text(self):
        with self.assertRaises(ValueError):
            Decode() << object()

    def test_accepts_decode(self):
        tested = Decode()
        tt = TranslatedText("Yes")
        tested.add(tt)
        self.assertEqual(tt, tested.translations[0])

    def test_builder(self):
        """XML produced"""
        tested = Decode()
        tested << TranslatedText("Yes")
        doc = obj_to_doc(tested)
        self.assertEqual("Decode", doc.tag)
        self.assertEqual("TranslatedText", list(doc)[0].tag)


class TestCodeListItem(unittest.TestCase):
    def test_cannot_accept_non_decode(self):
        with self.assertRaises(ValueError):
            CodeListItem("M") << object()

    def test_accepts_decode(self):
        tested = CodeListItem("N")
        decode = Decode()
        tested.add(decode)
        self.assertEqual(decode, tested.decode)

    def test_builder_basic(self):
        """XML produced"""
        tested = CodeListItem("Y")
        tested << Decode()
        doc = obj_to_doc(tested)
        self.assertEqual("CodeListItem", doc.tag)
        self.assertEqual("", doc.get('mdsol:Specify', ''))
        self.assertEqual("", doc.get('mdsol:OrderNumber', ''))
        self.assertEqual("Y", doc.attrib['CodedValue'])
        self.assertEqual("Decode", list(doc)[0].tag)

    def test_builder_order_specify(self):
        """XML produced with optional params set"""
        tested = CodeListItem("Y", order_number=1, specify=True)
        tested << Decode()
        doc = obj_to_doc(tested)
        self.assertEqual("CodeListItem", doc.tag)
        self.assertEqual("Yes", doc.attrib['mdsol:Specify'])
        self.assertEqual("1", doc.attrib['mdsol:OrderNumber'])
        self.assertEqual("Y", doc.attrib['CodedValue'])
        self.assertEqual("Decode", list(doc)[0].tag)


class TestCodeList(unittest.TestCase):
    """Codelists contain codelistitems"""

    def test_cannot_accept_non_codelistitem(self):
        with self.assertRaises(ValueError):
            CodeList("CL1", "Codelist1", DataType.Integer) << object()

    def test_invalid_datatype(self):
        with self.assertRaises(ValueError):
            CodeList("CL1", "Codelist1", "IncorrectDataType")

    def test_accepts_codelistitem(self):
        tested = CodeList("CL1", "Codelist1", DataType.Integer)
        cl1 = CodeListItem("1")
        tested.add(cl1)
        self.assertEqual(cl1, tested.codelist_items[0])

    def test_builder(self):
        """XML produced"""
        tested = CodeList("CL_YN", "YesNo", DataType.String, sas_format_name="YESNO_CL")
        tested << CodeListItem("Y")
        doc = obj_to_doc(tested)
        self.assertEqual("CodeList", doc.tag)
        self.assertEqual(DataType.String.value, doc.attrib['DataType'])
        self.assertEqual("YESNO_CL", doc.attrib['SASFormatName'])
        self.assertEqual("CodeListItem", list(doc)[0].tag)


class TestConfirmationMessage(unittest.TestCase):
    def test_build(self):
        cm = MdsolConfirmationMessage("Form saved.")
        doc = obj_to_doc(cm)
        self.assertEqual("mdsol:ConfirmationMessage", doc.tag)
        self.assertEqual("Form saved.", doc.text)

    def test_lang_set(self):
        cm = MdsolConfirmationMessage("Form saved.", lang="en")
        doc = obj_to_doc(cm)
        self.assertEqual("en", doc.attrib['xml:lang'])


class TestMdsolCheckAction(unittest.TestCase):
    """Test extensions to ODM for Edit Checks Steps in Rave"""

    def test_build(self):
        tested = MdsolCheckAction(
            field_oid="Field",
            variable_oid="TheVAR",
            form_oid="The Form",
            form_repeat_number=1,
            folder_oid="The Folder",
            folder_repeat_number=2,
            record_position=3,
            check_action_type=ActionType.SetDataPoint,
            check_string="CString",  # Knowing which of these to use for each action type is
            check_script="CScript",  # the trick. Best to look in an Architect Loader Spreadsheet
            check_options="COptions")  # to get an idea
        doc = obj_to_doc(tested)
        self.assertEqual("mdsol:CheckAction", doc.tag)
        self.assertEqual("The Folder", doc.attrib['FolderOID'])
        self.assertEqual("The Form", doc.attrib['FormOID'])
        self.assertEqual("Field", doc.attrib['FieldOID'])
        self.assertEqual("TheVAR", doc.attrib['VariableOID'])
        self.assertEqual("1", doc.attrib['FormRepeatNumber'])
        self.assertEqual("2", doc.attrib['FolderRepeatNumber'])
        self.assertEqual("3", doc.attrib['RecordPosition'])
        self.assertEqual("CString", doc.attrib['String'])
        self.assertEqual("CScript", doc.attrib['Script'])
        self.assertEqual("COptions", doc.attrib['Options'])
        self.assertEqual(ActionType.SetDataPoint.value, doc.attrib['Type'])

    def test_invalid_action(self):
        with self.assertRaises(AttributeError):
            MdsolCheckAction(check_action_type='bad_name')


class TestMdsolCheckStep(unittest.TestCase):
    """Test extensions to ODM for Edit Checks Steps in Rave"""

    def test_build(self):
        tested = MdsolCheckStep(data_format="$1", static_value="1")
        doc = obj_to_doc(tested)
        self.assertEqual("mdsol:CheckStep", doc.tag)
        self.assertEqual("$1", doc.attrib['DataFormat'])
        self.assertEqual("1", doc.attrib['StaticValue'])
        # No function param
        self.assertEqual("", doc.attrib.get('Function', ''))

    def test_build_function(self):
        tested = MdsolCheckStep(function=StepType.Add)
        doc = obj_to_doc(tested)
        self.assertEqual("mdsol:CheckStep", doc.tag)
        self.assertEqual(StepType.Add.value, doc.attrib['Function'])
        # No data format param
        self.assertEqual("", doc.attrib.get('DataFormat', ''))

    def test_build_datastep(self):
        tested = MdsolCheckStep(variable_oid="VAROID", field_oid="FIELDOID",
                                form_oid="MyForm",
                                folder_oid="MyFolder",
                                record_position=0, form_repeat_number=2, folder_repeat_number=3,
                                logical_record_position=LogicalRecordPositionType.MaxBySubject)
        doc = obj_to_doc(tested)
        self.assertEqual("mdsol:CheckStep", doc.tag)
        self.assertEqual("VAROID", doc.attrib['VariableOID'])
        self.assertEqual("FIELDOID", doc.attrib['FieldOID'])
        self.assertEqual("MyForm", doc.attrib['FormOID'])
        self.assertEqual("MyFolder", doc.attrib['FolderOID'])
        self.assertEqual("0", doc.attrib['RecordPosition'])
        self.assertEqual("2", doc.attrib['FormRepeatNumber'])
        self.assertEqual("3", doc.attrib['FolderRepeatNumber'])
        self.assertEqual("MaxBySubject", doc.attrib['LogicalRecordPosition'])
        # No data format param
        self.assertEqual("", doc.attrib.get('DataFormat', ''))
        # No function param
        self.assertEqual("", doc.attrib.get('Function', ''))

    def test_build_custom_function(self):
        tested = MdsolCheckStep(custom_function="AlwaysTrue*")
        doc = obj_to_doc(tested)
        self.assertEqual("mdsol:CheckStep", doc.tag)
        self.assertEqual("AlwaysTrue*", doc.attrib['CustomFunction'])
        # No data format param
        self.assertEqual("", doc.attrib.get('DataFormat', ''))
        # No function param
        self.assertEqual("", doc.attrib.get('Function', ''))

    def test_invalid_function(self):
        with self.assertRaises(AttributeError):
            MdsolCheckStep(function='bad_name')

    def test_create_with_valid_lrp(self):
        """We create a function with a valid LRP value"""
        tested = MdsolCheckStep(custom_function="AlwaysTrue*",
                                logical_record_position=LogicalRecordPositionType.Last)
        doc = obj_to_doc(tested)
        self.assertEqual("mdsol:CheckStep", doc.tag)
        self.assertEqual("AlwaysTrue*", doc.attrib['CustomFunction'])
        self.assertEqual("Last", doc.attrib['LogicalRecordPosition'])

    def test_create_with_invalid_lrp(self):
        """We create a function with an invalid LRP value"""
        with self.assertRaises(AttributeError) as exc:
            tested = MdsolCheckStep(custom_function="AlwaysTrue*",
                                    logical_record_position='Wibble')
        self.assertEqual("Invalid Check Step Logical Record Position Wibble",
                         str(exc.exception))


class TestMdsolEditCheckDef(unittest.TestCase):
    """Test extensions to ODM for Edit Checks in Rave"""

    def test_build(self):
        tested = MdsolEditCheckDef("CHECK1")

        tested << MdsolCheckStep(data_format="$1", static_value="1")
        tested << MdsolCheckAction()

        doc = obj_to_doc(tested)
        self.assertEqual("mdsol:EditCheckDef", doc.tag)
        self.assertEqual("TRUE", doc.attrib['Active'])
        self.assertEqual("CHECK1", doc.attrib['OID'])
        self.assertEqual("FALSE", doc.attrib['NeedsRetesting'])
        self.assertEqual("FALSE", doc.attrib['BypassDuringMigration'])
        self.assertEqual("mdsol:CheckStep", list(doc)[0].tag)
        self.assertEqual("mdsol:CheckAction", list(doc)[1].tag)

    def test_cannot_accept_non_check_or_action_child(self):
        with self.assertRaises(ValueError):
            MdsolEditCheckDef("OID") << object()

    def test_accepts_check_step(self):
        tested = MdsolEditCheckDef("CHECK1")
        cs = MdsolCheckStep(data_format="$1", static_value="1")
        tested << cs
        self.assertEqual(cs, tested.check_steps[0])

    def test_accepts_check_action(self):
        tested = MdsolEditCheckDef("CHECK1")
        ca = MdsolCheckAction()
        tested << ca
        self.assertEqual(ca, tested.check_actions[0])


class TestMdsolDerivationStep(unittest.TestCase):
    """Test extensions to ODM for Derivation Steps in Rave"""

    def test_build(self):
        """
        We build a Derivation Value Step
        """
        tested = MdsolDerivationStep(data_format="$1", value="1")
        doc = obj_to_doc(tested)
        self.assertEqual("mdsol:DerivationStep", doc.tag)
        self.assertEqual("$1", doc.attrib['DataFormat'])
        self.assertEqual("1", doc.attrib['Value'])
        # No function param
        self.assertEqual("", doc.attrib.get('Function', ''))

    def test_build_function(self):
        """We build a Derivation Function Step"""
        tested = MdsolDerivationStep(function=StepType.Add)
        doc = obj_to_doc(tested)
        self.assertEqual("mdsol:DerivationStep", doc.tag)
        self.assertEqual(StepType.Add.value, doc.attrib['Function'])
        # No data format param
        self.assertEqual("", doc.attrib.get('DataFormat', ''))

    def test_build_datastep(self):
        """We build a Derivation Data Step"""
        tested = MdsolDerivationStep(variable_oid="VAROID", field_oid="FIELDOID",
                                     form_oid="VFORM",
                                     folder_oid="MyFolder",
                                     record_position=0, form_repeat_number=2, folder_repeat_number=3,
                                     logical_record_position=LogicalRecordPositionType.MaxBySubject)
        doc = obj_to_doc(tested)
        self.assertEqual("mdsol:DerivationStep", doc.tag)
        self.assertEqual("VAROID", doc.attrib['VariableOID'])
        self.assertEqual("FIELDOID", doc.attrib['FieldOID'])
        self.assertEqual("VFORM", doc.attrib['FormOID'])
        self.assertEqual("MyFolder", doc.attrib['FolderOID'])
        self.assertEqual("0", doc.attrib['RecordPosition'])
        self.assertEqual("2", doc.attrib['FormRepeatNumber'])
        self.assertEqual("3", doc.attrib['FolderRepeatNumber'])
        self.assertEqual("MaxBySubject", doc.attrib['LogicalRecordPosition'])
        # No data format param
        self.assertEqual("", doc.attrib.get('DataFormat', ''))
        # No function param
        self.assertEqual("", doc.attrib.get('Function', ''))

    def test_build_custom_function(self):
        """We build a Custom Function Step"""
        tested = MdsolDerivationStep(custom_function="AlwaysTrue*")
        doc = obj_to_doc(tested)
        self.assertEqual("mdsol:DerivationStep", doc.tag)
        self.assertEqual("AlwaysTrue*", doc.attrib['CustomFunction'])
        # No data format param
        self.assertEqual("", doc.attrib.get('DataFormat', ''))
        # No function param
        self.assertEqual("", doc.attrib.get('Function', ''))

    def test_invalid_function(self):
        """Trying to create a Derivation with an invalid step raises an exception"""
        with self.assertRaises(AttributeError):
            # StepType.IsPresent not valid for DerivationStep
            MdsolDerivationStep(function=StepType.IsPresent)

    def test_create_with_valid_lrp(self):
        """We create a function with a valid LRP value"""
        tested = MdsolDerivationStep(custom_function="AlwaysTrue*",
                                     logical_record_position=LogicalRecordPositionType.Last)
        doc = obj_to_doc(tested)
        self.assertEqual("mdsol:DerivationStep", doc.tag)
        self.assertEqual("AlwaysTrue*", doc.attrib['CustomFunction'])
        self.assertEqual("Last", doc.attrib['LogicalRecordPosition'])

    def test_create_with_invalid_lrp(self):
        """We create a function with an invalid LRP value"""
        with self.assertRaises(AttributeError) as exc:
            tested = MdsolDerivationStep(custom_function="AlwaysTrue*",
                                         logical_record_position='Wibble')
        self.assertEqual("Invalid Derivation Logical Record Position Wibble",
                         str(exc.exception))


class TestMdsolDerivationDef(unittest.TestCase):
    """Test extensions to ODM for Derivations in Rave"""

    def test_build(self):
        tested = MdsolDerivationDef("AGE",
                                    variable_oid="VAROID", field_oid="FIELDOID",
                                    form_oid="MyForm",
                                    folder_oid="MyFolder",
                                    record_position=0, form_repeat_number=2, folder_repeat_number=3,
                                    logical_record_position=LogicalRecordPositionType.MaxBySubject,
                                    all_variables_in_fields=True,
                                    all_variables_in_folders=True)
        doc = obj_to_doc(tested)
        tested << MdsolDerivationStep(function=StepType.Age)

        doc = obj_to_doc(tested)
        self.assertEqual("mdsol:DerivationDef", doc.tag)
        self.assertEqual("TRUE", doc.attrib['Active'])
        self.assertEqual("AGE", doc.attrib['OID'])
        self.assertEqual("FALSE", doc.attrib['NeedsRetesting'])
        self.assertEqual("FALSE", doc.attrib['BypassDuringMigration'])
        self.assertEqual("TRUE", doc.attrib['AllVariablesInFolders'])
        self.assertEqual("TRUE", doc.attrib['AllVariablesInFields'])
        self.assertEqual("VAROID", doc.attrib['VariableOID'])
        self.assertEqual("FIELDOID", doc.attrib['FieldOID'])
        self.assertEqual("MyForm", doc.attrib['FormOID'])
        self.assertEqual("MyFolder", doc.attrib['FolderOID'])
        self.assertEqual("0", doc.attrib['RecordPosition'])
        self.assertEqual("2", doc.attrib['FormRepeatNumber'])
        self.assertEqual("3", doc.attrib['FolderRepeatNumber'])
        self.assertEqual("MaxBySubject", doc.attrib['LogicalRecordPosition'])

        self.assertEqual("mdsol:DerivationStep", list(doc)[0].tag)

    def test_cannot_accept_any_child(self):
        with self.assertRaises(ValueError):
            MdsolDerivationDef("OID") << object()

    def test_accepts_derivation_step(self):
        tested = MdsolDerivationDef("AGE")
        ds = MdsolDerivationStep(function=StepType.Age)
        tested << ds
        self.assertEqual(ds, tested.derivation_steps[0])

    def test_create_with_valid_lrp(self):
        """We create a function with a valid LRP value"""
        tested = MdsolDerivationDef('TANK',
                                    logical_record_position=LogicalRecordPositionType.Last)
        doc = obj_to_doc(tested)
        self.assertEqual("mdsol:DerivationDef", doc.tag)
        self.assertEqual("Last", doc.attrib['LogicalRecordPosition'])

    def test_create_with_invalid_lrp(self):
        """We create a function with an invalid LRP value"""
        with self.assertRaises(AttributeError) as exc:
            tested = MdsolDerivationDef('TANK',
                                        logical_record_position='Wibble')
        self.assertEqual("Invalid Derivation Def Logical Record Position Wibble",
                         str(exc.exception))


class TestMetaDataVersion(unittest.TestCase):
    """Contains Metadata for study design. Rave only allows one, the spec allows many in an ODM doc"""

    def test_cannot_accept_non_protocol_child(self):
        with self.assertRaises(ValueError):
            MetaDataVersion("OID", "NAME") << object()

    def test_can_accept_protocol_child(self):
        prot = Protocol()
        tested = MetaDataVersion("OID", "NAME")(prot)
        self.assertEqual(prot, tested.protocol)

    def test_can_accept_study_eventdef_child(self):
        sed = StudyEventDef("OID", "Name", False, StudyEventDef.SCHEDULED)
        tested = MetaDataVersion("OID", "NAME")(sed)
        self.assertEqual(sed, tested.study_event_defs[0])

    def test_can_accept_confirmation_message(self):
        cm = MdsolConfirmationMessage("Form saved", lang="eng")
        tested = MetaDataVersion("OID", "NAME")(cm)
        self.assertEqual(cm, tested.confirmation_message)

    def test_can_accept_edit_check_def(self):
        ec = MdsolEditCheckDef("CHECK1")
        tested = MetaDataVersion("OID", "NAME")(ec)
        self.assertEqual(ec, tested.edit_checks[0])

    def test_can_accept_derivation_def(self):
        dd = MdsolDerivationDef("DEV1")
        tested = MetaDataVersion("OID", "NAME")(dd)
        self.assertEqual(dd, tested.derivations[0])

    def test_can_accept_custom_function_def(self):
        cf = MdsolCustomFunctionDef("DEV1", "return true;", language=MdsolCustomFunctionDef.C_SHARP)
        tested = MetaDataVersion("OID", "NAME")(cf)
        self.assertEqual(cf, tested.custom_functions[0])

    def test_builder(self):
        """XML produced"""
        tested = MetaDataVersion("OID", "NAME", description="A description",
                                 primary_formoid="DM",
                                 default_matrix_oid="DEFAULT",
                                 signature_prompt='I confirm.',
                                 delete_existing=True)

        tested << Protocol()
        tested << StudyEventDef("OID", "Name", False, StudyEventDef.SCHEDULED)
        tested << FormDef("FORM_OID", "FORM_Name")
        tested << ItemGroupDef("IG_DEMO", "Demography")
        tested << ItemDef("ID_AGE", "Demography", DataType.Integer, 3)
        tested << CodeList("C_YESNO", "Yes No", DataType.String)
        tested << MdsolLabelDef("LABEL1", "first label")
        tested << MdsolConfirmationMessage("Form has been submitted!")
        tested << MdsolCustomFunctionDef("AlwaysTrue*", "return true;")
        tested << MdsolEditCheckDef("DM01")
        tested << MdsolDerivationDef("AGE")

        doc = obj_to_doc(tested)
        self.assertEqual(doc.tag, "MetaDataVersion")
        self.assertEqual("OID", doc.attrib['OID'])
        self.assertEqual("NAME", doc.attrib['Name'])
        self.assertEqual("A description", doc.attrib['Description'])
        self.assertEqual("DEFAULT", doc.attrib['mdsol:DefaultMatrixOID'])
        self.assertEqual("I confirm.", doc.attrib['mdsol:SignaturePrompt'])
        self.assertEqual("DM", doc.attrib['mdsol:PrimaryFormOID'])
        self.assertEqual("Yes", doc.attrib['mdsol:DeleteExisting'])
        self.assertEqual("Protocol", list(doc)[0].tag)
        self.assertEqual("StudyEventDef", list(doc)[1].tag)
        self.assertEqual("FormDef", list(doc)[2].tag)
        self.assertEqual("ItemGroupDef", list(doc)[3].tag)
        self.assertEqual("ItemDef", list(doc)[4].tag)
        self.assertEqual("CodeList", list(doc)[5].tag)
        self.assertEqual("mdsol:ConfirmationMessage", list(doc)[6].tag)
        self.assertEqual("mdsol:LabelDef", list(doc)[7].tag)
        self.assertEqual("mdsol:EditCheckDef", list(doc)[8].tag)
        self.assertEqual("mdsol:DerivationDef", list(doc)[9].tag)
        self.assertEqual("mdsol:CustomFunctionDef", list(doc)[10].tag)


class TestStudy(unittest.TestCase):
    """Test Study Metadata class"""

    def test_oid(self):
        tested = Study('oid1')
        self.assertEqual('oid1', tested.oid)

    def test_default_project_type(self):
        tested = Study('oid1')
        self.assertEqual('Project', tested.project_type)

    def test_invalid_project_type(self):
        with self.assertRaises(ValueError):
            Study('oid1', project_type='Prijket')

    def test_provided_project_type(self):
        tested = Study('oid1', 'GlobalLibrary Volume')
        self.assertEqual('GlobalLibrary Volume', tested.project_type)

    def test_cannot_accept_itemdata(self):
        tested = Study('oid')
        with self.assertRaises(ValueError):
            tested << ItemData("Field1", "ValueC")

    def test_can_accept_globalvariables(self):
        tested = Study('oid')
        gv = GlobalVariables('MY_TEST_PROJECT')
        tested << gv
        self.assertEqual(gv, tested.global_variables)

    def test_cannot_accept_two_globalvariables(self):
        tested = Study('oid')(GlobalVariables('MY_TEST_PROJECT'))
        with self.assertRaises(ValueError):
            tested << GlobalVariables('Another_one')

    def test_can_accept_basic_definitions(self):
        tested = Study('oid')
        bd = BasicDefinitions()
        tested << bd
        self.assertEqual(bd, tested.basic_definitions)

    def test_cannot_accept_two_basic_definitions(self):
        tested = Study('oid')(BasicDefinitions())
        with self.assertRaises(ValueError):
            tested << BasicDefinitions()

    def test_can_accept_metadata_version(self):
        tested = Study('oid')
        mv = MetaDataVersion('OID', 'Name')
        tested << mv
        self.assertEqual(mv, tested.metadata_version)

    def test_cannot_accept_two_metadata_versions(self):
        tested = Study('oid')(MetaDataVersion('OID1', 'NAME1'))
        with self.assertRaises(ValueError):
            tested << MetaDataVersion('OID2', 'NAME2')

    def test_builder(self):
        """XML produced"""
        tested = Study('oid1', 'GlobalLibrary Volume')(
            GlobalVariables('MY_TEST_PROJECT'),
            BasicDefinitions(),
            MetaDataVersion("OID", "NAME")
        )
        doc = obj_to_doc(tested)
        self.assertEqual(doc.tag, "Study")
        self.assertEqual(doc.attrib['mdsol:ProjectType'], "GlobalLibrary Volume")
        self.assertEqual(list(doc)[0].tag, "GlobalVariables")
        self.assertEqual(list(doc)[1].tag, "BasicDefinitions")
        self.assertEqual(list(doc)[2].tag, "MetaDataVersion")


class TestAlias(unittest.TestCase):

    def test_alias(self):
        """Create an Alias"""
        obj = Alias(context="SDTM", name="DM.BRTHDTC")
        doc = obj_to_doc(obj)
        self.assertEqual('Alias', doc.tag)
        self.assertEqual('SDTM', doc.get('Context'))
        self.assertEqual('DM.BRTHDTC', doc.get('Name'))

    def test_add_alias_to_item(self):
        """Add an Alias to an ItemDef"""
        obj = ItemDef(oid="DM.BRTHDAT", name="Date of Birth", datatype=DataType.Date,
                      date_time_format="dd MMM yyyy")
        obj << Alias(context="SDTM", name="DM.BRTHDTC")
        doc = obj_to_doc(obj)
        self.assertEqual('ItemDef', doc.tag)
        self.assertEqual('Alias', list(doc)[0].tag)

    def test_add_alias_to_protocol(self):
        """Add an Alias to an Protocol"""
        obj = Protocol()
        obj << Alias(context="SDTM", name="DM.BRTHDTC")
        doc = obj_to_doc(obj)
        self.assertEqual('Protocol', doc.tag)
        self.assertEqual('Alias', list(doc)[0].tag)

    def test_add_alias_to_studyevent(self):
        """Add an Alias to an StudyEventDef"""
        obj = StudyEventDef(oid="DM", name="Screening", repeating=False, event_type=StudyEventDef.SCHEDULED)
        obj << Alias(context="MAPPING", name="Screening")
        doc = obj_to_doc(obj)
        self.assertEqual('StudyEventDef', doc.tag)
        self.assertEqual('Alias', list(doc)[0].tag)

    def test_add_alias_to_form(self):
        """Add an Alias to an FormDef"""
        obj = FormDef(oid="DM", name="Demographics", repeating=False)
        obj << Alias(context="MAPPING", name="DEMOG")
        doc = obj_to_doc(obj)
        self.assertEqual('FormDef', doc.tag)
        self.assertEqual('Alias', list(doc)[0].tag)

    def test_add_alias_to_itemgroup(self):
        """Add an Alias to an ItemGroupDef"""
        obj = ItemGroupDef(oid="VS", name="Vitals", repeating=False)
        obj << Alias(context="MAPPING", name="VITALS")
        doc = obj_to_doc(obj)
        self.assertEqual('ItemGroupDef', doc.tag)
        self.assertEqual('Alias', list(doc)[0].tag)

    def test_add_alias_to_codelist(self):
        """Add an Alias to an CodeList"""
        obj = CodeList(oid="VSTEST", name="Vitals Tests", datatype=DataType.String)
        obj << Alias(context="MAPPING", name="VITALS")
        doc = obj_to_doc(obj)
        self.assertEqual('CodeList', doc.tag)
        self.assertEqual('Alias', list(doc)[0].tag)

    def test_add_alias_to_codelistitem(self):
        """Add an Alias to an CodeListItem"""
        obj = CodeListItem(coded_value="SYSBP")
        obj << Alias(context="MAPPING", name="Systolic Blood Pressure")
        doc = obj_to_doc(obj)
        self.assertEqual('CodeListItem', doc.tag)
        self.assertEqual('Alias', list(doc)[0].tag)

