func (o ObjectSerializationTestCase) createGeneratorsFactoryMethod(
	methodName string, generators []dst.Stmt, ctx *CodeGenerationContext) dst.Decl {
	gopterPackage := ctx.MustGetImportedPackageName(GopterReference)
	mapType := &dst.MapType{
		Key:   dst.NewIdent("string"),
		Value: astbuilder.QualifiedTypeName(gopterPackage, "Gen"),
	}
	fn := &astbuilder.FuncDetails{
		Name: methodName,
		Body: generators,
	}
	fn.AddComments("is a factory method for creating gopter generators")
	fn.AddParameter("gens", mapType)
	return fn.DefineFunc()
}
createGeneratorsFactoryMethod generates the AST for a method creating gopter generators
package astmodel
import (
	"fmt"
	"go/token"
	"sort"
	"github.com/dave/dst"
	"github.com/pkg/errors"
	"k8s.io/klog/v2"
	"github.com/Azure/k8s-infra/hack/generator/pkg/astbuilder"
)
type ObjectSerializationTestCase struct {
	testName   string
	subject    TypeName
	objectType *ObjectType
	idFactory  IdentifierFactory
}
var _ TestCase = &ObjectSerializationTestCase{}
func NewObjectSerializationTestCase(
	name TypeName,
	objectType *ObjectType,
	idFactory IdentifierFactory) *ObjectSerializationTestCase {
	testName := fmt.Sprintf("%v_WhenSerializedToJson_DeserializesAsEqual", name.Name())
	return &ObjectSerializationTestCase{
		testName:   testName,
		subject:    name,
		objectType: objectType,
		idFactory:  idFactory,
	}
}
func (o ObjectSerializationTestCase) Name() string {
	return o.testName
}
func (o ObjectSerializationTestCase) References() TypeNameSet {
	result := NewTypeNameSet()
	return result
}
func (o ObjectSerializationTestCase) AsFuncs(name TypeName, genContext *CodeGenerationContext) []dst.Decl {
	var errs []error
	properties := o.makePropertyMap()
	simpleGenerators := o.createGenerators(properties, genContext, o.createIndependentGenerator)
	relatedGenerators := o.createGenerators(properties, genContext, o.createRelatedGenerator)
	o.removeByPackage(properties, GenRuntimeReference)
	o.removeByPackage(properties, ApiExtensionsReference)
	o.removeByPackage(properties, ApiExtensionsJsonReference)
	for _, p := range properties {
		errs = append(errs, errors.Errorf("No generator created for %v (%v)", p.PropertyName(), p.PropertyType()))
	}
	var result []dst.Decl
	if len(simpleGenerators) == 0 && len(relatedGenerators) == 0 {
		errs = append(errs, errors.Errorf("No property generators for %v", name))
	} else {
		result = append(result,
			o.createTestRunner(genContext),
			o.createTestMethod(genContext),
			o.createGeneratorDeclaration(genContext),
			o.createGeneratorMethod(genContext, len(simpleGenerators) > 0, len(relatedGenerators) > 0))
		if len(simpleGenerators) > 0 {
			result = append(result, o.createGeneratorsFactoryMethod(o.idOfIndependentGeneratorsFactoryMethod(), simpleGenerators, genContext))
		}
		if len(relatedGenerators) > 0 {
			result = append(result, o.createGeneratorsFactoryMethod(o.idOfRelatedGeneratorsFactoryMethod(), relatedGenerators, genContext))
		}
	}
	if len(errs) > 0 {
		i := "issues"
		if len(errs) == 1 {
			i = "issue"
		}
		klog.Warningf("Encountered %d %s creating JSON Serialisation test for %s", len(errs), i, name)
		for _, err := range errs {
			klog.Warning(err)
		}
	}
	return result
}
func (o ObjectSerializationTestCase) RequiredImports() *PackageImportSet {
	result := NewPackageImportSet()
	result.AddImportsOfReferences(JsonReference, ReflectReference, TestingReference)
	result.AddImportsOfReferences(CmpReference, CmpOptsReference)
	result.AddImportsOfReferences(GopterReference, GopterGenReference, GopterPropReference)
	result.AddImportOfReference(DiffReference)
	result.AddImportOfReference(PrettyReference)
	for _, prop := range o.objectType.Properties() {
		for _, ref := range prop.PropertyType().RequiredPackageReferences().AsSlice() {
			result.AddImportOfReference(ref)
		}
	}
	result.Remove(NewPackageImport(GenRuntimeReference))
	return result
}
func (o ObjectSerializationTestCase) Equals(_ TestCase) bool {
	panic("implement me")
}
func (o ObjectSerializationTestCase) createTestRunner(codegenContext *CodeGenerationContext) dst.Decl {
	const (
		parametersLocal  = "parameters"
		propertiesLocal  = "properties"
		propertyMethod   = "Property"
		testingRunMethod = "TestingRun"
	)
	gopterPackage := codegenContext.MustGetImportedPackageName(GopterReference)
	propPackage := codegenContext.MustGetImportedPackageName(GopterPropReference)
	testingPackage := codegenContext.MustGetImportedPackageName(TestingReference)
	t := dst.NewIdent("t")
	defineParameters := astbuilder.SimpleAssignment(
		dst.NewIdent(parametersLocal),
		token.DEFINE,
		astbuilder.CallQualifiedFunc(gopterPackage, "DefaultTestParameters"))
	configureMaxSize := astbuilder.SimpleAssignment(
		&dst.SelectorExpr{
			X:   dst.NewIdent(parametersLocal),
			Sel: dst.NewIdent("MaxSize"),
		},
		token.ASSIGN,
		astbuilder.IntLiteral(10))
	defineProperties := astbuilder.SimpleAssignment(
		dst.NewIdent(propertiesLocal),
		token.DEFINE,
		astbuilder.CallQualifiedFunc(gopterPackage, "NewProperties", dst.NewIdent(parametersLocal)))
	testName := astbuilder.StringLiteralf("Round trip of %v via JSON returns original", o.Subject())
	propForAll := astbuilder.CallQualifiedFunc(
		propPackage,
		"ForAll",
		dst.NewIdent(o.idOfTestMethod()),
		astbuilder.CallFunc(o.idOfGeneratorMethod(o.subject)))
	defineTestCase := astbuilder.InvokeQualifiedFunc(
		propertiesLocal,
		propertyMethod,
		testName,
		propForAll)
	runTests := astbuilder.InvokeQualifiedFunc(propertiesLocal, testingRunMethod, t)
	fn := astbuilder.NewTestFuncDetails(
		testingPackage,
		o.testName,
		defineParameters,
		configureMaxSize,
		defineProperties,
		defineTestCase,
		runTests)
	return fn.DefineFunc()
}
func (o ObjectSerializationTestCase) createTestMethod(codegenContext *CodeGenerationContext) dst.Decl {
	const (
		binId        = "bin"
		actualId     = "actual"
		actualFmtId  = "actualFmt"
		matchId      = "match"
		subjectId    = "subject"
		subjectFmtId = "subjectFmt"
		resultId     = "result"
		errId        = "err"
	)
	jsonPackage := codegenContext.MustGetImportedPackageName(JsonReference)
	cmpPackage := codegenContext.MustGetImportedPackageName(CmpReference)
	cmpoptsPackage := codegenContext.MustGetImportedPackageName(CmpOptsReference)
	prettyPackage := codegenContext.MustGetImportedPackageName(PrettyReference)
	diffPackage := codegenContext.MustGetImportedPackageName(DiffReference)
	serialize := astbuilder.SimpleAssignmentWithErr(
		dst.NewIdent(binId),
		token.DEFINE,
		astbuilder.CallQualifiedFunc(jsonPackage, "Marshal", dst.NewIdent(subjectId)))
	serializeFailed := astbuilder.ReturnIfNotNil(
		dst.NewIdent(errId),
		astbuilder.CallQualifiedFunc("err", "Error"))
	declare := astbuilder.NewVariable(actualId, o.subject.name)
	deserialize := astbuilder.SimpleAssignment(
		dst.NewIdent("err"),
		token.ASSIGN,
		astbuilder.CallQualifiedFunc(jsonPackage, "Unmarshal",
			dst.NewIdent(binId),
			&dst.UnaryExpr{
				Op: token.AND,
				X:  dst.NewIdent(actualId),
			}))
	deserializeFailed := astbuilder.ReturnIfNotNil(
		dst.NewIdent(errId),
		astbuilder.CallQualifiedFunc("err", "Error"))
	equateEmpty := astbuilder.CallQualifiedFunc(cmpoptsPackage, "EquateEmpty")
	compare := astbuilder.SimpleAssignment(
		dst.NewIdent(matchId),
		token.DEFINE,
		astbuilder.CallQualifiedFunc(cmpPackage, "Equal",
			dst.NewIdent(subjectId),
			dst.NewIdent(actualId),
			equateEmpty))
	prettyPrint := &dst.IfStmt{
		Cond: &dst.UnaryExpr{
			Op: token.NOT,
			X:  dst.NewIdent(matchId),
		},
		Body: &dst.BlockStmt{
			List: []dst.Stmt{
				astbuilder.SimpleAssignment(
					dst.NewIdent(actualFmtId),
					token.DEFINE,
					astbuilder.CallQualifiedFunc(prettyPackage, "Sprint", dst.NewIdent(actualId))),
				astbuilder.SimpleAssignment(
					dst.NewIdent(subjectFmtId),
					token.DEFINE,
					astbuilder.CallQualifiedFunc(prettyPackage, "Sprint", dst.NewIdent(subjectId))),
				astbuilder.SimpleAssignment(
					dst.NewIdent(resultId),
					token.DEFINE,
					astbuilder.CallQualifiedFunc(diffPackage, "Diff", dst.NewIdent(subjectFmtId), dst.NewIdent(actualFmtId))),
				astbuilder.Returns(dst.NewIdent(resultId)),
			},
		},
	}
	ret := astbuilder.Returns(astbuilder.StringLiteral(""))
	fn := &astbuilder.FuncDetails{
		Name: o.idOfTestMethod(),
		Returns: []*dst.Field{
			{
				Type: dst.NewIdent("string"),
			},
		},
		Body: []dst.Stmt{
			serialize,
			serializeFailed,
			declare,
			deserialize,
			deserializeFailed,
			compare,
			prettyPrint,
			ret,
		},
	}
	fn.AddParameter("subject", o.Subject())
	fn.AddComments(fmt.Sprintf(
		"runs a test to see if a specific instance of %v round trips to JSON and back losslessly",
		o.Subject()))
	return fn.DefineFunc()
}
func (o ObjectSerializationTestCase) createGeneratorDeclaration(genContext *CodeGenerationContext) dst.Decl {
	comment := fmt.Sprintf(
		"Generator of %v instances for property testing - lazily instantiated by %v()",
		o.Subject(),
		o.idOfGeneratorMethod(o.subject))
	gopterPackage := genContext.MustGetImportedPackageName(GopterReference)
	decl := astbuilder.VariableDeclaration(
		o.idOfSubjectGeneratorGlobal(),
		astbuilder.QualifiedTypeName(gopterPackage, "Gen"),
		comment)
	return decl
}
func (o ObjectSerializationTestCase) createGeneratorMethod(ctx *CodeGenerationContext, haveSimpleGenerators bool, haveRelatedGenerators bool) dst.Decl {
	gopterPackage := ctx.MustGetImportedPackageName(GopterReference)
	genPackage := ctx.MustGetImportedPackageName(GopterGenReference)
	fn := &astbuilder.FuncDetails{
		Name: o.idOfGeneratorMethod(o.subject),
		Returns: []*dst.Field{
			{
				Type: astbuilder.QualifiedTypeName(gopterPackage, "Gen"),
			},
		},
	}
	fn.AddComments(
		fmt.Sprintf("returns a generator of %v instances for property testing.", o.Subject()),
		fmt.Sprintf("We first initialize %v with a simplified generator based on the fields with primitive types", o.idOfSubjectGeneratorGlobal()),
		"then replacing it with a more complex one that also handles complex fields.",
		"This ensures any cycles in the object graph properly terminate.",
		"The call to gen.Struct() captures the map, so we have to create a new one for the second generator.")
	earlyReturn := astbuilder.ReturnIfNotNil(
		dst.NewIdent(o.idOfSubjectGeneratorGlobal()),
		dst.NewIdent(o.idOfSubjectGeneratorGlobal()))
	fn.AddStatements(earlyReturn)
	if haveSimpleGenerators {
		makeIndependentMap := astbuilder.SimpleAssignment(
			dst.NewIdent("independentGenerators"),
			token.DEFINE,
			astbuilder.MakeMap(
				dst.NewIdent("string"),
				astbuilder.QualifiedTypeName(gopterPackage, "Gen")))
		addIndependentGenerators := astbuilder.InvokeFunc(
			o.idOfIndependentGeneratorsFactoryMethod(),
			dst.NewIdent("independentGenerators"))
		createIndependentGenerator := astbuilder.SimpleAssignment(
			dst.NewIdent(o.idOfSubjectGeneratorGlobal()),
			token.ASSIGN,
			astbuilder.CallQualifiedFunc(
				genPackage,
				"Struct",
				astbuilder.CallQualifiedFunc("reflect", "TypeOf", &dst.CompositeLit{Type: o.Subject()}),
				dst.NewIdent("independentGenerators")))
		fn.AddStatements(makeIndependentMap, addIndependentGenerators, createIndependentGenerator)
	}
	if haveRelatedGenerators {
		makeAllMap := astbuilder.SimpleAssignment(
			dst.NewIdent("allGenerators"),
			token.DEFINE,
			astbuilder.MakeMap(
				dst.NewIdent("string"),
				astbuilder.QualifiedTypeName(gopterPackage, "Gen")))
		fn.AddStatements(makeAllMap)
		if haveSimpleGenerators {
			addIndependentGenerators := astbuilder.InvokeFunc(
				o.idOfIndependentGeneratorsFactoryMethod(),
				dst.NewIdent("allGenerators"))
			fn.AddStatements(addIndependentGenerators)
		}
		addRelatedGenerators := astbuilder.InvokeFunc(
			o.idOfRelatedGeneratorsFactoryMethod(),
			dst.NewIdent("allGenerators"))
		createFullGenerator := astbuilder.SimpleAssignment(
			dst.NewIdent(o.idOfSubjectGeneratorGlobal()),
			token.ASSIGN,
			astbuilder.CallQualifiedFunc(
				genPackage,
				"Struct",
				astbuilder.CallQualifiedFunc("reflect", "TypeOf", &dst.CompositeLit{Type: o.Subject()}),
				dst.NewIdent("allGenerators")))
		fn.AddStatements(addRelatedGenerators, createFullGenerator)
	}
	normalReturn := astbuilder.Returns(dst.NewIdent(o.idOfSubjectGeneratorGlobal()))
	fn.AddStatements(normalReturn)
	return fn.DefineFunc()
}