view Resources/CodeGeneration/stonegentool_test.py @ 971:bc7b249dfbd0 toa2019082902

Added EMSCRIPTEN_FETCH_REPLACE flag to requests to prevent stale cache results
author Benjamin Golinvaux <bgo@osimis.io>
date Thu, 29 Aug 2019 13:11:49 +0200
parents 342f3e04bfa9
children
line wrap: on
line source

#
#        1         2         3         4         5         6         7         8
# 345678901234567890123456789012345678901234567890123456789012345678901234567890
#

from stonegentool import \
EatToken,SplitListOfTypes,ParseTemplateType,ProcessSchema, \
CheckSchemaSchema,LoadSchema,trim,ComputeRequiredDeclarationOrder, \
GetTemplatingDictFromSchemaFilename,MakeTemplate,MakeTemplateFromFile,LoadSchemaFromString,GetTemplatingDictFromSchema
import unittest
import os
import re
import pprint
from jinja2 import Template

def RemoveDateTimeLine(s : str):
  # regex are non-multiline by default, and $ does NOT match the end of the line
  s2 = re.sub(r"^// autogenerated by stonegentool on .*\n","",s)
  return s2

class TestStonegentool(unittest.TestCase):
  def test_EatToken_empty(self):
    c = r""
    a,b = EatToken(c)
    self.assertEqual(a,r"")
    self.assertEqual(b,r"")

  def test_EatToken_simpleNonTemplate(self):
    c = r"int32"
    a,b = EatToken(c)
    self.assertEqual(a,r"int32")
    self.assertEqual(b,r"")

  def test_EatToken_simpleTemplate(self):
    c = r"vector<string>"
    a,b = EatToken(c)
    self.assertEqual(a,r"vector<string>")
    self.assertEqual(b,r"")

  def test_EatToken_complexTemplate(self):
    c = r"vector<map<int64,string>>,vector<map<int32,string>>"
    a,b = EatToken(c)
    self.assertEqual(a,r"vector<map<int64,string>>")
    self.assertEqual(b,r"vector<map<int32,string>>")

  def test_EatToken_complexTemplates(self):
    c = r"vector<map<vector<string>,map<int32,string>>>,map<int32,string>,map<map<int32,string>,string>"
    a,b = EatToken(c)
    self.assertEqual(a,r"vector<map<vector<string>,map<int32,string>>>")
    self.assertEqual(b,r"map<int32,string>,map<map<int32,string>,string>")
    a,b = EatToken(b)
    self.assertEqual(a,r"map<int32,string>")
    self.assertEqual(b,r"map<map<int32,string>,string>")

  def test_SplitListOfTypes(self):
    c = r"vector<map<vector<string>,map<int32,string>>>,map<int32,string>,map<map<int32,string>,string>"
    lot = SplitListOfTypes(c)
    self.assertEqual(3,len(lot))
    self.assertEqual("vector<map<vector<string>,map<int32,string>>>",lot[0])
    self.assertEqual("map<int32,string>",lot[1])
    self.assertEqual("map<map<int32,string>,string>",lot[2])

  def test_SplitListOfTypes_bogus(self):
    c = r"vector<map<vector<string>,map<int32,string>>,map<int32,string>,map<map<int32,string>,string"
    self.assertRaises(Exception,SplitListOfTypes,c) # the argument c must be passed to assertRaises, not as a normal call of SplitListOfTypes
    
  def test_ParseTemplateType_true(self):
    c = "map<vector<map<int,vector<string>>>,map<vector<int>,vector<string>>>"
    (ok,a,b) = ParseTemplateType(c)
    self.assertEqual(ok,True)
    self.assertEqual(a,"map")
    self.assertEqual(b,["vector<map<int,vector<string>>>","map<vector<int>,vector<string>>"])

    (ok2,a2,b2) = ParseTemplateType(b[0])
    self.assertEqual(ok2,True)
    self.assertEqual(a2,"vector")
    self.assertEqual(b2,["map<int,vector<string>>"])

    (ok3,a3,b3) = ParseTemplateType(b[1])
    self.assertEqual(ok3,True)
    self.assertEqual(a3,"map")
    self.assertEqual(b3,["vector<int>","vector<string>"])

    (ok4,a4,b4) = ParseTemplateType(b2[0])
    self.assertEqual(ok4,True)
    self.assertEqual(a4,"map")
    self.assertEqual(b4,["int","vector<string>"])
    
  def test_ParseSchema(self):
    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml')
    obj = LoadSchema(fn)
    # we're happy if it does not crash :)
    CheckSchemaSchema(obj)

  def test_ComputeRequiredDeclarationOrder(self):
    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml')
    obj = LoadSchema(fn)
    genOrder: str = ComputeRequiredDeclarationOrder(obj)
    self.assertEqual(5,len(genOrder))
    self.assertEqual("A",genOrder[0])
    self.assertEqual("B",genOrder[1])
    self.assertEqual("C",genOrder[2])
    self.assertEqual("Message1",genOrder[3])
    self.assertEqual("Message2",genOrder[4])

  # def test_GeneratePreambleEnumerationAndStructs(self):
  #   fn = os.path.join(os.path.dirname(__file__), 'test', 'test1.jsonc')
  #   obj = LoadSchema(fn)
  #   (_,genc,_) = ProcessSchema(obj)

  def test_genEnums(self):
    self.maxDiff = None
    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml')
    obj = LoadSchema(fn)
    genOrder: str = ComputeRequiredDeclarationOrder(obj)
    processedSchema = ProcessSchema(obj, genOrder)
    self.assertTrue('rootName' in processedSchema)

    structs = {}
    for v in processedSchema['structs']:
      structs[v['name']] = v
    enums = {}
    for v in processedSchema['enums']:
      enums[v['name']] = v

    self.assertTrue('C' in structs)
    self.assertTrue('someBs' in structs['C']['fields'])
    self.assertTrue('CrispType' in enums)
    self.assertTrue('Message1' in structs)
    self.assertEqual('int32', structs['Message1']['fields']['memberInt32'].type)
    self.assertEqual('string', structs['Message1']['fields']['memberString'].type)
    self.assertEqual('EnumMonth0', structs['Message1']['fields']['memberEnumMonth'].type)
    self.assertEqual('bool', structs['Message1']['fields']['memberBool'].type)
    self.assertEqual('float32', structs['Message1']['fields']['memberFloat32'].type)
    self.assertEqual('float64', structs['Message1']['fields']['memberFloat64'].type)

  def test_GenerateTypeScriptEnums(self):
    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml')
    tdico = GetTemplatingDictFromSchemaFilename(fn)
    template = Template("""  // end of generic methods
{% for enum in enums%}  export enum {{enum['name']}} {
{% for key in enum['fields']%}    {{key}},
{%endfor%}  };

{%endfor%}""")
    renderedCode = template.render(**tdico)
    renderedCodeRef = """  // end of generic methods
  export enum MovieType {
    RomCom,
    Horror,
    ScienceFiction,
    Vegetables,
  };

  export enum CrispType {
    SaltAndPepper,
    CreamAndChives,
    Paprika,
    Barbecue,
  };

  export enum EnumMonth0 {
    January,
    February,
    March,
  };

"""
    self.assertEqual(renderedCodeRef,renderedCode)

  def test_GenerateCplusplusEnums(self):
    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml')
    tdico = GetTemplatingDictFromSchemaFilename(fn)
    template = Template("""  // end of generic methods
{% for enum in enums%}  enum {{enum['name']}} {
{% for key in enum['fields']%}    {{key}},
{%endfor%}  };

{%endfor%}""")
    renderedCode = template.render(**tdico)
    renderedCodeRef = """  // end of generic methods
  enum MovieType {
    RomCom,
    Horror,
    ScienceFiction,
    Vegetables,
  };

  enum CrispType {
    SaltAndPepper,
    CreamAndChives,
    Paprika,
    Barbecue,
  };

  enum EnumMonth0 {
    January,
    February,
    March,
  };

"""
    self.assertEqual(renderedCodeRef,renderedCode)

  def test_generateTsStructType(self):
    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml')
    tdico = GetTemplatingDictFromSchemaFilename(fn)

#     template = MakeTemplate("""  // end of generic methods
# {% for struct in struct%}  export class {{struct['name']}} {
#   {% for key in struct['fields']%}    {{key}}:{{struct['fields'][key]}},
#   {% endfor %}  
#     constructor() {
#   {% for key in struct['fields']%}
#     {% if NeedsConstruction(struct['fields']['key'])}
#       {{key}} = new {{CanonToTs(struct['fields']['key'])}};
#     {% end if %}
#   {% endfor %}
#     }
# {% endfor %}
#     public StoneSerialize(): string {
#       let container: object = {};
#       container['type'] = '{{rootName}}.{{struct['name']}}';
#       container['value'] = this;
#       return JSON.stringify(container);
#     }  };""")
    template = MakeTemplate("""  // end of generic methods
{% for struct in structs%}  export class {{struct['name']}} {
{% for key in struct['fields']%}    {{key}}:{{CanonToTs(struct['fields'][key]['type'])}};
{% endfor %}
    constructor() {
{% for key in struct['fields']%}      this.{{key}} = new {{CanonToTs(struct['fields'][key]['type'])}}();
{% endfor %}    }

    public StoneSerialize(): string {
      let container: object = {};
      container['type'] = '{{rootName}}.{{struct['name']}}';
      container['value'] = this;
      return JSON.stringify(container);
    }
  };

{% endfor %}""")
    renderedCode = template.render(**tdico)
    renderedCodeRef = """  // end of generic methods
  export class A {
    someStrings:Array<string>;
    someInts2:Array<number>;
    movies:Array<MovieType>;

    constructor() {
      this.someStrings = new Array<string>();
      this.someInts2 = new Array<number>();
      this.movies = new Array<MovieType>();
    }

    public StoneSerialize(): string {
      let container: object = {};
      container['type'] = 'TestStoneCodeGen.A';
      container['value'] = this;
      return JSON.stringify(container);
    }
  };

  export class B {
    someAs:Array<A>;
    someInts:Array<number>;

    constructor() {
      this.someAs = new Array<A>();
      this.someInts = new Array<number>();
    }

    public StoneSerialize(): string {
      let container: object = {};
      container['type'] = 'TestStoneCodeGen.B';
      container['value'] = this;
      return JSON.stringify(container);
    }
  };

  export class C {
    someBs:Array<B>;
    ddd:Array<string>;

    constructor() {
      this.someBs = new Array<B>();
      this.ddd = new Array<string>();
    }

    public StoneSerialize(): string {
      let container: object = {};
      container['type'] = 'TestStoneCodeGen.C';
      container['value'] = this;
      return JSON.stringify(container);
    }
  };

  export class Message1 {
    memberInt32:number;
    memberString:string;
    memberEnumMonth:EnumMonth0;
    memberBool:boolean;
    memberFloat32:number;
    memberFloat64:number;

    constructor() {
      this.memberInt32 = new number();
      this.memberString = new string();
      this.memberEnumMonth = new EnumMonth0();
      this.memberBool = new boolean();
      this.memberFloat32 = new number();
      this.memberFloat64 = new number();
    }

    public StoneSerialize(): string {
      let container: object = {};
      container['type'] = 'TestStoneCodeGen.Message1';
      container['value'] = this;
      return JSON.stringify(container);
    }
  };

  export class Message2 {
    memberString:string;
    memberStringWithDefault:string;
    memberVectorOfMessage1:Array<Message1>;
    memberVectorOfString:Array<string>;
    memberMapStringString:Map<string, string>;
    memberMapStringStruct:Map<string, Message1>;
    memberMapEnumFloat:Map<CrispType, number>;
    memberEnumMovieType:MovieType;
    memberJson:Object;

    constructor() {
      this.memberString = new string();
      this.memberStringWithDefault = new string();
      this.memberVectorOfMessage1 = new Array<Message1>();
      this.memberVectorOfString = new Array<string>();
      this.memberMapStringString = new Map<string, string>();
      this.memberMapStringStruct = new Map<string, Message1>();
      this.memberMapEnumFloat = new Map<CrispType, number>();
      this.memberEnumMovieType = new MovieType();
      this.memberJson = new Object();
    }

    public StoneSerialize(): string {
      let container: object = {};
      container['type'] = 'TestStoneCodeGen.Message2';
      container['value'] = this;
      return JSON.stringify(container);
    }
  };

"""
    # print(renderedCode)
    self.maxDiff = None
    self.assertEqual(renderedCodeRef, renderedCode)

  def test_generateWholeTsFile(self):
    schemaFile = \
      os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml')
    tdico = GetTemplatingDictFromSchemaFilename(schemaFile)
    tsTemplateFile = \
      os.path.join(os.path.dirname(__file__), 'template.in.ts.j2')
    template = MakeTemplateFromFile(tsTemplateFile)
    renderedCode = template.render(**tdico)
    print(renderedCode)

  def test_GenerateTypeScriptHandlerInterface(self):
    pass

  def test_GenerateCppHandlerInterface(self):
    pass

  def test_GenerateTypeScriptDispatcher(self):
    pass

  def test_GenerateCppDispatcher(self):
    pass

  def test_StringDefaultValueInTs(self):
    schema = LoadSchemaFromString("""
                                  rootName: MyTest
                                  struct Toto:
                                    withoutDefault: string
                                    withDefault: string = \"tutu\"
                                  """)
    tdico = GetTemplatingDictFromSchema(schema)

    tsTemplateFile = os.path.join(os.path.dirname(__file__), 'template.in.ts.j2')
    template = MakeTemplateFromFile(tsTemplateFile)
    renderedCode = template.render(**tdico)
    self.assertIn("withDefault = \"tutu\"", renderedCode)
    # print(renderedCode)

    cppTemplateFile = os.path.join(os.path.dirname(__file__), 'template.in.h.j2')
    template = MakeTemplateFromFile(cppTemplateFile)
    renderedCode = template.render(**tdico)
    print(renderedCode)
    self.assertIn("withDefault = \"tutu\"", renderedCode)


  def test_EnumDefaultValue(self):
    schema = LoadSchemaFromString("""
                                  rootName: MyTest
                                  enum MyEnum:
                                    - Toto
                                    - Tutu
                                  struct Toto:
                                    withoutDefault: MyEnum
                                    withDefault: MyEnum = Toto
                                  """)
    tdico = GetTemplatingDictFromSchema(schema)

    tsTemplateFile = os.path.join(os.path.dirname(__file__), 'template.in.ts.j2')
    template = MakeTemplateFromFile(tsTemplateFile)
    renderedCode = template.render(**tdico)
    # print(renderedCode)
    self.assertIn("withDefault = MyEnum.Toto", renderedCode)

    tsTemplateFile = os.path.join(os.path.dirname(__file__), 'template.in.h.j2')
    template = MakeTemplateFromFile(tsTemplateFile)
    renderedCode = template.render(**tdico)
    self.assertIn("withDefault = MyTest::MyEnum_Toto", renderedCode)
    # print(renderedCode)


# def test(self):
#   s = 'hello world'
#   self.assertEqual(s.split(), ['hello', 'world'])
#   # check that s.split fails when the separator is not a string
#   with self.assertRaises(TypeError):
#   s.split(2)

if __name__ == '__main__':
  unittest.main()