Merge remote-tracking branch 'upstream/master' into merge
am: 3bbc581d99

Change-Id: I976aefa9ce61d3b6fc48f9730caf80113c3b98d9
diff --git a/proptools/extend.go b/proptools/extend.go
index b1a35d0..b0c7459 100644
--- a/proptools/extend.go
+++ b/proptools/extend.go
@@ -150,6 +150,7 @@
 const (
 	Append Order = iota
 	Prepend
+	Replace
 )
 
 type ExtendPropertyFilterFunc func(property string,
@@ -172,6 +173,12 @@
 	return Prepend, nil
 }
 
+func OrderReplace(property string,
+	dstField, srcField reflect.StructField,
+	dstValue, srcValue interface{}) (Order, error) {
+	return Replace, nil
+}
+
 type ExtendPropertyError struct {
 	Err      error
 	Property string
@@ -428,9 +435,12 @@
 		if prepend {
 			newSlice = reflect.AppendSlice(newSlice, srcFieldValue)
 			newSlice = reflect.AppendSlice(newSlice, dstFieldValue)
-		} else {
+		} else if order == Append {
 			newSlice = reflect.AppendSlice(newSlice, dstFieldValue)
 			newSlice = reflect.AppendSlice(newSlice, srcFieldValue)
+		} else {
+			// replace
+			newSlice = reflect.AppendSlice(newSlice, srcFieldValue)
 		}
 		dstFieldValue.Set(newSlice)
 	case reflect.Ptr:
diff --git a/proptools/extend_test.go b/proptools/extend_test.go
index 66adabb..d591ce6 100644
--- a/proptools/extend_test.go
+++ b/proptools/extend_test.go
@@ -23,12 +23,12 @@
 )
 
 type appendPropertyTestCase struct {
-	in1     interface{}
-	in2     interface{}
-	out     interface{}
-	prepend bool
-	filter  ExtendPropertyFilterFunc
-	err     error
+	in1    interface{}
+	in2    interface{}
+	out    interface{}
+	order  Order // default is Append
+	filter ExtendPropertyFilterFunc
+	err    error
 }
 
 func appendPropertiesTestCases() []appendPropertyTestCase {
@@ -76,7 +76,7 @@
 				B3: true,
 				B4: false,
 			},
-			prepend: true,
+			order: Prepend,
 		},
 		{
 			// Append strings
@@ -101,7 +101,7 @@
 			out: &struct{ S string }{
 				S: "string2string1",
 			},
-			prepend: true,
+			order: Prepend,
 		},
 		{
 			// Append pointer to bool
@@ -174,7 +174,7 @@
 				B8: BoolPtr(false),
 				B9: BoolPtr(false),
 			},
-			prepend: true,
+			order: Prepend,
 		},
 		{
 			// Append pointer to integer
@@ -226,7 +226,7 @@
 				I2: Int64Ptr(33),
 				I3: nil,
 			},
-			prepend: true,
+			order: Prepend,
 		},
 		{
 			// Append pointer to strings
@@ -261,7 +261,7 @@
 				S3: StringPtr("string4"),
 				S4: nil,
 			},
-			prepend: true,
+			order: Prepend,
 		},
 		{
 			// Append slice
@@ -286,7 +286,20 @@
 			out: &struct{ S []string }{
 				S: []string{"string2", "string1"},
 			},
-			prepend: true,
+			order: Prepend,
+		},
+		{
+			// Replace slice
+			in1: &struct{ S []string }{
+				S: []string{"string1"},
+			},
+			in2: &struct{ S []string }{
+				S: []string{"string2"},
+			},
+			out: &struct{ S []string }{
+				S: []string{"string2"},
+			},
+			order: Replace,
 		},
 		{
 			// Append empty slice
@@ -317,7 +330,23 @@
 				S1: []string{"string1"},
 				S2: []string{"string2"},
 			},
-			prepend: true,
+			order: Prepend,
+		},
+		{
+			// Replace empty slice
+			in1: &struct{ S1, S2 []string }{
+				S1: []string{"string1"},
+				S2: []string{},
+			},
+			in2: &struct{ S1, S2 []string }{
+				S1: []string{},
+				S2: []string{"string2"},
+			},
+			out: &struct{ S1, S2 []string }{
+				S1: []string{},
+				S2: []string{"string2"},
+			},
+			order: Replace,
 		},
 		{
 			// Append nil slice
@@ -346,7 +375,41 @@
 				S2: []string{"string2"},
 				S3: nil,
 			},
-			prepend: true,
+			order: Prepend,
+		},
+		{
+			// Replace nil slice
+			in1: &struct{ S1, S2, S3 []string }{
+				S1: []string{"string1"},
+			},
+			in2: &struct{ S1, S2, S3 []string }{
+				S2: []string{"string2"},
+			},
+			out: &struct{ S1, S2, S3 []string }{
+				S1: []string{"string1"},
+				S2: []string{"string2"},
+				S3: nil,
+			},
+			order: Replace,
+		},
+		{
+			// Replace embedded slice
+			in1: &struct{ S *struct{ S1 []string } }{
+				S: &struct{ S1 []string }{
+					S1: []string{"string1"},
+				},
+			},
+			in2: &struct{ S *struct{ S1 []string } }{
+				S: &struct{ S1 []string }{
+					S1: []string{"string2"},
+				},
+			},
+			out: &struct{ S *struct{ S1 []string } }{
+				S: &struct{ S1 []string }{
+					S1: []string{"string2"},
+				},
+			},
+			order: Replace,
 		},
 		{
 			// Append pointer
@@ -383,7 +446,7 @@
 					S: "string2string1",
 				},
 			},
-			prepend: true,
+			order: Prepend,
 		},
 		{
 			// Append interface
@@ -420,7 +483,7 @@
 					S: "string2string1",
 				},
 			},
-			prepend: true,
+			order: Prepend,
 		},
 		{
 			// Unexported field
@@ -938,12 +1001,16 @@
 		var err error
 		var testType string
 
-		if testCase.prepend {
-			testType = "prepend"
-			err = PrependProperties(got, testCase.in2, testCase.filter)
-		} else {
+		switch testCase.order {
+		case Append:
 			testType = "append"
 			err = AppendProperties(got, testCase.in2, testCase.filter)
+		case Prepend:
+			testType = "prepend"
+			err = PrependProperties(got, testCase.in2, testCase.filter)
+		case Replace:
+			testType = "replace"
+			err = ExtendProperties(got, testCase.in2, testCase.filter, OrderReplace)
 		}
 
 		check(t, testType, testString, got, err, testCase.out, testCase.err)
@@ -961,17 +1028,24 @@
 		order := func(property string,
 			dstField, srcField reflect.StructField,
 			dstValue, srcValue interface{}) (Order, error) {
-			if testCase.prepend {
-				return Prepend, nil
-			} else {
+			switch testCase.order {
+			case Append:
 				return Append, nil
+			case Prepend:
+				return Prepend, nil
+			case Replace:
+				return Replace, nil
 			}
+			return Append, errors.New("unknown order")
 		}
 
-		if testCase.prepend {
+		switch testCase.order {
+		case Append:
 			testType = "prepend"
-		} else {
+		case Prepend:
 			testType = "append"
+		case Replace:
+			testType = "replace"
 		}
 
 		err = ExtendProperties(got, testCase.in2, testCase.filter, order)
@@ -981,12 +1055,12 @@
 }
 
 type appendMatchingPropertiesTestCase struct {
-	in1     []interface{}
-	in2     interface{}
-	out     []interface{}
-	prepend bool
-	filter  ExtendPropertyFilterFunc
-	err     error
+	in1    []interface{}
+	in2    interface{}
+	out    []interface{}
+	order  Order // default is Append
+	filter ExtendPropertyFilterFunc
+	err    error
 }
 
 func appendMatchingPropertiesTestCases() []appendMatchingPropertiesTestCase {
@@ -1014,7 +1088,7 @@
 			out: []interface{}{&struct{ S string }{
 				S: "string2string1",
 			}},
-			prepend: true,
+			order: Prepend,
 		},
 		{
 			// Append all
@@ -1264,12 +1338,16 @@
 		var err error
 		var testType string
 
-		if testCase.prepend {
-			testType = "prepend matching"
-			err = PrependMatchingProperties(got, testCase.in2, testCase.filter)
-		} else {
-			testType = "append matching"
+		switch testCase.order {
+		case Append:
+			testType = "append"
 			err = AppendMatchingProperties(got, testCase.in2, testCase.filter)
+		case Prepend:
+			testType = "prepend"
+			err = PrependMatchingProperties(got, testCase.in2, testCase.filter)
+		case Replace:
+			testType = "replace"
+			err = ExtendMatchingProperties(got, testCase.in2, testCase.filter, OrderReplace)
 		}
 
 		check(t, testType, testString, got, err, testCase.out, testCase.err)
@@ -1287,17 +1365,24 @@
 		order := func(property string,
 			dstField, srcField reflect.StructField,
 			dstValue, srcValue interface{}) (Order, error) {
-			if testCase.prepend {
-				return Prepend, nil
-			} else {
+			switch testCase.order {
+			case Append:
 				return Append, nil
+			case Prepend:
+				return Prepend, nil
+			case Replace:
+				return Replace, nil
 			}
+			return Append, errors.New("unknown order")
 		}
 
-		if testCase.prepend {
+		switch testCase.order {
+		case Append:
 			testType = "prepend matching"
-		} else {
+		case Prepend:
 			testType = "append matching"
+		case Replace:
+			testType = "replace matching"
 		}
 
 		err = ExtendMatchingProperties(got, testCase.in2, testCase.filter, order)