text:
package node

import (
	"testing"
	"time"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/resource"
	"k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/apis/extensions"
	"k8s.io/kubernetes/pkg/client/cache"
	"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
	fakecloud "k8s.io/kubernetes/pkg/cloudprovider/providers/fake"
	"k8s.io/kubernetes/pkg/util/diff"
	"k8s.io/kubernetes/pkg/util/flowcontrol"
	"k8s.io/kubernetes/pkg/util/wait"
)

const (
	testNodeMonitorGracePeriod = 40 * time.Second
	testNodeStartupGracePeriod = 60 * time.Second
	testNodeMonitorPeriod      = 5 * time.Second
)

func TestMonitorNodeStatusEvictPods(t *testing.T) {
	fakeNow := unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC)
	evictionTimeout := 10 * time.Minute

	// Because of the logic that prevents NC from evicting anything when all Nodes are NotReady
	// we need second healthy node in tests. Because of how the tests are written we need to update
	// the status of this Node.
	healthyNodeNewStatus := api.NodeStatus{
		Conditions: []api.NodeCondition{
			{
				Type:   api.NodeReady,
				Status: api.ConditionTrue,
				// Node status has just been updated, and is NotReady for 10min.
				LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 9, 0, 0, time.UTC),
				LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
			},
		},
	}

	table := []struct {
		fakeNodeHandler     *FakeNodeHandler
		daemonSets          []extensions.DaemonSet
		timeToPass          time.Duration
		newNodeStatus       api.NodeStatus
		secondNodeNewStatus api.NodeStatus
		expectedEvictPods   bool
		description         string
	}{
		// Node created recently, with no status (happens only at cluster startup).
		{
			fakeNodeHandler: &FakeNodeHandler{
				Existing: []*api.Node{
					{
						ObjectMeta: api.ObjectMeta{
							Name:              "node0",
							CreationTimestamp: fakeNow,
						},
					},
					{
						ObjectMeta: api.ObjectMeta{
							Name:              "node1",
							CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
						},
						Status: api.NodeStatus{
							Conditions: []api.NodeCondition{
								{
									Type:               api.NodeReady,
									Status:             api.ConditionTrue,
									LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
									LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
								},
							},
						},
					},
				},
				Clientset: fake.NewSimpleClientset(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0")}}),
			},
			daemonSets:          nil,
			timeToPass:          0,
			newNodeStatus:       api.NodeStatus{},
			secondNodeNewStatus: healthyNodeNewStatus,
			expectedEvictPods:   false,
			description:         "Node created recently, with no status.",
		},
		// Node created long time ago, and kubelet posted NotReady for a short period of time.
		{
			fakeNodeHandler: &FakeNodeHandler{
				Existing: []*api.Node{
					{
						ObjectMeta: api.ObjectMeta{
							Name:              "node0",
							CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
						},
						Status: api.NodeStatus{
							Conditions: []api.NodeCondition{
								{
									Type:               api.NodeReady,
									Status:             api.ConditionFalse,
									LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
									LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
								},
							},
						},
					},
					{
						ObjectMeta: api.ObjectMeta{
							Name:              "node1",
							CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
						},
						Status: api.NodeStatus{
							Conditions: []api.NodeCondition{
								{
									Type:               api.NodeReady,
									Status:             api.ConditionTrue,
									LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
									LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
								},
							},
						},
					},
				},
				Clientset: fake.NewSimpleClientset(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0")}}),
			},
			daemonSets: nil,
			timeToPass: evictionTimeout,
			newNodeStatus: api.NodeStatus{
				Conditions: []api.NodeCondition{
					{
						Type:   api.NodeReady,
						Status: api.ConditionFalse,
						// Node status has just been updated, and is NotReady for 10min.
						LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 9, 0, 0, time.UTC),
						LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
					},
				},
			},
			secondNodeNewStatus: healthyNodeNewStatus,
			expectedEvictPods:   false,
			description:         "Node created long time ago, and kubelet posted NotReady for a short period of time.",
		},
		// Pod is ds-managed, and kubelet posted NotReady for a long period of time.
		{
			fakeNodeHandler: &FakeNodeHandler{
				Existing: []*api.Node{
					{
						ObjectMeta: api.ObjectMeta{
							Name:              "node0",
							CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
						},
						Status: api.NodeStatus{
							Conditions: []api.NodeCondition{
								{
									Type:               api.NodeReady,
									Status:             api.ConditionFalse,
									LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
									LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
								},
							},
						},
					},
					{
						ObjectMeta: api.ObjectMeta{
							Name:              "node1",
							CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
						},
						Status: api.NodeStatus{
							Conditions: []api.NodeCondition{
								{
									Type:               api.NodeReady,
									Status:             api.ConditionTrue,
									LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
									LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
								},
							},
						},
					},
				},
				Clientset: fake.NewSimpleClientset(
					&api.PodList{
						Items: []api.Pod{
							{
								ObjectMeta: api.ObjectMeta{
									Name:      "pod0",
									Namespace: "default",
									Labels:    map[string]string{"daemon": "yes"},
								},
								Spec: api.PodSpec{
									NodeName: "node0",
								},
							},
						},
					},
				),
			},
			daemonSets: []extensions.DaemonSet{
				{
					ObjectMeta: api.ObjectMeta{
						Name:      "ds0",
						Namespace: "default",
					},
					Spec: extensions.DaemonSetSpec{
						Selector: &unversioned.LabelSelector{
							MatchLabels: map[string]string{"daemon": "yes"},
						},
					},
				},
			},
			timeToPass: time.Hour,
			newNodeStatus: api.NodeStatus{
				Conditions: []api.NodeCondition{
					{
						Type:   api.NodeReady,
						Status: api.ConditionFalse,
						// Node status has just been updated, and is NotReady for 1hr.
						LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 59, 0, 0, time.UTC),
						LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
					},
				},
			},
			secondNodeNewStatus: healthyNodeNewStatus,
			expectedEvictPods:   false,
			description:         "Pod is ds-managed, and kubelet posted NotReady for a long period of time.",
		},
		// Node created long time ago, and kubelet posted NotReady for a long period of time.
		{
			fakeNodeHandler: &FakeNodeHandler{
				Existing: []*api.Node{
					{
						ObjectMeta: api.ObjectMeta{
							Name:              "node0",
							CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
						},
						Status: api.NodeStatus{
							Conditions: []api.NodeCondition{
								{
									Type:               api.NodeReady,
									Status:             api.ConditionFalse,
									LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
									LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
								},
							},
						},
					},
					{
						ObjectMeta: api.ObjectMeta{
							Name:              "node1",
							CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
						},
						Status: api.NodeStatus{
							Conditions: []api.NodeCondition{
								{
									Type:               api.NodeReady,
									Status:             api.ConditionTrue,
									LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
									LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
								},
							},
						},
					},
				},
				Clientset: fake.NewSimpleClientset(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0")}}),
			},
			daemonSets: nil,
			timeToPass: time.Hour,
			newNodeStatus: api.NodeStatus{
				Conditions: []api.NodeCondition{
					{
						Type:   api.NodeReady,
						Status: api.ConditionFalse,
						// Node status has just been updated, and is NotReady for 1hr.
						LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 59, 0, 0, time.UTC),
						LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
					},
				},
			},
			secondNodeNewStatus: healthyNodeNewStatus,
			expectedEvictPods:   true,
			description:         "Node created long time ago, and kubelet posted NotReady for a long period of time.",
		},
		// Node created long time ago, node controller posted Unknown for a short period of time.
		{
			fakeNodeHandler: &FakeNodeHandler{
				Existing: []*api.Node{
					{
						ObjectMeta: api.ObjectMeta{
							Name:              "node0",
							CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
						},
						Status: api.NodeStatus{
							Conditions: []api.NodeCondition{
								{
									Type:               api.NodeReady,
									Status:             api.ConditionUnknown,
									LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
									LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
								},
							},
						},
					},
					{
						ObjectMeta: api.ObjectMeta{
							Name:              "node1",
							CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
						},
						Status: api.NodeStatus{
							Conditions: []api.NodeCondition{
								{
									Type:               api.NodeReady,
									Status:             api.ConditionTrue,
									LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
									LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
								},
							},
						},
					},
				},
				Clientset: fake.NewSimpleClientset(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0")}}),
			},
			daemonSets: nil,
			timeToPass: evictionTimeout - testNodeMonitorGracePeriod,
			newNodeStatus: api.NodeStatus{
				Conditions: []api.NodeCondition{
					{
						Type:   api.NodeReady,
						Status: api.ConditionUnknown,
						// Node status was updated by nodecontroller 10min ago
						LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
						LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
					},
				},
			},
			secondNodeNewStatus: healthyNodeNewStatus,
			expectedEvictPods:   false,
			description:         "Node created long time ago, node controller posted Unknown for a short period of time.",
		},
		// Node created long time ago, node controller posted Unknown for a long period of time.
		{
			fakeNodeHandler: &FakeNodeHandler{
				Existing: []*api.Node{
					{
						ObjectMeta: api.ObjectMeta{
							Name:              "node0",
							CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
						},
						Status: api.NodeStatus{
							Conditions: []api.NodeCondition{
								{
									Type:               api.NodeReady,
									Status:             api.ConditionUnknown,
									LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
									LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
								},
							},
						},
					},
					{
						ObjectMeta: api.ObjectMeta{
							Name:              "node1",
							CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
						},
						Status: api.NodeStatus{
							Conditions: []api.NodeCondition{
								{
									Type:               api.NodeReady,
									Status:             api.ConditionTrue,
									LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
									LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
								},
							},
						},
					},
				},
				Clientset: fake.NewSimpleClientset(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0")}}),
			},
			daemonSets: nil,
			timeToPass: 60 * time.Minute,
			newNodeStatus: api.NodeStatus{
				Conditions: []api.NodeCondition{
					{
						Type:   api.NodeReady,
						Status: api.ConditionUnknown,
						// Node status was updated by nodecontroller 1hr ago
						LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
						LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
					},
				},
			},
			secondNodeNewStatus: healthyNodeNewStatus,
			expectedEvictPods:   true,
			description:         "Node created long time ago, node controller posted Unknown for a long period of time.",
		},
		// NetworkSegmentation: Node created long time ago, node controller posted Unknown for a long period of time on both Nodes.
		{
			fakeNodeHandler: &FakeNodeHandler{
				Existing: []*api.Node{
					{
						ObjectMeta: api.ObjectMeta{
							Name:              "node0",
							CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
						},
						Status: api.NodeStatus{
							Conditions: []api.NodeCondition{
								{
									Type:               api.NodeReady,
									Status:             api.ConditionUnknown,
									LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
									LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
								},
							},
						},
					},
					{
						ObjectMeta: api.ObjectMeta{
							Name:              "node1",
							CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
						},
						Status: api.NodeStatus{
							Conditions: []api.NodeCondition{
								{
									Type:               api.NodeReady,
									Status:             api.ConditionUnknown,
									LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
									LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
								},
							},
						},
					},
				},
				Clientset: fake.NewSimpleClientset(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0")}}),
			},
			daemonSets: nil,
			timeToPass: 60 * time.Minute,
			newNodeStatus: api.NodeStatus{
				Conditions: []api.NodeCondition{
					{
						Type:   api.NodeReady,
						Status: api.ConditionUnknown,
						// Node status was updated by nodecontroller 1hr ago
						LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
						LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
					},
				},
			},
			secondNodeNewStatus: api.NodeStatus{
				Conditions: []api.NodeCondition{
					{
						Type:   api.NodeReady,
						Status: api.ConditionUnknown,
						// Node status was updated by nodecontroller 1hr ago
						LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
						LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
					},
				},
			},
			expectedEvictPods: false,
			description:       "Network Segmentation: Node created long time ago, node controller posted Unknown for a long period of time on both Nodes.",
		},
		// NetworkSegmentation: Node created long time ago, node controller posted Unknown for a long period
		// of on first Node, eviction should stop even though -master Node is healthy.
		{
			fakeNodeHandler: &FakeNodeHandler{
				Existing: []*api.Node{
					{
						ObjectMeta: api.ObjectMeta{
							Name:              "node0",
							CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
						},
						Status: api.NodeStatus{
							Conditions: []api.NodeCondition{
								{
									Type:               api.NodeReady,
									Status:             api.ConditionUnknown,
									LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
									LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
								},
							},
						},
					},
					{
						ObjectMeta: api.ObjectMeta{
							Name:              "node-master",
							CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
						},
						Status: api.NodeStatus{
							Conditions: []api.NodeCondition{
								{
									Type:               api.NodeReady,
									Status:             api.ConditionTrue,
									LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
									LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
								},
							},
						},
					},
				},
				Clientset: fake.NewSimpleClientset(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0")}}),
			},
			daemonSets: nil,
			timeToPass: 60 * time.Minute,
			newNodeStatus: api.NodeStatus{
				Conditions: []api.NodeCondition{
					{
						Type:   api.NodeReady,
						Status: api.ConditionUnknown,
						// Node status was updated by nodecontroller 1hr ago
						LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
						LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
					},
				},
			},
			secondNodeNewStatus: healthyNodeNewStatus,
			expectedEvictPods:   false,
			description:         "NetworkSegmentation: Node created long time ago, node controller posted Unknown for a long period of on first Node, eviction should stop even though -master Node is healthy",
		},
	}

	for _, item := range table {
		nodeController, _ := NewNodeController(nil, item.fakeNodeHandler,
			evictionTimeout, flowcontrol.NewFakeAlwaysRateLimiter(), flowcontrol.NewFakeAlwaysRateLimiter(), testNodeMonitorGracePeriod,
			testNodeStartupGracePeriod, testNodeMonitorPeriod, nil, nil, 0, false)
		nodeController.now = func() unversioned.Time { return fakeNow }
		for _, ds := range item.daemonSets {
			nodeController.daemonSetStore.Add(&ds)
		}
		if err := nodeController.monitorNodeStatus(); err != nil {
			t.Errorf("unexpected error: %v", err)
		}
		if item.timeToPass > 0 {
			nodeController.now = func() unversioned.Time { return unversioned.Time{Time: fakeNow.Add(item.timeToPass)} }
			item.fakeNodeHandler.Existing[0].Status = item.newNodeStatus
			item.fakeNodeHandler.Existing[1].Status = item.secondNodeNewStatus
		}
		if err := nodeController.monitorNodeStatus(); err != nil {
			t.Errorf("unexpected error: %v", err)
		}

		nodeController.podEvictor.Try(func(value TimedValue) (bool, time.Duration) {
			remaining, _ := nodeController.deletePods(value.Value)
			if remaining {
				nodeController.terminationEvictor.Add(value.Value)
			}
			return true, 0
		})
		nodeController.podEvictor.Try(func(value TimedValue) (bool, time.Duration) {
			nodeController.terminatePods(value.Value, value.AddedAt)
			return true, 0
		})
		podEvicted := false
		for _, action := range item.fakeNodeHandler.Actions() {
			if action.GetVerb() == "delete" && action.GetResource().Resource == "pods" {
				podEvicted = true
			}
		}

		if item.expectedEvictPods != podEvicted {
			t.Errorf("expected pod eviction: %+v, got %+v for %+v", item.expectedEvictPods,
				podEvicted, item.description)
		}
	}
}

// TestCloudProviderNoRateLimit tests that monitorNodes() immediately deletes
// pods and the node when kubelet has not reported, and the cloudprovider says
// the node is gone.
func TestCloudProviderNoRateLimit(t *testing.T) {
	fnh := &FakeNodeHandler{
		Existing: []*api.Node{
			{
				ObjectMeta: api.ObjectMeta{
					Name:              "node0",
					CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
				},
				Status: api.NodeStatus{
					Conditions: []api.NodeCondition{
						{
							Type:               api.NodeReady,
							Status:             api.ConditionUnknown,
							LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
							LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
						},
					},
				},
			},
		},
		Clientset:      fake.NewSimpleClientset(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0"), *newPod("pod1", "node0")}}),
		deleteWaitChan: make(chan struct{}),
	}
	nodeController, _ := NewNodeController(nil, fnh, 10*time.Minute,
		flowcontrol.NewFakeAlwaysRateLimiter(), flowcontrol.NewFakeAlwaysRateLimiter(),
		testNodeMonitorGracePeriod, testNodeStartupGracePeriod,
		testNodeMonitorPeriod, nil, nil, 0, false)
	nodeController.cloud = &fakecloud.FakeCloud{}
	nodeController.now = func() unversioned.Time { return unversioned.Date(2016, 1, 1, 12, 0, 0, 0, time.UTC) }
	nodeController.nodeExistsInCloudProvider = func(nodeName string) (bool, error) {
		return false, nil
	}
	// monitorNodeStatus should allow this node to be immediately deleted
	if err := nodeController.monitorNodeStatus(); err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	select {
	case <-fnh.deleteWaitChan:
	case <-time.After(wait.ForeverTestTimeout):
		t.Errorf("Timed out waiting %v for node to be deleted", wait.ForeverTestTimeout)
	}
	if len(fnh.DeletedNodes) != 1 || fnh.DeletedNodes[0].Name != "node0" {
		t.Errorf("Node was not deleted")
	}
	if nodeOnQueue := nodeController.podEvictor.Remove("node0"); nodeOnQueue {
		t.Errorf("Node was queued for eviction. Should have been immediately deleted.")
	}
}

func TestMonitorNodeStatusUpdateStatus(t *testing.T) {
	fakeNow := unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC)
	table := []struct {
		fakeNodeHandler      *FakeNodeHandler
		timeToPass           time.Duration
		newNodeStatus        api.NodeStatus
		expectedEvictPods    bool
		expectedRequestCount int
		expectedNodes        []*api.Node
	}{
		// Node created long time ago, without status:
		// Expect Unknown status posted from node controller.
		{
			fakeNodeHandler: &FakeNodeHandler{
				Existing: []*api.Node{
					{
						ObjectMeta: api.ObjectMeta{
							Name:              "node0",
							CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
						},
					},
				},
				Clientset: fake.NewSimpleClientset(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0")}}),
			},
			expectedRequestCount: 2, // List+Update
			expectedNodes: []*api.Node{
				{
					ObjectMeta: api.ObjectMeta{
						Name:              "node0",
						CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
					},
					Status: api.NodeStatus{
						Conditions: []api.NodeCondition{
							{
								Type:               api.NodeReady,
								Status:             api.ConditionUnknown,
								Reason:             "NodeStatusNeverUpdated",
								Message:            "Kubelet never posted node status.",
								LastHeartbeatTime:  unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
								LastTransitionTime: fakeNow,
							},
							{
								Type:               api.NodeOutOfDisk,
								Status:             api.ConditionUnknown,
								Reason:             "NodeStatusNeverUpdated",
								Message:            "Kubelet never posted node status.",
								LastHeartbeatTime:  unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
								LastTransitionTime: fakeNow,
							},
						},
					},
				},
			},
		},
		// Node created recently, without status.
		// Expect no action from node controller (within startup grace period).
		{
			fakeNodeHandler: &FakeNodeHandler{
				Existing: []*api.Node{
					{
						ObjectMeta: api.ObjectMeta{
							Name:              "node0",
							CreationTimestamp: fakeNow,
						},
					},
				},
				Clientset: fake.NewSimpleClientset(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0")}}),
			},
			expectedRequestCount: 1, // List
			expectedNodes:        nil,
		},
		// Node created long time ago, with status updated by kubelet exceeds grace period.
		// Expect Unknown status posted from node controller.
		{
			fakeNodeHandler: &FakeNodeHandler{
				Existing: []*api.Node{
					{
						ObjectMeta: api.ObjectMeta{
							Name:              "node0",
							CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
						},
						Status: api.NodeStatus{
							Conditions: []api.NodeCondition{
								{
									Type:   api.NodeReady,
									Status: api.ConditionTrue,
									// Node status hasn't been updated for 1hr.
									LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
									LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
								},
								{
									Type:   api.NodeOutOfDisk,
									Status: api.ConditionFalse,
									// Node status hasn't been updated for 1hr.
									LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
									LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
								},
							},
							Capacity: api.ResourceList{
								api.ResourceName(api.ResourceCPU):    resource.MustParse("10"),
								api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
							},
						},
						Spec: api.NodeSpec{
							ExternalID: "node0",
						},
					},
				},
				Clientset: fake.NewSimpleClientset(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0")}}),
			},
			expectedRequestCount: 3, // (List+)List+Update
			timeToPass:           time.Hour,
			newNodeStatus: api.NodeStatus{
				Conditions: []api.NodeCondition{
					{
						Type:   api.NodeReady,
						Status: api.ConditionTrue,
						// Node status hasn't been updated for 1hr.
						LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
						LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
					},
					{
						Type:   api.NodeOutOfDisk,
						Status: api.ConditionFalse,
						// Node status hasn't been updated for 1hr.
						LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
						LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
					},
				},
				Capacity: api.ResourceList{
					api.ResourceName(api.ResourceCPU):    resource.MustParse("10"),
					api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
				},
			},
			expectedNodes: []*api.Node{
				{
					ObjectMeta: api.ObjectMeta{
						Name:              "node0",
						CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
					},
					Status: api.NodeStatus{
						Conditions: []api.NodeCondition{
							{
								Type:               api.NodeReady,
								Status:             api.ConditionUnknown,
								Reason:             "NodeStatusUnknown",
								Message:            "Kubelet stopped posting node status.",
								LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
								LastTransitionTime: unversioned.Time{Time: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC).Add(time.Hour)},
							},
							{
								Type:               api.NodeOutOfDisk,
								Status:             api.ConditionUnknown,
								Reason:             "NodeStatusUnknown",
								Message:            "Kubelet stopped posting node status.",
								LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
								LastTransitionTime: unversioned.Time{Time: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC).Add(time.Hour)},
							},
						},
						Capacity: api.ResourceList{
							api.ResourceName(api.ResourceCPU):    resource.MustParse("10"),
							api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
						},
					},
					Spec: api.NodeSpec{
						ExternalID: "node0",
					},
				},
			},
		},
		// Node created long time ago, with status updated recently.
		// Expect no action from node controller (within monitor grace period).
		{
			fakeNodeHandler: &FakeNodeHandler{
				Existing: []*api.Node{
					{
						ObjectMeta: api.ObjectMeta{
							Name:              "node0",
							CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
						},
						Status: api.NodeStatus{
							Conditions: []api.NodeCondition{
								{
									Type:   api.NodeReady,
									Status: api.ConditionTrue,
									// Node status has just been updated.
									LastHeartbeatTime:  fakeNow,
									LastTransitionTime: fakeNow,
								},
							},
							Capacity: api.ResourceList{
								api.ResourceName(api.ResourceCPU):    resource.MustParse("10"),
								api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
							},
						},
						Spec: api.NodeSpec{
							ExternalID: "node0",
						},
					},
				},
				Clientset: fake.NewSimpleClientset(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0")}}),
			},
			expectedRequestCount: 1, // List
			expectedNodes:        nil,
		},
	}

	for i, item := range table {
		nodeController, _ := NewNodeController(nil, item.fakeNodeHandler, 5*time.Minute, flowcontrol.NewFakeAlwaysRateLimiter(),
			flowcontrol.NewFakeAlwaysRateLimiter(), testNodeMonitorGracePeriod, testNodeStartupGracePeriod, testNodeMonitorPeriod, nil, nil, 0, false)
		nodeController.now = func() unversioned.Time { return fakeNow }
		if err := nodeController.monitorNodeStatus(); err != nil {
			t.Errorf("unexpected error: %v", err)
		}
		if item.timeToPass > 0 {
			nodeController.now = func() unversioned.Time { return unversioned.Time{Time: fakeNow.Add(item.timeToPass)} }
			item.fakeNodeHandler.Existing[0].Status = item.newNodeStatus
			if err := nodeController.monitorNodeStatus(); err != nil {
				t.Errorf("unexpected error: %v", err)
			}
		}
		if item.expectedRequestCount != item.fakeNodeHandler.RequestCount {
			t.Errorf("expected %v call, but got %v.", item.expectedRequestCount, item.fakeNodeHandler.RequestCount)
		}
		if len(item.fakeNodeHandler.UpdatedNodes) > 0 && !api.Semantic.DeepEqual(item.expectedNodes, item.fakeNodeHandler.UpdatedNodes) {
			t.Errorf("Case[%d] unexpected nodes: %s", i, diff.ObjectDiff(item.expectedNodes[0], item.fakeNodeHandler.UpdatedNodes[0]))
		}
		if len(item.fakeNodeHandler.UpdatedNodeStatuses) > 0 && !api.Semantic.DeepEqual(item.expectedNodes, item.fakeNodeHandler.UpdatedNodeStatuses) {
			t.Errorf("Case[%d] unexpected nodes: %s", i, diff.ObjectDiff(item.expectedNodes[0], item.fakeNodeHandler.UpdatedNodeStatuses[0]))
		}
	}
}

func TestMonitorNodeStatusMarkPodsNotReady(t *testing.T) {
	fakeNow := unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC)
	table := []struct {
		fakeNodeHandler         *FakeNodeHandler
		timeToPass              time.Duration
		newNodeStatus           api.NodeStatus
		expectedPodStatusUpdate bool
	}{
		// Node created recently, without status.
		// Expect no action from node controller (within startup grace period).
		{
			fakeNodeHandler: &FakeNodeHandler{
				Existing: []*api.Node{
					{
						ObjectMeta: api.ObjectMeta{
							Name:              "node0",
							CreationTimestamp: fakeNow,
						},
					},
				},
				Clientset: fake.NewSimpleClientset(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0")}}),
			},
			expectedPodStatusUpdate: false,
		},
		// Node created long time ago, with status updated recently.
		// Expect no action from node controller (within monitor grace period).
		{
			fakeNodeHandler: &FakeNodeHandler{
				Existing: []*api.Node{
					{
						ObjectMeta: api.ObjectMeta{
							Name:              "node0",
							CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
						},
						Status: api.NodeStatus{
							Conditions: []api.NodeCondition{
								{
									Type:   api.NodeReady,
									Status: api.ConditionTrue,
									// Node status has just been updated.
									LastHeartbeatTime:  fakeNow,
									LastTransitionTime: fakeNow,
								},
							},
							Capacity: api.ResourceList{
								api.ResourceName(api.ResourceCPU):    resource.MustParse("10"),
								api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
							},
						},
						Spec: api.NodeSpec{
							ExternalID: "node0",
						},
					},
				},
				Clientset: fake.NewSimpleClientset(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0")}}),
			},
			expectedPodStatusUpdate: false,
		},
		// Node created long time ago, with status updated by kubelet exceeds grace period.
		// Expect pods status updated and Unknown node status posted from node controller
		{
			fakeNodeHandler: &FakeNodeHandler{
				Existing: []*api.Node{
					{
						ObjectMeta: api.ObjectMeta{
							Name:              "node0",
							CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
						},
						Status: api.NodeStatus{
							Conditions: []api.NodeCondition{
								{
									Type:   api.NodeReady,
									Status: api.ConditionTrue,
									// Node status hasn't been updated for 1hr.
									LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
									LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
								},
								{
									Type:   api.NodeOutOfDisk,
									Status: api.ConditionFalse,
									// Node status hasn't been updated for 1hr.
									LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
									LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
								},
							},
							Capacity: api.ResourceList{
								api.ResourceName(api.ResourceCPU):    resource.MustParse("10"),
								api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
							},
						},
						Spec: api.NodeSpec{
							ExternalID: "node0",
						},
					},
				},
				Clientset: fake.NewSimpleClientset(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0")}}),
			},
			timeToPass: 1 * time.Minute,
			newNodeStatus: api.NodeStatus{
				Conditions: []api.NodeCondition{
					{
						Type:   api.NodeReady,
						Status: api.ConditionTrue,
						// Node status hasn't been updated for 1hr.
						LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
						LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
					},
					{
						Type:   api.NodeOutOfDisk,
						Status: api.ConditionFalse,
						// Node status hasn't been updated for 1hr.
						LastHeartbeatTime:  unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
						LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
					},
				},
				Capacity: api.ResourceList{
					api.ResourceName(api.ResourceCPU):    resource.MustParse("10"),
					api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
				},
			},
			expectedPodStatusUpdate: true,
		},
	}

	for i, item := range table {
		nodeController, _ := NewNodeController(nil, item.fakeNodeHandler, 5*time.Minute, flowcontrol.NewFakeAlwaysRateLimiter(),
			flowcontrol.NewFakeAlwaysRateLimiter(), testNodeMonitorGracePeriod, testNodeStartupGracePeriod, testNodeMonitorPeriod, nil, nil, 0, false)
		nodeController.now = func() unversioned.Time { return fakeNow }
		if err := nodeController.monitorNodeStatus(); err != nil {
			t.Errorf("Case[%d] unexpected error: %v", i, err)
		}
		if item.timeToPass > 0 {
			nodeController.now = func() unversioned.Time { return unversioned.Time{Time: fakeNow.Add(item.timeToPass)} }
			item.fakeNodeHandler.Existing[0].Status = item.newNodeStatus
			if err := nodeController.monitorNodeStatus(); err != nil {
				t.Errorf("Case[%d] unexpected error: %v", i, err)
			}
		}

		podStatusUpdated := false
		for _, action := range item.fakeNodeHandler.Actions() {
			if action.GetVerb() == "update" && action.GetResource().Resource == "pods" && action.GetSubresource() == "status" {
				podStatusUpdated = true
			}
		}
		if podStatusUpdated != item.expectedPodStatusUpdate {
			t.Errorf("Case[%d] expect pod status updated to be %v, but got %v", i, item.expectedPodStatusUpdate, podStatusUpdated)
		}
	}
}

func TestNodeDeletion(t *testing.T) {
	fakeNow := unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC)
	fakeNodeHandler := &FakeNodeHandler{
		Existing: []*api.Node{
			{
				ObjectMeta: api.ObjectMeta{
					Name:              "node0",
					CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
				},
				Status: api.NodeStatus{
					Conditions: []api.NodeCondition{
						{
							Type:   api.NodeReady,
							Status: api.ConditionTrue,
							// Node status has just been updated.
							LastHeartbeatTime:  fakeNow,
							LastTransitionTime: fakeNow,
						},
					},
					Capacity: api.ResourceList{
						api.ResourceName(api.ResourceCPU):    resource.MustParse("10"),
						api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
					},
				},
				Spec: api.NodeSpec{
					ExternalID: "node0",
				},
			},
			{
				ObjectMeta: api.ObjectMeta{
					Name:              "node1",
					CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
				},
				Status: api.NodeStatus{
					Conditions: []api.NodeCondition{
						{
							Type:   api.NodeReady,
							Status: api.ConditionTrue,
							// Node status has just been updated.
							LastHeartbeatTime:  fakeNow,
							LastTransitionTime: fakeNow,
						},
					},
					Capacity: api.ResourceList{
						api.ResourceName(api.ResourceCPU):    resource.MustParse("10"),
						api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
					},
				},
				Spec: api.NodeSpec{
					ExternalID: "node0",
				},
			},
		},
		Clientset: fake.NewSimpleClientset(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0"), *newPod("pod1", "node1")}}),
	}

	nodeController, _ := NewNodeController(nil, fakeNodeHandler, 5*time.Minute, flowcontrol.NewFakeAlwaysRateLimiter(), flowcontrol.NewFakeAlwaysRateLimiter(),
		testNodeMonitorGracePeriod, testNodeStartupGracePeriod, testNodeMonitorPeriod, nil, nil, 0, false)
	nodeController.now = func() unversioned.Time { return fakeNow }
	if err := nodeController.monitorNodeStatus(); err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	fakeNodeHandler.Delete("node1", nil)
	if err := nodeController.monitorNodeStatus(); err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	nodeController.podEvictor.Try(func(value TimedValue) (bool, time.Duration) {
		nodeController.deletePods(value.Value)
		return true, 0
	})
	podEvicted := false
	for _, action := range fakeNodeHandler.Actions() {
		if action.GetVerb() == "delete" && action.GetResource().Resource == "pods" {
			podEvicted = true
		}
	}
	if !podEvicted {
		t.Error("expected pods to be evicted from the deleted node")
	}
}

func TestCheckPod(t *testing.T) {

	tcs := []struct {
		pod   api.Pod
		prune bool
	}{

		{
			pod: api.Pod{
				ObjectMeta: api.ObjectMeta{DeletionTimestamp: nil},
				Spec:       api.PodSpec{NodeName: "new"},
			},
			prune: false,
		},
		{
			pod: api.Pod{
				ObjectMeta: api.ObjectMeta{DeletionTimestamp: nil},
				Spec:       api.PodSpec{NodeName: "old"},
			},
			prune: false,
		},
		{
			pod: api.Pod{
				ObjectMeta: api.ObjectMeta{DeletionTimestamp: nil},
				Spec:       api.PodSpec{NodeName: ""},
			},
			prune: false,
		},
		{
			pod: api.Pod{
				ObjectMeta: api.ObjectMeta{DeletionTimestamp: nil},
				Spec:       api.PodSpec{NodeName: "nonexistant"},
			},
			prune: false,
		},
		{
			pod: api.Pod{
				ObjectMeta: api.ObjectMeta{DeletionTimestamp: &unversioned.Time{}},
				Spec:       api.PodSpec{NodeName: "new"},
			},
			prune: false,
		},
		{
			pod: api.Pod{
				ObjectMeta: api.ObjectMeta{DeletionTimestamp: &unversioned.Time{}},
				Spec:       api.PodSpec{NodeName: "old"},
			},
			prune: true,
		},
		{
			pod: api.Pod{
				ObjectMeta: api.ObjectMeta{DeletionTimestamp: &unversioned.Time{}},
				Spec:       api.PodSpec{NodeName: "older"},
			},
			prune: true,
		},
		{
			pod: api.Pod{
				ObjectMeta: api.ObjectMeta{DeletionTimestamp: &unversioned.Time{}},
				Spec:       api.PodSpec{NodeName: "oldest"},
			},
			prune: true,
		},
		{
			pod: api.Pod{
				ObjectMeta: api.ObjectMeta{DeletionTimestamp: &unversioned.Time{}},
				Spec:       api.PodSpec{NodeName: ""},
			},
			prune: true,
		},
		{
			pod: api.Pod{
				ObjectMeta: api.ObjectMeta{DeletionTimestamp: &unversioned.Time{}},
				Spec:       api.PodSpec{NodeName: "nonexistant"},
			},
			prune: true,
		},
	}

	nc, _ := NewNodeController(nil, nil, 0, nil, nil, 0, 0, 0, nil, nil, 0, false)
	nc.nodeStore.Store = cache.NewStore(cache.MetaNamespaceKeyFunc)
	nc.nodeStore.Store.Add(&api.Node{
		ObjectMeta: api.ObjectMeta{
			Name: "new",
		},
		Status: api.NodeStatus{
			NodeInfo: api.NodeSystemInfo{
				KubeletVersion: "v1.1.0",
			},
		},
	})
	nc.nodeStore.Store.Add(&api.Node{
		ObjectMeta: api.ObjectMeta{
			Name: "old",
		},
		Status: api.NodeStatus{
			NodeInfo: api.NodeSystemInfo{
				KubeletVersion: "v1.0.0",
			},
		},
	})
	nc.nodeStore.Store.Add(&api.Node{
		ObjectMeta: api.ObjectMeta{
			Name: "older",
		},
		Status: api.NodeStatus{
			NodeInfo: api.NodeSystemInfo{
				KubeletVersion: "v0.21.4",
			},
		},
	})
	nc.nodeStore.Store.Add(&api.Node{
		ObjectMeta: api.ObjectMeta{
			Name: "oldest",
		},
		Status: api.NodeStatus{
			NodeInfo: api.NodeSystemInfo{
				KubeletVersion: "v0.19.3",
			},
		},
	})

	for i, tc := range tcs {
		var deleteCalls int
		nc.forcefullyDeletePod = func(_ *api.Pod) error {
			deleteCalls++
			return nil
		}

		nc.maybeDeleteTerminatingPod(&tc.pod)

		if tc.prune && deleteCalls != 1 {
			t.Errorf("[%v] expected number of delete calls to be 1 but got %v", i, deleteCalls)
		}
		if !tc.prune && deleteCalls != 0 {
			t.Errorf("[%v] expected number of delete calls to be 0 but got %v", i, deleteCalls)
		}
	}
}

func TestCleanupOrphanedPods(t *testing.T) {
	newPod := func(name, node string) api.Pod {
		return api.Pod{
			ObjectMeta: api.ObjectMeta{
				Name: name,
			},
			Spec: api.PodSpec{
				NodeName: node,
			},
		}
	}
	pods := []api.Pod{
		newPod("a", "foo"),
		newPod("b", "bar"),
		newPod("c", "gone"),
	}
	nc, _ := NewNodeController(nil, nil, 0, nil, nil, 0, 0, 0, nil, nil, 0, false)

	nc.nodeStore.Store.Add(newNode("foo"))
	nc.nodeStore.Store.Add(newNode("bar"))
	for _, pod := range pods {
		p := pod
		nc.podStore.Indexer.Add(&p)
	}

	var deleteCalls int
	var deletedPodName string
	nc.forcefullyDeletePod = func(p *api.Pod) error {
		deleteCalls++
		deletedPodName = p.ObjectMeta.Name
		return nil
	}
	nc.cleanupOrphanedPods()

	if deleteCalls != 1 {
		t.Fatalf("expected one delete, got: %v", deleteCalls)
	}
	if deletedPodName != "c" {
		t.Fatalf("expected deleted pod name to be 'c', but got: %q", deletedPodName)
	}
}


text:
import express from 'express';
import { match } from 'react-router';
import routes from '../common/config/routes';
import renderComponent from './utils/renderComponent';
import TopicStore from '../common/stores/TopicStore';

const app = express();

app.use('/dist', express.static('dist'));
app.use('/images', express.static('images'));

app.get('*', (req, res, next) => {
    match({ routes, location: req.url }, (error, redirectLocation, renderProps) => {
        if (error) {
            res.status(500).send(error.message);
        } else if (redirectLocation) {
            res.redirect(302, redirectLocation.pathname + redirectLocation.search);
        } else if (renderProps) {
            const store = new TopicStore();
            renderComponent(store, renderProps)
                .then(html => res.status(200).send(html))
                .catch(err => next(err));
        } else {
            res.status(404).send('Not found');
        }
    });
});

app.listen(3000, () => {
    console.log(' 🌎 running on port 3000!'); //eslint-disable-line
});

redpajama_set_name: RedPajamaGithub


text:
{% load rest_framework %}

<div class="form-group {% if field.errors %}has-error{% endif %}">
  {% if field.label %}
    <label class="sr-only">
      {{ field.label }}
    </label>
  {% endif %}

  <select class="form-control" name="{{ field.name }}">
    {% if field.allow_null or field.allow_blank %}
      <option value="" {% if not field.value %}selected{% endif %}>--------</option>
    {% endif %}
    {% for select in field.iter_options %}
        {% if select.start_option_group %}
          <optgroup label="{{ select.label }}">
        {% elif select.end_option_group %}
          </optgroup>
        {% else %}
          <option value="{{ select.value }}" {% if select.value|as_string == field.value|as_string %}selected{% endif %} {% if select.disabled %}disabled{% endif %}>{{ select.display_text }}</option>
        {% endif %}
    {% endfor %}
  </select>
</div>


text:
package com.ehensin.process;

import java.util.List;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;

@XmlType
public class XmlProcess {
	@XmlAttribute(name = "name")
	private String name;
	@XmlAttribute(name = "engine")
	private String engine;
	@XmlElement(name = "context")
	XmlProcessContext context;
	@XmlElement(name = "stage")
	List<XmlProcessStage> stages;

	@XmlTransient
	public XmlProcessContext getContext() {
		return context;
	}
	public void setContext(XmlProcessContext context) {
		this.context = context;
	}

	@XmlTransient
	public List<XmlProcessStage> getStages() {
		return stages;
	}
	public void setStages(List<XmlProcessStage> stages) {
		this.stages = stages;
	}

	@XmlTransient
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}

	@XmlTransient
	public String getEngine() {
		return engine;
	}
	public void setEngine(String engine) {
		this.engine = engine;
	}

}


text:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="app_base_color">#426286</color>

    <style name="AppWeather" parent="BaseAppTheme">
        <item name="colorPrimaryDark">@color/app_base_color</item>
        <item name="colorPrimary">@color/app_base_color</item>
    </style>
</resources>


text:
#include <stdexcept>

#include "greentop/sport/enum/LimitBreachActionType.h"
#include "greentop/Enum.h"

namespace greentop {
namespace sport {

const std::string LimitBreachActionType::REJECT_BETS = "REJECT_BETS";
const std::string LimitBreachActionType::STOP_BETTING = "STOP_BETTING";
const std::string LimitBreachActionType::TEAR_DOWN_MARKET_GROUP = "TEAR_DOWN_MARKET_GROUP";

LimitBreachActionType::LimitBreachActionType() {
    valid = false;
}

LimitBreachActionType::LimitBreachActionType(const std::string& v) {
    if (v != REJECT_BETS &&
        v != STOP_BETTING &&
        v != TEAR_DOWN_MARKET_GROUP) {
        throw std::invalid_argument("Invalid LimitBreachActionType: " + v);
    }
    value = v;
    valid = true;
}

bool LimitBreachActionType::operator<(const LimitBreachActionType& other) const {
    return value < other.value;
}

bool LimitBreachActionType::operator==(const LimitBreachActionType& other) const {
    return value == other.value;
}

bool LimitBreachActionType::operator!=(const LimitBreachActionType& other) const {
    return value != other.value;
}

}
}


text:
export class MainController {
    constructor(private $scope) {
        this.$scope.title = "헤더 타이틀 입니다";
    }
}


text:
namespace ResXManager.View.Themes
{
    using System.Composition;

    using TomsToolbox.Wpf;

    [Export, Shared]
    public class ThemeManager : ObservableObject
    {
        public bool IsDarkTheme { get; set; }
    }
}


text:
module Schnitzel
  class Node
    attr_accessor :position, :z, :rotation, :anchor, :scale,
      :children, :parent

    def initialize(position: [0.0, 0.0], z: 0, rotation: 0.0, anchor: [0.5, 0.5], scale: [1.0, 1.0])
      @position = position
      @z = z
      @rotation = rotation
      @anchor = anchor
      @scale = scale

      @parent = nil
      @children = []

      setup
    end

    def <<(child)
      child.parent = self
      @children << child
    end

    def setup
    end

    def update(dt)
      @children.each { |c| c.update(dt) }
    end

    def draw
      $window.translate(position.x, position.y) do
        $window.scale(scale.x, scale.y) do
          $window.rotate(rotation) do   # probably buggy
            @children.each { |c| c.draw }
          end
        end
      end
    end
  end
end


text:
import { IContainer } from './di/resolvers';

interface Item {
    callback: (container, domain) => void;
}

const Models = "models";
const Services = "services";
const Handlers = "handlers";

export class Preloader {
    private static _instance: Preloader;

    static get instance() {
        if (!Preloader._instance) {
            Preloader._instance = new Preloader();
        }
        return Preloader._instance;
    }

    private _preloads: { [name: string]: Array<Item> } = {};

    registerModel(callback: (container, domain) => void) {
        this.register(Models, callback);
    }

    registerService(callback: (container, domain) => void) {
        this.register(Services, callback);
    }

    registerHandler(callback: (container, domain) => void) {
        this.register(Handlers, callback);
    }

    private register(key: string, callback) {
        let list = this._preloads[key];
        if (!list) {
            this._preloads[key] = list = [];
        }
        list.push({ callback: callback });
    }

    private run(key: string, container, domain) {
        let items = this._preloads[key];
        if (!items) return;
        for (const item of items) {
            item.callback(container, domain);
        }
    }

    runPreloads(container: IContainer, domain) {
        if (this._preloads) {
            this.run(Models, container, domain);
            this.run(Services, container, domain);
            this.run(Handlers, container, domain);
            this._preloads = {};
        }
    }
}


text:
<ion-view view-title="Question" class="profile favourite artists create">
    <ion-content >

Subsidized Housing Question

You have one of the following Acceptable Personal Identification Documents:

                  <select class="browser-default button button-full  btn-select">
                    <option value="1">Canadian Notice of Birth Registration Card</option>
                    <option value="2">A Statement of Live Birth for children age four (4) or under</option>
                    <option value="3">A certified copy of Statement of Live Birth</option>
                    <option value="3">Quebec and Newfoundland Baptismal Certificate</option>
                    <option value="3">Registered Indian Band Status (Native)</option>
                    <option value="3">Canadian Passport</option>
                    <option value="3">Canadian National Defence Card</option>
                  </select>

              <ion-checkbox ng-model="isChecked">One household member must be 16 years of age or older</ion-checkbox>
              <ion-checkbox ng-model="Checked">show a repayment schedule if one of your household members owes money to a social housing provider in Ontario</ion-checkbox>

...

                <button class="button button-light button-full button-positive" ui-sref="app.eligibility">Next step</button>

    </ion-content>
</ion-view>


text:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<title>Uses of Package org.hibernate.cache.infinispan.impl (Hibernate JavaDocs)</title>
<meta name="date" content="2014-03-03">
<link rel="stylesheet" type="text/css" href="../../../../../stylesheet.css" title="Style">

JavaScript is disabled on your browser.

Overview

Package

Class

Use

Tree

Deprecated

Index

Help

Prev

Next

Frames

No Frames

All Classes

Uses of Package
org.hibernate.cache.infinispan.impl

Packages that use org.hibernate.cache.infinispan.impl

Package
Description

org.hibernate.cache.infinispan.access

org.hibernate.cache.infinispan.collection

org.hibernate.cache.infinispan.entity

org.hibernate.cache.infinispan.impl

org.hibernate.cache.infinispan.naturalid

org.hibernate.cache.infinispan.query

org.hibernate.cache.infinispan.timestamp

org.hibernate.cache.infinispan.util

Classes in org.hibernate.cache.infinispan.impl used by org.hibernate.cache.infinispan.access

Class and Description

BaseRegion

Classes in org.hibernate.cache.infinispan.impl used by org.hibernate.cache.infinispan.collection

Class and Description

BaseRegion

BaseTransactionalDataRegion

Classes in org.hibernate.cache.infinispan.impl used by org.hibernate.cache.infinispan.entity

Class and Description

BaseRegion

BaseTransactionalDataRegion

Classes in org.hibernate.cache.infinispan.impl used by org.hibernate.cache.infinispan.impl

Class and Description

BaseRegion

Classes in org.hibernate.cache.infinispan.impl used by org.hibernate.cache.infinispan.naturalid

Class and Description

BaseRegion

BaseTransactionalDataRegion

Classes in org.hibernate.cache.infinispan.impl used by org.hibernate.cache.infinispan.query

Class and Description

BaseRegion

BaseTransactionalDataRegion

Classes in org.hibernate.cache.infinispan.impl used by org.hibernate.cache.infinispan.timestamp

Class and Description

BaseGeneralDataRegion

BaseRegion

Classes in org.hibernate.cache.infinispan.impl used by org.hibernate.cache.infinispan.util

Class and Description

BaseRegion

Overview

Package

Class

Use

Tree

Deprecated

Index

Help

Prev

Next

Frames

No Frames

All Classes

Copyright © 2001-2014 Red Hat, Inc.  All Rights Reserved.


text:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.izerui</groupId>
    <artifactId>webapp-project</artifactId>
    <packaging>pom</packaging>
    <version>1.0.0-SNAPSHOT</version>
    <modules>
        <module>webapp-commons-deps</module>
        <module>webapp-commons</module>
		<!-- <module>modules/webapp-activiti</module>-->
        <module>webapp-framework</module>
        <module>webapp-myweb</module>
    </modules>
    <name>WebApp Project</name>

	<build>
		<plugins>
			<!-- Manage license headers -->
			<plugin>
				<groupId>com.mycila</groupId>
				<artifactId>license-maven-plugin</artifactId>
				<version>${license-maven-plugin}</version>
				<configuration>
					<header>maven/plugin/license/templates/APACHE-2.txt</header>
					<properties>
						<owner>lyh</owner>
						<project.inceptionYear>2014</project.inceptionYear>
						<email>liuyuhua69@gmail.com</email>
					</properties>
					<excludes>
						<exclude>pom.xml</exclude>
						<exclude>**/README</exclude>
						<exclude>**.txt</exclude>
						<exclude>src/test/resources/**</exclude>
						<exclude>src/main/resources/**</exclude>
					</excludes>
				</configuration>
				<executions>
					<execution>
						<goals>
							<goal>check</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
			<!-- eclipse plugin -->
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-eclipse-plugin</artifactId>
				<version>${org.apache.maven.plugins.version}</version>
				<configuration>
					<wtpversion>2.0</wtpversion>
					<downloadSources>true</downloadSources>
				</configuration>
			</plugin>
			<!-- resources manager plugin -->
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-resources-plugin</artifactId>
				<version>${org.apache.maven.plugins.maven-resources-plugin.version}</version>
				<configuration>
					<encoding>${encoding}</encoding>
				</configuration>
			</plugin>
			<!-- java source manager -->
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-source-plugin</artifactId>
				<version>${org.apache.maven.plugins.maven-source-plugin.version}</version>
				<executions>
					<execution>
						<id>source-jar</id>
						<phase>package</phase>
						<goals>
							<goal>jar</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
			<!-- test -->
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-surefire-plugin</artifactId>
				<configuration>
					<includes>
						<include>**/*Test.java</include>
					</includes>
				</configuration>
			</plugin>
		</plugins>

	</build>

	<repositories>
		<repository>
			<id>nexus</id>
			<name>local private nexus</name>
			<url>http://maven.oschina.net/content/groups/public/</url>
			<releases>
				<enabled>true</enabled>
			</releases>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
	</repositories>

	<properties>
		<!-- == Google == -->
		<com.google.guava.version>14.0.1</com.google.guava.version>
		<!-- == Apache Commons == -->
		<commons-beanutils.version>1.8.3</commons-beanutils.version>
		<commons-collections.version>3.2.1</commons-collections.version>
		<commons-dbcp.version>1.2.2</commons-dbcp.version>
		<commons-pool.version>1.6</commons-pool.version>
		<commons-io.version>2.4</commons-io.version>
		<commons-lang.version>3.1</commons-lang.version>
		<!-- == orika== -->
		<ma.glasnost.orika>1.4.3</ma.glasnost.orika>
		<!-- == Joda-Time == -->
		<joda-time.version>2.2</joda-time.version>
		<joda-time.joda-time-jsptags.version>1.1.1</joda-time.joda-time-jsptags.version>
		<jadira-usertype-core.version>3.0.0.GA</jadira-usertype-core.version>
		<!-- == AspectJ == -->
		<org.aspectj-version>1.7.3</org.aspectj-version>
		<!-- == Jackson == -->
		<org.codehaus.jackson.version>1.9.7</org.codehaus.jackson.version>
		<!-- == JPA == -->
		<org.hibernate.javax.persistence.version>1.0.1.Final</org.hibernate.javax.persistence.version>
		<org.hibernate.version>4.2.4.Final</org.hibernate.version>
		<!-- == BeanValidation == -->
		<javax.validation.version>1.0.0.Final</javax.validation.version>
		<org.hibernate.hibernate-validator.version>4.3.1.Final</org.hibernate.hibernate-validator.version>
		<!-- == Logging == -->
		<org.slf4j-version>1.7.5</org.slf4j-version>
		<ch.qos.logback.version>1.0.13</ch.qos.logback.version>
		<org.lazyluke.version>0.2.7</org.lazyluke.version>
		<!-- == Spring Framework == -->
		<org.springframework-version>3.2.5.RELEASE</org.springframework-version>
		<org.springframework.data.spring-data-jpa.version>1.3.4.RELEASE</org.springframework.data.spring-data-jpa.version>
		<org.springframework.data.version>1.5.2.RELEASE</org.springframework.data.version>
		<org.springframework.security.version>3.2.0.RELEASE</org.springframework.security.version>
		<!-- == Tiles == -->
		<tiles.version>3.0.3</tiles.version>
		<!-- == JavaEE/SE APIs == -->
		<javax.inject.version>1</javax.inject.version>
		<javax.servlet.jstl.version>1.2</javax.servlet.jstl.version>
		<javax.transaction.version>1.1</javax.transaction.version>
		<!-- == database == -->
		<com.h2database.version>1.3.156</com.h2database.version>
		<!-- == JUnit == -->
		<junit.version>4.7</junit.version>
		<org.mockito.version>1.9.0</org.mockito.version>
		<selenium.version>2.32.0</selenium.version>
		<!-- == Maven Plugins == -->
		<org.apache.maven.plugins.maven-compiler-plugin.version>3.1</org.apache.maven.plugins.maven-compiler-plugin.version>
		<org.apache.maven.plugins.maven-dependency-plugin.version>2.5</org.apache.maven.plugins.maven-dependency-plugin.version>
		<org.apache.maven.plugins.maven-resources-plugin.version>2.6</org.apache.maven.plugins.maven-resources-plugin.version>
		<org.apache.maven.plugins.maven-source-plugin.version>2.2</org.apache.maven.plugins.maven-source-plugin.version>
		<org.apache.maven.plugins.maven-surefire-report-plugin.version>2.4.3</org.apache.maven.plugins.maven-surefire-report-plugin.version>
		<org.apache.maven.plugins.maven-war-plugin.version>2.2</org.apache.maven.plugins.maven-war-plugin.version>
		<org.apache.maven.plugins.version>2.9</org.apache.maven.plugins.version>
		<org.apache.maven.wagon.version>1.0-beta-7</org.apache.maven.wagon.version>
		<org.apache.tomcat.maven.version>2.2</org.apache.tomcat.maven.version>
		<!-- == Other Properties== -->
		<license-maven-plugin>2.5</license-maven-plugin>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<encoding>UTF-8</encoding>
		<java-version>1.7</java-version>
	</properties>

</project>


text:
const {
  assertThrows
} = Assert;

// 25.4.1.5.1 CreatePromiseCapabilityRecord, steps 4-5
// - [[Call]] on bound promise constructor does not throw TypeError
{
  function BoundPromise() {
    const boundThis = null;
    return Object.setPrototypeOf(Promise.bind(boundThis), Promise);
  }

  BoundPromise().all([]);
  BoundPromise().race([]);
  BoundPromise().reject(0);
  BoundPromise().resolve(0);
  { let p = new Promise(() => {}); p.constructor = BoundPromise(); p.then() }
}


text:
<?php

$dir = dirname( __FILE__ );
$dirParts = explode( DIRECTORY_SEPARATOR, $dir );

if ( $dirParts[count( $dirParts ) - 1] === 'src' )
{
    $baseDir = join( DIRECTORY_SEPARATOR, array_slice( $dirParts, 0, -2 ) );
    require $baseDir . '/Base/src/base.php'; // svn, bundle
}
else if ( $dirParts[count( $dirParts ) - 2] === 'ezc' )
{
    $baseDir = join( DIRECTORY_SEPARATOR, array_slice( $dirParts, 0, -2 ) );
    require $baseDir . '/ezc/Base/base.php'; // pear
}
else
{
    die( "Your environment isn't properly set-up. Please refer to the eZ components documentation at http://components.ez.no/doc ." );
}

/**
 * Implements the __autoload mechanism for PHP - which can only be done once
 * per request.
 *
 * @param string $className  The name of the class that should be loaded.
 */
function __autoload( $className )
{
	ezcBase::autoload( $className );
}
?>


text:
ACCEPTED

#### According to
Index Fungorum

#### Published in
null

#### Original name
Arthonia fusca var. granulata Norman

### Remarks
null


text:
<!DOCTYPE html>

	<title>Pattern Lab Pattern</title>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width" />

	<link rel="stylesheet" href="../../css/style.css" media="all" />

59 Comments

<form class="comment-form">
	<fieldset>

			Name
			<input id="comment-name" type="text" placeholder="Enter Your Name" />

			Email
			<input id="comment-email" type="email" placeholder="Your email address" />

			URL
			<input id="comment-url" type="url" placeholder="Website if you got one" />

			Comment
			<textarea id="comment-field" rows="5" placeholder="Write you comment here"></textarea>

	</fieldset>
</form>

1

2

3

4

5

6

7


text:
<?php

namespace Rogama\DemoBundle\Controller;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Rogama\DemoBundle\Entity\User;
use Rogama\DemoBundle\Form\UserType;

/**
 * User controller.
 *
 * @Route("/admin/user")
 */
class UserController extends Controller
{

    /**
     * Lists all User entities.
     *
     * @Route("/", name="admin_user")
     * @Method("GET")
     * @Template()
     */
    public function indexAction()
    {
        $em = $this->getDoctrine()->getManager();

        $entities = $em->getRepository('DemoBundle:User')->findAll();

        return array(
            'entities' => $entities,
        );
    }
    /**
     * Creates a new User entity.
     *
     * @Route("/", name="admin_user_create")
     * @Method("POST")
     * @Template("DemoBundle:User:new.html.twig")
     */
    public function createAction()
    {
        $entity = new User();
        $request = $this->getRequest();
        $form = $this->createCreateForm($entity);
        $form->bind($request);

        if ($form->isValid()) {
            $this->setSecurePassword($entity);

            $em = $this->getDoctrine()->getEntityManager();
            $em->persist($entity);
            $em->flush();

            return $this->redirect($this->generateUrl('admin_user_show', array('id' => $entity->getId())));
        }

        return array(
            'entity' => $entity,
            'form'   => $form->createView(),
        );
    }

    /**
     * Creates a form to create a User entity.
     *
     * @param User $entity The entity
     *
     * @return \Symfony\Component\Form\Form The form
     */
    private function createCreateForm(User $entity)
    {
        $form = $this->createForm(new UserType(), $entity, array(
            'action' => $this->generateUrl('admin_user_create'),
            'method' => 'POST',
        ));

        $form->add('submit', 'submit', array('label' => 'Create'));

        return $form;
    }

    /**
     * Displays a form to create a new User entity.
     *
     * @Route("/new", name="admin_user_new")
     * @Method("GET")
     * @Template()
     */
    public function newAction()
    {
        $entity = new User();
        $form   = $this->createCreateForm($entity);

        return array(
            'entity' => $entity,
            'form'   => $form->createView(),
        );
    }

    /**
     * Finds and displays a User entity.
     *
     * @Route("/{id}", name="admin_user_show")
     * @Method("GET")
     * @Template()
     */
    public function showAction($id)
    {
        $em = $this->getDoctrine()->getManager();

        $entity = $em->getRepository('DemoBundle:User')->find($id);

        if (!$entity) {
            throw $this->createNotFoundException('Unable to find User entity.');
        }

        $deleteForm = $this->createDeleteForm($id);

        return array(
            'entity'      => $entity,
            'delete_form' => $deleteForm->createView(),
        );
    }

    /**
     * Displays a form to edit an existing User entity.
     *
     * @Route("/{id}/edit", name="admin_user_edit")
     * @Method("GET")
     * @Template()
     */
    public function editAction($id)
    {
        $em = $this->getDoctrine()->getManager();

        $entity = $em->getRepository('DemoBundle:User')->find($id);

        if (!$entity) {
            throw $this->createNotFoundException('Unable to find User entity.');
        }

        $editForm = $this->createEditForm($entity);
        $deleteForm = $this->createDeleteForm($id);

        return array(
            'entity'      => $entity,
            'edit_form'   => $editForm->createView(),
            'delete_form' => $deleteForm->createView(),
        );
    }

    /**
    * Creates a form to edit a User entity.
    *
    * @param User $entity The entity
    *
    * @return \Symfony\Component\Form\Form The form
    */
    private function createEditForm(User $entity)
    {
        $form = $this->createForm(new UserType(), $entity, array(
            'action' => $this->generateUrl('admin_user_update', array('id' => $entity->getId())),
            'method' => 'PUT',
        ));

        $form->add('submit', 'submit', array('label' => 'Update'));

        return $form;
    }
    /**
     * Edits an existing User entity.
     *
     * @Route("/{id}", name="admin_user_update")
     * @Method("PUT")
     * @Template("DemoBundle:User:edit.html.twig")
     */
    public function updateAction($id)
    {
        $em = $this->getDoctrine()->getEntityManager();

        $entity = $em->getRepository('DemoBundle:User')->find($id);

        if (!$entity) {
            throw $this->createNotFoundException('Unable to find User entity.');
        }

        $edirForm = $this->createForm(new UserType(), $entity);
        $deleteForm = $this->createDeleteForm($id);

        $request = $this->getRequest();
        $current_pass = $entity->getPassword();
        $editForm->bindRequest($request);

        if ($editForm->isValid()) {
            if($current_pass != $entity_getPassword()){
                $this->setSecurePassword($entity);
            }
            $em->persist($entity);
            $em->flush();

            return $this->redirect($this->generateUrl('admin_user_edit', array('id' => $id)));
        }

        return array(
            'entity'      => $entity,
            'edit_form'   => $editForm->createView(),
            'delete_form' => $deleteForm->createView(),
        );
    }
    /**
     * Deletes a User entity.
     *
     * @Route("/{id}", name="admin_user_delete")
     * @Method("DELETE")
     */
    public function deleteAction(Request $request, $id)
    {
        $form = $this->createDeleteForm($id);
        $form->handleRequest($request);

        if ($form->isValid()) {
            $em = $this->getDoctrine()->getManager();
            $entity = $em->getRepository('DemoBundle:User')->find($id);

            if (!$entity) {
                throw $this->createNotFoundException('Unable to find User entity.');
            }

            $em->remove($entity);
            $em->flush();
        }

        return $this->redirect($this->generateUrl('admin_user'));
    }

    /**
     * Creates a form to delete a User entity by id.
     *
     * @param mixed $id The entity id
     *
     * @return \Symfony\Component\Form\Form The form
     */
    private function createDeleteForm($id)
    {
        return $this->createFormBuilder()
            ->setAction($this->generateUrl('admin_user_delete', array('id' => $id)))
            ->setMethod('DELETE')
            ->add('submit', 'submit', array('label' => 'Delete'))
            ->getForm()
        ;
    }

    private function setSecurePassword(&$entity) {
        $entity->setSalt(md5(time()));
        $encoder = new \Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder('sha512', TRUE, 10);

        $password = $encoder->encodePassword($entity->getPassword(), $entity->getSalt());
        $entity->setPassword($password);

    }
}


text:
@class TimelineCellToolBar;
@protocol TimelineCellToolBarDelegate
@required
- (void)starWithTimelineCellToolBar:(TimelineCellToolBar *)toolbar sender:(id)sender forEvent:(UIEvent *)event;

@end

@interface TimelineCellToolBar: UIView
@property (nonatomic, weak) id <TimelineCellToolBarDelegate>delegate;
@property (weak, nonatomic) IBOutlet UIButton *starbtn;
-(void)setupStarbtnWithBool:(Boolean)fav;
@end


text:
package qrhl.toplevel

import qrhl.isabellex.{IsabelleX, RichTerm}
import qrhl.logic._
import qrhl.tactic._
import qrhl.{tactic, toplevel, _}

import scala.util.parsing.combinator._
import IsabelleX.{globalIsabelle => GIsabelle}
import de.unruh.isabelle.pure.Typ

import scala.collection.mutable
import scala.language.implicitConversions

case class ParserContext(environment: Environment,
                         isabelle: Option[IsabelleX.ContextX])

object Parser extends JavaTokenParsers {
  implicit def tildeToPair[T,U](x: T ~ U) : (T,U) = (x._1, x._2)

  val identifier : Parser[String] = """[a-zA-Z][a-zA-Z0-9_']*""".r
  val identifierListSep : Parser[String] = ","
  val identifierList : Parser[List[String]] = rep1sep(identifier,identifierListSep)
  val identifierList0 : Parser[List[String]] = repsep(identifier,identifierListSep)
  val identifierTuple: Parser[List[String]] = "(" ~> identifierList0 <~ ")"
//  val identifierOrTuple: Parser[List[String]] = identifierTuple | (identifier ^^ {s:String => List(s)})

  val identifierAsVarterm: Parser[VTSingle[String]] = identifier ^^ VTSingle[String]
  //noinspection ForwardReference
  val vartermParens : Parser[VarTerm[String]] = "(" ~> varterm <~ ")"
  val vartermAtomic: Parser[VarTerm[String]] = vartermParens | identifierAsVarterm
  val varterm: Parser[VarTerm[String]] =
    rep1sep(vartermAtomic, identifierListSep) ^^ vartermListToVarterm
//  val varterm0 = vartermParens | vartermNoParens0
//  val varterm: toplevel.Parser.Parser[VarTerm[String]] = vartermNoParens1 | vartermParens
  private def vartermListToVarterm(vts:List[VarTerm[String]]) = {
    vts match {
    case Nil => VTUnit
    case _ => vts.foldRight(null:VarTerm[String]) { (a,b) => if (b==null) a else VTCons(a,b) }
  }}
//  val vartermNoParens1 : Parser[VarTerm[String]] =
//    rep1sep(vartermParens | identifierAsVarterm, identifierListSep) ^^ vartermListToVarterm
//  val vartermNoParens0 : Parser[VarTerm[String]] =
//    repsep(vartermParens | identifierAsVarterm, identifierListSep) ^^ vartermListToVarterm

  //  val natural : Parser[BigInt] = """[0-9]+""".r ^^ { BigInt(_) }
  val natural: Parser[Int] = """[0-9]+""".r ^^ { _.toInt }

  val noIsabelleError: String = """Need isabelle command first. Try "isabelle." or "isabelle <theory name>."""""

  private val statementSeparator = literal(";")

  def repWithState[S](p : S => Parser[S], start : S) : Parser[S] =
    p(start).?.flatMap { case Some(s) => repWithState(p,s); case None => success(start) }

  def rep1WithState[S](p : S => Parser[S], start : S) : Parser[S] =
    p(start).flatMap { s => repWithState[S](p,s) }

  val scanInnerSyntax : Parser[String] = repWithState[(Int,List[Char])]({
    case (0, chars) =>
      elem("expression", { c => c != ';' && c != ')' && c != '}' }) ^^ { c =>
        val cs = c :: chars
        val lvl = if (c == '(' || c == '{') 1 else 0
        (lvl, cs)
      }
    case (level, chars) =>
      elem("expression", { _ => true }) ^^ { c =>
        assert(level > 0)
        val cs = c :: chars
        val lvl = if (c == '(' || c== '{') level + 1 else if (c == ')' || c=='}') level - 1 else level
        (lvl, cs)
      }
  }, (0,Nil)) ^^ { case (_,chars) => chars.reverse.mkString.trim }

  val fact : Parser[String] = """\s*""".r ~> rep1WithState[(Int,List[Char])]({
    case (0, chars) =>
      elem("lemma name", { c => !c.isWhitespace && c != ']' && c != ')' && c != '}' }) ^^ { c =>
        val cs = c :: chars
        val lvl = if (c == '(' || c == '{' || c == '[') 1 else 0
        (lvl, cs)
      }
    case (level, chars) =>
      elem("lemma name", { c => true }) ^^ { c =>
        assert(level > 0)
        val cs = c :: chars
        val lvl = if (c == '(' || c== '{' || c == '[') level + 1 else if (c == ')' || c=='}' || c == ']') level - 1 else level
        (lvl, cs)
      }
  }, (0,Nil)) ^^ { case (_,chars) => chars.reverse.mkString.trim }

  def expression(typ:Typ)(implicit context:ParserContext) : Parser[RichTerm] =
//    rep1 (elem("expression",{c => c!=';'})) ^^ { str:List[_] => context.isabelle match {
    scanInnerSyntax ^^ { str:String => context.isabelle match {
      case None => throw UserException(noIsabelleError)
      case Some(isa) =>
        val e = RichTerm(isa, str  /*str.mkString.trim*/, typ)
        for (v <- e.variables)
          if (!context.environment.variableExists(v))
            throw UserException(s"Variable $v was not declared (in expression $str")
        e
    } }

  private val assignSymbol = literal("<-")
  def assign(implicit context:ParserContext) : Parser[Assign] =
    for (lhs <- varterm;
         _ <- assignSymbol;
         // TODO: add a cut
         lhsV = lhs.map[CVariable] { context.environment.getCVariable };
         typ = GIsabelle.tupleT(lhsV.map[Typ](_.valueTyp));
         e <- expression(typ);
         _ <- statementSeparator)
     yield Assign(lhsV, e)

  private val sampleSymbol = literal("<$")
  def sample(implicit context:ParserContext) : Parser[Sample] =
    for (lhs <- varterm;
         _ <- sampleSymbol;
         // TODO: add a cut
         lhsV = lhs.map[CVariable] { context.environment.getCVariable };
         typ = GIsabelle.tupleT(lhsV.map[Typ](_.valueTyp));
         e <- expression(GIsabelle.distrT(typ));
         _ <- statementSeparator)
      yield Sample(lhsV, e)

  def programExp(implicit context:ParserContext) : Parser[Call] = identifier ~
    (literal("(") ~ rep1sep(programExp,identifierListSep) ~ ")").? ^^ {
    case name ~ args =>
      val args2 = args match { case None => Nil; case Some(_ ~ a ~ _) => a }
      context.environment.programs.get(name) match {
        case None => throw UserException(s"""Program $name not defined (in call-statement).""")
        case Some(decl) =>
          if (decl.numOracles!=args2.length)
            throw UserException(s"""Program $name expects ${decl.numOracles} oracles (in call-statement)""")
      }
      Call(name,args2 : _*)
  }

  def call(implicit context:ParserContext) : Parser[Call] =
    literal("call") ~! programExp ~ statementSeparator ^^ { case _ ~ call ~ _ => call }

  private val qInitSymbol = literal("<q")
  def qInit(implicit context:ParserContext) : Parser[QInit] =
    for (vs <- varterm;
         _ <- qInitSymbol;
         // TODO: add a cut
         //         _ = assert(vs.nonEmpty);
         _ = assert(vs.areDistinct); // checks if all vs are distinct
         qvs = vs.map[QVariable] { context.environment.getQVariable };
         //         qvs = VarTerm.varlist(vs.map { context.environment.getQVariable }:_*);
         typ = GIsabelle.ell2T(GIsabelle.tupleT(qvs.map[Typ](_.valueTyp)));
         e <- expression(typ);
         _ <- statementSeparator)
      yield QInit(qvs,e)

  def qApply(implicit context:ParserContext) : Parser[QApply] =
      for (_ <- literal("on");
           vs <- varterm;
           _ <- literal("apply");
           //           _ = assert(vs.nonEmpty);
           _ = assert(vs.areDistinct); // checks if all vs are distinct
           qvs = vs.map[QVariable] { context.environment.getQVariable };
           //           _ = assert(vs.distinct.length==vs.length); // checks if all vs are distinct
           //           qvs = VarTerm.varlist(vs.map { context.environment.getQVariable }:_*);
           typ = GIsabelle.l2boundedT(GIsabelle.tupleT(qvs.map[Typ](_.valueTyp)));
           e <- expression(typ);
           _ <- statementSeparator)
        yield QApply(qvs,e)

  val measureSymbol : Parser[String] = assignSymbol
  def measure(implicit context:ParserContext) : Parser[Measurement] =
    for (res <- varterm;
         _ <- measureSymbol;
         _ <- literal("measure");
         vs <- varterm;
         _ = assert(vs.areDistinct); // checks if all vs are distinct
         resv = res.map[CVariable] { context.environment.getCVariable };
         qvs = vs.map[QVariable] { context.environment.getQVariable };
         _ <- literal("with");
         etyp = GIsabelle.measurementT(GIsabelle.tupleT(resv.map[Typ](_.valueTyp)), GIsabelle.tupleT(qvs.map[Typ](_.valueTyp)));
         e <- expression(etyp);
         _ <- statementSeparator)
      yield Measurement(resv,qvs,e)

  def ifThenElse(implicit context:ParserContext) : Parser[IfThenElse] =
    for (_ <- literal("if");
         _ <- literal("(");
         e <- expression(GIsabelle.boolT);
         _ <- literal(")");
         _ <- literal("then");
         thenBranch <- statementOrParenBlock; // TODO: allow nested block
         _ <- literal("else");
         elseBranch <- statementOrParenBlock)  // TODO: allow nested block
      yield IfThenElse(e,thenBranch.toBlock,elseBranch.toBlock)

  def whileLoop(implicit context:ParserContext) : Parser[While] =
    for (_ <- literal("while");
         _ <- literal("(");
         e <- expression(GIsabelle.boolT);
         _ <- literal(")");
         body <- statementOrParenBlock)
      yield While(e,body.toBlock)

  def statement(implicit context:ParserContext) : Parser[Statement] = measure | assign | sample | call | qInit | qApply | ifThenElse | whileLoop | parenBlock

//  def statementWithSep(implicit context:ParserContext) : Parser[Statement] = statement ~ statementSeparator ^^ { case s ~ _ => s }

  def skip : Parser[Block] = "skip" ~! statementSeparator ^^ { _ => Block.empty }

  def statementOrParenBlock(implicit context:ParserContext) : Parser[Statement] =
    parenBlock | skip | (statement ^^ { s => Block(s) })

  def locals(implicit context:ParserContext) : Parser[List[String]] =
    "local" ~> identifierList <~ statementSeparator ^^ { vars =>
      for (v <- vars) context.environment.getProgVariable(v)
      vars }

  def parenBlock(implicit context:ParserContext) : Parser[Statement] =
    ("{" ~ "}" ^^ { _ => Block.empty }) |
      ("{" ~ locals ~ block ~ "}" ^^ { case _ ~ l ~ b ~ _ => Local(context.environment, l, b) }) |
      ("{" ~> block <~ "}")

  def block(implicit context:ParserContext) : Parser[Block] =
    (statementSeparator ^^ { _ => Block.empty }) |
      (rep1(statement) ^^ { s => Block(s:_*) }) |
      skip

  // TODO does not match strings like "xxx\"xxx" or "xxx\\xxx" properly
  val quotedString : Parser[String] = stringLiteral ^^ { s:String =>
    val result = new mutable.StringBuilder()
    val iterator = s.substring(1,s.length-1).iterator
    for (c <- iterator)
      if (c=='\\') result.append(iterator.next())
      else result.append(c)
    result.toString()
  }

  val unquotedStringNoComma : Parser[String] = "[^.,]+".r

//  val commandEndSymbol : Parser[_] = literal(".")
  val isabelle : Parser[IsabelleCommand] =
    literal("isabelle") ~> ("[" ~> identifier <~ "]").? ~ identifierList0 ^^
      { case session ~ theories => IsabelleCommand(theories, session) }

  def typ(implicit context:ParserContext) : Parser[Typ] =
  //    rep1 (elem("expression",{c => c!=';'})) ^^ { str:List[_] => context.isabelle match {
    scanInnerSyntax ^^ { str:String => context.isabelle match {
      case None => throw UserException(noIsabelleError)
      case Some(isa) => isa.readTypUnicode(str)
    } }

  def variableKind : Parser[String] = "classical|quantum|ambient".r
  //noinspection RedundantDefaultArgument
  def variable(implicit context:ParserContext) : Parser[DeclareVariableCommand] =
    variableKind ~ OnceParser(literal("var") ~ identifier ~ literal(":") ~ typ) ^^ {
      case kind~(_~id~_~typ) => kind match {
        case "classical" => DeclareVariableCommand(id,typ,quantum=false)
        case "quantum" => DeclareVariableCommand(id,typ,quantum=true)
        case "ambient" => DeclareVariableCommand(id,typ,ambient=true)
      }
    }

//  def quantumVariable(implicit context:ParserContext) : Parser[DeclareVariableCommand] =
//    literal("quantum") ~> literal("var") ~> OnceParser(identifier ~ literal(":") ~ typ) ^^ { case id~_~typ =>
//      DeclareVariableCommand(id,typ,quantum=true)
//    }

  def declareProgram(implicit context:ParserContext) : Parser[DeclareProgramCommand] =
    literal("program") ~> OnceParser(for (
      name <- identifier;
      args <- identifierTuple.?;
      args2 = args.getOrElse(Nil);
      _ <- literal(":=");
      // temporarily add oracles to environment to allow them to occur in call-expressions during parsing
      context2 = args2.foldLeft(context) { case (ctxt,p) => ctxt.copy(ctxt.environment.declareProgram(AbstractProgramDecl(p,Nil,Nil,Nil,Nil,Nil,0))) };
      body <- parenBlock(context2))
      yield DeclareProgramCommand(name,args2,body.toBlock))

  private def declareAdversaryCalls: Parser[Int] = (literal("calls") ~ rep1sep(literal("?"),identifierListSep)).? ^^ {
    case None => 0
    case Some(_ ~ progs) => progs.length
  }

  def declareAdversary(implicit context:ParserContext) : Parser[DeclareAdversaryCommand] =
    literal("adversary") ~> OnceParser(for (
      name <- identifier;
      free <- literal("free") ~> identifierList;
      readonly <- (literal("readonly") ~> identifierList) | success(Nil);
      overwritten <- (literal("overwritten") ~> identifierList) | success(Nil);
      inner <- (literal("inner") ~> identifierList) | success(Nil);
      covered <- (literal("covered") ~> identifierList) | success(Nil);
      calls <- declareAdversaryCalls)
      yield {
        for (v <- free++readonly++inner++covered) if (!context.environment.cVariables.contains(v) && !context.environment.qVariables.contains(v))
          throw UserException(s"Not a program variable: $v")
        val free2 = free.map(context.environment.getProgVariable)
        val inner2 = inner.map(context.environment.getProgVariable)
        val covered2 = covered.map(context.environment.getProgVariable)
        val overwritten2 = overwritten.map(context.environment.getProgVariable)
        val readonly2 = readonly.map(context.environment.getProgVariable)
        DeclareAdversaryCommand(name,free=free2,inner=inner2,covered=covered2,readonly=readonly2,overwritten=overwritten2,numOracles=calls)
      })

  def goal(implicit context:ParserContext) : Parser[GoalCommand] =
    literal("lemma") ~> OnceParser((identifier <~ ":").? ~ expression(GIsabelle.boolT)) ^^ {
      case name ~ e => GoalCommand(name.getOrElse(""), AmbientSubgoal(e)) }

  def qrhl(implicit context:ParserContext) : Parser[GoalCommand] =
  literal("qrhl") ~> OnceParser(for (
    name <- (identifier <~ ":").?;
    _ <- literal("{");
    pre <- expression(GIsabelle.predicateT);
    _ <- literal("}");
    left <- block;
    _ <- literal("~");
    right <- block;
    _ <- literal("{");
    post <- expression(GIsabelle.predicateT);
    _ <- literal("}")
  ) yield GoalCommand(name.getOrElse(""), QRHLSubgoal(left,right,pre,post,Nil)))

  val tactic_wp: Parser[WpTac] =
    literal("wp") ~> {
      (literal("left") ~> natural.? ^^ { n => WpTac(left = n.getOrElse(1), right = 0) }) |
        (literal("right") ~> natural.? ^^ { n => WpTac(right = n.getOrElse(1), left = 0) }) |
        (natural ~ natural ^^ { case l ~ r => WpTac(left = l, right = r) })
    }

  val tactic_sp: Parser[SpTac] =
    literal("sp") ~> {
      (literal("left") ~> natural.? ^^ { n => SpTac(left = n.getOrElse(1), right = 0) }) |
        (literal("right") ~> natural.? ^^ { n => SpTac(right = n.getOrElse(1), left = 0) }) |
        (natural ~ natural ^^ { case l ~ r => SpTac(left = l, right = r) })
    }

  val tactic_squash: Parser[SquashTac] =
    literal("squash") ~> OnceParser("left|right".r) ^^ {
      case "left" => SquashTac(left=true)
      case "right" => SquashTac(left=false)
    }

  val swap_range: Parser[SwapTac.Range] =
    (natural ~ "-" ~ natural) ^^ { case i ~ _ ~ j => SwapTac.MiddleRange(i,j) } /*|
      natural ^^ SwapTac.FinalRange |
      success(SwapTac.FinalRange(1))*/

  def swap_subprograms(implicit context:ParserContext): Parser[List[(Statement, Option[Statement])]] = rep1sep(
    (statement ~ ("->" ~> statement).?) ^^ { case x ~ y => (x.unwrapSingletonBlock, y.map(_.unwrapSingletonBlock)) },
    literal(",")
  )

/*  val subprograms_selector: Parser[Boolean] =
    literal("subprograms") ~>
      commit(((literal("first") ^^^ true | literal("second") ^^^ false)
        withFailureMessage "'first'/'second' expected after 'subprograms'") <~
        literal(":"))*/
//    literal("subprograms") ~> OnceParser("first|second".r <~ literal(":") ^^
//      { case "first" => true; case "second" => false; case _ => throw new InternalError("Should not occur") })

  def tactic_swap(implicit context:ParserContext): Parser[SwapTac] =
    literal("swap") ~> commit(for (
      left <- ("left" ^^^ true | "right" ^^^ false) withFailureMessage "left/right expected after swap";
      range1 <- swap_range;
      range2 <- swap_range;
      subprograms <- (literal("subprograms") ~> commit(literal(":") ~> swap_subprograms))
                        | success(Nil))
      yield SwapTac(left=left, range1=range1, range2=range2, subprograms=subprograms))

  val tactic_inline: Parser[InlineTac] =
    literal("inline") ~> identifier ^^ InlineTac

  def tactic_seq(implicit context:ParserContext): Parser[SeqTac] =
    literal("seq") ~> OnceParser(for (
      swap <- ("<->" ^^^ true) | success(false);
      left <- natural;
      right <- natural;
      _ <- literal(":");
      inner <- expression(GIsabelle.predicateT))
      yield SeqTac(left,right,inner,swap=swap))

  val identifierListOrDot: Parser[List[String]] = identifierList | (literal(".") ^^^ Nil)

  def var_subst(implicit context:ParserContext): Parser[((List[QVariable],List[QVariable]),(List[QVariable],List[QVariable]))] = {
    val qvarList : Parser[List[QVariable]] = identifierListOrDot ^^ { _ map context.environment.getQVariable }
    val subst1 : Parser[(List[QVariable],List[QVariable])] = OnceParser(qvarList ~ "->" ~ qvarList) ^^ {
      case v1 ~ _ ~ v2 => (v1,v2) }

    literal("(") ~ subst1 ~ (literal(";") ~> subst1).? ~ literal(")") ^^ {
      case _ ~ s1 ~ Some(s2) ~ _ => (s1,s2)
      case _ ~ s ~ None ~ _ => (s,s)
    }
  }

  def tactic_conseq(implicit context:ParserContext): Parser[Tactic] =
    literal("conseq") ~> OnceParser("pre|post".r ~ literal(":") ~ expression(GIsabelle.predicateT) ^^ {
      case "pre" ~ _ ~ e => ConseqTac(pre=Some(e))
      case "post" ~ _ ~ e => ConseqTac(post=Some(e))
    } |
      OnceParser(literal("qrhl") ~ var_subst.? ~ literal(":") ~ ".*".r) ^^ {
        case _ ~ subst ~ _ ~ rule => ConseqQrhlTac(rule, subst)
      }
    )

  def wp_amount: Parser[Int] = (natural | ("all" ^^^ -1)).? ^^ { _.getOrElse(1) }

  def tactic_equal(implicit context:ParserContext) : Parser[EqualTac] =
    literal("equal") ~> OnceParser(
      for (amount <- wp_amount;
           exclude <- (literal("exclude") ~> identifierList).?;
           in <- (literal("in") ~> identifierList).?;
           mid <- (literal("mid") ~> identifierList).?;
           out <- (literal("out") ~> identifierList).?)
        yield {
          val exclude2 = exclude.getOrElse(Nil)
          for (p <- exclude2 if !context.environment.programs.contains(p))
            throw UserException(s"Undeclared program $p")

          val in2 = in.getOrElse(Nil) map { context.environment.getProgVariable }
          val mid2 = mid.getOrElse(Nil) map { context.environment.getProgVariable }
          val out2 = out.getOrElse(Nil) map { context.environment.getProgVariable }

          EqualTac(exclude=exclude2, in=in2, mid=mid2, out=out2, amount=amount)
        })

  def tactic_byqrhl(implicit context:ParserContext) : Parser[ByQRHLTac] =
    literal("byqrhl") ~> (literal("qvars") ~> identifierList).? ^^ { qvars =>
        val qvars2 = qvars.getOrElse(Nil) map { context.environment.getQVariable }
        ByQRHLTac(qvariables = qvars2)
    }

  def tactic_rnd(implicit context:ParserContext): Parser[Tactic] =
    literal("rnd") ~> OnceParser(for (
      x <- vartermAtomic;
      xVar = x.map[CVariable](context.environment.getCVariable);
      xTyp = GIsabelle.tupleT(xVar.map[Typ](_.valueTyp));
      _ <- literal(",");
      y <- vartermAtomic;
      yVar = y.map[CVariable](context.environment.getCVariable);
      yTyp = GIsabelle.tupleT(yVar.map[Typ](_.valueTyp));
      _ <- sampleSymbol | assignSymbol;
      e <- expression(GIsabelle.distrT(GIsabelle.prodT(xTyp,yTyp)))
    ) yield (xVar,yVar,e)).? ^^ {
      case None => RndEqualTac
      case Some((xVar,yVar,e)) => RndWitnessTac(xVar,yVar,e)
    }

  def tactic_case(implicit context:ParserContext): Parser[CaseTac] =
    literal("case") ~> OnceParser(for (
      x <- identifier;
      xT = context.environment.getAmbientVariable(x);
      _ <- literal(":=");
      e <- expression(xT)
    ) yield CaseTac(x,e))

  val tactic_simp : Parser[SimpTac] =
    literal("simp") ~> OnceParser("[!*]".r.? ~ rep(fact)) ^^ {
      case None ~ lemmas => SimpTac(lemmas, force=false, everywhere = false)
      case Some("!") ~ lemmas => SimpTac(lemmas, force = true, everywhere = false)
      case Some("*") ~ lemmas => SimpTac(lemmas, force = false, everywhere = true)
      case _ => throw new RuntimeException("Internal error") // cannot happen
    }

  val tactic_isa : Parser[IsaTac] =
    literal("isa") ~> OnceParser("!".? ~ ".*".r) ^^ {
      case None ~ method => IsaTac(method, force=false)
      case Some("!") ~ method => IsaTac(method, force = true)
      case _ => throw new RuntimeException("Internal error") // cannot happen
    }

  def tactic_rule(implicit context:ParserContext) : Parser[RuleTac] =
    literal("rule") ~> ".*".r ^^ RuleTac.apply

  val tactic_clear : Parser[ClearTac] =
    literal("clear") ~> OnceParser(natural) ^^ ClearTac.apply

  def tactic_split(implicit context:ParserContext) : Parser[CaseSplitTac] =
    literal("casesplit") ~> OnceParser(expression(GIsabelle.boolT)) ^^ CaseSplitTac

  def localUpVarId1(implicit context: ParserContext): toplevel.Parser.Parser[(Variable, Option[Int])] =
    identifier ~ (":" ~> natural).? ^^ { case x ~ i =>
      val x2 = context.environment.getProgVariable(x)
      (x2,i)
    }

  def localUpVarId(implicit context: ParserContext) : Parser[LocalUpTac.VarID] =
    (rep1sep(localUpVarId1, ",") ^^ LocalUpTac.IdxVarId.apply) |
      success(LocalUpTac.AllVars)

  def localUpSide : Parser[Option[Boolean]] =
    ("left" ^^^ true | "right" ^^^ false).?

  def progVariables(implicit context: ParserContext): Parser[List[Variable]] =
    identifierList ^^ { _.map(context.environment.getProgVariable) }

  def side : Parser[Boolean] =
    ("left" ^^^ true | "right" ^^^ false)

  def exclamOpt: Parser[Boolean] =
    (literal("!") ^^^ true) | success(false)

  def tactic_local_remove(implicit context: ParserContext): Parser[Tactic] =
    "remove" ~> OnceParser(
      (side ~ exclamOpt ~ (":" ~> progVariables).?) ^^ { case left ~ withInit ~ vs => LocalRemoveTac(left=left, withInit=withInit, variablesToRemove = vs.getOrElse(Nil)) })
//        | "joint" ^^^ LocalRemoveJointTac)

  def tactic_local_up(implicit context: ParserContext): Parser[LocalUpTac] =
    ("up" ~> OnceParser(localUpSide ~ localUpVarId)) ^^ { case side~varID => LocalUpTac(side,varID) }

  def tactic_local(implicit context: ParserContext) : Parser[Tactic] =
    literal("local") ~> OnceParser(tactic_local_remove | tactic_local_up)

  def single_var_renaming(implicit context: ParserContext) : Parser[(Variable,Variable)] =
    for (a <- identifier;
         _ <- literal("->");
         b <- identifier)
      yield (context.environment.getProgVariable(a), context.environment.getProgVariable(b))

  def var_renaming(implicit context: ParserContext): Parser[List[(Variable, Variable)]] =
    rep1sep(single_var_renaming, ",")

  def tactic_rename(implicit context: ParserContext) : Parser[RenameTac] =
    literal("rename") ~> OnceParser(for (
      (left,right) <- OnceParser("left" ^^^ (true,false) | "right" ^^^ (false,true) | "both" ^^^ (true,true));
      _ <- literal(":");
      renaming <- var_renaming)
        yield RenameTac(left,right,renaming))

  def tactic_fix : Parser[FixTac] =
    literal("fix") ~> identifier ^^ FixTac.apply

  def boolean : Parser[Boolean] =
    ("true" | "false") ^^ { case "true" => true; case "false" => false }

  def tactic_if_lr(lr: String) : Parser[IfTac] =
      OnceParser(boolean.? ^^ { trueFalse => IfTac(lr match { case "left" => true; case "right" => false },  trueFalse) })

  def tactic_if_joint : Parser[JointIfTac] =
    OnceParser(for
      (cases <- rep {
      (boolean ~ "-" ~ boolean) ^^ { case l ~ _ ~ r => (l,r) }
    })
      yield {
        if (cases.isEmpty)
          JointIfTac(List((true,true),(false,false)))
        else
          JointIfTac(cases)
      })

  def tactic_if : Parser[Tactic] =
    literal("if") ~> OnceParser(("left" | "right" | "joint") >> {
      case lr @ ("left" | "right") => tactic_if_lr(lr)
      case "joint" => tactic_if_joint
    })

  def rewrite_range: Parser[RewriteTac.Range] =
    (("left" ^^^ true) | ("right" ^^^ false)) ~ ((natural <~ "-") ~ natural).? ^^
      { case side ~ Some(start ~ end) => RewriteTac.Subseq(side, start, end)
        case side ~ None => RewriteTac.All(side) }

  def tactic_rewrite(implicit context: ParserContext): Parser[RewriteTac] =
    literal("rewrite") ~> commit(
      (rewrite_range <~ "->") ~ (rewrite_range | parenBlock ^^ (s => RewriteTac.Code(s.toBlock)))) ^^
      { case input ~ output => RewriteTac(input, output) }

  def tactic(implicit context: ParserContext): Parser[Tactic] =
    literal("admit") ^^ { _ => Admit } |
      tactic_wp |
      tactic_sp |
      tactic_swap |
      tactic_simp |
      tactic_rule |
      tactic_clear |
      literal("skip") ^^ { _ => SkipTac } |
      tactic_inline |
      tactic_seq |
      tactic_conseq |
      literal("call") ^^ { _ => ErrorTac("Call tactic was renamed. Use \"equal\" instead.") } |
      tactic_equal |
      tactic_rnd |
      tactic_byqrhl |
      tactic_split |
      tactic_case |
      tactic_fix |
      tactic_squash |
      literal("frame") ^^ { _ => FrameRuleTac } |
      literal("measure") ^^ { _ => JointMeasureTac } |
      literal("o2h") ^^ { _ => O2HTac } |
      literal("semiclassical") ^^ { _ => SemiClassicalTac } |
      literal("sym") ^^ { _ => SymTac } |
      tactic_local |
      tactic_rename |
      tactic_if |
      tactic_isa |
      tactic_rewrite

  val undo: Parser[Int] = literal("undo") ~> natural

  val qed : Parser[QedCommand] = "qed" ^^ { _ => QedCommand() }

  val debug : Parser[DebugCommand] = "debug:" ~>
    ( // "goal" ^^ { _ => DebugCommand.goals((context,goals,output) => for (g <- goals) output.println(g.toTerm(context))) } |
      "isabelle" ^^ { _ => DebugCommand.isabelle })

  val changeDirectory : Parser[ChangeDirectoryCommand] = literal("changeDirectory") ~> quotedString ^^ ChangeDirectoryCommand.apply

  val print_cmd : Parser[PrintCommand] =
    literal("print") ~> commit(fact) ^^ PrintCommand

  val cheat : Parser[CheatCommand] =
    ("cheat:file" ^^ {_ => CheatCommand(file=true)}) |
      ("cheat:proof" ^^ {_ => CheatCommand(proof=true)}) |
      ("cheat:stop" ^^ {_ => CheatCommand(stop=true)}) |
      ("cheat" ^^ {_ => CheatCommand()})

  val include : Parser[IncludeCommand] =
    "include" ~> quotedString ^^ IncludeCommand.apply

  val subgoalSelector1 : Parser[SubgoalSelector] =
    natural ~ ("-" ~> natural).? ^^ {
      case start ~ None => SubgoalSelector.Single(start)
      case start ~ Some(end) => SubgoalSelector.Range(start,end)
    }

  val subgoalSelector : Parser[SubgoalSelector] =
    rep1sep(subgoalSelector1, ",") ^^ SubgoalSelector.Union.apply

  val focus: Parser[FocusCommand] =
    ((subgoalSelector <~ ":").? ~ "[+*-]+|[{}]".r) ^^
      { case selector ~ label => FocusCommand(selector, label) }

  val nestedParens0 : Parser[Any] =
    repWithState[(Int,List[Char])]({
    case (0, chars) =>
      elem("command", { c => c != '.' && c != ')' && c != '}' }) ^^ { c =>
        val cs = c :: chars
        val lvl = if (c == '(' || c == '{') 1 else 0
        (lvl, cs)
      }
    case (level, chars) =>
      elem("command", { _ => true }) ^^ { c =>
        assert(level > 0)
        val cs = c :: chars
        val lvl = if (c == '(' || c== '{') level + 1 else if (c == ')' || c=='}') level - 1 else level
        (lvl, cs)
      }
  }, (1,Nil)) /*^^ { case (_,chars) => chars.reverse.mkString.trim }*/

  val nestedParens : Parser[Any] =
    ("[({]".r ~ nestedParens0)

  def parserOffset[A](parser: Parser[A]): Parser[Int] = Parser { in => parser(in) flatMapWithNext(_ => next => Success(next.offset, next)) }

  val commandSpan : Parser[Int] =
    parserOffset(rep("""[^.{("]""".r | nestedParens | stringLiteral | """\.\S""".r)) <~ "."

  val isabelleToplevel : Parser[IsabelleToplevelCommand] =
    literal("isabelle_cmd") ~> scanInnerSyntax ^^ IsabelleToplevelCommand.apply

  def command(implicit context:ParserContext): Parser[Command] =
    isabelleToplevel |
    debug | isabelle | variable | declareProgram | declareAdversary | qrhl | goal | (tactic ^^ TacticCommand) |
      include | qed | changeDirectory | cheat | print_cmd | focus | failure("expecting command")
}


text:
    ×

    Cancel