Add proptools.TypeEqual
When appending properties, it may be necessary to determine if two property structs are the same "type". A simple Go type comparison is not sufficient, as there may be interface{} values in the property structs that contain different types. Add proptools.TypeEqual that returns true if they have equal types and all embedded pointers to structs and interfaces to pointers to structs have the same nilitude and type.
This commit is contained in:
parent
8011768729
commit
e4b0d35966
4 changed files with 236 additions and 7 deletions
|
@ -68,10 +68,12 @@ bootstrap_go_package(
|
|||
"proptools/clone.go",
|
||||
"proptools/extend.go",
|
||||
"proptools/proptools.go",
|
||||
"proptools/typeequal.go",
|
||||
],
|
||||
testSrcs = [
|
||||
"proptools/clone_test.go",
|
||||
"proptools/extend_test.go",
|
||||
"proptools/typeequal_test.go",
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ default $
|
|||
# Variant:
|
||||
# Type: bootstrap_go_package
|
||||
# Factory: github.com/google/blueprint/bootstrap.func·003
|
||||
# Defined: Blueprints:78:1
|
||||
# Defined: Blueprints:80:1
|
||||
|
||||
build $
|
||||
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap/pkg/github.com/google/blueprint/bootstrap.a $
|
||||
|
@ -108,7 +108,7 @@ default $
|
|||
# Variant:
|
||||
# Type: bootstrap_go_package
|
||||
# Factory: github.com/google/blueprint/bootstrap.func·003
|
||||
# Defined: Blueprints:97:1
|
||||
# Defined: Blueprints:99:1
|
||||
|
||||
build $
|
||||
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg/github.com/google/blueprint/bootstrap/bpdoc.a $
|
||||
|
@ -181,7 +181,8 @@ build $
|
|||
${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a $
|
||||
: g.bootstrap.compile ${g.bootstrap.srcDir}/proptools/clone.go $
|
||||
${g.bootstrap.srcDir}/proptools/extend.go $
|
||||
${g.bootstrap.srcDir}/proptools/proptools.go | $
|
||||
${g.bootstrap.srcDir}/proptools/proptools.go $
|
||||
${g.bootstrap.srcDir}/proptools/typeequal.go | $
|
||||
${g.bootstrap.compileCmd}
|
||||
pkgPath = github.com/google/blueprint/proptools
|
||||
default $
|
||||
|
@ -192,7 +193,7 @@ default $
|
|||
# Variant:
|
||||
# Type: bootstrap_core_go_binary
|
||||
# Factory: github.com/google/blueprint/bootstrap.func·005
|
||||
# Defined: Blueprints:140:1
|
||||
# Defined: Blueprints:142:1
|
||||
|
||||
build ${g.bootstrap.buildDir}/.bootstrap/choosestage/obj/choosestage.a: $
|
||||
g.bootstrap.compile ${g.bootstrap.srcDir}/choosestage/choosestage.go | $
|
||||
|
@ -214,7 +215,7 @@ default ${g.bootstrap.BinDir}/choosestage
|
|||
# Variant:
|
||||
# Type: bootstrap_core_go_binary
|
||||
# Factory: github.com/google/blueprint/bootstrap.func·005
|
||||
# Defined: Blueprints:130:1
|
||||
# Defined: Blueprints:132:1
|
||||
|
||||
build ${g.bootstrap.buildDir}/.bootstrap/gotestmain/obj/gotestmain.a: $
|
||||
g.bootstrap.compile ${g.bootstrap.srcDir}/gotestmain/gotestmain.go | $
|
||||
|
@ -236,7 +237,7 @@ default ${g.bootstrap.BinDir}/gotestmain
|
|||
# Variant:
|
||||
# Type: bootstrap_core_go_binary
|
||||
# Factory: github.com/google/blueprint/bootstrap.func·005
|
||||
# Defined: Blueprints:135:1
|
||||
# Defined: Blueprints:137:1
|
||||
|
||||
build ${g.bootstrap.buildDir}/.bootstrap/gotestrunner/obj/gotestrunner.a: $
|
||||
g.bootstrap.compile ${g.bootstrap.srcDir}/gotestrunner/gotestrunner.go $
|
||||
|
@ -258,7 +259,7 @@ default ${g.bootstrap.BinDir}/gotestrunner
|
|||
# Variant:
|
||||
# Type: bootstrap_core_go_binary
|
||||
# Factory: github.com/google/blueprint/bootstrap.func·005
|
||||
# Defined: Blueprints:109:1
|
||||
# Defined: Blueprints:111:1
|
||||
|
||||
build ${g.bootstrap.buildDir}/.bootstrap/minibp/obj/minibp.a: $
|
||||
g.bootstrap.compile ${g.bootstrap.srcDir}/bootstrap/minibp/main.go | $
|
||||
|
|
76
proptools/typeequal.go
Normal file
76
proptools/typeequal.go
Normal file
|
@ -0,0 +1,76 @@
|
|||
// Copyright 2015 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package proptools
|
||||
|
||||
import "reflect"
|
||||
|
||||
// TypeEqual takes two property structs, and returns true if they are of equal type, any embedded
|
||||
// pointers to structs or interfaces having matching nilitude, and any interface{} values in any
|
||||
// embedded structs, pointers to structs, or interfaces are also of equal type.
|
||||
func TypeEqual(s1, s2 interface{}) bool {
|
||||
return typeEqual(reflect.ValueOf(s1), reflect.ValueOf(s2))
|
||||
}
|
||||
|
||||
func typeEqual(v1, v2 reflect.Value) bool {
|
||||
if v1.Type() != v2.Type() {
|
||||
return false
|
||||
}
|
||||
|
||||
if v1.Kind() == reflect.Interface {
|
||||
if v1.IsNil() != v2.IsNil() {
|
||||
return false
|
||||
}
|
||||
if v1.IsNil() {
|
||||
return true
|
||||
}
|
||||
v1 = v1.Elem()
|
||||
v2 = v2.Elem()
|
||||
if v1.Type() != v2.Type() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if v1.Kind() == reflect.Ptr {
|
||||
if v1.Type().Elem().Kind() != reflect.Struct {
|
||||
return true
|
||||
}
|
||||
if v1.IsNil() != v2.IsNil() {
|
||||
return false
|
||||
}
|
||||
if v1.IsNil() {
|
||||
return true
|
||||
}
|
||||
v1 = v1.Elem()
|
||||
v2 = v2.Elem()
|
||||
}
|
||||
|
||||
if v1.Kind() != reflect.Struct {
|
||||
return true
|
||||
}
|
||||
|
||||
for i := 0; i < v1.NumField(); i++ {
|
||||
v1 := v1.Field(i)
|
||||
v2 := v2.Field(i)
|
||||
|
||||
switch kind := v1.Kind(); kind {
|
||||
case reflect.Interface, reflect.Ptr, reflect.Struct:
|
||||
if !typeEqual(v1, v2) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
150
proptools/typeequal_test.go
Normal file
150
proptools/typeequal_test.go
Normal file
|
@ -0,0 +1,150 @@
|
|||
// Copyright 2015 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package proptools
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var typeEqualTestCases = []struct {
|
||||
in1 interface{}
|
||||
in2 interface{}
|
||||
out bool
|
||||
}{
|
||||
{
|
||||
// Matching structs
|
||||
in1: struct{ S1 string }{},
|
||||
in2: struct{ S1 string }{},
|
||||
out: true,
|
||||
},
|
||||
{
|
||||
// Mismatching structs
|
||||
in1: struct{ S1 string }{},
|
||||
in2: struct{ S2 string }{},
|
||||
out: false,
|
||||
},
|
||||
{
|
||||
// Matching pointer to struct
|
||||
in1: &struct{ S1 string }{},
|
||||
in2: &struct{ S1 string }{},
|
||||
out: true,
|
||||
},
|
||||
{
|
||||
// Mismatching pointer to struct
|
||||
in1: &struct{ S1 string }{},
|
||||
in2: &struct{ S2 string }{},
|
||||
out: false,
|
||||
},
|
||||
{
|
||||
// Matching embedded structs
|
||||
in1: struct{ S struct{ S1 string } }{},
|
||||
in2: struct{ S struct{ S1 string } }{},
|
||||
out: true,
|
||||
},
|
||||
{
|
||||
// Misatching embedded structs
|
||||
in1: struct{ S struct{ S1 string } }{},
|
||||
in2: struct{ S struct{ S2 string } }{},
|
||||
out: false,
|
||||
},
|
||||
{
|
||||
// Matching embedded pointer to struct
|
||||
in1: &struct{ S *struct{ S1 string } }{S: &struct{ S1 string }{}},
|
||||
in2: &struct{ S *struct{ S1 string } }{S: &struct{ S1 string }{}},
|
||||
out: true,
|
||||
},
|
||||
{
|
||||
// Mismatching embedded pointer to struct
|
||||
in1: &struct{ S *struct{ S1 string } }{S: &struct{ S1 string }{}},
|
||||
in2: &struct{ S *struct{ S2 string } }{S: &struct{ S2 string }{}},
|
||||
out: false,
|
||||
},
|
||||
{
|
||||
// Matching embedded nil pointer to struct
|
||||
in1: &struct{ S *struct{ S1 string } }{},
|
||||
in2: &struct{ S *struct{ S1 string } }{},
|
||||
out: true,
|
||||
},
|
||||
{
|
||||
// Mismatching embedded nil pointer to struct
|
||||
in1: &struct{ S *struct{ S1 string } }{},
|
||||
in2: &struct{ S *struct{ S2 string } }{},
|
||||
out: false,
|
||||
},
|
||||
{
|
||||
// Mismatching nilitude embedded pointer to struct
|
||||
in1: &struct{ S *struct{ S1 string } }{S: &struct{ S1 string }{}},
|
||||
in2: &struct{ S *struct{ S1 string } }{},
|
||||
out: false,
|
||||
},
|
||||
{
|
||||
// Matching embedded interface to pointer to struct
|
||||
in1: &struct{ S interface{} }{S: &struct{ S1 string }{}},
|
||||
in2: &struct{ S interface{} }{S: &struct{ S1 string }{}},
|
||||
out: true,
|
||||
},
|
||||
{
|
||||
// Mismatching embedded interface to pointer to struct
|
||||
in1: &struct{ S interface{} }{S: &struct{ S1 string }{}},
|
||||
in2: &struct{ S interface{} }{S: &struct{ S2 string }{}},
|
||||
out: false,
|
||||
},
|
||||
{
|
||||
// Matching embedded nil interface to pointer to struct
|
||||
in1: &struct{ S interface{} }{},
|
||||
in2: &struct{ S interface{} }{},
|
||||
out: true,
|
||||
},
|
||||
{
|
||||
// Mismatching nilitude embedded interface to pointer to struct
|
||||
in1: &struct{ S interface{} }{S: &struct{ S1 string }{}},
|
||||
in2: &struct{ S interface{} }{},
|
||||
out: false,
|
||||
},
|
||||
{
|
||||
// Matching pointer to non-struct
|
||||
in1: struct{ S1 *string }{ S1: StringPtr("test1") },
|
||||
in2: struct{ S1 *string }{ S1: StringPtr("test2") },
|
||||
out: true,
|
||||
},
|
||||
{
|
||||
// Matching nilitude pointer to non-struct
|
||||
in1: struct{ S1 *string }{ S1: StringPtr("test1") },
|
||||
in2: struct{ S1 *string }{},
|
||||
out: true,
|
||||
},
|
||||
{
|
||||
// Mismatching pointer to non-struct
|
||||
in1: struct{ S1 *string }{},
|
||||
in2: struct{ S2 *string }{},
|
||||
out: false,
|
||||
},
|
||||
}
|
||||
|
||||
func TestTypeEqualProperties(t *testing.T) {
|
||||
for _, testCase := range typeEqualTestCases {
|
||||
testString := fmt.Sprintf("%#v, %#v -> %t", testCase.in1, testCase.in2, testCase.out)
|
||||
|
||||
got := TypeEqual(testCase.in1, testCase.in2)
|
||||
|
||||
if got != testCase.out {
|
||||
t.Errorf("test case: %s", testString)
|
||||
t.Errorf("incorrect output")
|
||||
t.Errorf(" expected: %t", testCase.out)
|
||||
t.Errorf(" got: %t", got)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue