diff --git a/pkg/gen/filters/filterpy/filters.go b/pkg/gen/filters/filterpy/filters.go index 0e5b29c2..4dbe218a 100644 --- a/pkg/gen/filters/filterpy/filters.go +++ b/pkg/gen/filters/filterpy/filters.go @@ -15,4 +15,5 @@ func PopulateFuncMap(fm template.FuncMap) { fm["pyVars"] = pyVars fm["pyType"] = pyType fm["pyExtern"] = pyExtern + fm["pyTestValue"] = pyTestValue } diff --git a/pkg/gen/filters/filterpy/py_testvalue.go b/pkg/gen/filters/filterpy/py_testvalue.go new file mode 100644 index 00000000..6d52c893 --- /dev/null +++ b/pkg/gen/filters/filterpy/py_testvalue.go @@ -0,0 +1,93 @@ +package filterpy + +import ( + "fmt" + + "github.com/apigear-io/cli/pkg/gen/filters/common" + "github.com/apigear-io/cli/pkg/model" +) + +// ToTestValueString returns the test value string for a given schema. +// We intentionally ignore arrays in order to return the test value of the inner type. +func ToTestValueString(prefix string, schema *model.Schema) (string, error) { + if schema == nil { + return "xxx", fmt.Errorf("pyTestValue schema is nil") + } + if schema.Module == nil { + return "xxx", fmt.Errorf("pyTestValue schema module is nil") + } + var text string + switch schema.KindType { + case model.TypeString: + text = "\"xyz\"" + case model.TypeInt, model.TypeInt32, model.TypeInt64: + text = "1" + case model.TypeFloat, model.TypeFloat32, model.TypeFloat64: + text = "1.1" + case model.TypeBool: + text = "True" + case model.TypeVoid: + return ToDefaultString(schema, prefix) + case model.TypeEnum: + e_local := schema.LookupEnum("", schema.Type) + e_imported := schema.LookupEnum(schema.Import, schema.Type) + if e_local == nil && e_imported == nil { + return "xxx", fmt.Errorf("pyTestValue enum not found: %s", schema.Dump()) + } + // if enum is local it is found both as e_local and e_imported + name := common.CamelTitleCase(e_imported.Name) + member := common.SnakeUpperCase(e_imported.Members[0].Name) + if len(e_imported.Members) > 1 { + member = common.SnakeUpperCase(e_imported.Members[1].Name) + } + if e_local == nil { + prefix = fmt.Sprintf("%s.api.", e_imported.Module.Name) + } + text = fmt.Sprintf("%s%s.%s", prefix, name, member) + case model.TypeStruct: + s_local := schema.LookupStruct("", schema.Type) + s_imported := schema.LookupStruct(schema.Import, schema.Type) + if s_local == nil && s_imported == nil { + return "xxx", fmt.Errorf("pyTestValue struct not found: %s", schema.Dump()) + } + // if struct is local it is found both as s_local and s_imported + ident := common.CamelTitleCase(s_imported.Name) + if s_local == nil { + prefix = fmt.Sprintf("%s.api.", s_imported.Module.Name) + } + text = fmt.Sprintf("%s%s()", prefix, ident) + case model.TypeExtern: + xe := parsePyExtern(schema) + if xe.Default != "" { + text = xe.Default + } else { + py_module := "" + if xe.Import != "" { + py_module = fmt.Sprintf("%s.", xe.Import) + } + text = fmt.Sprintf("%s%s()", py_module, xe.Name) + } + case model.TypeInterface: + i_local := schema.LookupInterface("", schema.Type) + i_imported := schema.LookupInterface(schema.Import, schema.Type) + if i_local == nil && i_imported == nil { + return "xxx", fmt.Errorf("pyTestValue interface not found: %s", schema.Dump()) + } + // if interface is local it is found both as s_local and s_imported + ident := common.CamelTitleCase(i_imported.Name) + if i_local == nil { + prefix = fmt.Sprintf("%s.api.", i_imported.Module.Name) + } + text = fmt.Sprintf("%s%s()", prefix, ident) + default: + return "xxx", fmt.Errorf("pyTestValue unknown schema %s", schema.Dump()) + } + return text, nil +} + +func pyTestValue(prefix string, node *model.TypedNode) (string, error) { + if node == nil { + return "xxx", fmt.Errorf("pyTestValue node is nil") + } + return ToTestValueString(prefix, &node.Schema) +} diff --git a/pkg/gen/filters/filterpy/py_testvalue_test.go b/pkg/gen/filters/filterpy/py_testvalue_test.go new file mode 100644 index 00000000..d86b488e --- /dev/null +++ b/pkg/gen/filters/filterpy/py_testvalue_test.go @@ -0,0 +1,140 @@ +package filterpy + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +// test with all the types +// properties, operation params, operation return, signal params, struct fields +func TestTestValueFromIdl(t *testing.T) { + t.Parallel() + syss := loadTestSystems(t) + var propTests = []struct { + mn string + in string + pn string + rt string + }{ + {"test", "Test1", "propVoid", "None"}, + {"test", "Test1", "propBool", "True"}, + {"test", "Test1", "propInt", "1"}, + {"test", "Test1", "propInt32", "1"}, + {"test", "Test1", "propInt64", "1"}, + {"test", "Test1", "propFloat", "1.1"}, + {"test", "Test1", "propFloat32", "1.1"}, + {"test", "Test1", "propFloat64", "1.1"}, + {"test", "Test1", "propString", "\"xyz\""}, + {"test", "Test1", "propBoolArray", "True"}, // all the array types return value intentionally, it may be put into empty array + {"test", "Test1", "propIntArray", "1"}, + {"test", "Test1", "propInt32Array", "1"}, + {"test", "Test1", "propInt64Array", "1"}, + {"test", "Test1", "propFloatArray", "1.1"}, + {"test", "Test1", "propFloat32Array", "1.1"}, + {"test", "Test1", "propFloat64Array", "1.1"}, + {"test", "Test1", "propStringArray", "\"xyz\""}, + } + for _, sys := range syss { + for _, tt := range propTests { + t.Run(tt.pn, func(t *testing.T) { + prop := sys.LookupProperty(tt.mn, tt.in, tt.pn) + assert.NotNil(t, prop) + r, err := pyTestValue("", prop) + assert.NoError(t, err) + assert.Equal(t, tt.rt, r) + }) + } + } +} + +func TestTestValueSymbolsFromIdl(t *testing.T) { + t.Parallel() + syss := loadTestSystems(t) + var propTests = []struct { + mn string + in string + pn string + rt string + }{ + {"test", "Test2", "propEnum", "Enum1.NOT_DEFAULT"}, + {"test", "Test2", "propStruct", "Struct1()"}, + {"test", "Test2", "propInterface", "Interface1()"}, + {"test", "Test2", "propEnumArray", "Enum1.NOT_DEFAULT"}, + {"test", "Test2", "propStructArray", "Struct1()"}, + {"test", "Test2", "propInterfaceArray", "Interface1()"}, + } + for _, sys := range syss { + for _, tt := range propTests { + t.Run(tt.pn, func(t *testing.T) { + prop := sys.LookupProperty(tt.mn, tt.in, tt.pn) + assert.NotNil(t, prop) + r, err := pyTestValue("", prop) + assert.NoError(t, err) + assert.Equal(t, tt.rt, r) + }) + } + } +} + +func TestTestValueWithErrors(t *testing.T) { + t.Parallel() + s, err := pyTestValue("", nil) + assert.Error(t, err) + assert.Equal(t, "xxx", s) +} + +func TestTestValueReturnsExternDefault(t *testing.T) { + syss := loadExternSystems(t) + var propTests = []struct { + mn string + in string + pn string + rt string + }{ + {"demo", "Iface1", "prop1", "XType1()"}, + {"demo", "Iface1", "prop2", "XType2()"}, + {"demo", "Iface1", "prop3", "XType3A()"}, + } + for _, sys := range syss { + for _, tt := range propTests { + t.Run(tt.pn, func(t *testing.T) { + prop := sys.LookupProperty(tt.mn, tt.in, tt.pn) + assert.NotNil(t, prop) + r, err := pyTestValue("", prop) + assert.NoError(t, err) + assert.Equal(t, tt.rt, r) + }) + } + } +} + +func TestTestValueReturnsDefaultExterns(t *testing.T) { + t.Parallel() + table := []struct { + module_name string + interface_name string + operation_name string + result string + }{ + {"test_apigear_next", "Iface1", "prop1", "XType1()"}, + {"test_apigear_next", "Iface1", "prop2", "demo.x.XType2()"}, + {"test_apigear_next", "Iface1", "prop3", "demo.x.createXType3A()"}, + {"test_apigear_next", "Iface1", "propList", "demo.x.createXType3A()"}, + {"test_apigear_next", "Iface1", "propImportedEnum", "test.api.Enum1.NOT_DEFAULT"}, + {"test_apigear_next", "Iface1", "propImportedStruct", "test.api.Struct1()"}, + } + syss := loadExternSystemsYAML(t) + prefix := "my_prefix::" + for _, sys := range syss { + for _, tt := range table { + t.Run(tt.operation_name, func(t *testing.T) { + prop := sys.LookupProperty(tt.module_name, tt.interface_name, tt.operation_name) + assert.NotNil(t, prop) + r, err := pyTestValue(prefix, prop) + assert.NoError(t, err) + assert.Equal(t, tt.result, r) + }) + } + } +} diff --git a/pkg/gen/filters/testdata/test.module.yaml b/pkg/gen/filters/testdata/test.module.yaml index 7c7f53d0..e4a3ad1e 100644 --- a/pkg/gen/filters/testdata/test.module.yaml +++ b/pkg/gen/filters/testdata/test.module.yaml @@ -319,9 +319,11 @@ enums: - name: Enum1 members: - name: Default + - name: NotDefault - name: EnumLowerNames members: - name: firstValue + - name: secondValue structs: - name: Struct1