Skip to content

Commit

Permalink
feat(cpp): add cpp filter for test value
Browse files Browse the repository at this point in the history
  • Loading branch information
dorotaphanSiili committed Dec 24, 2024
1 parent 9091ce5 commit 2d5e723
Show file tree
Hide file tree
Showing 5 changed files with 315 additions and 0 deletions.
102 changes: 102 additions & 0 deletions pkg/gen/filters/filtercpp/cpp_testvalue.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package filtercpp

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("cppTestValue schema is nil")
}
if schema.Module == nil {
return "xxx", fmt.Errorf("cppTestValue schema module is nil")
}
var text string
switch schema.KindType {
case model.TypeString:
text = "std::string(\"xyz\")"
case model.TypeInt, model.TypeInt32:
text = "1"
case model.TypeInt64:
text = "1LL"
case model.TypeFloat, model.TypeFloat32:
text = "1.1f"
case model.TypeFloat64:
text = "1.1"
case model.TypeBool:
text = "true"
case model.TypeVoid:
return ToDefaultString(prefix, schema)
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("cppTestValue enum not found: %s", schema.Dump())
}
// if enum is local it is found both as e_local and e_imported
name := e_imported.Name
member := e_imported.Members[0].Name
if len(e_imported.Members) > 1 {
member = e_imported.Members[1].Name
}
if e_local == nil {
moduleNamespace := common.CamelTitleCase(e_imported.Module.Name)
prefix = fmt.Sprintf("%s::", moduleNamespace)
}
text = fmt.Sprintf("%s%sEnum::%s", prefix, name, member)
// all types return deafualt value, but cannot be passed to deafult filter
// due to variants with array. Here we want to return default element, not deafult empty array.
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("cppTestValue struct not found: %s", schema.Dump())
}
// if struct is local it is found both as s_local and s_imported
name := s_imported.Name
if s_local == nil {
moduleNamespace := common.CamelTitleCase(s_imported.Module.Name)
prefix = fmt.Sprintf("%s::", moduleNamespace)
}
text = fmt.Sprintf("%s%s()", prefix, name)
case model.TypeExtern:
xe := parseCppExtern(schema)
if xe.Default != "" {
text = xe.Default
} else {
namespace_prefix := ""
if xe.NameSpace != "" {
namespace_prefix = fmt.Sprintf("%s::", xe.NameSpace)
}
text = fmt.Sprintf("%s%s()", namespace_prefix, 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("cppTestValue interface not found: %s", schema.Dump())
}
// if interface is local it is found both as s_local and s_imported
name := i_imported.Name
if i_local == nil {
moduleNamespace := common.CamelTitleCase(i_imported.Module.Name)
prefix = fmt.Sprintf("%s::", moduleNamespace)
}
text = fmt.Sprintf("%s%s()", prefix, name)
default:
return "xxx", fmt.Errorf("pyTestValue unknown schema %s", schema.Dump())
}
return text, nil
}

func cppTestValue(prefix string, node *model.TypedNode) (string, error) {
if node == nil {
return "xxx", fmt.Errorf("cppTestValue node is nil")
}
return ToTestValueString(prefix, &node.Schema)
}
187 changes: 187 additions & 0 deletions pkg/gen/filters/filtercpp/cpp_testvalue_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
package filtercpp

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", "void"},
{"test", "Test1", "propBool", "true"},
{"test", "Test1", "propInt", "1"},
{"test", "Test1", "propInt32", "1"},
{"test", "Test1", "propInt64", "1LL"},
{"test", "Test1", "propFloat", "1.1f"},
{"test", "Test1", "propFloat32", "1.1f"},
{"test", "Test1", "propFloat64", "1.1"},
{"test", "Test1", "propString", "std::string(\"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", "1LL"},
{"test", "Test1", "propFloatArray", "1.1f"},
{"test", "Test1", "propFloat32Array", "1.1f"},
{"test", "Test1", "propFloat64Array", "1.1"},
{"test", "Test1", "propStringArray", "std::string(\"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 := cppTestValue("", 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::NotDefault"},
{"test", "InterfaceNamesCheck", "lowerEnumProp", "EnumLowerNames::secondValue"},
{"test", "Test2", "propStruct", "Struct1()"},
{"test", "Test2", "propInterface", "Interface1()"},
{"test", "Test2", "propEnumArray", "Enum1::NotDefault"},
{"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 := cppTestValue("", prop)
assert.NoError(t, err)
assert.Equal(t, tt.rt, r)
})
}
}
}

func TestTestValueWithErrors(t *testing.T) {
t.Parallel()
s, err := cppTestValue("", nil)
assert.Error(t, err)
assert.Equal(t, "xxx", s)
}

func TestTestValueFromIdlWithPrefix_makesNoDifference(t *testing.T) {
t.Parallel()
syss := loadTestSystems(t)
var propTests = []struct {
mn string
in string
pn string
rt string
}{
{"test", "Test1", "propVoid", "void"},
{"test", "Test1", "propBool", "true"},
{"test", "Test1", "propInt", "1"},
{"test", "Test1", "propInt32", "1"},
{"test", "Test1", "propInt64", "1LL"},
{"test", "Test1", "propFloat", "1.1f"},
{"test", "Test1", "propFloat32", "1.1f"},
{"test", "Test1", "propFloat64", "1.1"},
{"test", "Test1", "propString", "std::string(\"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", "1LL"},
{"test", "Test1", "propFloatArray", "1.1f"},
{"test", "Test1", "propFloat32Array", "1.1f"},
{"test", "Test1", "propFloat64Array", "1.1"},
{"test", "Test1", "propStringArray", "std::string(\"xyz\")"},
}
prefix := "my_prefix::"
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 := cppTestValue(prefix, prop)
assert.NoError(t, err)
assert.Equal(t, tt.rt, r)
})
}
}
}

func TestTestValueSymbolsFromIdlWithPrefix(t *testing.T) {
t.Parallel()
syss := loadTestSystems(t)
var propTests = []struct {
mn string
in string
pn string
rt string
}{
{"test", "Test2", "propEnum", "my_prefix::Enum1Enum::NotDefault"},
{"test", "Test2", "propStruct", "my_prefix::Struct1()"},
{"test", "Test2", "propInterface", "my_prefix::Interface1()"},
{"test", "Test2", "propEnumArray", "my_prefix::Enum1Enum::NotDefault"},
{"test", "Test2", "propStructArray", "my_prefix::Struct1()"},
{"test", "Test2", "propInterfaceArray", "my_prefix::Interface1()"},
}
prefix := "my_prefix::"
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 := cppTestValue(prefix, prop)
assert.NoError(t, err)
assert.Equal(t, tt.rt, r)
})
}
}
}

func TestTestValueExterns(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::XtypeFactory::create()"},
{"test_apigear_next", "Iface1", "propList", "demo::x::XtypeFactory::create()"},
{"test_apigear_next", "Iface1", "propImportedEnum", "Test::Enum1Enum::NotDefault"},
{"test_apigear_next", "Iface1", "propImportedStruct", "Test::Struct1()"},
}
syss := loadExternSystems(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 := cppTestValue(prefix, prop)
assert.NoError(t, err)
assert.Equal(t, tt.result, r)
})
}
}
}
8 changes: 8 additions & 0 deletions pkg/gen/filters/filtercpp/extern.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,11 @@ func cppExtern(xe *model.Extern) CppExtern {
ConanVersion: conanVersion,
}
}

func cppExterns(externs []*model.Extern) []CppExtern {
var items = []CppExtern{}
for _, ex := range externs {
items = append(items, cppExtern(ex))
}
return items
}
2 changes: 2 additions & 0 deletions pkg/gen/filters/filtercpp/filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@ func PopulateFuncMap(fm template.FuncMap) {
fm["cppType"] = cppType
fm["cppTypeRef"] = cppTypeRef
fm["cppExtern"] = cppExtern
fm["cppExterns"] = cppExterns
fm["cppTestValue"] = cppTestValue
}
16 changes: 16 additions & 0 deletions pkg/gen/filters/filtercpp/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,21 @@ func loadExternSystems(t *testing.T) []*model.System {
err = sys1.Validate()
assert.NoError(t, err)

parser := model.NewDataParser(sys1)
err = parser.ParseFile("../testdata/test.module.yaml")
assert.NoError(t, err)
err = sys1.Validate()
assert.NoError(t, err)

err = parser.ParseFile("../testdata/extern_types.module.yaml")
assert.NoError(t, err)
err = sys1.Validate()
assert.NoError(t, err)

err = parser.ParseFile("../testdata/test_apigear_next.module.yaml")
assert.NoError(t, err)
err = sys1.Validate()
assert.NoError(t, err)

return []*model.System{sys1}
}

0 comments on commit 2d5e723

Please sign in to comment.