Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 545c5927 authored by Paul Duffin's avatar Paul Duffin
Browse files

Refactor build_release and test code

Minor restructuring of the build_release pruning code to make it easier
to add logic for handling fields of maps of structs. That includes:
1. Moving some code that is specific to clearing a selected field
   inside the associated if block.
2. Replacing an if with a switch.
3. Improving the error handling by separating the reporting of the
   container that broke from information about which field could not be
   set. That allows follow up code to provide information about the map
   key instead.

The tests were restructed by:
1. Switching from using AssertDeepEquals to compare the structs to
   comparing the output of marshalling the structs to JSON. That was
   for a couple of reasons. Firstly, because JSON will marshal (and so
   allow comparison of) the contents of pointers to structs whereas
   AssertDeepEquals will just compare the pointers themselves.
   Secondly, because JSON can pretty print the output and make it
   easier to read.
2. Using a func to create a new instance of the input structure for
   each test. That is to allow the test to modify the input structure,
   e.g. by clearing a field in a struct that is pointed to by a map.
   The test previously relied on the input structure being immutable
   and passed by value but a follow up change will change that by
   adding a map field that contains pointers to structs.

Bug: 204763318
Test: m nothing
Change-Id: I84dc99621497b7263e30466895b823eb02cb2b56
parent 5ddefa3d
Loading
Loading
Loading
Loading
+39 −27
Original line number Diff line number Diff line
@@ -230,7 +230,9 @@ func (p *propertyPruner) gatherFields(structType reflect.Type, containingStructA
			return container.Field(fieldIndex)
		}

		zeroValue := reflect.Zero(field.Type)
		fieldType := field.Type
		if selector(name, field) {
			zeroValue := reflect.Zero(fieldType)
			fieldPruner := func(container reflect.Value) {
				if containingStructAccessor != nil {
					// This is an embedded structure so first access the field for the embedded
@@ -243,7 +245,7 @@ func (p *propertyPruner) gatherFields(structType reflect.Type, containingStructA

				defer func() {
					if r := recover(); r != nil {
					panic(fmt.Errorf("%s for fieldIndex %d of field %s of container %#v", r, fieldIndex, name, container.Interface()))
						panic(fmt.Errorf("%s\n\tfor field (index %d, name %s)", r, fieldIndex, name))
					}
				}()

@@ -251,13 +253,14 @@ func (p *propertyPruner) gatherFields(structType reflect.Type, containingStructA
				container.Field(fieldIndex).Set(zeroValue)
			}

		if selector(name, field) {
			property := prunerProperty{
				name,
				fieldPruner,
			}
			p.properties = append(p.properties, property)
		} else if field.Type.Kind() == reflect.Struct {
		} else {
			switch fieldType.Kind() {
			case reflect.Struct:
				// Gather fields from the nested or embedded structure.
				var subNamePrefix string
				if field.Anonymous {
@@ -265,16 +268,25 @@ func (p *propertyPruner) gatherFields(structType reflect.Type, containingStructA
				} else {
					subNamePrefix = name + "."
				}
			p.gatherFields(field.Type, fieldGetter, subNamePrefix, selector)
				p.gatherFields(fieldType, fieldGetter, subNamePrefix, selector)
			}
		}
	}
}

// pruneProperties will prune (set to zero value) any properties in the supplied struct.
// pruneProperties will prune (set to zero value) any properties in the struct referenced by the
// supplied struct pointer.
//
// The struct must be of the same type as was originally passed to newPropertyPruner to create this
// propertyPruner.
func (p *propertyPruner) pruneProperties(propertiesStruct interface{}) {

	defer func() {
		if r := recover(); r != nil {
			panic(fmt.Errorf("%s\n\tof container %#v", r, propertiesStruct))
		}
	}()

	structValue := reflect.ValueOf(propertiesStruct)
	for _, property := range p.properties {
		property.prunerFunc(structValue)
+39 −19
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
package sdk

import (
	"encoding/json"
	"fmt"
	"testing"

@@ -132,7 +133,8 @@ func TestPropertyPrunerByBuildRelease(t *testing.T) {
		Nested       nested
	}

	input := testBuildReleasePruner{
	inputFactory := func() testBuildReleasePruner {
		return testBuildReleasePruner{
			Default:      "Default",
			S_and_T_only: "S_and_T_only",
			T_later:      "T_later",
@@ -140,46 +142,64 @@ func TestPropertyPrunerByBuildRelease(t *testing.T) {
				F1_only: "F1_only",
			},
		}
	}

	marshal := func(t interface{}) string {
		bytes, err := json.MarshalIndent(t, "", "  ")
		if err != nil {
			panic(err)
		}
		return string(bytes)
	}

	assertJsonEquals := func(t *testing.T, expected, actual interface{}) {
		t.Helper()
		expectedJson := marshal(expected)
		actualJson := marshal(actual)
		if actualJson != expectedJson {
			t.Errorf("test struct: expected:\n%s\n got:\n%s", expectedJson, actualJson)
		}
	}

	t.Run("target S", func(t *testing.T) {
		testStruct := input
		testStruct := inputFactory()
		pruner := newPropertyPrunerByBuildRelease(&testStruct, buildReleaseS)
		pruner.pruneProperties(&testStruct)

		expected := input
		expected := inputFactory()
		expected.T_later = ""
		expected.Nested.F1_only = ""
		android.AssertDeepEquals(t, "test struct", expected, testStruct)
		assertJsonEquals(t, expected, testStruct)
	})

	t.Run("target T", func(t *testing.T) {
		testStruct := input
		testStruct := inputFactory()
		pruner := newPropertyPrunerByBuildRelease(&testStruct, buildReleaseT)
		pruner.pruneProperties(&testStruct)

		expected := input
		expected := inputFactory()
		expected.Nested.F1_only = ""
		android.AssertDeepEquals(t, "test struct", expected, testStruct)
		assertJsonEquals(t, expected, testStruct)
	})

	t.Run("target F1", func(t *testing.T) {
		testStruct := input
		testStruct := inputFactory()
		pruner := newPropertyPrunerByBuildRelease(&testStruct, buildReleaseFuture1)
		pruner.pruneProperties(&testStruct)

		expected := input
		expected := inputFactory()
		expected.S_and_T_only = ""
		android.AssertDeepEquals(t, "test struct", expected, testStruct)
		assertJsonEquals(t, expected, testStruct)
	})

	t.Run("target F2", func(t *testing.T) {
		testStruct := input
		testStruct := inputFactory()
		pruner := newPropertyPrunerByBuildRelease(&testStruct, buildReleaseFuture2)
		pruner.pruneProperties(&testStruct)

		expected := input
		expected := inputFactory()
		expected.S_and_T_only = ""
		expected.Nested.F1_only = ""
		android.AssertDeepEquals(t, "test struct", expected, testStruct)
		assertJsonEquals(t, expected, testStruct)
	})
}