import os, sys

expected_collections="""module ODLMetaObjects {
  class ODLMetaObjects::Other;
  class ODLMetaObjects::Foo;
  class Other  {
    relationship ODLMetaObjects::Foo so inverse ODLMetaObjects::Foo::os;
    relationship ODLMetaObjects::Foo bo inverse ODLMetaObjects::Foo::ob;
    relationship ODLMetaObjects::Foo lo inverse ODLMetaObjects::Foo::ol;
  };
  class Foo  {
    attribute bag <boolean> bb;
    attribute set <boolean> bs;
    attribute list <boolean> bl;
    attribute bag <short> sb;
    attribute set <short> ss;
    attribute list <short> sl;
    attribute bag <string> s2b;
    attribute set <string> s2s;
    attribute list <string> s2l;
    attribute bag <unsigned short> usb;
    attribute set <unsigned short> uss;
    attribute list <unsigned short> usl;
    attribute bag <long> lb;
    attribute set <long> ls;
    attribute list <long> ll;
    attribute bag <unsigned long> ulb;
    attribute set <unsigned long> uls;
    attribute list <unsigned long> ull;
    attribute bag <long long> llb;
    attribute set <long long> lls;
    attribute list <long long> lll;
    attribute bag <float> fb;
    attribute set <float> fs;
    attribute list <float> fl;
    attribute bag <double> db;
    attribute set <double> ds;
    attribute list <double> dl;
    attribute dictionary <boolean,boolean> bbd;
    attribute dictionary <boolean,short> bsd;
    attribute dictionary <boolean,string> bs2d;
    attribute dictionary <boolean,unsigned short> busd;
    attribute dictionary <boolean,long> bld;
    attribute dictionary <boolean,unsigned long> buld;
    attribute dictionary <boolean,long long> blld;
    attribute dictionary <boolean,float> bfd;
    attribute dictionary <boolean,double> bdd;
    attribute dictionary <boolean,ODLMetaObjects::Other> bo;
    attribute dictionary <short,boolean> sbd;
    attribute dictionary <short,short> ssd;
    attribute dictionary <short,string> ss2d;
    attribute dictionary <short,unsigned short> susd;
    attribute dictionary <short,long> sld;
    attribute dictionary <short,unsigned long> suld;
    attribute dictionary <short,long long> slld;
    attribute dictionary <short,float> sfd;
    attribute dictionary <short,double> sdd;
    attribute dictionary <short,ODLMetaObjects::Other> so;
    attribute dictionary <string,boolean> s2bd;
    attribute dictionary <string,short> s2sd;
    attribute dictionary <string,string> s2s2d;
    attribute dictionary <string,unsigned short> s2usd;
    attribute dictionary <string,long> s2ld;
    attribute dictionary <string,unsigned long> s2uld;
    attribute dictionary <string,long long> s2lld;
    attribute dictionary <string,float> s2fd;
    attribute dictionary <string,double> s2dd;
    attribute dictionary <string,ODLMetaObjects::Other> s2o;
    attribute dictionary <unsigned short,boolean> usbd;
    attribute dictionary <unsigned short,short> ussd;
    attribute dictionary <unsigned short,string> uss2d;
    attribute dictionary <unsigned short,unsigned short> ususd;
    attribute dictionary <unsigned short,long> usld;
    attribute dictionary <unsigned short,unsigned long> usuld;
    attribute dictionary <unsigned short,long long> uslld;
    attribute dictionary <unsigned short,float> usfd;
    attribute dictionary <unsigned short,double> usdd;
    attribute dictionary <unsigned short,ODLMetaObjects::Other> uso;
    attribute dictionary <long,boolean> lbd;
    attribute dictionary <long,short> lsd;
    attribute dictionary <long,string> ls2d;
    attribute dictionary <long,unsigned short> lusd;
    attribute dictionary <long,long> lld;
    attribute dictionary <long,unsigned long> luld;
    attribute dictionary <long,long long> llld;
    attribute dictionary <long,float> lfd;
    attribute dictionary <long,double> ldd;
    attribute dictionary <long,ODLMetaObjects::Other> lo;
    attribute dictionary <unsigned long,boolean> ulbd;
    attribute dictionary <unsigned long,short> ulsd;
    attribute dictionary <unsigned long,string> uls2d;
    attribute dictionary <unsigned long,unsigned short> ulusd;
    attribute dictionary <unsigned long,long> ulld;
    attribute dictionary <unsigned long,unsigned long> ululd;
    attribute dictionary <unsigned long,long long> ullld;
    attribute dictionary <unsigned long,float> ulfd;
    attribute dictionary <unsigned long,double> uldd;
    attribute dictionary <unsigned long,ODLMetaObjects::Other> ulo;
    attribute dictionary <long long,boolean> llbd;
    attribute dictionary <long long,short> llsd;
    attribute dictionary <long long,string> lls2d;
    attribute dictionary <long long,unsigned short> llusd;
    attribute dictionary <long long,long> llld2;
    attribute dictionary <long long,unsigned long> lluld;
    attribute dictionary <long long,long long> lllld;
    attribute dictionary <long long,float> llfd;
    attribute dictionary <long long,double> lldd;
    attribute dictionary <long long,ODLMetaObjects::Other> llo;
    attribute dictionary <float,boolean> fbd;
    attribute dictionary <float,short> fsd;
    attribute dictionary <float,string> fs2d;
    attribute dictionary <float,unsigned short> fusd;
    attribute dictionary <float,long> fld;
    attribute dictionary <float,unsigned long> fuld;
    attribute dictionary <float,long long> flld;
    attribute dictionary <float,float> ffd;
    attribute dictionary <float,double> fdd;
    attribute dictionary <float,ODLMetaObjects::Other> fo;
    attribute dictionary <double,boolean> dbd;
    attribute dictionary <double,short> dsd;
    attribute dictionary <double,string> ds2d;
    attribute dictionary <double,unsigned short> dusd;
    attribute dictionary <double,long> dld;
    attribute dictionary <double,unsigned long> duld;
    attribute dictionary <double,long long> dlld;
    attribute dictionary <double,float> dfd;
    attribute dictionary <double,double> ddd;
    attribute dictionary <double,ODLMetaObjects::Other> do;
    relationship set <ODLMetaObjects::Other> os inverse ODLMetaObjects::Other::so;
    relationship bag <ODLMetaObjects::Other> ob inverse ODLMetaObjects::Other::bo;
    relationship list <ODLMetaObjects::Other> ol inverse ODLMetaObjects::Other::lo;
  };
};
"""

expected_constants="""module ODLMetaObjects {
  const short TOP_LEVEL = 69;
  module Test {
    class ODLMetaObjects::Test::Foo;
    const string MODULE_LEVEL = "FOO_CONST";
    class Foo  {
      const boolean CLASS_LEVEL = 1;
      const short CLASS_LEVEL_SHORT = 15;
      const float CLASS_LEVEL_FLOAT = 3.14;
      const boolean CLASS_LEVEL_FALSE = 0;
      const string CLASS_LEVEL_REFERENCE = MODULE_LEVEL;
      const short CLASS_LEVEL_GLOBAL_REFERENCE = TOP_LEVEL;
      attribute string bar;
    };
  };
};
"""

expected_xml="""module ODLMetaObjects {
  module SimpleDom {
    class ODLMetaObjects::SimpleDom::Node;
    class ODLMetaObjects::SimpleDom::Document;
    class ODLMetaObjects::SimpleDom::Attribute;
    class ODLMetaObjects::SimpleDom::Element;
    class ODLMetaObjects::SimpleDom::Text;
    class Node  {
      relationship set <ODLMetaObjects::SimpleDom::Node> childNodes inverse ODLMetaObjects::SimpleDom::Node::parentNode;
      relationship ODLMetaObjects::SimpleDom::Node parentNode inverse ODLMetaObjects::SimpleDom::Node::childNodes;
    };
    class Document extends ODLMetaObjects::SimpleDom::Node (extent xml_documents) {
      attribute string root;
    };
    class Attribute extends ODLMetaObjects::SimpleDom::Node {
      attribute string prefix;
      attribute string namespaceUri;
      attribute string localName;
      attribute string value;
      relationship ODLMetaObjects::SimpleDom::Element ownerElement inverse ODLMetaObjects::SimpleDom::Element::attrs;
    };
    class Element extends ODLMetaObjects::SimpleDom::Node {
      attribute string prefix;
      attribute string namespaceUri;
      attribute string localName;
      relationship set <ODLMetaObjects::SimpleDom::Attribute> attrs inverse ODLMetaObjects::SimpleDom::Attribute::ownerElement;
    };
    class Text extends ODLMetaObjects::SimpleDom::Node {
      attribute string data;
    };
  };
};
"""
expected_enumeration = """module ODLMetaObjects {
  class ODLMetaObjects::Professor;
  enum WeekDays {Mon, Tue, Wed, Thu, Fri, Sat, Sun};
  class Professor  {
    attribute ODLMetaObjects::WeekDays foo;
  };
};
"""

expected_types = """module ODLMetaObjects {
  class ODLMetaObjects::TestType;
  class ODLMetaObjects::TypeDefs;
  class TestType  {
    attribute boolean b;
    attribute short s;
    attribute char c;
    attribute string s2;
    attribute unsigned short us;
    attribute date da;
    attribute time t;
    attribute timestamp ts;
    attribute long l;
    attribute unsigned long ul;
    attribute long long ll;
    attribute float f;
    attribute double d;
    attribute octet o;
    attribute interval i;
  };
  typedef boolean myBoolean;
  typedef short myShort;
  typedef char myChar;
  typedef string myString;
  typedef unsigned short myUShort;
  typedef date myDate;
  typedef time myTime;
  typedef timestamp myTimestamp;
  typedef long myLong;
  typedef unsigned long myULong;
  typedef long long myLLong;
  typedef float myFloat;
  typedef double myDouble;
  typedef octet myOctet;
  typedef interval myInterval;
  typedef ODLMetaObjects::TestType myTestType;
  struct TestStruct {
    string foo;
  };
  typedef ODLMetaObjects::TestStruct myStruct;
  enum TestEnum {Foo};
  typedef ODLMetaObjects::TestEnum myEnum;
  class TypeDefs  {
    attribute ODLMetaObjects::myBoolean b;
    attribute ODLMetaObjects::myShort s;
    attribute ODLMetaObjects::myChar c;
    attribute ODLMetaObjects::myString s2;
    attribute ODLMetaObjects::myUShort us;
    attribute ODLMetaObjects::myDate da;
    attribute ODLMetaObjects::myTime t;
    attribute ODLMetaObjects::myTimestamp ts;
    attribute ODLMetaObjects::myLong l;
    attribute ODLMetaObjects::myULong ul;
    attribute ODLMetaObjects::myLLong ll;
    attribute ODLMetaObjects::myFloat f;
    attribute ODLMetaObjects::myDouble d;
    attribute ODLMetaObjects::myOctet o;
    attribute ODLMetaObjects::myInterval i;
    attribute list <ODLMetaObjects::myBoolean> bl;
    attribute dictionary <ODLMetaObjects::myBoolean,ODLMetaObjects::myString> bsd;
    attribute ODLMetaObjects::myTestType tt;
    attribute ODLMetaObjects::myStruct tst;
    attribute ODLMetaObjects::myEnum te;
  };
};
"""

expected_file = """module ODLMetaObjects {
  class ODLMetaObjects::BigFile;
  class BigFile  {
    attribute blob data;
  };
};
"""

expected_object_attribute="""module ODLMetaObjects {
  class ODLMetaObjects::Professor;
  class ODLMetaObjects::Student;
  class Professor  {
    attribute short points;
  };
  class Student  {
    attribute ODLMetaObjects::Professor spitBallTarget;
  };
};
"""

expected_varsity1="""module ODLMetaObjects {
  module Test {
    class ODLMetaObjects::Test::Professor;
    class ODLMetaObjects::Test::Student;
    class Professor  {
      attribute string name;
      attribute unsigned short employee_id;
      attribute string ssn;
      relationship ODLMetaObjects::Test::Student teaches inverse ODLMetaObjects::Test::Student::learns_from;
    };
    class Student  (extent students) {
      attribute string name;
      attribute unsigned short student_id;
      readonly attribute string ssn;
      relationship ODLMetaObjects::Test::Professor learns_from inverse ODLMetaObjects::Test::Professor::teaches;
    };
  };
};
"""
expected_ftss_example="""module ODLMetaObjects {
  class ODLMetaObjects::Resource;
  typedef list <string> AclList;
  class Resource  {
    attribute dictionary <string,ODLMetaObjects::AclList> acl;
  };
};
"""

expected_strucutre = """module ODLMetaObjects {
  module Test {
    class ODLMetaObjects::Test::Person;
    class ODLMetaObjects::Test::Organization;
    struct Address {
      string street;
      string zip;
      struct PhoneNum {
  string areaCode;
  string exchange;
  string local;
} pNum;
    };
    class Person  {
      attribute string name;
      struct ContactInfo {
        string phone;
        string fax;
        struct Address {
  string street;
  string zip;
  struct PhoneNum {
  string areaCode;
  string exchange;
  string local;
} pNum;
} officeAddr;
        enum SEX {MALE, FEMALE} sex;
      };
      attribute ODLMetaObjects::Test::Address homeAddr;
      attribute ODLMetaObjects::Test::Person::ContactInfo contact;
    };
    class Organization  {
      attribute string name;
      attribute ODLMetaObjects::Test::Address addr;
    };
  };
};
"""

expected_varsity2="""module ODLMetaObjects {
  class ODLMetaObjects::Person;
  class ODLMetaObjects::Professor;
  class ODLMetaObjects::Student;
  class Person  (extent people) {
    attribute string name;
    attribute string ssn;
  };
  class Professor extends ODLMetaObjects::Person {
    relationship list <ODLMetaObjects::Student> teaches inverse ODLMetaObjects::Student::learns_from;
  };
  class Student extends ODLMetaObjects::Person {
    relationship ODLMetaObjects::Professor learns_from inverse ODLMetaObjects::Professor::teaches;
  };
};
"""

expected_union="""module ODLMetaObjects {
  module Test {
    class ODLMetaObjects::Test::Person;
    enum UnionType {ONE, TWO, THREE, FOUR};
    union MyUnion switch (ODLMetaObjects::Test::UnionType) {
      case ONE: string o;
      case TWO: short t;
      default: float other;
    };
    class Person  {
      attribute ODLMetaObjects::Test::MyUnion u;
    };
  };
};
"""

expected_operations="""module ODLMetaObjects {
  module Test {
    class ODLMetaObjects::Test::Person;
    enum ErrorCode {STUPID_ERROR, DUMB_ERROR};
    exception MyException {
      enum ErrorCode {STUPID_ERROR, DUMB_ERROR} code;
      string reason;
    };
    exception MyOtherException {
    };
    class Person  {
      attribute string name;
      string simple();
      string myop(in string one, inout string two, out string three) raises (ODLMetaObjects::Test::MyException);
      string myonewayop(in string four, inout string five, out string diz) raises (ODLMetaObjects::Test::MyOtherException);
    };
  };
};
"""

expected_interfaces="""module ODLMetaObjects {
  module Test {
    interface ODLMetaObjects::Test::Animal;
    interface ODLMetaObjects::Test::Person;
    interface ODLMetaObjects::Test::HomeOwner;
    interface ODLMetaObjects::Test::HomeOwnerPerson;
    class ODLMetaObjects::Test::Professor;
    interface Animal {
      attribute string species
    };
;
    interface Person : ODLMetaObjects::Test::Animal {
      attribute string name
      attribute string ssn
      relationship ODLMetaObjects::Test::Person spouse_of inverse ODLMetaObjects::Test::Person::spouse
      relationship ODLMetaObjects::Test::Person spouse inverse ODLMetaObjects::Test::Person::spouse_of
    };
;
    interface HomeOwner {
      attribute string homeAddress
    };
;
    interface HomeOwnerPerson : ODLMetaObjects::Test::Person,ODLMetaObjects::Test::HomeOwner {
    };
;
    class Professor   : ODLMetaObjects::Test::HomeOwnerPerson {
      attribute list <string> classes;
    };
  };
};
"""

g_files = [('collections.odl',expected_collections),
           ('constants.odl',expected_constants),
           ('xml.odl',expected_xml),
           ('enumerations.odl',expected_enumeration),
           ('types.odl',expected_types),
           ('file.odl',expected_file),
           ('object_attribute.odl',expected_object_attribute),
           ('varsity1.odl',expected_varsity1),
           ('ftss_example.odl',expected_ftss_example),
           ('structure.odl',expected_strucutre),
           ('varsity2.odl',expected_varsity2),
           ('union.odl',expected_union),
           ('operations.odl',expected_operations),
           ('interfaces.odl',expected_interfaces)
           ]

from Ft.Ods import Database
from Ft.Ods.StorageManager import Adapters
from Ft.Ods.Parsers.Odl import PostProcessor

from Ft.Ods.Parsers.Odl import OdlParser
parsers = [OdlParser]
try:
    from Ft.Ods.Parsers.Odl import OdlParserc
    parsers.append(OdlParserc)
except:
    pass

def TestFile(tester,db,fileName,expected,parser):
    parser = parser.new()
    parse_tree = parser.parse(open(fileName).read())

    processor = PostProcessor.Processor(db)
    processor.run(parse_tree)

    schema = db.schema().resolve('ODLMetaObjects')
    res = schema._4ods_getOdl()
    #print res
    tester.compare(expected,res,diff=1)

import test_odl_parser
BASE_DIR = os.path.abspath(os.path.split(test_odl_parser.__file__)[0])

DBNAME=os.environ.get("ODS_TEST_DB","ods:test")



def Test(tester):

    mang = Adapters.GetManager()
    if mang.exists(DBNAME):
        mang.destroy(DBNAME)

    mang.new(DBNAME,Adapters.GetAdapter())
    db = Database.Database()
    db.open(DBNAME)
    try:
        for parser in parsers:
            tester.startGroup(parser.__name__)
            for file,expected in g_files:
                realFile = os.path.join(BASE_DIR,file)
                tx = db.new()
                tx.begin()
                tester.startTest(file)
                TestFile(tester,db,realFile,expected,parser)
                tester.testDone()
                tx.abort()
            tester.groupDone()
    finally:
        db.close()
        mang.destroy(DBNAME)


