text:
package controller

import (
	"errors"
	"reflect"
	"testing"
	"time"

	osb "github.com/pmorie/go-open-service-broker-client/v2"
	fakeosb "github.com/pmorie/go-open-service-broker-client/v2/fake"

	"github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1"
	"github.com/kubernetes-incubator/service-catalog/test/fake"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/diff"

	"strings"

	corev1 "k8s.io/api/core/v1"
	clientgotesting "k8s.io/client-go/testing"
)

// TestShouldReconcileClusterServiceBroker ensures that with the expected conditions the
// reconciler is reported as needing to run.
//
// The test cases are proving:
// - broker without ready condition will reconcile
// - broker with deletion timestamp set will reconcile
// - broker without ready condition, with status will reconcile
// - broker without ready condition, without status will reconcile
// - broker with status/ready, past relist interval will reconcile
// - broker with status/ready, within relist interval will NOT reconcile
// - broker with status/ready/checksum, will reconcile
func TestShouldReconcileClusterServiceBroker(t *testing.T) {
	// Anonymous struct fields:
	// name: short description of the test
	// broker: broker object to test
	// now: what time the interval is calculated with respect to interval
	// reconcile: whether or not the reconciler should run, the return of
	// shouldReconcileClusterServiceBroker
	cases := []struct {
		name      string
		broker    *v1beta1.ClusterServiceBroker
		now       time.Time
		reconcile bool
		err       error
	}{
		{
			name: "no status",
			broker: func() *v1beta1.ClusterServiceBroker {
				broker := getTestClusterServiceBroker()
				broker.Spec.RelistDuration = &metav1.Duration{Duration: 3 * time.Minute}
				return broker
			}(),
			now:       time.Now(),
			reconcile: true,
		},
		{
			name: "deletionTimestamp set",
			broker: func() *v1beta1.ClusterServiceBroker {
				broker := getTestClusterServiceBrokerWithStatus(v1beta1.ConditionTrue)
				broker.DeletionTimestamp = &metav1.Time{}
				broker.Spec.RelistDuration = &metav1.Duration{Duration: 3 * time.Hour}
				return broker
			}(),
			now:       time.Now(),
			reconcile: true,
		},
		{
			name: "no ready condition",
			broker: func() *v1beta1.ClusterServiceBroker {
				broker := getTestClusterServiceBroker()
				broker.Status = v1beta1.ClusterServiceBrokerStatus{
					Conditions: []v1beta1.ServiceBrokerCondition{
						{
							Type:   v1beta1.ServiceBrokerConditionType("NotARealCondition"),
							Status: v1beta1.ConditionTrue,
						},
					},
				}
				broker.Spec.RelistDuration = &metav1.Duration{Duration: 3 * time.Minute}
				return broker
			}(),
			now:       time.Now(),
			reconcile: true,
		},
		{
			name: "not ready",
			broker: func() *v1beta1.ClusterServiceBroker {
				broker := getTestClusterServiceBrokerWithStatus(v1beta1.ConditionFalse)
				broker.Spec.RelistDuration = &metav1.Duration{Duration: 3 * time.Minute}
				return broker
			}(),
			now:       time.Now(),
			reconcile: true,
		},
		{
			name: "ready, interval elapsed",
			broker: func() *v1beta1.ClusterServiceBroker {
				broker := getTestClusterServiceBrokerWithStatus(v1beta1.ConditionTrue)
				broker.Spec.RelistDuration = &metav1.Duration{Duration: 3 * time.Minute}
				return broker
			}(),
			now:       time.Now(),
			reconcile: true,
		},
		{
			name: "good steady state - ready, interval not elapsed, but last state change was a long time ago",
			broker: func() *v1beta1.ClusterServiceBroker {
				lastTransitionTime := metav1.NewTime(time.Now().Add(-30 * time.Minute))
				lastRelistTime := metav1.NewTime(time.Now().Add(-2 * time.Minute))
				broker := getTestClusterServiceBrokerWithStatusAndTime(v1beta1.ConditionTrue, lastTransitionTime, lastRelistTime)
				broker.Spec.RelistDuration = &metav1.Duration{Duration: 3 * time.Minute}
				return broker
			}(),
			now:       time.Now(),
			reconcile: false,
		},
		{
			name: "good steady state - ready, interval has elapsed, last state change was a long time ago",
			broker: func() *v1beta1.ClusterServiceBroker {
				lastTransitionTime := metav1.NewTime(time.Now().Add(-30 * time.Minute))
				lastRelistTime := metav1.NewTime(time.Now().Add(-4 * time.Minute))
				broker := getTestClusterServiceBrokerWithStatusAndTime(v1beta1.ConditionTrue, lastTransitionTime, lastRelistTime)
				broker.Spec.RelistDuration = &metav1.Duration{Duration: 3 * time.Minute}
				return broker
			}(),
			now:       time.Now(),
			reconcile: true,
		},
		{
			name: "ready, interval not elapsed",
			broker: func() *v1beta1.ClusterServiceBroker {
				broker := getTestClusterServiceBrokerWithStatus(v1beta1.ConditionTrue)
				broker.Spec.RelistDuration = &metav1.Duration{Duration: 3 * time.Hour}
				return broker
			}(),
			now:       time.Now(),
			reconcile: false,
		},
		{
			name: "ready, interval not elapsed, spec changed",
			broker: func() *v1beta1.ClusterServiceBroker {
				broker := getTestClusterServiceBrokerWithStatus(v1beta1.ConditionTrue)
				broker.Generation = 2
				broker.Status.ReconciledGeneration = 1
				broker.Spec.RelistDuration = &metav1.Duration{Duration: 3 * time.Hour}
				return broker
			}(),
			now:       time.Now(),
			reconcile: true,
		},
		{
			name: "ready, duration behavior, nil duration",
			broker: func() *v1beta1.ClusterServiceBroker {
				broker := getTestClusterServiceBrokerWithStatus(v1beta1.ConditionTrue)
				broker.Spec.RelistBehavior = v1beta1.ServiceBrokerRelistBehaviorDuration
				broker.Spec.RelistDuration = nil
				return broker
			}(),
			now:       time.Now(),
			reconcile: false,
		},
		{
			name: "ready, manual behavior",
			broker: func() *v1beta1.ClusterServiceBroker {
				broker := getTestClusterServiceBrokerWithStatus(v1beta1.ConditionTrue)
				broker.Spec.RelistBehavior = v1beta1.ServiceBrokerRelistBehaviorManual
				return broker
			}(),
			now:       time.Now(),
			reconcile: false,
		},
	}

	for _, tc := range cases {
		var ltt *time.Time
		if len(tc.broker.Status.Conditions) != 0 {
			ltt = &tc.broker.Status.Conditions[0].LastTransitionTime.Time
		}

		if tc.broker.Spec.RelistDuration != nil {
			interval := tc.broker.Spec.RelistDuration.Duration
			lastRelistTime := tc.broker.Status.LastCatalogRetrievalTime
			t.Logf("%v: now: %v, interval: %v, last transition time: %v, last relist time: %v", tc.name, tc.now, interval, ltt, lastRelistTime)
		} else {
			t.Logf("broker.Spec.RelistDuration set to nil")
		}

		actual := shouldReconcileClusterServiceBroker(tc.broker, tc.now)

		if e, a := tc.reconcile, actual; e != a {
			t.Errorf("%v: unexpected result: %s", tc.name, expectedGot(e, a))
		}
	}
}

// TestReconcileClusterServiceBrokerExistingServiceClassAndServicePlan
// verifies a simple, successful run of reconcileClusterServiceBroker() when a
// ClusterServiceClass and plan already exist.  This test will cause
// reconcileBroker() to fetch the catalog from the ClusterServiceBroker,
// create a Service Class for the single service that it lists and reconcile
// the service class ensuring the name and id of the relisted service matches
// the existing entry and updates the service catalog. There will be two
// additional reconciles of plans before the final broker update
func TestReconcileClusterServiceBrokerExistingServiceClassAndServicePlan(t *testing.T) {
	fakeKubeClient, fakeCatalogClient, fakeClusterServiceBrokerClient, testController, sharedInformers := newTestController(t, getTestCatalogConfig())

	testClusterServiceClass := getTestClusterServiceClass()
	testClusterServicePlan := getTestClusterServicePlan()
	testClusterServicePlanNonbindable := getTestClusterServicePlanNonbindable()
	sharedInformers.ClusterServiceClasses().Informer().GetStore().Add(testClusterServiceClass)

	fakeCatalogClient.AddReactor("list", "clusterserviceclasses", func(action clientgotesting.Action) (bool, runtime.Object, error) {
		return true, &v1beta1.ClusterServiceClassList{
			Items: []v1beta1.ClusterServiceClass{
				*testClusterServiceClass,
			},
		}, nil
	})

	if err := testController.reconcileClusterServiceBroker(getTestClusterServiceBroker()); err != nil {
		t.Fatalf("This should not fail: %v", err)
	}

	brokerActions := fakeClusterServiceBrokerClient.Actions()
	assertNumberOfClusterServiceBrokerActions(t, brokerActions, 1)
	assertGetCatalog(t, brokerActions[0])

	listRestrictions := clientgotesting.ListRestrictions{
		Labels: labels.Everything(),
		Fields: fields.OneTermEqualSelector("spec.clusterServiceBrokerName", "test-broker"),
	}

	actions := fakeCatalogClient.Actions()
	assertNumberOfActions(t, actions, 6)
	assertList(t, actions[0], &v1beta1.ClusterServiceClass{}, listRestrictions)
	assertList(t, actions[1], &v1beta1.ClusterServicePlan{}, listRestrictions)
	assertUpdate(t, actions[2], testClusterServiceClass)
	assertCreate(t, actions[3], testClusterServicePlan)
	assertCreate(t, actions[4], testClusterServicePlanNonbindable)

	// 4 update action for broker status subresource
	updatedClusterServiceBroker := assertUpdateStatus(t, actions[5], getTestClusterServiceBroker())
	assertClusterServiceBrokerReadyTrue(t, updatedClusterServiceBroker)

	// verify no kube resources created
	kubeActions := fakeKubeClient.Actions()
	assertNumberOfActions(t, kubeActions, 0)
}

func TestReconcileClusterServiceBrokerRemovedClusterServiceClass(t *testing.T) {
	fakeKubeClient, fakeCatalogClient, fakeClusterServiceBrokerClient, testController, sharedInformers := newTestController(t, getTestCatalogConfig())

	testClusterServiceClass := getTestClusterServiceClass()
	testRemovedClusterServiceClass := getTestRemovedClusterServiceClass()
	testClusterServicePlan := getTestClusterServicePlan()
	testClusterServicePlanNonbindable := getTestClusterServicePlanNonbindable()
	sharedInformers.ClusterServiceClasses().Informer().GetStore().Add(testClusterServiceClass)
	sharedInformers.ClusterServiceClasses().Informer().GetStore().Add(testRemovedClusterServiceClass)

	fakeCatalogClient.AddReactor("list", "clusterserviceclasses", func(action clientgotesting.Action) (bool, runtime.Object, error) {
		return true, &v1beta1.ClusterServiceClassList{
			Items: []v1beta1.ClusterServiceClass{
				*testClusterServiceClass,
				*testRemovedClusterServiceClass,
			},
		}, nil
	})

	if err := testController.reconcileClusterServiceBroker(getTestClusterServiceBroker()); err != nil {
		t.Fatalf("This should not fail: %v", err)
	}

	brokerActions := fakeClusterServiceBrokerClient.Actions()
	assertNumberOfClusterServiceBrokerActions(t, brokerActions, 1)
	assertGetCatalog(t, brokerActions[0])

	listRestrictions := clientgotesting.ListRestrictions{
		Labels: labels.Everything(),
		Fields: fields.OneTermEqualSelector("spec.clusterServiceBrokerName", "test-broker"),
	}

	actions := fakeCatalogClient.Actions()
	assertNumberOfActions(t, actions, 7)
	assertList(t, actions[0], &v1beta1.ClusterServiceClass{}, listRestrictions)
	assertList(t, actions[1], &v1beta1.ClusterServicePlan{}, listRestrictions)
	assertUpdate(t, actions[2], testClusterServiceClass)
	assertUpdateStatus(t, actions[3], testRemovedClusterServiceClass)
	assertCreate(t, actions[4], testClusterServicePlan)
	assertCreate(t, actions[5], testClusterServicePlanNonbindable)

	updatedClusterServiceBroker := assertUpdateStatus(t, actions[6], getTestClusterServiceBroker())
	assertClusterServiceBrokerReadyTrue(t, updatedClusterServiceBroker)

	// verify no kube resources created
	kubeActions := fakeKubeClient.Actions()
	assertNumberOfActions(t, kubeActions, 0)
}

func TestReconcileClusterServiceBrokerRemovedClusterServicePlan(t *testing.T) {
	fakeKubeClient, fakeCatalogClient, fakeClusterServiceBrokerClient, testController, sharedInformers := newTestController(t, getTestCatalogConfig())

	testClusterServiceClass := getTestClusterServiceClass()
	testClusterServicePlan := getTestClusterServicePlan()
	testClusterServicePlanNonbindable := getTestClusterServicePlanNonbindable()
	testRemovedClusterServicePlan := getTestRemovedClusterServicePlan()
	sharedInformers.ClusterServiceClasses().Informer().GetStore().Add(testClusterServiceClass)
	sharedInformers.ClusterServicePlans().Informer().GetStore().Add(testRemovedClusterServicePlan)

	fakeCatalogClient.AddReactor("list", "clusterserviceclasses", func(action clientgotesting.Action) (bool, runtime.Object, error) {
		return true, &v1beta1.ClusterServiceClassList{
			Items: []v1beta1.ClusterServiceClass{
				*testClusterServiceClass,
			},
		}, nil
	})
	fakeCatalogClient.AddReactor("list", "clusterserviceplans", func(action clientgotesting.Action) (bool, runtime.Object, error) {
		return true, &v1beta1.ClusterServicePlanList{
			Items: []v1beta1.ClusterServicePlan{
				*testRemovedClusterServicePlan,
			},
		}, nil
	})

	if err := testController.reconcileClusterServiceBroker(getTestClusterServiceBroker()); err != nil {
		t.Fatalf("This should not fail: %v", err)
	}

	brokerActions := fakeClusterServiceBrokerClient.Actions()
	assertNumberOfClusterServiceBrokerActions(t, brokerActions, 1)
	assertGetCatalog(t, brokerActions[0])

	listRestrictions := clientgotesting.ListRestrictions{
		Labels: labels.Everything(),
		Fields: fields.OneTermEqualSelector("spec.clusterServiceBrokerName", "test-broker"),
	}

	actions := fakeCatalogClient.Actions()
	assertNumberOfActions(t, actions, 7)
	assertList(t, actions[0], &v1beta1.ClusterServiceClass{}, listRestrictions)
	assertList(t, actions[1], &v1beta1.ClusterServicePlan{}, listRestrictions)
	assertUpdate(t, actions[2], testClusterServiceClass)
	assertCreate(t, actions[3], testClusterServicePlan)
	assertCreate(t, actions[4], testClusterServicePlanNonbindable)
	assertUpdateStatus(t, actions[5], testRemovedClusterServicePlan)

	updatedClusterServiceBroker := assertUpdateStatus(t, actions[6], getTestClusterServiceBroker())
	assertClusterServiceBrokerReadyTrue(t, updatedClusterServiceBroker)

	// verify no kube resources created
	kubeActions := fakeKubeClient.Actions()
	assertNumberOfActions(t, kubeActions, 0)
}

// TestReconcileClusterServiceBrokerExistingClusterServiceClassDifferentBroker simulates catalog
// refresh where broker lists a service which matches an existing, already
// cataloged service but the service points to a different ClusterServiceBroker.  Results in an error.
func TestReconcileClusterServiceBrokerExistingClusterServiceClassDifferentBroker(t *testing.T) {
	fakeKubeClient, fakeCatalogClient, fakeClusterServiceBrokerClient, testController, sharedInformers := newTestController(t, getTestCatalogConfig())

	testClusterServiceClass := getTestClusterServiceClass()
	testClusterServiceClass.Spec.ClusterServiceBrokerName = "notTheSame"

	sharedInformers.ClusterServiceClasses().Informer().GetStore().Add(testClusterServiceClass)

	if err := testController.reconcileClusterServiceBroker(getTestClusterServiceBroker()); err == nil {
		t.Fatal("The same service class should not belong to two different brokers.")
	}

	brokerActions := fakeClusterServiceBrokerClient.Actions()
	assertNumberOfClusterServiceBrokerActions(t, brokerActions, 1)
	assertGetCatalog(t, brokerActions[0])

	actions := fakeCatalogClient.Actions()
	assertNumberOfActions(t, actions, 3)

	listRestrictions := clientgotesting.ListRestrictions{
		Labels: labels.Everything(),
		Fields: fields.OneTermEqualSelector("spec.clusterServiceBrokerName", "test-broker"),
	}
	assertList(t, actions[0], &v1beta1.ClusterServiceClass{}, listRestrictions)
	assertList(t, actions[1], &v1beta1.ClusterServicePlan{}, listRestrictions)
	updatedClusterServiceBroker := assertUpdateStatus(t, actions[2], getTestClusterServiceBroker())
	assertClusterServiceBrokerReadyFalse(t, updatedClusterServiceBroker)

	// verify no kube resources created
	kubeActions := fakeKubeClient.Actions()
	assertNumberOfActions(t, kubeActions, 0)

	events := getRecordedEvents(testController)

	expectedEvent := warningEventBuilder(errorSyncingCatalogReason).msgf(
		"Error reconciling ClusterServiceClass (K8S: %q ExternalName: %q) (broker %q):",
		testClusterServiceClassGUID, testClusterServiceClassName, testClusterServiceBrokerName,
	).msgf(
		"ClusterServiceClass (K8S: %q ExternalName: %q) already exists for Broker %q",
		testClusterServiceClassGUID, testClusterServiceClassName, "notTheSame",
	)
	if err := checkEvents(events, expectedEvent.stringArr()); err != nil {
		t.Fatal(err)
	}
}

// TestReconcileClusterServiceBrokerExistingClusterServicePlanDifferentClass simulates catalog
// refresh where broker lists a service plan which matches an existing, already
// cataloged service plan but the plan points to a different ClusterServiceClass.  Results in an error.
func TestReconcileClusterServiceBrokerExistingClusterServicePlanDifferentClass(t *testing.T) {
	fakeKubeClient, fakeCatalogClient, fakeClusterServiceBrokerClient, testController, sharedInformers := newTestController(t, getTestCatalogConfig())

	testClusterServicePlan := getTestClusterServicePlan()
	testClusterServicePlan.Spec.ClusterServiceBrokerName = "notTheSame"
	testClusterServicePlan.Spec.ClusterServiceClassRef = v1beta1.ClusterObjectReference{
		Name: "notTheSameClass",
	}

	sharedInformers.ClusterServicePlans().Informer().GetStore().Add(testClusterServicePlan)

	if err := testController.reconcileClusterServiceBroker(getTestClusterServiceBroker()); err == nil {
		t.Fatal("The same service class should not belong to two different brokers.")
	}

	brokerActions := fakeClusterServiceBrokerClient.Actions()
	assertNumberOfClusterServiceBrokerActions(t, brokerActions, 1)
	assertGetCatalog(t, brokerActions[0])

	actions := fakeCatalogClient.Actions()
	assertNumberOfActions(t, actions, 4)

	listRestrictions := clientgotesting.ListRestrictions{
		Labels: labels.Everything(),
		Fields: fields.OneTermEqualSelector("spec.clusterServiceBrokerName", "test-broker"),
	}
	assertList(t, actions[0], &v1beta1.ClusterServiceClass{}, listRestrictions)
	assertList(t, actions[1], &v1beta1.ClusterServicePlan{}, listRestrictions)
	assertCreate(t, actions[2], getTestClusterServiceClass())
	updatedClusterServiceBroker := assertUpdateStatus(t, actions[3], getTestClusterServiceBroker())
	assertClusterServiceBrokerReadyFalse(t, updatedClusterServiceBroker)

	// verify no kube resources created
	kubeActions := fakeKubeClient.Actions()
	assertNumberOfActions(t, kubeActions, 0)

	events := getRecordedEvents(testController)

	expectedEvent := warningEventBuilder(errorSyncingCatalogReason).msgf(
		"Error reconciling ClusterServicePlan (K8S: %q ExternalName: %q):",
		testClusterServicePlanGUID, testClusterServicePlanName,
	).msgf(
		"ClusterServicePlan (K8S: %q ExternalName: %q) already exists for Broker %q",
		testClusterServicePlanGUID, testClusterServicePlanName, "notTheSame",
	)
	if err := checkEvents(events, expectedEvent.stringArr()); err != nil {
		t.Fatal(err)
	}
}

// TestReconcileClusterServiceBrokerDelete simulates a broker reconciliation where broker was marked for deletion.
// Results in service class and broker both being deleted.
func TestReconcileClusterServiceBrokerDelete(t *testing.T) {
	fakeKubeClient, fakeCatalogClient, fakeClusterServiceBrokerClient, testController, _ := newTestController(t, getTestCatalogConfig())

	testClusterServiceClass := getTestClusterServiceClass()
	testClusterServicePlan := getTestClusterServicePlan()

	broker := getTestClusterServiceBroker()
	broker.DeletionTimestamp = &metav1.Time{}
	broker.Finalizers = []string{v1beta1.FinalizerServiceCatalog}
	fakeCatalogClient.AddReactor("get", "clusterservicebrokers", func(action clientgotesting.Action) (bool, runtime.Object, error) {
		return true, broker, nil
	})
	fakeCatalogClient.AddReactor("list", "clusterserviceclasses", func(action clientgotesting.Action) (bool, runtime.Object, error) {
		return true, &v1beta1.ClusterServiceClassList{
			Items: []v1beta1.ClusterServiceClass{
				*testClusterServiceClass,
			},
		}, nil
	})
	fakeCatalogClient.AddReactor("list", "clusterserviceplans", func(action clientgotesting.Action) (bool, runtime.Object, error) {
		return true, &v1beta1.ClusterServicePlanList{
			Items: []v1beta1.ClusterServicePlan{
				*testClusterServicePlan,
			},
		}, nil
	})

	err := testController.reconcileClusterServiceBroker(broker)
	if err != nil {
		t.Fatalf("This should not fail : %v", err)
	}

	brokerActions := fakeClusterServiceBrokerClient.Actions()
	assertNumberOfClusterServiceBrokerActions(t, brokerActions, 0)

	// Verify no core kube actions occurred
	kubeActions := fakeKubeClient.Actions()
	assertNumberOfActions(t, kubeActions, 0)

	actions := fakeCatalogClient.Actions()
	// The four actions should be:
	// - list serviceplans
	// - delete serviceplans
	// - list serviceclasses
	// - delete serviceclass
	// - update the ready condition
	// - get the broker
	// - remove the finalizer
	assertNumberOfActions(t, actions, 7)

	listRestrictions := clientgotesting.ListRestrictions{
		Labels: labels.Everything(),
		Fields: fields.OneTermEqualSelector("spec.clusterServiceBrokerName", broker.Name),
	}
	assertList(t, actions[0], &v1beta1.ClusterServiceClass{}, listRestrictions)
	assertList(t, actions[1], &v1beta1.ClusterServicePlan{}, listRestrictions)
	assertDelete(t, actions[2], testClusterServicePlan)
	assertDelete(t, actions[3], testClusterServiceClass)
	updatedClusterServiceBroker := assertUpdateStatus(t, actions[4], broker)
	assertClusterServiceBrokerReadyFalse(t, updatedClusterServiceBroker)

	assertGet(t, actions[5], broker)

	updatedClusterServiceBroker = assertUpdateStatus(t, actions[6], broker)
	assertEmptyFinalizers(t, updatedClusterServiceBroker)

	events := getRecordedEvents(testController)

	expectedEvent := normalEventBuilder(successClusterServiceBrokerDeletedReason).msg(
		"The broker test-broker was deleted successfully.",
	)
	if err := checkEvents(events, expectedEvent.stringArr()); err != nil {
		t.Fatal(err)
	}
}

// TestReconcileClusterServiceBrokerErrorFetchingCatalog simulates broker reconciliation where
// OSB client responds with an error for getting the catalog which in turn causes
// reconcileClusterServiceBroker() to return an error.
func TestReconcileClusterServiceBrokerErrorFetchingCatalog(t *testing.T) {
	fakeKubeClient, fakeCatalogClient, fakeClusterServiceBrokerClient, testController, _ := newTestController(t, fakeosb.FakeClientConfiguration{
		CatalogReaction: &fakeosb.CatalogReaction{
			Error: errors.New("ooops"),
		},
	})

	broker := getTestClusterServiceBroker()

	if err := testController.reconcileClusterServiceBroker(broker); err == nil {
		t.Fatal("Should have failed to get the catalog.")
	}

	brokerActions := fakeClusterServiceBrokerClient.Actions()
	assertNumberOfClusterServiceBrokerActions(t, brokerActions, 1)
	assertGetCatalog(t, brokerActions[0])

	actions := fakeCatalogClient.Actions()
	assertNumberOfActions(t, actions, 2)

	updatedClusterServiceBroker := assertUpdateStatus(t, actions[0], broker)
	assertClusterServiceBrokerReadyFalse(t, updatedClusterServiceBroker)

	updatedClusterServiceBroker = assertUpdateStatus(t, actions[1], broker)
	assertClusterServiceBrokerOperationStartTimeSet(t, updatedClusterServiceBroker, true)

	assertNumberOfActions(t, fakeKubeClient.Actions(), 0)

	events := getRecordedEvents(testController)

	expectedEvent := warningEventBuilder(errorFetchingCatalogReason).msg("Error getting broker catalog:").msg("ooops")
	if err := checkEvents(events, expectedEvent.stringArr()); err != nil {
		t.Fatal(err)
	}
}

// TestReconcileClusterServiceBrokerZeroServices simulates broker reconciliation where
// OSB client responds with zero services which causes reconcileClusterServiceBroker()
// to return an error
func TestReconcileClusterServiceBrokerZeroServices(t *testing.T) {
	fakeKubeClient, fakeCatalogClient, fakeClusterServiceBrokerClient, testController, _ := newTestController(t, fakeosb.FakeClientConfiguration{
		CatalogReaction: &fakeosb.CatalogReaction{
			Response: &osb.CatalogResponse{},
		},
	})

	broker := getTestClusterServiceBroker()

	if err := testController.reconcileClusterServiceBroker(broker); err == nil {
		t.Fatal("ClusterServiceBroker should not have had any Service Classes.")
	}

	brokerActions := fakeClusterServiceBrokerClient.Actions()
	assertNumberOfClusterServiceBrokerActions(t, brokerActions, 1)
	assertGetCatalog(t, brokerActions[0])

	actions := fakeCatalogClient.Actions()
	assertNumberOfActions(t, actions, 1)

	updatedClusterServiceBroker := assertUpdateStatus(t, actions[0], broker)
	assertClusterServiceBrokerReadyFalse(t, updatedClusterServiceBroker)

	assertNumberOfActions(t, fakeKubeClient.Actions(), 0)

	events := getRecordedEvents(testController)
	assertNumEvents(t, events, 1)

	expectedEvent := warningEventBuilder(errorSyncingCatalogReason).msgf(
		"Error getting catalog payload for broker %q; received zero services; at least one service is required",
		testClusterServiceBrokerName,
	)
	if err := checkEvents(events, expectedEvent.stringArr()); err != nil {
		t.Fatal(err)
	}
}

func TestReconcileClusterServiceBrokerWithAuth(t *testing.T) {
	basicAuthInfo := &v1beta1.ServiceBrokerAuthInfo{
		Basic: &v1beta1.BasicAuthConfig{
			SecretRef: &v1beta1.ObjectReference{
				Namespace: "test-ns",
				Name:      "auth-secret",
			},
		},
	}
	bearerAuthInfo := &v1beta1.ServiceBrokerAuthInfo{
		Bearer: &v1beta1.BearerTokenAuthConfig{
			SecretRef: &v1beta1.ObjectReference{
				Namespace: "test-ns",
				Name:      "auth-secret",
			},
		},
	}
	basicAuthSecret := &corev1.Secret{
		Data: map[string][]byte{
			v1beta1.BasicAuthUsernameKey: []byte("foo"),
			v1beta1.BasicAuthPasswordKey: []byte("bar"),
		},
	}
	bearerAuthSecret := &corev1.Secret{
		Data: map[string][]byte{
			v1beta1.BearerTokenKey: []byte("token"),
		},
	}

	// The test cases here are testing the correctness of authentication with broker
	//
	// Anonymous struct fields:
	// name: short description of the test
	// authInfo: broker auth configuration
	// secret: auth secret to be returned upon request from Service Catalog
	// shouldSucceed: whether authentication should succeed
	cases := []struct {
		name          string
		authInfo      *v1beta1.ServiceBrokerAuthInfo
		secret        *corev1.Secret
		shouldSucceed bool
	}{
		{
			name:          "basic auth - normal",
			authInfo:      basicAuthInfo,
			secret:        basicAuthSecret,
			shouldSucceed: true,
		},
		{
			name:          "basic auth - invalid secret",
			authInfo:      basicAuthInfo,
			secret:        bearerAuthSecret,
			shouldSucceed: false,
		},
		{
			name:          "basic auth - secret not found",
			authInfo:      basicAuthInfo,
			secret:        nil,
			shouldSucceed: false,
		},
		{
			name:          "bearer auth - normal",
			authInfo:      bearerAuthInfo,
			secret:        bearerAuthSecret,
			shouldSucceed: true,
		},
		{
			name:          "bearer auth - invalid secret",
			authInfo:      bearerAuthInfo,
			secret:        basicAuthSecret,
			shouldSucceed: false,
		},
		{
			name:          "bearer auth - secret not found",
			authInfo:      bearerAuthInfo,
			secret:        nil,
			shouldSucceed: false,
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()
			testReconcileClusterServiceBrokerWithAuth(t, tc.authInfo, tc.secret, tc.shouldSucceed)
		})
	}
}

func testReconcileClusterServiceBrokerWithAuth(t *testing.T, authInfo *v1beta1.ServiceBrokerAuthInfo, secret *corev1.Secret, shouldSucceed bool) {
	fakeKubeClient, fakeCatalogClient, fakeClusterServiceBrokerClient, testController, _ := newTestController(t, fakeosb.FakeClientConfiguration{})

	broker := getTestClusterServiceBrokerWithAuth(authInfo)
	if secret != nil {
		addGetSecretReaction(fakeKubeClient, secret)
	} else {
		addGetSecretNotFoundReaction(fakeKubeClient)
	}
	testClusterServiceClass := getTestClusterServiceClass()
	fakeClusterServiceBrokerClient.CatalogReaction = &fakeosb.CatalogReaction{
		Response: &osb.CatalogResponse{
			Services: []osb.Service{
				{
					ID:   testClusterServiceClass.Spec.ExternalID,
					Name: testClusterServiceClass.Name,
				},
			},
		},
	}

	err := testController.reconcileClusterServiceBroker(broker)
	if shouldSucceed && err != nil {
		t.Fatal("Should have succeeded to get the catalog for the broker. got error: ", err)
	}

	brokerActions := fakeClusterServiceBrokerClient.Actions()
	if shouldSucceed {
		// GetCatalog
		assertNumberOfClusterServiceBrokerActions(t, brokerActions, 1)
		assertGetCatalog(t, brokerActions[0])
	} else {
		assertNumberOfClusterServiceBrokerActions(t, brokerActions, 0)
	}

	actions := fakeCatalogClient.Actions()
	if shouldSucceed {
		assertNumberOfActions(t, actions, 2)
		assertCreate(t, actions[0], testClusterServiceClass)
		updatedClusterServiceBroker := assertUpdateStatus(t, actions[1], broker)
		assertClusterServiceBrokerReadyTrue(t, updatedClusterServiceBroker)
	} else {
		assertNumberOfActions(t, actions, 1)
		updatedClusterServiceBroker := assertUpdateStatus(t, actions[0], broker)
		assertClusterServiceBrokerReadyFalse(t, updatedClusterServiceBroker)
	}

	// verify one kube action occurred
	kubeActions := fakeKubeClient.Actions()
	assertNumberOfActions(t, kubeActions, 1)

	getAction := kubeActions[0].(clientgotesting.GetAction)
	if e, a := "get", getAction.GetVerb(); e != a {
		t.Fatalf("Unexpected verb on action; %s", expectedGot(e, a))
	}
	if e, a := "secrets", getAction.GetResource().Resource; e != a {
		t.Fatalf("Unexpected resource on action; %s", expectedGot(e, a))
	}

	events := getRecordedEvents(testController)
	assertNumEvents(t, events, 1)

	var expectedEvent string
	if shouldSucceed {
		expectedEvent = corev1.EventTypeNormal + " " + successFetchedCatalogReason + " " + successFetchedCatalogMessage
	} else {
		expectedEvent = corev1.EventTypeWarning + " " + errorAuthCredentialsReason + " " + `Error getting broker auth credentials`
	}
	if e, a := expectedEvent, events[0]; !strings.HasPrefix(a, e) {
		t.Fatalf("Received unexpected event, %s", expectedGot(e, a))
	}
}

// TestReconcileClusterServiceBrokerWithReconcileError simulates broker reconciliation where
// creation of a service class causes an error which causes ReconcileClusterServiceBroker to
// return an error
func TestReconcileClusterServiceBrokerWithReconcileError(t *testing.T) {
	fakeKubeClient, fakeCatalogClient, fakeClusterServiceBrokerClient, testController, _ := newTestController(t, getTestCatalogConfig())

	broker := getTestClusterServiceBroker()

	fakeCatalogClient.AddReactor("create", "clusterserviceclasses", func(action clientgotesting.Action) (bool, runtime.Object, error) {
		return true, nil, errors.New("error creating serviceclass")
	})

	if err := testController.reconcileClusterServiceBroker(broker); err == nil {
		t.Fatal("There should have been an error.")
	}

	brokerActions := fakeClusterServiceBrokerClient.Actions()
	assertNumberOfClusterServiceBrokerActions(t, brokerActions, 1)
	assertGetCatalog(t, brokerActions[0])

	actions := fakeCatalogClient.Actions()
	assertNumberOfActions(t, actions, 4)

	listRestrictions := clientgotesting.ListRestrictions{
		Labels: labels.Everything(),
		Fields: fields.OneTermEqualSelector("spec.clusterServiceBrokerName", broker.Name),
	}
	assertList(t, actions[0], &v1beta1.ClusterServiceClass{}, listRestrictions)
	assertList(t, actions[1], &v1beta1.ClusterServicePlan{}, listRestrictions)

	// the two plans in the catalog as two separate actions

	createSCAction := actions[2].(clientgotesting.CreateAction)
	createdSC, ok := createSCAction.GetObject().(*v1beta1.ClusterServiceClass)
	if !ok {
		t.Fatalf("couldn't convert to a ClusterServiceClass: %+v", createSCAction.GetObject())
	}
	if e, a := getTestClusterServiceClass(), createdSC; !reflect.DeepEqual(e, a) {
		t.Fatalf("unexpected diff for created ClusterServiceClass: %v,\n\nEXPECTED: %+v\n\nACTUAL:  %+v", diff.ObjectReflectDiff(e, a), e, a)
	}
	updatedClusterServiceBroker := assertUpdateStatus(t, actions[3], broker)
	assertClusterServiceBrokerReadyFalse(t, updatedClusterServiceBroker)

	kubeActions := fakeKubeClient.Actions()
	assertNumberOfActions(t, kubeActions, 0)

	events := getRecordedEvents(testController)

	expectedEvent := warningEventBuilder(errorSyncingCatalogReason).msgf(
		"Error reconciling ClusterServiceClass (K8S: %q ExternalName: %q) (broker %q):",
		testClusterServiceClassGUID, testClusterServiceClassName, testClusterServiceBrokerName,
	).msg("error creating serviceclass")
	if err := checkEvents(events, expectedEvent.stringArr()); err != nil {
		t.Fatal(err)
	}
}

// TestReconcileClusterServiceBrokerSuccessOnFinalRetry verifies that reconciliation can
// succeed on the last attempt before timing out of the retry loop
func TestReconcileClusterServiceBrokerSuccessOnFinalRetry(t *testing.T) {
	fakeKubeClient, fakeCatalogClient, fakeClusterServiceBrokerClient, testController, _ := newTestController(t, getTestCatalogConfig())

	testClusterServiceClass := getTestClusterServiceClass()

	broker := getTestClusterServiceBroker()
	// seven days ago, before the last refresh period
	startTime := metav1.NewTime(time.Now().Add(-7 * 24 * time.Hour))
	broker.Status.OperationStartTime = &startTime

	if err := testController.reconcileClusterServiceBroker(broker); err != nil {
		t.Fatalf("This should not fail : %v", err)
	}

	brokerActions := fakeClusterServiceBrokerClient.Actions()
	assertNumberOfClusterServiceBrokerActions(t, brokerActions, 1)
	assertGetCatalog(t, brokerActions[0])

	actions := fakeCatalogClient.Actions()
	assertNumberOfActions(t, actions, 7)

	listRestrictions := clientgotesting.ListRestrictions{
		Labels: labels.Everything(),
		Fields: fields.OneTermEqualSelector("spec.clusterServiceBrokerName", broker.Name),
	}

	// first action should be an update action to clear OperationStartTime
	updatedClusterServiceBroker := assertUpdateStatus(t, actions[0], getTestClusterServiceBroker())
	assertClusterServiceBrokerOperationStartTimeSet(t, updatedClusterServiceBroker, false)

	assertList(t, actions[1], &v1beta1.ClusterServiceClass{}, listRestrictions)
	assertList(t, actions[2], &v1beta1.ClusterServicePlan{}, listRestrictions)
	assertCreate(t, actions[3], testClusterServiceClass)
	assertCreate(t, actions[4], getTestClusterServicePlan())
	assertCreate(t, actions[5], getTestClusterServicePlanNonbindable())

	updatedClusterServiceBroker = assertUpdateStatus(t, actions[6], getTestClusterServiceBroker())
	assertClusterServiceBrokerReadyTrue(t, updatedClusterServiceBroker)

	// verify no kube resources created
	kubeActions := fakeKubeClient.Actions()
	assertNumberOfActions(t, kubeActions, 0)
}

// TestReconcileClusterServiceBrokerFailureOnFinalRetry verifies that reconciliation
// completes in the event of an error after the retry duration elapses.
func TestReconcileClusterServiceBrokerFailureOnFinalRetry(t *testing.T) {
	fakeKubeClient, fakeCatalogClient, fakeClusterServiceBrokerClient, testController, _ := newTestController(t, fakeosb.FakeClientConfiguration{
		CatalogReaction: &fakeosb.CatalogReaction{
			Error: errors.New("ooops"),
		},
	})

	broker := getTestClusterServiceBroker()
	startTime := metav1.NewTime(time.Now().Add(-7 * 24 * time.Hour))
	broker.Status.OperationStartTime = &startTime

	if err := testController.reconcileClusterServiceBroker(broker); err != nil {
		t.Fatalf("Should have return no error because the retry duration has elapsed: %v", err)
	}

	brokerActions := fakeClusterServiceBrokerClient.Actions()
	assertNumberOfClusterServiceBrokerActions(t, brokerActions, 1)
	assertGetCatalog(t, brokerActions[0])

	actions := fakeCatalogClient.Actions()
	assertNumberOfActions(t, actions, 2)

	updatedClusterServiceBroker := assertUpdateStatus(t, actions[0], broker)
	assertClusterServiceBrokerReadyFalse(t, updatedClusterServiceBroker)

	updatedClusterServiceBroker = assertUpdateStatus(t, actions[1], broker)
	assertClusterServiceBrokerCondition(t, updatedClusterServiceBroker, v1beta1.ServiceBrokerConditionFailed, v1beta1.ConditionTrue)
	assertClusterServiceBrokerOperationStartTimeSet(t, updatedClusterServiceBroker, false)

	assertNumberOfActions(t, fakeKubeClient.Actions(), 0)

	events := getRecordedEvents(testController)

	expectedEventPrefixes := []string{
		warningEventBuilder(errorFetchingCatalogReason).String(),
		warningEventBuilder(errorReconciliationRetryTimeoutReason).String(),
	}

	if err := checkEventPrefixes(events, expectedEventPrefixes); err != nil {
		t.Fatal(err)
	}
}

// TestReconcileClusterServiceBrokerWithStatusUpdateError verifies that the reconciler
// returns an error when there is a conflict updating the status of the resource.
// This is an otherwise successful scenario where the update to set the
// ready condition fails.
func TestReconcileClusterServiceBrokerWithStatusUpdateError(t *testing.T) {
	fakeKubeClient, fakeCatalogClient, fakeClusterServiceBrokerClient, testController, _ := newTestController(t, getTestCatalogConfig())

	testClusterServiceClass := getTestClusterServiceClass()

	broker := getTestClusterServiceBroker()

	fakeCatalogClient.AddReactor("update", "clusterservicebrokers", func(action clientgotesting.Action) (bool, runtime.Object, error) {
		return true, nil, errors.New("update error")
	})

	err := testController.reconcileClusterServiceBroker(broker)
	if err == nil {
		t.Fatalf("expected error from but got none")
	}
	if e, a := "update error", err.Error(); e != a {
		t.Fatalf("unexpected error returned: %s", expectedGot(e, a))
	}

	brokerActions := fakeClusterServiceBrokerClient.Actions()
	assertNumberOfClusterServiceBrokerActions(t, brokerActions, 1)
	assertGetCatalog(t, brokerActions[0])

	actions := fakeCatalogClient.Actions()
	assertNumberOfActions(t, actions, 6)

	listRestrictions := clientgotesting.ListRestrictions{
		Labels: labels.Everything(),
		Fields: fields.OneTermEqualSelector("spec.clusterServiceBrokerName", broker.Name),
	}

	assertList(t, actions[0], &v1beta1.ClusterServiceClass{}, listRestrictions)
	assertList(t, actions[1], &v1beta1.ClusterServicePlan{}, listRestrictions)
	assertCreate(t, actions[2], testClusterServiceClass)
	assertCreate(t, actions[3], getTestClusterServicePlan())
	assertCreate(t, actions[4], getTestClusterServicePlanNonbindable())

	// 4 update action for broker status subresource
	updatedClusterServiceBroker := assertUpdateStatus(t, actions[5], getTestClusterServiceBroker())
	assertClusterServiceBrokerReadyTrue(t, updatedClusterServiceBroker)

	// verify no kube resources created
	kubeActions := fakeKubeClient.Actions()
	assertNumberOfActions(t, kubeActions, 0)
}

// TestUpdateServiceBrokerCondition ensures that with specific conditions
// the broker correctly reflects the changes during updateServiceBrokerCondition().
//
// The test cases are proving:
// - broker transitions from unset status to not ready results in status change and new time
// - broker transitions from not ready to not ready results in no changes
// - broker transitions from not ready to not ready and with reason & msg updates results in no time change, but reflects new reason & msg
// - broker transitions from not ready to ready results in status change & new time
// - broker transitions from ready to ready results in no status change
// - broker transitions from ready to not ready results in status change & new time
// - condition reason & message should always be updated
func TestUpdateServiceBrokerCondition(t *testing.T) {
	// Anonymous struct fields:
	// name: short description of the test
	// input: broker object to test
	// status: new condition status
	// reason: condition reason
	// message: condition message
	// transitionTimeChanged: true if the test conditions should result in transition time change
	cases := []struct {
		name                  string
		input                 *v1beta1.ClusterServiceBroker
		status                v1beta1.ConditionStatus
		reason                string
		message               string
		transitionTimeChanged bool
	}{

		{
			name:                  "initially unset",
			input:                 getTestClusterServiceBroker(),
			status:                v1beta1.ConditionFalse,
			transitionTimeChanged: true,
		},
		{
			name:                  "not ready -> not ready",
			input:                 getTestClusterServiceBrokerWithStatus(v1beta1.ConditionFalse),
			status:                v1beta1.ConditionFalse,
			transitionTimeChanged: false,
		},
		{
			name:                  "not ready -> not ready with reason and message change",
			input:                 getTestClusterServiceBrokerWithStatus(v1beta1.ConditionFalse),
			status:                v1beta1.ConditionFalse,
			reason:                "foo",
			message:               "bar",
			transitionTimeChanged: false,
		},
		{
			name:                  "not ready -> ready",
			input:                 getTestClusterServiceBrokerWithStatus(v1beta1.ConditionFalse),
			status:                v1beta1.ConditionTrue,
			transitionTimeChanged: true,
		},
		{
			name:                  "ready -> ready",
			input:                 getTestClusterServiceBrokerWithStatus(v1beta1.ConditionTrue),
			status:                v1beta1.ConditionTrue,
			transitionTimeChanged: false,
		},
		{
			name:                  "ready -> not ready",
			input:                 getTestClusterServiceBrokerWithStatus(v1beta1.ConditionTrue),
			status:                v1beta1.ConditionFalse,
			transitionTimeChanged: true,
		},
	}

	for _, tc := range cases {
		_, fakeCatalogClient, _, testController, _ := newTestController(t, getTestCatalogConfig())

		inputClone := tc.input.DeepCopy()

		err := testController.updateClusterServiceBrokerCondition(tc.input, v1beta1.ServiceBrokerConditionReady, tc.status, tc.reason, tc.message)
		if err != nil {
			t.Errorf("%v: error updating broker condition: %v", tc.name, err)
			continue
		}

		if !reflect.DeepEqual(tc.input, inputClone) {
			t.Errorf("%v: updating broker condition mutated input: %s", tc.name, expectedGot(inputClone, tc.input))
			continue
		}

		actions := fakeCatalogClient.Actions()
		if ok := expectNumberOfActions(t, tc.name, actions, 1); !ok {
			continue
		}

		updatedClusterServiceBroker, ok := expectUpdateStatus(t, tc.name, actions[0], tc.input)
		if !ok {
			continue
		}

		updateActionObject, ok := updatedClusterServiceBroker.(*v1beta1.ClusterServiceBroker)
		if !ok {
			t.Errorf("%v: couldn't convert to broker", tc.name)
			continue
		}

		var initialTs metav1.Time
		if len(inputClone.Status.Conditions) != 0 {
			initialTs = inputClone.Status.Conditions[0].LastTransitionTime
		}

		if e, a := 1, len(updateActionObject.Status.Conditions); e != a {
			t.Errorf("%v: %s", tc.name, expectedGot(e, a))
		}

		outputCondition := updateActionObject.Status.Conditions[0]
		newTs := outputCondition.LastTransitionTime

		if tc.transitionTimeChanged && initialTs == newTs {
			t.Errorf("%v: transition time didn't change when it should have", tc.name)
			continue
		} else if !tc.transitionTimeChanged && initialTs != newTs {
			t.Errorf("%v: transition time changed when it shouldn't have", tc.name)
			continue
		}
		if e, a := tc.reason, outputCondition.Reason; e != "" && e != a {
			t.Errorf("%v: condition reasons didn't match; %s", tc.name, expectedGot(e, a))
			continue
		}
		if e, a := tc.message, outputCondition.Message; e != "" && e != a {
			t.Errorf("%v: condition message didn't match; %s", tc.name, expectedGot(e, a))
		}
	}
}

func TestReconcileClusterServicePlanFromClusterServiceBrokerCatalog(t *testing.T) {
	updatedPlan := func() *v1beta1.ClusterServicePlan {
		p := getTestClusterServicePlan()
		p.Spec.Description = "new-description"
		p.Spec.ExternalName = "new-value"
		p.Spec.Free = false
		p.Spec.ExternalMetadata = &runtime.RawExtension{Raw: []byte(`{"field1": "value1"}`)}
		p.Spec.ServiceInstanceCreateParameterSchema = &runtime.RawExtension{Raw: []byte(`{"field1": "value1"}`)}
		p.Spec.ServiceInstanceUpdateParameterSchema = &runtime.RawExtension{Raw: []byte(`{"field1": "value1"}`)}
		p.Spec.ServiceBindingCreateParameterSchema = &runtime.RawExtension{Raw: []byte(`{"field1": "value1"}`)}

		return p
	}

	cases := []struct {
		name                    string
		newServicePlan          *v1beta1.ClusterServicePlan
		existingServicePlan     *v1beta1.ClusterServicePlan
		listerServicePlan       *v1beta1.ClusterServicePlan
		shouldError             bool
		errText                 *string
		catalogClientPrepFunc   func(*fake.Clientset)
		catalogActionsCheckFunc func(t *testing.T, name string, actions []clientgotesting.Action)
	}{
		{
			name:           "new plan",
			newServicePlan: getTestClusterServicePlan(),
			shouldError:    false,
			catalogActionsCheckFunc: func(t *testing.T, name string, actions []clientgotesting.Action) {
				expectNumberOfActions(t, name, actions, 1)
				expectCreate(t, name, actions[0], getTestClusterServicePlan())
			},
		},
		{
			name:                "exists, but for a different broker",
			newServicePlan:      getTestClusterServicePlan(),
			existingServicePlan: getTestClusterServicePlan(),
			listerServicePlan: func() *v1beta1.ClusterServicePlan {
				p := getTestClusterServicePlan()
				p.Spec.ClusterServiceBrokerName = "something-else"
				return p
			}(),
			shouldError: true,
			errText:     strPtr(`ClusterServiceBroker "test-broker": ClusterServicePlan "test-plan" already exists for Broker "something-else"`),
		},
		{
			name:                "plan update",
			newServicePlan:      updatedPlan(),
			existingServicePlan: getTestClusterServicePlan(),
			shouldError:         false,
			catalogActionsCheckFunc: func(t *testing.T, name string, actions []clientgotesting.Action) {
				expectNumberOfActions(t, name, actions, 1)
				expectUpdate(t, name, actions[0], updatedPlan())
			},
		},
		{
			name:                "plan update - failure",
			newServicePlan:      updatedPlan(),
			existingServicePlan: getTestClusterServicePlan(),
			catalogClientPrepFunc: func(client *fake.Clientset) {
				client.AddReactor("update", "clusterserviceplans", func(action clientgotesting.Action) (bool, runtime.Object, error) {
					return true, nil, errors.New("oops")
				})
			},
			shouldError: true,
			errText:     strPtr("oops"),
		},
	}

	broker := getTestClusterServiceBroker()

	for _, tc := range cases {
		_, fakeCatalogClient, _, testController, sharedInformers := newTestController(t, noFakeActions())
		if tc.catalogClientPrepFunc != nil {
			tc.catalogClientPrepFunc(fakeCatalogClient)
		}

		if tc.listerServicePlan != nil {
			sharedInformers.ClusterServicePlans().Informer().GetStore().Add(tc.listerServicePlan)
		}

		err := testController.reconcileClusterServicePlanFromClusterServiceBrokerCatalog(broker, tc.newServicePlan, tc.existingServicePlan)
		if err != nil {
			if !tc.shouldError {
				t.Errorf("%v: unexpected error from method under test: %v", tc.name, err)
				continue
			} else if tc.errText != nil && *tc.errText != err.Error() {
				t.Errorf("%v: unexpected error text from method under test; %s", tc.name, expectedGot(tc.errText, err.Error()))
				continue
			}
		}

		if tc.catalogActionsCheckFunc != nil {
			actions := fakeCatalogClient.Actions()
			tc.catalogActionsCheckFunc(t, tc.name, actions)
		}
	}
}


text:
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Diagnostics.Contracts;
using System;

// Disable the "this variable is not used" warning as every field would imply it.
#pragma warning disable 0414
// Disable the "this variable is never assigned to".
#pragma warning disable 0067
// Disable the "this event is never assigned to".
#pragma warning disable 0649
// Disable the "this variable is never used".
#pragma warning disable 0169
// Disable the "new keyword not required" warning.
#pragma warning disable 0109
// Disable the "extern without DllImport" warning.
#pragma warning disable 0626
// Disable the "could hide other member" warning, can happen on certain properties.
#pragma warning disable 0108

namespace System.Windows.Documents
{
  abstract public partial class DocumentPaginator
  {
    #region Methods and constructors
    public virtual new void CancelAsync(Object userState)
    {
    }

    public virtual new void ComputePageCount()
    {
    }

    public virtual new void ComputePageCountAsync()
    {
    }

    public virtual new void ComputePageCountAsync(Object userState)
    {
    }

    protected DocumentPaginator()
    {
    }

    public abstract DocumentPage GetPage(int pageNumber);

    public virtual new void GetPageAsync(int pageNumber, Object userState)
    {
    }

    public virtual new void GetPageAsync(int pageNumber)
    {
    }

    protected virtual new void OnComputePageCountCompleted(System.ComponentModel.AsyncCompletedEventArgs e)
    {
    }

    protected virtual new void OnGetPageCompleted(GetPageCompletedEventArgs e)
    {
    }

    protected virtual new void OnPagesChanged(PagesChangedEventArgs e)
    {
    }
    #endregion

    #region Properties and indexers
    public abstract bool IsPageCountValid
    {
      get;
    }

    public abstract int PageCount
    {
      get;
    }

    public abstract System.Windows.Size PageSize
    {
      get;
      set;
    }

    public abstract IDocumentPaginatorSource Source
    {
      get;
    }
    #endregion

    #region Events
    public event System.ComponentModel.AsyncCompletedEventHandler ComputePageCountCompleted
    {
      add
      {
      }
      remove
      {
      }
    }

    public event GetPageCompletedEventHandler GetPageCompleted
    {
      add
      {
      }
      remove
      {
      }
    }

    public event PagesChangedEventHandler PagesChanged
    {
      add
      {
      }
      remove
      {
      }
    }
    #endregion
  }
}

redpajama_set_name: RedPajamaGithub


text:
using UnityEngine;
using UnityEngine.UI;
public class UIControl : MonoBehaviour {
    public Text diceValueText;
    public Text currentPlayerText;
    public Button buttonRoll;
    public Button buttonSkip;
    public Button buttonNewGame;
    public Image winnerPanel;
    public Text labelWinner;
    public Text textWinner;

    Color GetCorrespondingColor(HorseColor color)
    {
        switch(color)
        {
            case HorseColor.Red: return Color.red;
            case HorseColor.Blue: return Color.blue;
            case HorseColor.Yellow: return Color.yellow;
            case HorseColor.Green: return Color.green;
            default: return Color.white;
        }
    }
    void Start()
    {
        buttonRoll.onClick.AddListener(delegate { ButtonRoll_OnClick(); });
        buttonSkip.onClick.AddListener(delegate { ButtonSkip_OnClick(); });
        buttonNewGame.onClick.AddListener(delegate { ButtonNewGame_OnClick(); });
        HideWinnerPanel();
    }
    void Update()
    {
        currentPlayerText.text = GameState.Instance.CurrentPlayer.ToString();
        currentPlayerText.color = GetCorrespondingColor(GameState.Instance.CurrentPlayer);
        diceValueText.text = GameState.Instance.CurrentDiceValue.ToString();
        buttonRoll.interactable = (GameState.Instance.CurrentDiceValue == 0);
        buttonSkip.interactable = !buttonRoll.interactable;
        if (GameState.Instance.Winner != HorseColor.None)
            DisplayWinnerPanel();
    }
    void DisplayWinnerPanel()
    {
        this.enabled = false;
        winnerPanel.enabled = true;
        labelWinner.enabled = true;
        textWinner.enabled = true;
        buttonRoll.interactable = false;
        buttonSkip.interactable = false;
        textWinner.text = GameState.Instance.Winner.ToString();
        textWinner.color = GetCorrespondingColor(GameState.Instance.Winner);
    }
    void HideWinnerPanel()
    {
        winnerPanel.enabled = false;
        labelWinner.enabled = false;
        textWinner.enabled = false;
        this.enabled = true;
    }
    public void ButtonRoll_OnClick()
    {
        GameState.Instance.CurrentDiceValue = Random.Range(1, 7); // Set dice value randomly from the range 0 - 6
    }
    public void ButtonSkip_OnClick()
    {
        GameState.Instance.ResetDiceAndChangePlayer();
    }
    public void ButtonNewGame_OnClick()
    {
        HideWinnerPanel();
        GameState.Instance.ResetGameState();
    }
}


text:
@interface NPViewController : UIViewController

@end


text:
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

using System.Linq;
using Microsoft.CodeAnalysis.Editor.CSharp.Outlining;
using Microsoft.CodeAnalysis.Editor.Implementation.Outlining;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Outlining
{
    public class CommentTests : AbstractOutlinerTests
    {
        [Fact, Trait(Traits.Feature, Traits.Features.Outlining)]
        public void TestSimpleComment1()
        {
            var tree = ParseLines("// Hello",
                                        "// C#!",
                                        "class C",
                                        "{",
                                        "}");

            var typeDecl = tree.DigToFirstTypeDeclaration();
            var trivia = typeDecl.GetLeadingTrivia();
            Assert.Equal(4, trivia.Count);

            var regions = CSharpOutliningHelpers.CreateCommentRegions(trivia).ToList();
            Assert.Equal(1, regions.Count);

            var actualRegion = regions[0];
            var expectedRegion = new OutliningSpan(
                TextSpan.FromBounds(0, 16),
                "// Hello ...",
                autoCollapse: true);

            AssertRegion(expectedRegion, actualRegion);
        }

        [Fact, Trait(Traits.Feature, Traits.Features.Outlining)]
        public void TestSimpleComment2()
        {
            var tree = ParseLines("// Hello",
                                        "//",
                                        "// C#!",
                                        "class C",
                                        "{",
                                        "}");

            var typeDecl = tree.DigToFirstTypeDeclaration();
            var trivia = typeDecl.GetLeadingTrivia();
            Assert.Equal(6, trivia.Count);

            var regions = CSharpOutliningHelpers.CreateCommentRegions(trivia).ToList();
            Assert.Equal(1, regions.Count);

            var actualRegion = regions[0];
            var expectedRegion = new OutliningSpan(
                TextSpan.FromBounds(0, 20),
                "// Hello ...",
                autoCollapse: true);

            AssertRegion(expectedRegion, actualRegion);
        }

        [Fact, Trait(Traits.Feature, Traits.Features.Outlining)]
        public void TestSimpleComment3()
        {
            var tree = ParseLines("// Hello",
                                        string.Empty,
                                        "// C#!",
                                        "class C",
                                        "{",
                                        "}");

            var typeDecl = tree.DigToFirstTypeDeclaration();
            var trivia = typeDecl.GetLeadingTrivia();
            Assert.Equal(5, trivia.Count);

            var regions = CSharpOutliningHelpers.CreateCommentRegions(trivia).ToList();
            Assert.Equal(1, regions.Count);

            var actualRegion = regions[0];
            var expectedRegion = new OutliningSpan(
                TextSpan.FromBounds(0, 18),
                "// Hello ...",
                autoCollapse: true);

            AssertRegion(expectedRegion, actualRegion);
        }

        [Fact, Trait(Traits.Feature, Traits.Features.Outlining)]
        public void TestSingleLineCommentGroupFollowedByDocumentationComment()
        {
            var tree = ParseLines("// Hello",
                                        string.Empty,
                                        "// C#!",
                                        "/// <summary></summary>",
                                        "class C",
                                        "{",
                                        "}");

            var typeDecl = tree.DigToFirstTypeDeclaration();
            var trivia = typeDecl.GetLeadingTrivia();
            Assert.Equal(6, trivia.Count);

            var regions = CSharpOutliningHelpers.CreateCommentRegions(trivia).ToList();
            Assert.Equal(1, regions.Count);

            var actualRegion = regions[0];
            var expectedRegion = new OutliningSpan(
                TextSpan.FromBounds(0, 18),
                "// Hello ...",
                autoCollapse: true);

            AssertRegion(expectedRegion, actualRegion);
        }

        [Fact, Trait(Traits.Feature, Traits.Features.Outlining)]
        public void TestMultilineComment1()
        {
            var tree = ParseLines("/* Hello",
                                        "C#! */",
                                        "class C",
                                        "{",
                                        "}");

            var typeDecl = tree.DigToFirstTypeDeclaration();
            var trivia = typeDecl.GetLeadingTrivia();
            Assert.Equal(2, trivia.Count);

            var regions = CSharpOutliningHelpers.CreateCommentRegions(trivia).ToList();
            Assert.Equal(1, regions.Count);

            var actualRegion = regions[0];
            var expectedRegion = new OutliningSpan(
                TextSpan.FromBounds(0, 16),
                "/* Hello ...",
                autoCollapse: true);

            AssertRegion(expectedRegion, actualRegion);
        }

        [Fact, Trait(Traits.Feature, Traits.Features.Outlining)]
        public void TestMultilineCommentOnOneLine()
        {
            var tree = ParseLines("/* Hello C#! */",
                                        "class C",
                                        "{",
                                        "}");

            var typeDecl = tree.DigToFirstTypeDeclaration();
            var trivia = typeDecl.GetLeadingTrivia();
            Assert.Equal(2, trivia.Count);

            var regions = CSharpOutliningHelpers.CreateCommentRegions(trivia).ToList();
            Assert.Equal(1, regions.Count);

            var actualRegion = regions[0];
            var expectedRegion = new OutliningSpan(
                TextSpan.FromBounds(0, 15),
                "/* Hello C#! ...",
                autoCollapse: true);

            AssertRegion(expectedRegion, actualRegion);
        }
    }
}


text:
package com.xiaoaiqinqin.common.redis;

import org.apache.commons.lang3.StringUtils;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * All rights Reserved, Designed By www.xiaoaiqinqin.com
 *
 * @Description:
 * @author: 小艾亲亲
 * @date: 2017年7月11日 下午9:56:47
 */
public class SingleRedisUtil {

	private static final String HOST = "47.7";
	private static final Integer PORT = 6379;
	private static final String AUTH = "Lcq123!";
	// 最大激活数
	private static final int MAX_ACTIVE = 1024;
	// 最大空闲数
	private static final int MAX_IDLE = 200;
	// 等待可用连接的最大时间,单位毫秒,-1用不超时间
	private static final int MAX_WAIT = 10000;
	private static int TIMEOUT = 10000;
	// 在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;
	private static boolean TEST_ON_BORROW = true;
	private static JedisPool jedisPool = null;
	private static Jedis jedis = null;
	static {
		jedis = new Jedis(HOST, PORT);
		jedis.auth(AUTH);

		try {
			JedisPoolConfig config = new JedisPoolConfig();
			config.setMaxTotal(MAX_ACTIVE);
			config.setMaxIdle(MAX_IDLE);
			config.setMaxWaitMillis(MAX_WAIT);
			config.setTestOnBorrow(TEST_ON_BORROW);
			jedisPool = new JedisPool(config, HOST, PORT, TIMEOUT, AUTH);
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	/**
	 * 获取Jedis实例
	 *
	 * @return
	 */
	public synchronized static Jedis getJedis() {
		try {
			if (jedisPool != null) {
				Jedis resource = jedisPool.getResource();
				return resource;
			} else {
				return null;
			}
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	public static void setKeyValue(String key, String value, Integer expireSeconds) {

		getJedis().setex(key, expireSeconds, value);
	}

	/**
	 * 登录成功
	 * @param token
	 * @param userId
	 */
	public static void loginToken(String token,String userId){
		getJedis().setex(CacheTypePrefix.LOGIN_STATE+token, 3600*24*7, userId);
	}

	/**
	 * 根据token获取用户ID
	 * @param token
	 * @return
	 */
	public static String getLoginUserByToken(String token){
		return getValueByKey(CacheTypePrefix.LOGIN_STATE+token);
	}

	/**
	 * 延长过期时间
	 * @param token
	 */
	public static void extendLoginState(String token) {
		String userId = getLoginUserByToken(token);

		if(!StringUtils.isEmpty(userId)){
			loginToken(token, userId);
		}
	}

	public static String getValueByKey(String key) {
		return getJedis().get(key);
	}

	public static Long deleteValueByKey(String key) {
		return getJedis().del(key);
	}

	public static void main(String[] args) {
		String key = "lcq";
		String value = "lcq123";
		setKeyValue(key, value, 9999);

		String result = getValueByKey(key);
		System.out.println(result);
	}
}


text:
import { URI, UriComponents } from 'vs/base/common/uri';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IEditorSerializer } from 'vs/workbench/common/editor';
import { WebviewContentOptions, WebviewExtensionDescription, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview';
import { WebviewIcons } from 'vs/workbench/contrib/webviewPanel/browser/webviewIconManager';
import { WebviewInput } from './webviewEditorInput';
import { IWebviewWorkbenchService } from './webviewWorkbenchService';

export type SerializedWebviewOptions = WebviewOptions & WebviewContentOptions;

interface SerializedIconPath {
	light: string | UriComponents;
	dark: string | UriComponents;
}

export interface SerializedWebview {
	readonly id: string;
	readonly origin: string | undefined;
	readonly viewType: string;
	readonly providedId: string | undefined;
	readonly title: string;
	readonly options: SerializedWebviewOptions;
	readonly extensionLocation: UriComponents | undefined;
	readonly extensionId: string | undefined;
	readonly state: any;
	readonly iconPath: SerializedIconPath | undefined;
	readonly group?: number;
}

export interface DeserializedWebview {
	readonly id: string;
	readonly origin: string | undefined;
	readonly viewType: string;
	readonly providedId: string | undefined;
	readonly title: string;
	readonly webviewOptions: WebviewOptions;
	readonly contentOptions: WebviewContentOptions;
	readonly extension: WebviewExtensionDescription | undefined;
	readonly state: any;
	readonly iconPath: WebviewIcons | undefined;
	readonly group?: number;
}

export class WebviewEditorInputSerializer implements IEditorSerializer {

	public static readonly ID = WebviewInput.typeId;

	public constructor(
		@IWebviewWorkbenchService private readonly _webviewWorkbenchService: IWebviewWorkbenchService
	) { }

	public canSerialize(input: WebviewInput): boolean {
		return this._webviewWorkbenchService.shouldPersist(input);
	}

	public serialize(input: WebviewInput): string | undefined {
		if (!this._webviewWorkbenchService.shouldPersist(input)) {
			return undefined;
		}

		const data = this.toJson(input);
		try {
			return JSON.stringify(data);
		} catch {
			return undefined;
		}
	}

	public deserialize(
		_instantiationService: IInstantiationService,
		serializedEditorInput: string
	): WebviewInput {
		const data = this.fromJson(JSON.parse(serializedEditorInput));
		return this._webviewWorkbenchService.reviveWebview({
			webviewInitInfo: {
				id: data.id,
				providedId: data.providedId,
				origin: data.origin,
				options: data.webviewOptions,
				contentOptions: data.contentOptions,
				extension: data.extension,
			},
			viewType: data.viewType,
			title: data.title,
			iconPath: data.iconPath,
			state: data.state,
			group: data.group
		});
	}

	protected fromJson(data: SerializedWebview): DeserializedWebview {
		return {
			...data,
			extension: reviveWebviewExtensionDescription(data.extensionId, data.extensionLocation),
			iconPath: reviveIconPath(data.iconPath),
			state: reviveState(data.state),
			webviewOptions: restoreWebviewOptions(data.options),
			contentOptions: restoreWebviewContentOptions(data.options),
		};
	}

	protected toJson(input: WebviewInput): SerializedWebview {
		return {
			id: input.id,
			origin: input.webview.origin,
			viewType: input.viewType,
			providedId: input.providedId,
			title: input.getName(),
			options: { ...input.webview.options, ...input.webview.contentOptions },
			extensionLocation: input.extension ? input.extension.location : undefined,
			extensionId: input.extension && input.extension.id ? input.extension.id.value : undefined,
			state: input.webview.state,
			iconPath: input.iconPath ? { light: input.iconPath.light, dark: input.iconPath.dark, } : undefined,
			group: input.group
		};
	}
}

export function reviveWebviewExtensionDescription(
	extensionId: string | undefined,
	extensionLocation: UriComponents | undefined,
): WebviewExtensionDescription | undefined {
	if (!extensionId) {
		return undefined;
	}

	const location = reviveUri(extensionLocation);
	if (!location) {
		return undefined;
	}

	return {
		id: new ExtensionIdentifier(extensionId),
		location,
	};
}

function reviveIconPath(data: SerializedIconPath | undefined) {
	if (!data) {
		return undefined;
	}

	const light = reviveUri(data.light);
	const dark = reviveUri(data.dark);
	return light && dark ? { light, dark } : undefined;
}

function reviveUri(data: string | UriComponents): URI;
function reviveUri(data: string | UriComponents | undefined): URI | undefined;
function reviveUri(data: string | UriComponents | undefined): URI | undefined {
	if (!data) {
		return undefined;
	}

	try {
		if (typeof data === 'string') {
			return URI.parse(data);
		}
		return URI.from(data);
	} catch {
		return undefined;
	}
}

function reviveState(state: unknown | undefined): undefined | string {
	return typeof state === 'string' ? state : undefined;
}

export function restoreWebviewOptions(options: SerializedWebviewOptions): WebviewOptions {
	return options;
}

export function restoreWebviewContentOptions(options: SerializedWebviewOptions): WebviewContentOptions {
	return {
		...options,
		localResourceRoots: options.localResourceRoots?.map(uri => reviveUri(uri)),
	};
}


text:
<?php

namespace common\models\base;

use common\models\User;

/**
 * This is the model class for table "{{%file}}".
 *
 * @property integer $id
 * @property integer $uploaderId
 * @property string $name
 * @property string $path
 * @property string $mimeType
 * @property string $extension
 * @property integer $size
 * @property integer $createdAt
 * @property integer $updatedAt
 */
class BaseFile extends \common\models\base\BaseActiveRecord
{
    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return '{{%file}}';
    }

    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['uploaderId', 'name'], 'required'],
            [['uploaderId', 'size', 'createdAt', 'updatedAt'], 'integer'],
            [['name', 'path', 'mimeType'], 'string', 'max' => 200],
            [['extension'], 'string', 'max' => 50],
            [['uploaderId'], 'exist', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['uploaderId' => 'id']],
        ];
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels()
    {
        return [
            'id' => '主键',
            'uploaderId' => '上传者',
            'name' => '文件名',
            'path' => '存储路径',
            'mimeType' => 'Mime Type',
            'extension' => '扩展名',
            'size' => '文件大小',
            'createdAt' => '创建时间',
            'updatedAt' => '更新时间',
        ];
    }
}


text:
<ion-tabs class="tabs-icon-top tabs-color-active-positive">
  <ion-tab title="{{'Playback' | translate}}" icon="mopidy-mobile-icon-playback" href="/playback">
    <ion-nav-view name="playback"></ion-nav-view>
  </ion-tab>
  <ion-tab title="{{'Tracklist' | translate}}" icon="mopidy-mobile-icon-tracklist" href="/tracklist">
    <ion-nav-view name="tracklist"></ion-nav-view>
  </ion-tab>
  <ion-tab title="{{'Library' | translate}}" icon="mopidy-mobile-icon-library" href="/library">
    <ion-nav-view name="library"></ion-nav-view>
  </ion-tab>
  <ion-tab title="{{'Playlists' | translate}}" icon="mopidy-mobile-icon-playlists" href="/playlists">
    <ion-nav-view name="playlists"></ion-nav-view>
  </ion-tab>
  <ion-tab title="{{'Settings' | translate}}" icon="mopidy-mobile-icon-settings" href="/settings">
    <ion-nav-view name="settings"></ion-nav-view>
  </ion-tab>
</ion-tabs>


text:
package org.scalaide.core.project

import org.junit.Test
import org.junit.Assert._
import org.scalaide.core.IScalaInstallation
import org.scalaide.core.internal.project.ScalaInstallation
import org.scalaide.core.IScalaPlugin
import scala.tools.nsc.settings.ScalaVersion
import scala.tools.nsc.settings.SpecificScalaVersion
import org.scalaide.util.eclipse.OSGiUtils
import scala.tools.nsc.settings.SpecificScalaVersion
import org.scalaide.core.internal.project.BundledScalaInstallation
import org.eclipse.core.runtime.Platform
import org.scalaide.core.internal.project.MultiBundleScalaInstallation
import org.osgi.framework.Bundle
import org.eclipse.core.runtime.Path
import org.eclipse.core.runtime.IPath

class ScalaInstallationTest {

  /**
   * check the installations of Scala based on a bundle of jars
   */
  @Test
  def bundledInstallationsTest(): Unit = {
    val bundledInstallations = ScalaInstallation.bundledInstallations

    IScalaPlugin().scalaVersion match {
      case SpecificScalaVersion(2, 10, _, _) =>
        assertEquals("Unexpected Scala bundle", 0, bundledInstallations.length)
      case SpecificScalaVersion(2, 11, _, _) =>
        assertEquals("Wrong number of Scala bundles", 1, bundledInstallations.length)
        val scalaInstallation = bundledInstallations(0)

        scalaInstallation.version match {
          case SpecificScalaVersion(2, 10, _, _) =>
          case _ =>
            fail(s"Unexpected bundled Scala version: ${scalaInstallation.version}")
        }

        val bundlePath = OSGiUtils.pathInBundle(Platform.getBundle("org.scala-ide.scala210.jars"), "target").get.removeLastSegments(1)

        assertEquals("Wrong library jar", bundlePath.append(BundledScalaInstallation.ScalaLibraryPath), scalaInstallation.library.classJar)
        assertEquals("Wrong compiler jar", bundlePath.append(BundledScalaInstallation.ScalaCompilerPath), scalaInstallation.compiler.classJar)

        val expectedAllJars = Seq(
          bundlePath.append(BundledScalaInstallation.ScalaLibraryPath),
          bundlePath.append(BundledScalaInstallation.ScalaCompilerPath),
          bundlePath.append(BundledScalaInstallation.ScalaActorPath),
          bundlePath.append(BundledScalaInstallation.ScalaReflectPath),
          bundlePath.append(BundledScalaInstallation.ScalaSwingPath)).sortBy(_.toOSString())

        assertEquals("Wrong all jars", expectedAllJars, scalaInstallation.allJars.map(_.classJar).sortBy(_.toOSString()))

        val expectedAllSourceJars = Seq(
          bundlePath.append(BundledScalaInstallation.ScalaLibrarySourcesPath),
          bundlePath.append(BundledScalaInstallation.ScalaCompilerSourcesPath),
          bundlePath.append(BundledScalaInstallation.ScalaActorSourcesPath),
          bundlePath.append(BundledScalaInstallation.ScalaReflectSourcesPath),
          bundlePath.append(BundledScalaInstallation.ScalaSwingSourcesPath)).sortBy(_.toOSString())

        assertEquals("Wrong all source jars", expectedAllSourceJars, scalaInstallation.allJars.flatMap(_.sourceJar).sortBy(_.toOSString()))

      case SpecificScalaVersion(2, 12, _, _) =>
        assertEquals("Unexpected Scala bundle", 0, bundledInstallations.length)
      case v =>
        fail(s"Unsupported Scala version: $v")
    }
  }

  @Test
  def multiBundleInstallationsTest(): Unit = {
    val multiBundleInstallations = ScalaInstallation.multiBundleInstallations

    IScalaPlugin().scalaVersion match {
      case SpecificScalaVersion(2, 10, _, _) =>
        assertEquals("Unexpected Scala bundle", 1, multiBundleInstallations.length)
        checkMultiBundleInstallation(2, 10, multiBundleInstallations.head)
      case SpecificScalaVersion(2, 11, _, _) =>
        assertEquals("Wrong number of Scala bundles", 1, multiBundleInstallations.length)
        checkMultiBundleInstallation(2, 11, multiBundleInstallations.head)
      case SpecificScalaVersion(2, 12, _, _) =>
        assertEquals("Wrong number of Scala bundles", 1, multiBundleInstallations.length)
        checkMultiBundleInstallation(2, 12, multiBundleInstallations.head)
      case v =>
        fail(s"Unsupported Scala version: $v")
    }
  }

  val m2RepoLocationPattern = "(.*/)([^/]+)/([^/]+)/[^/]+\\.jar".r
  val pluginsLocationPattern = "(.*/)([^/]+)_([^/]+)\\.jar".r

  def checkMultiBundleInstallation(major: Int, minor: Int, scalaInstallation: IScalaInstallation) = {

    def isLibraryBundle(bundle: Bundle) = {
      val version = bundle.getVersion()
      bundle.getSymbolicName() == MultiBundleScalaInstallation.ScalaLibraryBundleId &&
        version.getMajor() == major &&
        version.getMinor() == minor
    }

    val libraryBundle = IScalaPlugin().getBundle().getBundleContext().getBundles().toList.find(isLibraryBundle).get

    val libraryPath = OSGiUtils.getBundlePath(libraryBundle).get

    // create a path builder depending on when the library bundle jar is coming from: a plugins folder or a m2 repo
    val bundlePathBuilder: (String) => IPath = libraryPath.toString match {
      case pluginsLocationPattern(pluginsFolder, MultiBundleScalaInstallation.ScalaLibraryBundleId, versionString) =>
        val pluginsPath = new Path(pluginsFolder)
        (bundleId: String) => pluginsPath.append(s"${bundleId}_${versionString}.jar")
      case m2RepoLocationPattern(repoFolder, MultiBundleScalaInstallation.ScalaLibraryBundleId, versionString) =>
        val repoPath = new Path(repoFolder)
        (bundleId: String) => repoPath.append(s"${bundleId}/${versionString}/${bundleId}-${versionString}.jar")
      case v =>
        fail(s"Didn't understood '$v'")
        // never reachs here
        null
    }

    assertEquals("Wrong library jar", bundlePathBuilder(MultiBundleScalaInstallation.ScalaLibraryBundleId), scalaInstallation.library.classJar)
    assertEquals("Wrong compiler jar", bundlePathBuilder(MultiBundleScalaInstallation.ScalaCompilerBundleId), scalaInstallation.compiler.classJar)

    val expectedAllJars = Seq(
      bundlePathBuilder(MultiBundleScalaInstallation.ScalaLibraryBundleId),
      bundlePathBuilder(MultiBundleScalaInstallation.ScalaCompilerBundleId),
      bundlePathBuilder(MultiBundleScalaInstallation.ScalaActorsBundleId),
      bundlePathBuilder(MultiBundleScalaInstallation.ScalaReflectBundleId)).sortBy(_.toOSString())

    assertEquals("Wrong all jars", expectedAllJars, scalaInstallation.allJars.map(_.classJar).sortBy(_.toOSString()))

    val expectedAllSourceJars = Seq(
      bundlePathBuilder(MultiBundleScalaInstallation.ScalaLibraryBundleId + ".source"),
      bundlePathBuilder(MultiBundleScalaInstallation.ScalaCompilerBundleId + ".source"),
      bundlePathBuilder(MultiBundleScalaInstallation.ScalaActorsBundleId + ".source"),
      bundlePathBuilder(MultiBundleScalaInstallation.ScalaReflectBundleId + ".source")).sortBy(_.toOSString())

    assertEquals("Wrong all sources jars", expectedAllSourceJars, scalaInstallation.allJars.flatMap(_.sourceJar).sortBy(_.toOSString()))

  }

}


text:
<?php

namespace Drupal\layout_builder\Plugin\SectionStorage;

use Drupal\Component\Plugin\Context\ContextInterface as ComponentContextInterface;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Plugin\Context\EntityContext;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
use Drupal\field_ui\FieldUI;
use Drupal\layout_builder\DefaultsSectionStorageInterface;
use Drupal\layout_builder\Entity\SampleEntityGeneratorInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Routing\RouteCollection;

/**
 * Defines the 'defaults' section storage type.
 *
 * DefaultsSectionStorage uses a positive weight because:
 * - It must be picked after
 *   \Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage.
 * - The default weight is 0, so other custom implementations will also take
 *   precedence unless otherwise specified.
 *
 * @SectionStorage(
 *   id = "defaults",
 *   weight = 20,
 *   context_definitions = {
 *     "display" = @ContextDefinition("entity:entity_view_display"),
 *     "view_mode" = @ContextDefinition("string", default_value = "default"),
 *   },
 * )
 *
 * @internal
 *   Plugin classes are internal.
 */
class DefaultsSectionStorage extends SectionStorageBase implements ContainerFactoryPluginInterface, DefaultsSectionStorageInterface {

  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * The entity type bundle info.
   *
   * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
   */
  protected $entityTypeBundleInfo;

  /**
   * The sample entity generator.
   *
   * @var \Drupal\layout_builder\Entity\SampleEntityGeneratorInterface
   */
  protected $sampleEntityGenerator;

  /**
   * {@inheritdoc}
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $entity_type_bundle_info, SampleEntityGeneratorInterface $sample_entity_generator) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);

    $this->entityTypeManager = $entity_type_manager;
    $this->entityTypeBundleInfo = $entity_type_bundle_info;
    $this->sampleEntityGenerator = $sample_entity_generator;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('entity_type.manager'),
      $container->get('entity_type.bundle.info'),
      $container->get('layout_builder.sample_entity_generator')
    );
  }

  /**
   * {@inheritdoc}
   */
  protected function getSectionList() {
    return $this->getContextValue('display');
  }

  /**
   * Gets the entity storing the defaults.
   *
   * @return \Drupal\layout_builder\Entity\LayoutEntityDisplayInterface
   *   The entity storing the defaults.
   */
  protected function getDisplay() {
    return $this->getSectionList();
  }

  /**
   * {@inheritdoc}
   */
  public function getStorageId() {
    return $this->getDisplay()->id();
  }

  /**
   * {@inheritdoc}
   */
  public function getRedirectUrl() {
    return Url::fromRoute("entity.entity_view_display.{$this->getDisplay()->getTargetEntityTypeId()}.view_mode", $this->getRouteParameters());
  }

  /**
   * {@inheritdoc}
   */
  public function getLayoutBuilderUrl($rel = 'view') {
    return Url::fromRoute("layout_builder.{$this->getStorageType()}.{$this->getDisplay()->getTargetEntityTypeId()}.$rel", $this->getRouteParameters());
  }

  /**
   * Provides the route parameters needed to generate a URL for this object.
   *
   * @return mixed[]
   *   An associative array of parameter names and values.
   */
  protected function getRouteParameters() {
    $display = $this->getDisplay();
    $entity_type = $this->entityTypeManager->getDefinition($display->getTargetEntityTypeId());
    $route_parameters = FieldUI::getRouteBundleParameter($entity_type, $display->getTargetBundle());
    $route_parameters['view_mode_name'] = $display->getMode();
    return $route_parameters;
  }

  /**
   * {@inheritdoc}
   */
  public function buildRoutes(RouteCollection $collection) {
    foreach ($this->getEntityTypes() as $entity_type_id => $entity_type) {
      // Try to get the route from the current collection.
      if (!$entity_route = $collection->get($entity_type->get('field_ui_base_route'))) {
        continue;
      }

      $path = $entity_route->getPath() . '/display/{view_mode_name}/layout';

      $defaults = [];
      $defaults['entity_type_id'] = $entity_type_id;
      // If the entity type has no bundles and it doesn't use {bundle} in its
      // admin path, use the entity type.
      if (strpos($path, '{bundle}') === FALSE) {
        if (!$entity_type->hasKey('bundle')) {
          $defaults['bundle'] = $entity_type_id;
        }
        else {
          $defaults['bundle_key'] = $entity_type->getBundleEntityType();
        }
      }

      $requirements = [];
      $requirements['_field_ui_view_mode_access'] = 'administer ' . $entity_type_id . ' display';

      $options = $entity_route->getOptions();
      $options['_admin_route'] = FALSE;

      $this->buildLayoutRoutes($collection, $this->getPluginDefinition(), $path, $defaults, $requirements, $options, $entity_type_id, 'entity_view_display');

      // Set field_ui.route_enhancer to run on the manage layout form.
      if (isset($defaults['bundle_key'])) {
        $collection->get("layout_builder.defaults.$entity_type_id.view")
          ->setOption('_field_ui', TRUE)
          ->setDefault('bundle', '');
      }

      $route_names = [
        "entity.entity_view_display.{$entity_type_id}.default",
        "entity.entity_view_display.{$entity_type_id}.view_mode",
      ];
      foreach ($route_names as $route_name) {
        if (!$route = $collection->get($route_name)) {
          continue;
        }

        $route->addDefaults([
          'section_storage_type' => $this->getStorageType(),
          'section_storage' => '',
        ] + $defaults);
        $parameters['section_storage']['layout_builder_tempstore'] = TRUE;
        $parameters = NestedArray::mergeDeep($parameters, $route->getOption('parameters') ?: []);
        $route->setOption('parameters', $parameters);
      }
    }
  }

  /**
   * Returns an array of relevant entity types.
   *
   * @return \Drupal\Core\Entity\EntityTypeInterface[]
   *   An array of entity types.
   */
  protected function getEntityTypes() {
    return array_filter($this->entityTypeManager->getDefinitions(), function (EntityTypeInterface $entity_type) {
      return $entity_type->entityClassImplements(FieldableEntityInterface::class) && $entity_type->hasHandlerClass('form', 'layout_builder') && $entity_type->hasViewBuilderClass() && $entity_type->get('field_ui_base_route');
    });
  }

  /**
   * {@inheritdoc}
   */
  public function getContextsDuringPreview() {
    $contexts = parent::getContextsDuringPreview();

    // During preview add a sample entity for the target entity type and bundle.
    $display = $this->getDisplay();
    $entity = $this->sampleEntityGenerator->get($display->getTargetEntityTypeId(), $display->getTargetBundle());

    $contexts['layout_builder.entity'] = EntityContext::fromEntity($entity);
    return $contexts;
  }

  /**
   * {@inheritdoc}
   */
  public function deriveContextsFromRoute($value, $definition, $name, array $defaults) {
    $contexts = [];

    if ($entity = $this->extractEntityFromRoute($value, $defaults)) {
      $contexts['display'] = EntityContext::fromEntity($entity);
    }
    return $contexts;
  }

  /**
   * Extracts an entity from the route values.
   *
   * @param mixed $value
   *   The raw value from the route.
   * @param array $defaults
   *   The route defaults array.
   *
   * @return \Drupal\Core\Entity\EntityInterface|null
   *   The entity for the route, or NULL if none exist.
   *
   * @see \Drupal\layout_builder\SectionStorageInterface::deriveContextsFromRoute()
   * @see \Drupal\Core\ParamConverter\ParamConverterInterface::convert()
   */
  private function extractEntityFromRoute($value, array $defaults) {
    // If a bundle is not provided but a value corresponding to the bundle key
    // is, use that for the bundle value.
    if (empty($defaults['bundle']) && isset($defaults['bundle_key']) && !empty($defaults[$defaults['bundle_key']])) {
      $defaults['bundle'] = $defaults[$defaults['bundle_key']];
    }

    if (is_string($value) && strpos($value, '.') !== FALSE) {
      [$entity_type_id, $bundle, $view_mode] = explode('.', $value, 3);
    }
    elseif (!empty($defaults['entity_type_id']) && !empty($defaults['bundle']) && !empty($defaults['view_mode_name'])) {
      $entity_type_id = $defaults['entity_type_id'];
      $bundle = $defaults['bundle'];
      $view_mode = $defaults['view_mode_name'];
      $value = "$entity_type_id.$bundle.$view_mode";
    }
    else {
      return NULL;
    }

    $storage = $this->entityTypeManager->getStorage('entity_view_display');
    // If the display does not exist, create a new one.
    if (!$display = $storage->load($value)) {
      $display = $storage->create([
        'targetEntityType' => $entity_type_id,
        'bundle' => $bundle,
        'mode' => $view_mode,
        'status' => TRUE,
      ]);
    }
    return $display;
  }

  /**
   * {@inheritdoc}
   */
  public function label() {
    return $this->getDisplay()->label();
  }

  /**
   * {@inheritdoc}
   */
  public function save() {
    return $this->getDisplay()->save();
  }

  /**
   * {@inheritdoc}
   */
  public function isOverridable() {
    return $this->getDisplay()->isOverridable();
  }

  /**
   * {@inheritdoc}
   */
  public function setOverridable($overridable = TRUE) {
    $this->getDisplay()->setOverridable($overridable);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function setThirdPartySetting($module, $key, $value) {
    $this->getDisplay()->setThirdPartySetting($module, $key, $value);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function isLayoutBuilderEnabled() {
    return $this->getDisplay()->isLayoutBuilderEnabled();
  }

  /**
   * {@inheritdoc}
   */
  public function enableLayoutBuilder() {
    $this->getDisplay()->enableLayoutBuilder();
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function disableLayoutBuilder() {
    $this->getDisplay()->disableLayoutBuilder();
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getThirdPartySetting($module, $key, $default = NULL) {
    return $this->getDisplay()->getThirdPartySetting($module, $key, $default);
  }

  /**
   * {@inheritdoc}
   */
  public function getThirdPartySettings($module) {
    return $this->getDisplay()->getThirdPartySettings($module);
  }

  /**
   * {@inheritdoc}
   */
  public function unsetThirdPartySetting($module, $key) {
    $this->getDisplay()->unsetThirdPartySetting($module, $key);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getThirdPartyProviders() {
    return $this->getDisplay()->getThirdPartyProviders();
  }

  /**
   * {@inheritdoc}
   */
  public function access($operation, AccountInterface $account = NULL, $return_as_object = FALSE) {
    $result = AccessResult::allowedIf($this->isLayoutBuilderEnabled())->addCacheableDependency($this);
    return $return_as_object ? $result : $result->isAllowed();
  }

  /**
   * {@inheritdoc}
   */
  public function isApplicable(RefinableCacheableDependencyInterface $cacheability) {
    $cacheability->addCacheableDependency($this);
    return $this->isLayoutBuilderEnabled();
  }

  /**
   * {@inheritdoc}
   */
  public function setContext($name, ComponentContextInterface $context) {
    // Set the view mode context based on the display context.
    if ($name === 'display') {
      $this->setContextValue('view_mode', $context->getContextValue()->getMode());
    }
    parent::setContext($name, $context);
  }

}


text:
<?xml version="1.0"?>

<!-- ==================================================== -->
<!-- Ant build file for generating Avro specific classes. -->
<!-- See SpecificExample.java for usage information.      -->
<!-- ==================================================== -->

<project name="generate-avro-specific-classes" basedir="."
         default="generate" xmlns:ivy="antlib:org.apache.ivy.ant">

  <description>Compiles "specific" classes in the Avro example</description>

  <fail message="Ant 1.6.0 or greater is required">
    <condition><not><antversion atleast="1.6.0"/></not></condition>
  </fail>

  <!-- Source directory containing one or more .avsc (Avro schema) files. -->
  <property name="lib.dir" location="generate-specific-libs"/>

  <!-- Source directory containing one or more .avsc (Avro schema) files. -->
  <property name="input.dir" location="./"/>

  <!-- Destination directory for the generated .java files. -->
  <property name="output.dir" location="../"/>

  <!-- Type of text fields in generated Java files. Must be one of:
       String, CharSequence, Utf8 -->
  <property name="stringType" value="String"/>

  <!-- Jars needed by Avro SchemaTask -->
  <path id="lib-classpath">
    <fileset dir="${lib.dir}">
      <include name="*.jar"/>
    </fileset>
  </path>

  <property name="ivy.install.version" value="2.3.0-rc1" />
  <condition property="ivy.home" value="${env.IVY_HOME}">
    <isset property="env.IVY_HOME" />
  </condition>
  <property name="ivy.home" value="${user.home}/.ant" />
  <property name="ivy.jar.dir" value="${ivy.home}/lib" />
  <property name="ivy.jar.file" value="${ivy.jar.dir}/ivy.jar" />

  <target name="download-ivy" unless="offline">
    <mkdir dir="${ivy.jar.dir}"/>
    <!-- download Ivy from web site so that it can be used even without any special installation -->
    <get src="http://repo2.maven.org/maven2/org/apache/ivy/ivy/${ivy.install.version}/ivy-${ivy.install.version}.jar"
         dest="${ivy.jar.file}" usetimestamp="true"/>
  </target>

  <target name="init-ivy" depends="download-ivy">
    <path id="ivy.lib.path">
      <fileset dir="${ivy.jar.dir}" includes="*.jar"/>
    </path>
    <taskdef resource="org/apache/ivy/ant/antlib.xml"
             uri="antlib:org.apache.ivy.ant" classpathref="ivy.lib.path"/>
  </target>

  <!-- Use Ivy to pull needed jar files into lib directory -->
  <target name="retrieve-libs" depends="init-ivy">
    <mkdir dir="${lib.dir}"/>
    <property name="ivy.lib.dir" value="${lib.dir}"/>
    <ivy:configure/>
    <ivy:resolve inline="true" keep="true" organisation="org.apache.avro"
                 module="avro-compiler" revision="1.7.6"/>
    <ivy:retrieve pattern="${ivy.retrieve.pattern}"
                  conf="${ivy.configurations}"/>
    <ivy:resolve inline="true" keep="true" organisation="org.apache.avro"
                 module="avro-tools" revision="1.7.6"/>
    <ivy:retrieve pattern="${ivy.retrieve.pattern}"
                  conf="${ivy.configurations}"/>
  </target>

  <!-- Generate specific classes -->
  <target name="generate" depends="retrieve-libs">

    <taskdef name="schema"
             classname="org.apache.avro.compiler.specific.SchemaTask">
      <classpath refid="lib-classpath" />
    </taskdef>

    <schema destdir="${output.dir}" stringType="${stringType}">
      <fileset dir="${input.dir}">
        <include name="*.avsc" />
      </fileset>
    </schema>

  </target>

</project>


text:
use crate::command_prelude::*;
use cargo::ops;

pub fn cli() -> Command {
    subcommand("logout")
        .about("Remove an API token from the registry locally")
        .arg_quiet()
        .arg(opt("registry", "Registry to use").value_name("REGISTRY"))
        .after_help("Run `cargo help logout` for more detailed information.\n")
}

pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
    if !config.cli_unstable().credential_process {
        config
            .cli_unstable()
            .fail_if_stable_command(config, "logout", 8933)?;
    }
    ops::registry_logout(
        config,
        args.get_one::<String>("registry").map(String::as_str),
    )?;
    Ok(())
}


text:
cask 'visual-paradigm' do
  version '15.2,20190201'
  sha256 'da6a9d4f21579a5323c50cc1026a778ccb654fecd3e74eb5912dcdf54a9ab354'

  url "https://usa6.visual-paradigm.com/visual-paradigm/vp#{version.before_comma}/#{version.after_comma}/Visual_Paradigm_#{version.before_comma.dots_to_underscores}_#{version.after_comma}_OSX_WithJRE.dmg"
  appcast 'https://www.visual-paradigm.com/downloads/vp/checksum.html'
  name 'Visual Paradigm'
  homepage 'https://www.visual-paradigm.com/'

  app 'Visual Paradigm.app'
end


text:
from google.appengine.ext import ndb
from datetime import datetime

DEFAULT_STALKER_ATTRS = {
    'prana': 7,
    'prana_max': 7,
    'exp': 0,
}

class BaseModel(ndb.Model):
    @classmethod
    def getone(c, key_name):
        k = ndb.Key(c, key_name)
        return k.get()

    @property
    def id(self):
        return self.key.id()

class Stalker(BaseModel):
    user = ndb.UserProperty(required=True)
    dt = ndb.DateTimeProperty(auto_now_add=True)
    dt_next_action = ndb.DateTimeProperty(auto_now_add=True)
    name = ndb.StringProperty(required=True)
    room = ndb.KeyProperty(required=True)
    attrs = ndb.JsonProperty(default=DEFAULT_STALKER_ATTRS)

class Room(BaseModel):
    name = ndb.StringProperty(required=True)
    content = ndb.TextProperty(required=True)
    owner = ndb.UserProperty(required=True)
    roomclass = ndb.KeyProperty(required=True)
    stalkers = ndb.KeyProperty(repeated=True)

class Roomclass(BaseModel):
    name = ndb.StringProperty(required=True)

class Path(BaseModel):
    name = ndb.StringProperty(required=True)
    content = ndb.TextProperty(required=True)
    room_from = ndb.KeyProperty(required=True)
    room_to = ndb.KeyProperty()  # if null then telepot to random room of this class

class Effect(BaseModel):
    message = ndb.StringProperty(required=True)
    attrs = ndb.JsonProperty()  # changing stalker's attrs
    teleport = ndb.KeyProperty()  # teleport to room


text:
//This file is part of Myoddweb.Piger.
//
//    Myoddweb.Piger is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    Myoddweb.Piger is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with Myoddweb.Piger.  If not, see<https://www.gnu.org/licenses/gpl-3.0.en.html>.
#pragma once
#include "FadeWnd.h"
#include "IDisplay.h"
#include "ITray.h"
#include "CommonWnd.h"

class ActionMonitorDlg final : public CommonWnd, public IDisplay, FadeWnd
{
// Construction
public:
  explicit ActionMonitorDlg( ITray& tray);
  virtual ~ActionMonitorDlg();

  void Show(const std::wstring& sCommand) override;
  void Hide() override;
  void Active() override;
  void Inactive() override;

	// ClassWizard generated virtual function overrides
protected:
  void ShowWindow(const std::wstring& sCommand, BYTE bTrans );         //  show the window, (0 == hide)

  CFont *_fontTime;    //  the time font

  HGDIOBJ SelTimeFont( HDC hdc );

  void RedrawTime(HDC hdc );
  void RedrawBackground(HDC hdc) const;
  void RedrawText(const std::wstring& sCommand, HDC hdc);
  void RepositionWindow() const;

  RECT _mainWindowPosition;
  RECT _commandRectangle;
  void RecalculateAllRectangles( const std::wstring& sCommand);
  RECT CalculateMinimumRectPosition(const std::wstring& sCommand, HDC hdc );
  RECT CalculateCommandRectangle(const std::wstring& sCommand, HDC hdc);

protected:
  /**
   * \brief the current command
   */
  std::wstring _sCommand;

  /**
   * \brief the system tray
   */
  ITray& _tray;

  void OnPaint() override;
  bool OnInitDialog() override;

protected:
  std::mutex _mutex;
};