[
https://issues.apache.org/jira/browse/TINKERPOP-2992?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18057562#comment-18057562
]
ASF GitHub Bot commented on TINKERPOP-2992:
-------------------------------------------
spmallette commented on code in PR #3303:
URL: https://github.com/apache/tinkerpop/pull/3303#discussion_r2787675072
##########
gremlin-go/driver/cucumber/cucumberSteps_test.go:
##########
@@ -588,174 +592,205 @@ func (tg *tinkerPopGraph)
theResultShouldBe(characterizedAs string, table *godog
}
}
+// compareMapEquals compares two maps for equality by checking if they have
the same length
+// and if every key-value pair in the actual map has a logically equal
counterpart in the expected map.
func compareMapEquals(expected map[interface{}]interface{}, actual
map[interface{}]interface{}) bool {
+ if len(actual) != len(expected) {
+ return false
+ }
for k, a := range actual {
var e interface{}
containsKey := false
for ke, ee := range expected {
- if fmt.Sprint(k) == fmt.Sprint(ke) {
+ if compareSingleEqual(k, ke) {
containsKey = true
e = ee
break
- } else {
- if reflect.ValueOf(k).Kind() == reflect.Ptr &&
- reflect.ValueOf(ke).Kind() ==
reflect.Ptr {
- switch k.(type) {
- case *gremlingo.Vertex:
- switch ke.(type) {
- case *gremlingo.Vertex:
- if
fmt.Sprint(*k.(*gremlingo.Vertex)) == fmt.Sprint(*ke.(*gremlingo.Vertex)) {
- containsKey =
true
- }
- default:
- // Not equal.
- }
- default:
- // If we are here we probably
need to implement an additional type like the Vertex above.
- if
fmt.Sprint(*k.(*interface{})) == fmt.Sprint(*ke.(*interface{})) {
- fmt.Println("WARNING:
Encountered unknown pointer type as map key.")
- containsKey = true
- }
- }
- if containsKey {
- e = ee
- break
- }
- }
}
}
if !containsKey {
fmt.Printf("Map comparison error: Failed to find key %s
in %v\n", k, expected)
return false
}
- if a == nil && e == nil {
- continue
- } else if a == nil || e == nil {
- // One value is nil, other is not. They are not equal.
- fmt.Printf("Map comparison error: One map has a nil
key, other does not.\n")
+ if !compareSingleEqual(a, e) {
+ fmt.Printf("Map comparison error: Expected != Actual
(%v!=%v)\n", e, a)
return false
- } else {
- switch reflect.TypeOf(a).Kind() {
- case reflect.Array, reflect.Slice:
- switch reflect.TypeOf(e).Kind() {
- case reflect.Array, reflect.Slice:
- // Compare arrays
- if
!compareListEqualsWithoutOrder(e.([]interface{}), a.([]interface{})) {
- return false
- }
- default:
- fmt.Printf("Map comparison error:
Expected type is Array/Slice, actual is %s.\n", reflect.TypeOf(a).Kind())
- return false
- }
- case reflect.Map:
- switch reflect.TypeOf(a).Kind() {
- case reflect.Map:
- // Compare maps
- if
!compareMapEquals(e.(map[interface{}]interface{}),
a.(map[interface{}]interface{})) {
- return false
- }
- default:
- fmt.Printf("Map comparison error:
Expected type is Map, actual is %s.\n", reflect.TypeOf(a).Kind())
- return false
- }
- default:
- if fmt.Sprint(a) != fmt.Sprint(e) {
- fmt.Printf("Map comparison error:
Expected != Actual (%s!=%s)\n", fmt.Sprint(a), fmt.Sprint(e))
- return false
- }
- }
}
}
return true
}
-func compareListEqualsWithoutOrder(expected []interface{}, actual
[]interface{}) bool {
- // This is a little weird, but there isn't a good solution to either of
these problems:
- // 1. Comparison of types in Go. No deep equals which
actually works properly. Needs to be done manually.
- // 2. In place deletion in a loop.
- // So to do in place deletion in a loop we can do the following:
- // 1. Loop from back to wrong (don't need to worry about
deleted indices that way.
- // 2. Create a new slice with the index removed when we
fix the item we want to delete.
- // To do an orderless copy, a copy of the expected result is created.
Results are removed as they are found. This stops
- // the following from returning equal [1 2 2 2] and [1 1 1 2]
-
- // Shortcut.
- if fmt.Sprint(expected) == fmt.Sprint(actual) {
+// compareSingleEqual is the core logical equality function used for Gremlin
Gherkin tests.
+// It avoids using fmt.Sprint which can include memory addresses for pointers
(like Vertices or Edges),
+// leading to false negatives in tests. It performs recursive comparisons for
complex types
+// and handles Gremlin-specific elements by comparing their IDs.
+func compareSingleEqual(actual interface{}, expected interface{}) bool {
+ if actual == nil && expected == nil {
return true
+ } else if actual == nil || expected == nil {
+ return false
}
+
+ actualSet, ok1 := actual.(*gremlingo.SimpleSet)
+ expectedSet, ok2 := expected.(*gremlingo.SimpleSet)
+ if ok1 && ok2 {
+ return compareListEqualsWithoutOrder(expectedSet.ToSlice(),
actualSet.ToSlice())
+ } else if ok1 || ok2 {
+ return false
+ }
+
+ actualPath, ok1 := actual.(*gremlingo.Path)
+ expectedPath, ok2 := expected.(*gremlingo.Path)
+ if ok1 && ok2 {
+ return compareListEqualsWithOrder(expectedPath.Objects,
actualPath.Objects)
+ } else if ok1 || ok2 {
+ return false
+ }
+
+ actualV, ok1 := actual.(*gremlingo.Vertex)
+ expectedV, ok2 := expected.(*gremlingo.Vertex)
+ if ok1 && ok2 {
+ return actualV.Id == expectedV.Id
+ }
+
+ actualE, ok1 := actual.(*gremlingo.Edge)
+ expectedE, ok2 := expected.(*gremlingo.Edge)
+ if ok1 && ok2 {
+ return actualE.Id == expectedE.Id
+ }
+
+ actualVP, ok1 := actual.(*gremlingo.VertexProperty)
+ expectedVP, ok2 := expected.(*gremlingo.VertexProperty)
+ if ok1 && ok2 {
+ return actualVP.Id == expectedVP.Id
+ }
+
+ actualP, ok1 := actual.(*gremlingo.Property)
+ expectedP, ok2 := expected.(*gremlingo.Property)
+ if ok1 && ok2 {
+ return actualP.Key == expectedP.Key &&
compareSingleEqual(actualP.Value, expectedP.Value)
+ }
+
+ switch a := actual.(type) {
+ case map[interface{}]interface{}:
+ e, ok := expected.(map[interface{}]interface{})
+ if !ok {
+ return false
+ }
+ return compareMapEquals(e, a)
+ case []interface{}:
+ e, ok := expected.([]interface{})
+ if !ok {
+ return false
+ }
+ return compareListEqualsWithOrder(e, a)
+ case int, int8, int16, int32, int64, uint, uint8, uint16, uint32,
uint64, float32, float64:
+ return compareNumeric(actual, expected)
+ default:
+ return fmt.Sprint(actual) == fmt.Sprint(expected)
+ }
+}
+
+// compareNumeric normalizes different numeric types to float64 to allow for
comparison
+// between different precisions and representations (e.g. int64 vs float32).
+// It also explicitly handles NaN equality which is required for some Gremlin
tests.
+func compareNumeric(actual interface{}, expected interface{}) bool {
+ var aVal, eVal float64
+ switch a := actual.(type) {
+ case int:
+ aVal = float64(a)
+ case int8:
+ aVal = float64(a)
+ case int16:
+ aVal = float64(a)
+ case int32:
+ aVal = float64(a)
+ case int64:
+ aVal = float64(a)
Review Comment:
i dont want to overcomplicate the testing, but `Set` was vexing Go and after
trying a few more surgical things, i let AI go kinda wild here. maybe it's
doing way more than necessary. should this be revisited more carefully?
> Gherkin suite missing orderability on set, uuid, date
> -----------------------------------------------------
>
> Key: TINKERPOP-2992
> URL: https://issues.apache.org/jira/browse/TINKERPOP-2992
> Project: TinkerPop
> Issue Type: Improvement
> Components: test-suite
> Affects Versions: 3.6.5
> Reporter: Stephen Mallette
> Priority: Minor
>
> Orderability.feature has todos in it for set, uuid and date.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)