<commit_before>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package e2e

import (
	"fmt"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/pkg/api/unversioned"
	api "k8s.io/client-go/pkg/api/v1"
	extensions "k8s.io/client-go/pkg/apis/extensions/v1beta1"
	policy "k8s.io/client-go/pkg/apis/policy/v1beta1"
	"k8s.io/client-go/pkg/util/intstr"
	"k8s.io/kubernetes/pkg/util/wait"
	"k8s.io/kubernetes/test/e2e/framework"
)

// schedulingTimeout is longer specifically because sometimes we need to wait
// awhile to guarantee that we've been patient waiting for something ordinary
// to happen: a pod to get scheduled and move into Ready
const schedulingTimeout = 10 * time.Minute

var _ = framework.KubeDescribe("DisruptionController", func() {
	f := framework.NewDefaultFramework("disruption")
	var ns string
	var cs *kubernetes.Clientset

	BeforeEach(func() {
		// skip on GKE since alpha features are disabled
		framework.SkipIfProviderIs("gke")

		cs = f.StagingClient
		ns = f.Namespace.Name
	})

	It("should create a PodDisruptionBudget", func() {
		createPodDisruptionBudgetOrDie(cs, ns, intstr.FromString("1%"))
	})

	It("should update PodDisruptionBudget status", func() {
		createPodDisruptionBudgetOrDie(cs, ns, intstr.FromInt(2))

		createPodsOrDie(cs, ns, 3)
		waitForPodsOrDie(cs, ns, 3)

		// Since disruptionAllowed starts out 0, if we see it ever become positive,
		// that means the controller is working.
		err := wait.PollImmediate(framework.Poll, timeout, func() (bool, error) {
			pdb, err := cs.Policy().PodDisruptionBudgets(ns).Get("foo")
			if err != nil {
				return false, err
			}
			return pdb.Status.PodDisruptionsAllowed > 0, nil
		})
		Expect(err).NotTo(HaveOccurred())

	})

	evictionCases := []struct {
		description    string
		minAvailable   intstr.IntOrString
		podCount       int
		replicaSetSize int32
		shouldDeny     bool
		exclusive      bool
	}{
		{
			description:  "no PDB",
			minAvailable: intstr.FromString(""),
			podCount:     1,
			shouldDeny:   false,
		}, {
			description:  "too few pods, absolute",
			minAvailable: intstr.FromInt(2),
			podCount:     2,
			shouldDeny:   true,
		}, {
			description:  "enough pods, absolute",
			minAvailable: intstr.FromInt(2),
			podCount:     3,
			shouldDeny:   false,
		}, {
			description:    "enough pods, replicaSet, percentage",
			minAvailable:   intstr.FromString("90%"),
			replicaSetSize: 10,
			exclusive:      false,
			shouldDeny:     false,
		}, {
			description:    "too few pods, replicaSet, percentage",
			minAvailable:   intstr.FromString("90%"),
			replicaSetSize: 10,
			exclusive:      true,
			shouldDeny:     true,
		},
	}
	for i := range evictionCases {
		c := evictionCases[i]
		expectation := "should allow an eviction"
		if c.shouldDeny {
			expectation = "should not allow an eviction"
		}
		It(fmt.Sprintf("evictions: %s => %s", c.description, expectation), func() {
			createPodsOrDie(cs, ns, c.podCount)
			if c.replicaSetSize > 0 {
				createReplicaSetOrDie(cs, ns, c.replicaSetSize, c.exclusive)
			}

			if c.minAvailable.String() != "" {
				createPodDisruptionBudgetOrDie(cs, ns, c.minAvailable)
			}

			// Locate a running pod.
			var pod api.Pod
			err := wait.PollImmediate(framework.Poll, schedulingTimeout, func() (bool, error) {
				podList, err := cs.Pods(ns).List(api.ListOptions{})
				if err != nil {
					return false, err
				}

				for i := range podList.Items {
					if podList.Items[i].Status.Phase == api.PodRunning {
						pod = podList.Items[i]
						return true, nil
					}
				}

				return false, nil
			})
			Expect(err).NotTo(HaveOccurred())

			e := &policy.Eviction{
				ObjectMeta: api.ObjectMeta{
					Name:      pod.Name,
					Namespace: ns,
				},
			}

			if c.shouldDeny {
				// Since disruptionAllowed starts out false, wait at least 60s hoping that
				// this gives the controller enough time to have truly set the status.
				time.Sleep(timeout)

				err = cs.Pods(ns).Evict(e)
				Expect(err).Should(MatchError("Cannot evict pod as it would violate the pod's disruption budget."))
			} else {
				// Only wait for running pods in the "allow" case
				// because one of shouldDeny cases relies on the
				// replicaSet not fitting on the cluster.
				waitForPodsOrDie(cs, ns, c.podCount+int(c.replicaSetSize))

				// Since disruptionAllowed starts out false, if an eviction is ever allowed,
				// that means the controller is working.
				err = wait.PollImmediate(framework.Poll, timeout, func() (bool, error) {
					err = cs.Pods(ns).Evict(e)
					if err != nil {
						return false, nil
					} else {
						return true, nil
					}
				})
				Expect(err).NotTo(HaveOccurred())
			}
		})
	}

})

func createPodDisruptionBudgetOrDie(cs *kubernetes.Clientset, ns string, minAvailable intstr.IntOrString) {
	pdb := policy.PodDisruptionBudget{
		ObjectMeta: api.ObjectMeta{
			Name:      "foo",
			Namespace: ns,
		},
		Spec: policy.PodDisruptionBudgetSpec{
			Selector:     &unversioned.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
			MinAvailable: minAvailable,
		},
	}
	_, err := cs.Policy().PodDisruptionBudgets(ns).Create(&pdb)
	Expect(err).NotTo(HaveOccurred())
}

func createPodsOrDie(cs *kubernetes.Clientset, ns string, n int) {
	for i := 0; i < n; i++ {
		pod := &api.Pod{
			ObjectMeta: api.ObjectMeta{
				Name:      fmt.Sprintf("pod-%d", i),
				Namespace: ns,
				Labels:    map[string]string{"foo": "bar"},
			},
			Spec: api.PodSpec{
				Containers: []api.Container{
					{
						Name:  "busybox",
						Image: "gcr.io/google_containers/echoserver:1.4",
					},
				},
				RestartPolicy: api.RestartPolicyAlways,
			},
		}

		_, err := cs.Pods(ns).Create(pod)
		framework.ExpectNoError(err, "Creating pod %q in namespace %q", pod.Name, ns)
	}
}

func waitForPodsOrDie(cs *kubernetes.Clientset, ns string, n int) {
	By("Waiting for all pods to be running")
	err := wait.PollImmediate(framework.Poll, schedulingTimeout, func() (bool, error) {
		pods, err := cs.Core().Pods(ns).List(api.ListOptions{LabelSelector: "foo=bar"})
		if err != nil {
			return false, err
		}
		if pods == nil {
			return false, fmt.Errorf("pods is nil")
		}
		if len(pods.Items) < n {
			framework.Logf("pods: %v < %v", len(pods.Items), n)
			return false, nil
		}
		ready := 0
		for i := 0; i < n; i++ {
			if pods.Items[i].Status.Phase == api.PodRunning {
				ready++
			}
		}
		if ready < n {
			framework.Logf("running pods: %v < %v", ready, n)
			return false, nil
		}
		return true, nil
	})
	framework.ExpectNoError(err, "Waiting for pods in namespace %q to be ready", ns)
}

func createReplicaSetOrDie(cs *kubernetes.Clientset, ns string, size int32, exclusive bool) {
	container := api.Container{
		Name:  "busybox",
		Image: "gcr.io/google_containers/echoserver:1.4",
	}
	if exclusive {
		container.Ports = []api.ContainerPort{
			{HostPort: 5555, ContainerPort: 5555},
		}
	}

	rs := &extensions.ReplicaSet{
		ObjectMeta: api.ObjectMeta{
			Name:      "rs",
			Namespace: ns,
		},
		Spec: extensions.ReplicaSetSpec{
			Replicas: &size,
			Selector: &unversioned.LabelSelector{
				MatchLabels: map[string]string{"foo": "bar"},
			},
			Template: api.PodTemplateSpec{
				ObjectMeta: api.ObjectMeta{
					Labels: map[string]string{"foo": "bar"},
				},
				Spec: api.PodSpec{
					Containers: []api.Container{container},
				},
			},
		},
	}

	_, err := cs.Extensions().ReplicaSets(ns).Create(rs)
	framework.ExpectNoError(err, "Creating replica set %q in namespace %q", rs.Name, ns)
}
<commit_msg>Enable PDB e2e tests on GKE<commit_after>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package e2e

import (
	"fmt"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/pkg/api/unversioned"
	api "k8s.io/client-go/pkg/api/v1"
	extensions "k8s.io/client-go/pkg/apis/extensions/v1beta1"
	policy "k8s.io/client-go/pkg/apis/policy/v1beta1"
	"k8s.io/client-go/pkg/util/intstr"
	"k8s.io/kubernetes/pkg/util/wait"
	"k8s.io/kubernetes/test/e2e/framework"
)

// schedulingTimeout is longer specifically because sometimes we need to wait
// awhile to guarantee that we've been patient waiting for something ordinary
// to happen: a pod to get scheduled and move into Ready
const schedulingTimeout = 10 * time.Minute

var _ = framework.KubeDescribe("DisruptionController", func() {
	f := framework.NewDefaultFramework("disruption")
	var ns string
	var cs *kubernetes.Clientset

	BeforeEach(func() {
		cs = f.StagingClient
		ns = f.Namespace.Name
	})

	It("should create a PodDisruptionBudget", func() {
		createPodDisruptionBudgetOrDie(cs, ns, intstr.FromString("1%"))
	})

	It("should update PodDisruptionBudget status", func() {
		createPodDisruptionBudgetOrDie(cs, ns, intstr.FromInt(2))

		createPodsOrDie(cs, ns, 3)
		waitForPodsOrDie(cs, ns, 3)

		// Since disruptionAllowed starts out 0, if we see it ever become positive,
		// that means the controller is working.
		err := wait.PollImmediate(framework.Poll, timeout, func() (bool, error) {
			pdb, err := cs.Policy().PodDisruptionBudgets(ns).Get("foo")
			if err != nil {
				return false, err
			}
			return pdb.Status.PodDisruptionsAllowed > 0, nil
		})
		Expect(err).NotTo(HaveOccurred())

	})

	evictionCases := []struct {
		description    string
		minAvailable   intstr.IntOrString
		podCount       int
		replicaSetSize int32
		shouldDeny     bool
		exclusive      bool
	}{
		{
			description:  "no PDB",
			minAvailable: intstr.FromString(""),
			podCount:     1,
			shouldDeny:   false,
		}, {
			description:  "too few pods, absolute",
			minAvailable: intstr.FromInt(2),
			podCount:     2,
			shouldDeny:   true,
		}, {
			description:  "enough pods, absolute",
			minAvailable: intstr.FromInt(2),
			podCount:     3,
			shouldDeny:   false,
		}, {
			description:    "enough pods, replicaSet, percentage",
			minAvailable:   intstr.FromString("90%"),
			replicaSetSize: 10,
			exclusive:      false,
			shouldDeny:     false,
		}, {
			description:    "too few pods, replicaSet, percentage",
			minAvailable:   intstr.FromString("90%"),
			replicaSetSize: 10,
			exclusive:      true,
			shouldDeny:     true,
		},
	}
	for i := range evictionCases {
		c := evictionCases[i]
		expectation := "should allow an eviction"
		if c.shouldDeny {
			expectation = "should not allow an eviction"
		}
		It(fmt.Sprintf("evictions: %s => %s", c.description, expectation), func() {
			createPodsOrDie(cs, ns, c.podCount)
			if c.replicaSetSize > 0 {
				createReplicaSetOrDie(cs, ns, c.replicaSetSize, c.exclusive)
			}

			if c.minAvailable.String() != "" {
				createPodDisruptionBudgetOrDie(cs, ns, c.minAvailable)
			}

			// Locate a running pod.
			var pod api.Pod
			err := wait.PollImmediate(framework.Poll, schedulingTimeout, func() (bool, error) {
				podList, err := cs.Pods(ns).List(api.ListOptions{})
				if err != nil {
					return false, err
				}

				for i := range podList.Items {
					if podList.Items[i].Status.Phase == api.PodRunning {
						pod = podList.Items[i]
						return true, nil
					}
				}

				return false, nil
			})
			Expect(err).NotTo(HaveOccurred())

			e := &policy.Eviction{
				ObjectMeta: api.ObjectMeta{
					Name:      pod.Name,
					Namespace: ns,
				},
			}

			if c.shouldDeny {
				// Since disruptionAllowed starts out false, wait at least 60s hoping that
				// this gives the controller enough time to have truly set the status.
				time.Sleep(timeout)

				err = cs.Pods(ns).Evict(e)
				Expect(err).Should(MatchError("Cannot evict pod as it would violate the pod's disruption budget."))
			} else {
				// Only wait for running pods in the "allow" case
				// because one of shouldDeny cases relies on the
				// replicaSet not fitting on the cluster.
				waitForPodsOrDie(cs, ns, c.podCount+int(c.replicaSetSize))

				// Since disruptionAllowed starts out false, if an eviction is ever allowed,
				// that means the controller is working.
				err = wait.PollImmediate(framework.Poll, timeout, func() (bool, error) {
					err = cs.Pods(ns).Evict(e)
					if err != nil {
						return false, nil
					} else {
						return true, nil
					}
				})
				Expect(err).NotTo(HaveOccurred())
			}
		})
	}

})

func createPodDisruptionBudgetOrDie(cs *kubernetes.Clientset, ns string, minAvailable intstr.IntOrString) {
	pdb := policy.PodDisruptionBudget{
		ObjectMeta: api.ObjectMeta{
			Name:      "foo",
			Namespace: ns,
		},
		Spec: policy.PodDisruptionBudgetSpec{
			Selector:     &unversioned.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
			MinAvailable: minAvailable,
		},
	}
	_, err := cs.Policy().PodDisruptionBudgets(ns).Create(&pdb)
	Expect(err).NotTo(HaveOccurred())
}

func createPodsOrDie(cs *kubernetes.Clientset, ns string, n int) {
	for i := 0; i < n; i++ {
		pod := &api.Pod{
			ObjectMeta: api.ObjectMeta{
				Name:      fmt.Sprintf("pod-%d", i),
				Namespace: ns,
				Labels:    map[string]string{"foo": "bar"},
			},
			Spec: api.PodSpec{
				Containers: []api.Container{
					{
						Name:  "busybox",
						Image: "gcr.io/google_containers/echoserver:1.4",
					},
				},
				RestartPolicy: api.RestartPolicyAlways,
			},
		}

		_, err := cs.Pods(ns).Create(pod)
		framework.ExpectNoError(err, "Creating pod %q in namespace %q", pod.Name, ns)
	}
}

func waitForPodsOrDie(cs *kubernetes.Clientset, ns string, n int) {
	By("Waiting for all pods to be running")
	err := wait.PollImmediate(framework.Poll, schedulingTimeout, func() (bool, error) {
		pods, err := cs.Core().Pods(ns).List(api.ListOptions{LabelSelector: "foo=bar"})
		if err != nil {
			return false, err
		}
		if pods == nil {
			return false, fmt.Errorf("pods is nil")
		}
		if len(pods.Items) < n {
			framework.Logf("pods: %v < %v", len(pods.Items), n)
			return false, nil
		}
		ready := 0
		for i := 0; i < n; i++ {
			if pods.Items[i].Status.Phase == api.PodRunning {
				ready++
			}
		}
		if ready < n {
			framework.Logf("running pods: %v < %v", ready, n)
			return false, nil
		}
		return true, nil
	})
	framework.ExpectNoError(err, "Waiting for pods in namespace %q to be ready", ns)
}

func createReplicaSetOrDie(cs *kubernetes.Clientset, ns string, size int32, exclusive bool) {
	container := api.Container{
		Name:  "busybox",
		Image: "gcr.io/google_containers/echoserver:1.4",
	}
	if exclusive {
		container.Ports = []api.ContainerPort{
			{HostPort: 5555, ContainerPort: 5555},
		}
	}

	rs := &extensions.ReplicaSet{
		ObjectMeta: api.ObjectMeta{
			Name:      "rs",
			Namespace: ns,
		},
		Spec: extensions.ReplicaSetSpec{
			Replicas: &size,
			Selector: &unversioned.LabelSelector{
				MatchLabels: map[string]string{"foo": "bar"},
			},
			Template: api.PodTemplateSpec{
				ObjectMeta: api.ObjectMeta{
					Labels: map[string]string{"foo": "bar"},
				},
				Spec: api.PodSpec{
					Containers: []api.Container{container},
				},
			},
		},
	}

	_, err := cs.Extensions().ReplicaSets(ns).Create(rs)
	framework.ExpectNoError(err, "Creating replica set %q in namespace %q", rs.Name, ns)
}
<|endoftext|>


<commit_before>//go:build e2e
// +build e2e

/*
Copyright 2020 The Knative Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package e2e

import (
	"context"
	"net/http"
	"testing"

	pkgTest "knative.dev/pkg/test"
	"knative.dev/pkg/test/spoof"
	rtesting "knative.dev/serving/pkg/testing/v1"
	"knative.dev/serving/test"
	v1test "knative.dev/serving/test/v1"
)

// TestHelloHTTP2WithPortNameH2C validates that an http/2-only service can be
// reached if the portName is "h2c".
func TestHelloHTTP2WithPortNameH2C(t *testing.T) {
	t.Parallel()

	clients := Setup(t)

	// hellohttp2 returns client errors (4xx) if contacted via http1.1,
	// and behaves like helloworld if called with http/2.
	names := test.ResourceNames{
		Service: test.ObjectNameForTest(t),
		Image:   test.HelloHTTP2,
	}

	test.EnsureTearDown(t, clients, &names)

	t.Log("Creating a new Service")

	resources, err := v1test.CreateServiceReady(t, clients, &names, rtesting.WithNamedPort("h2c"))
	if err != nil {
		t.Fatalf("Failed to create initial Service: %v: %v", names.Service, err)
	}

	url := resources.Route.Status.URL.URL()
	if _, err := pkgTest.CheckEndpointState(
		context.Background(),
		clients.KubeClient,
		t.Logf,
		url,
		spoof.MatchesAllOf(spoof.IsStatusOK, spoof.MatchesBody(test.HelloHTTP2Text)),
		"HelloHTTP2ServesTextOnH2C",
		test.ServingFlags.ResolvableDomain,
		test.AddRootCAtoTransport(context.Background(), t.Logf, clients, test.ServingFlags.HTTPS),
	); err != nil {
		t.Fatalf("The endpoint %s for Route %s didn't serve the expected text %q: %v", url, names.Route, test.HelloHTTP2Text, err)
	}
}

// TestHelloHTTP2WithEmptyPortName validates that an http/2-only service
// is unreachable if the port name is not specified.
// TODO(knative/serving#4283): Once the feature is implemented, this test
// should succeed.
func TestHelloHTTP2WithEmptyPortName(t *testing.T) {
	t.Parallel()

	clients := Setup(t)

	// hellohttp2 returns client errors (4xx) if contacted via http1.1,
	// and behaves like helloworld if called with http/2.
	names := test.ResourceNames{
		Service: test.ObjectNameForTest(t),
		Image:   test.HelloHTTP2,
	}

	test.EnsureTearDown(t, clients, &names)

	t.Log("Creating a new Service")

	resources, err := v1test.CreateServiceReady(t, clients, &names, rtesting.WithNamedPort(""))
	if err != nil {
		t.Fatalf("Failed to create initial Service: %v: %v", names.Service, err)
	}

	url := resources.Route.Status.URL.URL()
	if _, err := pkgTest.CheckEndpointState(
		context.Background(),
		clients.KubeClient,
		t.Logf,
		url,
		spoof.MatchesAllOf(spoof.IsOneOfStatusCodes(http.StatusUpgradeRequired)),
		"HelloHTTP2ServesTextWithEmptyPort",
		test.ServingFlags.ResolvableDomain,
		test.AddRootCAtoTransport(context.Background(), t.Logf, clients, test.ServingFlags.HTTPS),
	); err != nil {
		t.Fatalf("The endpoint %s for Route %s didn't serve the expected status code %v: %v", url, names.Route, http.StatusUpgradeRequired, err)
	}

	t.Skip("HTP2 with empty port name is not implemented yet. See: https://github.com/knative/serving/issues/4283")
}
<commit_msg>skip http2 test early (#13047)<commit_after>//go:build e2e
// +build e2e

/*
Copyright 2020 The Knative Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package e2e

import (
	"context"
	"net/http"
	"testing"

	pkgTest "knative.dev/pkg/test"
	"knative.dev/pkg/test/spoof"
	rtesting "knative.dev/serving/pkg/testing/v1"
	"knative.dev/serving/test"
	v1test "knative.dev/serving/test/v1"
)

// TestHelloHTTP2WithPortNameH2C validates that an http/2-only service can be
// reached if the portName is "h2c".
func TestHelloHTTP2WithPortNameH2C(t *testing.T) {
	t.Parallel()

	clients := Setup(t)

	// hellohttp2 returns client errors (4xx) if contacted via http1.1,
	// and behaves like helloworld if called with http/2.
	names := test.ResourceNames{
		Service: test.ObjectNameForTest(t),
		Image:   test.HelloHTTP2,
	}

	test.EnsureTearDown(t, clients, &names)

	t.Log("Creating a new Service")

	resources, err := v1test.CreateServiceReady(t, clients, &names, rtesting.WithNamedPort("h2c"))
	if err != nil {
		t.Fatalf("Failed to create initial Service: %v: %v", names.Service, err)
	}

	url := resources.Route.Status.URL.URL()
	if _, err := pkgTest.CheckEndpointState(
		context.Background(),
		clients.KubeClient,
		t.Logf,
		url,
		spoof.MatchesAllOf(spoof.IsStatusOK, spoof.MatchesBody(test.HelloHTTP2Text)),
		"HelloHTTP2ServesTextOnH2C",
		test.ServingFlags.ResolvableDomain,
		test.AddRootCAtoTransport(context.Background(), t.Logf, clients, test.ServingFlags.HTTPS),
	); err != nil {
		t.Fatalf("The endpoint %s for Route %s didn't serve the expected text %q: %v", url, names.Route, test.HelloHTTP2Text, err)
	}
}

// TestHelloHTTP2WithEmptyPortName validates that an http/2-only service
// is unreachable if the port name is not specified.
// TODO(knative/serving#4283): Once the feature is implemented, this test
// should succeed.
func TestHelloHTTP2WithEmptyPortName(t *testing.T) {
	t.Parallel()
	t.Skip("HTTP2 with empty port name is not implemented yet. See: https://github.com/knative/serving/issues/4283")

	clients := Setup(t)

	// hellohttp2 returns client errors (4xx) if contacted via http1.1,
	// and behaves like helloworld if called with http/2.
	names := test.ResourceNames{
		Service: test.ObjectNameForTest(t),
		Image:   test.HelloHTTP2,
	}

	test.EnsureTearDown(t, clients, &names)

	t.Log("Creating a new Service")

	resources, err := v1test.CreateServiceReady(t, clients, &names, rtesting.WithNamedPort(""))
	if err != nil {
		t.Fatalf("Failed to create initial Service: %v: %v", names.Service, err)
	}

	url := resources.Route.Status.URL.URL()
	if _, err := pkgTest.CheckEndpointState(
		context.Background(),
		clients.KubeClient,
		t.Logf,
		url,
		spoof.MatchesAllOf(spoof.IsOneOfStatusCodes(http.StatusUpgradeRequired)),
		"HelloHTTP2ServesTextWithEmptyPort",
		test.ServingFlags.ResolvableDomain,
		test.AddRootCAtoTransport(context.Background(), t.Logf, clients, test.ServingFlags.HTTPS),
	); err != nil {
		t.Fatalf("The endpoint %s for Route %s didn't serve the expected status code %v: %v", url, names.Route, http.StatusUpgradeRequired, err)
	}

}
<|endoftext|>


<commit_before>// Copyright 2015, 2016 Eris Industries (UK) Ltd.
// This file is part of Eris-RT

// Eris-RT is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Eris-RT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Eris-RT.  If not, see <http://www.gnu.org/licenses/>.

// version provides the current Eris-DB version and a VersionIdentifier
// for the modules to identify their version with.

package version

import (
	"fmt"
)

// IMPORTANT: this version number needs to be manually kept
// in sync at the bottom of this file for the deployment scripts to parse
// the version number.
const (
	// Client identifier to advertise over the network
	erisClientIdentifier = "eris-db"
	// Major version component of the current release
	erisVersionMajor = 0
	// Minor version component of the current release
	erisVersionMinor = 12
	// Patch version component of the current release
	erisVersionPatch = 0
)

var erisVersion *VersionIdentifier

func init() {
	erisVersion = New(erisClientIdentifier, erisVersionMajor,
		erisVersionMinor, erisVersionPatch)
}

//------------------------------------------------------------------------------
// versioning globally for Eris-DB and scoped for modules

type VersionIdentifier struct {
	clientIdentifier string
	versionMajor     uint8
	versionMinor     uint8
	versionPatch     uint8
}

func New(client string, major, minor, patch uint8) *VersionIdentifier {
	v := new(VersionIdentifier)
	v.clientIdentifier = client
	v.versionMajor = major
	v.versionMinor = minor
	v.versionPatch = patch
	return v
}

// GetVersionString returns `client-major.minor.patch` for Eris-DB
// without a receiver, or for the version called on.
// MakeVersionString builds the same version string with provided parameters.
func GetVersionString() string { return erisVersion.GetVersionString() }
func (v *VersionIdentifier) GetVersionString() string {
	return fmt.Sprintf("%s-%d.%d.%d", v.clientIdentifier, v.versionMajor,
		v.versionMinor, v.versionPatch)
}

// note: the arguments are passed in as int (rather than uint8)
// because on asserting the version constructed from the configuration file
// the casting of an int to uint8 is uglier than expanding the type range here.
// Should the configuration file have an invalid integer (that could not convert)
// then this will equally be reflected in a failed assertion of the version string.
func MakeVersionString(client string, major, minor, patch int) string {
	return fmt.Sprintf("%s-%d.%d.%d", client, major, minor, patch)
}

// GetMinorVersionString returns `client-major.minor` for Eris-DB
// without a receiver, or for the version called on.
// MakeMinorVersionString builds the same version string with
// provided parameters.
func GetMinorVersionString() string { return erisVersion.GetVersionString() }
func (v *VersionIdentifier) GetMinorVersionString() string {
	return fmt.Sprintf("%s-%d.%d", v.clientIdentifier, v.versionMajor,
		v.versionMinor)
}

// note: similar remark applies here on the use of `int` over `uint8`
// for the arguments as above for MakeVersionString()
func MakeMinorVersionString(client string, major, minor, patch int) string {
	return fmt.Sprintf("%s-%d.%d", client, major, minor)
}

// GetVersion returns a tuple of client, major, minor, and patch as types,
// either for Eris-DB without a receiver or the called version structure.
func GetVersion() (client string, major, minor, patch uint8) {
	return erisVersion.GetVersion()
}
func (version *VersionIdentifier) GetVersion() (
	client string, major, minor, patch uint8) {
	return version.clientIdentifier, version.versionMajor, version.versionMinor,
		version.versionPatch
}

//------------------------------------------------------------------------------
// Matching functions

// MatchesMinorVersion matches the client identifier, major and minor version
// number of the reference version identifier to be equal with the receivers.
func MatchesMinorVersion(referenceVersion *VersionIdentifier) bool {
	return erisVersion.MatchesMinorVersion(referenceVersion)
}
func (version *VersionIdentifier) MatchesMinorVersion(
	referenceVersion *VersionIdentifier) bool {
	referenceClient, referenceMajor, referenceMinor, _ := referenceVersion.GetVersion()
	return version.clientIdentifier == referenceClient &&
		version.versionMajor == referenceMajor &&
		version.versionMinor == referenceMinor
}

//------------------------------------------------------------------------------
// Version number for tests/build_tool.sh

// IMPORTANT: Eris-DB version must be on the last line of this file for
// the deployment script tests/build_tool.sh to pick up the right label.
const VERSION = "0.12.0-rc3"
<commit_msg>Bump version, but label as development version<commit_after>// Copyright 2015, 2016 Eris Industries (UK) Ltd.
// This file is part of Eris-RT

// Eris-RT is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Eris-RT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Eris-RT.  If not, see <http://www.gnu.org/licenses/>.

// version provides the current Eris-DB version and a VersionIdentifier
// for the modules to identify their version with.

package version

import (
	"fmt"
)

// IMPORTANT: this version number needs to be manually kept
// in sync at the bottom of this file for the deployment scripts to parse
// the version number.
const (
	// Client identifier to advertise over the network
	erisClientIdentifier = "eris-db"
	// Major version component of the current release
	erisVersionMajor = 0
	// Minor version component of the current release
	erisVersionMinor = 12
	// Patch version component of the current release
	erisVersionPatch = 0
)

var erisVersion *VersionIdentifier

func init() {
	erisVersion = New(erisClientIdentifier, erisVersionMajor,
		erisVersionMinor, erisVersionPatch)
}

//------------------------------------------------------------------------------
// versioning globally for Eris-DB and scoped for modules

type VersionIdentifier struct {
	clientIdentifier string
	versionMajor     uint8
	versionMinor     uint8
	versionPatch     uint8
}

func New(client string, major, minor, patch uint8) *VersionIdentifier {
	v := new(VersionIdentifier)
	v.clientIdentifier = client
	v.versionMajor = major
	v.versionMinor = minor
	v.versionPatch = patch
	return v
}

// GetVersionString returns `client-major.minor.patch` for Eris-DB
// without a receiver, or for the version called on.
// MakeVersionString builds the same version string with provided parameters.
func GetVersionString() string { return erisVersion.GetVersionString() }
func (v *VersionIdentifier) GetVersionString() string {
	return fmt.Sprintf("%s-%d.%d.%d", v.clientIdentifier, v.versionMajor,
		v.versionMinor, v.versionPatch)
}

// note: the arguments are passed in as int (rather than uint8)
// because on asserting the version constructed from the configuration file
// the casting of an int to uint8 is uglier than expanding the type range here.
// Should the configuration file have an invalid integer (that could not convert)
// then this will equally be reflected in a failed assertion of the version string.
func MakeVersionString(client string, major, minor, patch int) string {
	return fmt.Sprintf("%s-%d.%d.%d", client, major, minor, patch)
}

// GetMinorVersionString returns `client-major.minor` for Eris-DB
// without a receiver, or for the version called on.
// MakeMinorVersionString builds the same version string with
// provided parameters.
func GetMinorVersionString() string { return erisVersion.GetVersionString() }
func (v *VersionIdentifier) GetMinorVersionString() string {
	return fmt.Sprintf("%s-%d.%d", v.clientIdentifier, v.versionMajor,
		v.versionMinor)
}

// note: similar remark applies here on the use of `int` over `uint8`
// for the arguments as above for MakeVersionString()
func MakeMinorVersionString(client string, major, minor, patch int) string {
	return fmt.Sprintf("%s-%d.%d", client, major, minor)
}

// GetVersion returns a tuple of client, major, minor, and patch as types,
// either for Eris-DB without a receiver or the called version structure.
func GetVersion() (client string, major, minor, patch uint8) {
	return erisVersion.GetVersion()
}
func (version *VersionIdentifier) GetVersion() (
	client string, major, minor, patch uint8) {
	return version.clientIdentifier, version.versionMajor, version.versionMinor,
		version.versionPatch
}

//------------------------------------------------------------------------------
// Matching functions

// MatchesMinorVersion matches the client identifier, major and minor version
// number of the reference version identifier to be equal with the receivers.
func MatchesMinorVersion(referenceVersion *VersionIdentifier) bool {
	return erisVersion.MatchesMinorVersion(referenceVersion)
}
func (version *VersionIdentifier) MatchesMinorVersion(
	referenceVersion *VersionIdentifier) bool {
	referenceClient, referenceMajor, referenceMinor, _ := referenceVersion.GetVersion()
	return version.clientIdentifier == referenceClient &&
		version.versionMajor == referenceMajor &&
		version.versionMinor == referenceMinor
}

//------------------------------------------------------------------------------
// Version number for tests/build_tool.sh

// IMPORTANT: Eris-DB version must be on the last line of this file for
// the deployment script tests/build_tool.sh to pick up the right label.
const VERSION = "0.12.0-develop"
<|endoftext|>


<commit_before>package version

const Version = "0.2.2"
<commit_msg>bump version to 0.3.0<commit_after>package version

const Version = "0.3.0"
<|endoftext|>


<commit_before>// Copyright 2014 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package version

const Version = "0.4.2"
<commit_msg>version: bump to 0.4.2+git<commit_after>// Copyright 2014 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package version

const Version = "0.4.2+git"
<|endoftext|>


<commit_before>// Copyright 2014 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package version

import (
	"github.com/coreos/fleet/Godeps/_workspace/src/github.com/coreos/go-semver/semver"
)

const Version = "0.10.0"

var SemVersion semver.Version

func init() {
	sv, err := semver.NewVersion(Version)
	if err != nil {
		panic("bad version string!")
	}
	SemVersion = *sv
}
<commit_msg>version: bump to v0.10.0+git<commit_after>// Copyright 2014 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package version

import (
	"github.com/coreos/fleet/Godeps/_workspace/src/github.com/coreos/go-semver/semver"
)

const Version = "0.10.0+git"

var SemVersion semver.Version

func init() {
	sv, err := semver.NewVersion(Version)
	if err != nil {
		panic("bad version string!")
	}
	SemVersion = *sv
}
<|endoftext|>


<commit_before>package version

import "fmt"

var (
	// Version return version no
	Version = "0.0.8"
	// GitCommit return Git commit
	GitCommit = "HEAD"
)

// FullVersion return full version string
func FullVersion() string {
	return fmt.Sprintf("%s, build %s", Version, GitCommit)
}
<commit_msg>change version.go<commit_after>package version

import "fmt"

var (
	// Version return version no
	Version = "0.0.8-p1"
	// GitCommit return Git commit
	GitCommit = "HEAD"
)

// FullVersion return full version string
func FullVersion() string {
	return fmt.Sprintf("%s, build %s", Version, GitCommit)
}
<|endoftext|>


<commit_before>package version

var (
	// Package is filled at linking time
	Package = "github.com/containerd/containerd"

	// Version holds the complete version number. Filled in at linking time.
	Version = "1.0.0-alpha5+unknown"

	// Revision is filled with the VCS (e.g. git) revision being used to build
	// the program at linking time.
	Revision = ""
)
<commit_msg>release: updated version to alpha6<commit_after>package version

var (
	// Package is filled at linking time
	Package = "github.com/containerd/containerd"

	// Version holds the complete version number. Filled in at linking time.
	Version = "1.0.0-alpha6+unknown"

	// Revision is filled with the VCS (e.g. git) revision being used to build
	// the program at linking time.
	Revision = ""
)
<|endoftext|>


<commit_before>package version

import (
	"fmt"

	"github.com/github/hub/git"
)

var Version = "2.7.0"

func FullVersion() (string, error) {
	gitVersion, err := git.Version()
	if err != nil {
		gitVersion = "git version (unavailable)"
	}
	return fmt.Sprintf("%s\nhub version %s", gitVersion, Version), err
}
<commit_msg>hub 2.8.0<commit_after>package version

import (
	"fmt"

	"github.com/github/hub/git"
)

var Version = "2.8.0"

func FullVersion() (string, error) {
	gitVersion, err := git.Version()
	if err != nil {
		gitVersion = "git version (unavailable)"
	}
	return fmt.Sprintf("%s\nhub version %s", gitVersion, Version), err
}
<|endoftext|>


<commit_before>package version

// NOTE: we should probably be versioning the ABCI and the abci-cli separately

const Maj = "0"
const Min = "10"
const Fix = "2"

const Version = "0.10.2"
<commit_msg>version bump<commit_after>package version

// NOTE: we should probably be versioning the ABCI and the abci-cli separately

const Maj = "0"
const Min = "10"
const Fix = "3"

const Version = "0.10.3-dev"
<|endoftext|>


<commit_before>package avm

import (
	"bytes"
	"errors"
	"fmt"
	"net/http"

	"github.com/ava-labs/avalanchego/api"
	"github.com/ava-labs/avalanchego/database"
	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/utils/json"
	"github.com/ava-labs/avalanchego/utils/logging"
	"github.com/ava-labs/avalanchego/utils/math"
	"github.com/ava-labs/avalanchego/utils/wrappers"
)

const (
	// Maximum number of transaction IDs that can be fetched at a time in
	// GetAcceptedTxIDRange
	maxFetchedByRange = 4096
)

var (
	errNoTxAtThatIndex = errors.New("no accepted transaction at that index")
	errNoTxs           = errors.New("no transactions have been accepted")
	indexDbPrefix      = []byte("index")

	// Maps to the byte representation of the next accepted index
	nextAcceptedIndexKey []byte = []byte("next")
)

// indexer indexes all accepted transactions by the order in which they were accepted
type indexer struct {
	// The index of the next accepted transaction
	nextAcceptedIndex uint64
	db                database.Database
	log               logging.Logger
}

func newIndexer(db database.Database, log logging.Logger) (*indexer, error) {
	i := &indexer{
		db:  db,
		log: log,
	}
	// Find out what the next accepted index is
	nextAcceptedIndexBytes, err := i.db.Get(nextAcceptedIndexKey)
	if err == database.ErrNotFound {
		// Couldn't find it in the database. Must not have accepted any txs in previous runs.
		i.log.Info("next accepted index %d", i.nextAcceptedIndex)
		return i, nil
	} else if err != nil {
		return nil, fmt.Errorf("couldn't get next accepted index from database: %w", err)
	}
	p := wrappers.Packer{Bytes: nextAcceptedIndexBytes}
	i.nextAcceptedIndex = p.UnpackLong()
	if p.Err != nil {
		return nil, fmt.Errorf("couldn't parse next accepted index from bytes: %w", err)
	}
	i.log.Info("next accepted index %d", i.nextAcceptedIndex)
	return i, nil
}

// Index that the given transaction is accepted
// Returned error should be treated as fatal
func (i *indexer) markAccepted(txID ids.ID) error {
	batch := i.db.NewBatch()

	p := wrappers.Packer{MaxSize: wrappers.LongLen}
	p.PackLong(i.nextAcceptedIndex)
	if p.Err != nil {
		return fmt.Errorf("couldn't convert next accepted index to bytes: %w", p.Err)
	}
	// Next accepted index --> tx at that index
	err := batch.Put(p.Bytes, txID[:])
	if err != nil {
		return fmt.Errorf("couldn't put accepted transaction %s into index: %w", txID, err)
	}

	// Increment the next accepted index and persist last accepted index
	i.nextAcceptedIndex++
	p = wrappers.Packer{MaxSize: wrappers.LongLen}
	p.PackLong(i.nextAcceptedIndex)
	if p.Err != nil {
		return fmt.Errorf("couldn't convert next accepted index to bytes: %w", p.Err)
	}
	err = batch.Put(nextAcceptedIndexKey, p.Bytes)
	if err != nil {
		return fmt.Errorf("couldn't put accepted transaction %s into index: %w", txID, err)
	}

	// Write to underlying data store
	err = batch.Write()
	if err != nil {
		return fmt.Errorf("couldn't write batch: %w", err)
	}
	return nil
}

// Returns the [index]th accepted transaction
// For example, if [index] == 0, returns the first accepted transaction
// If [index] == 1, returns the second accepted transaction, etc.
func (i *indexer) getAcceptedTxByIndex(index uint64) (ids.ID, error) {
	lastAcceptedIndex, ok := i.lastAcceptedIndex()
	if !ok || index > lastAcceptedIndex {
		return ids.Empty, errNoTxAtThatIndex
	}

	p := wrappers.Packer{MaxSize: wrappers.LongLen}
	p.PackLong(index)
	if p.Err != nil {
		return ids.Empty, fmt.Errorf("couldn't convert index to bytes: %w", p.Err)
	}

	txIDBytes, err := i.db.Get(p.Bytes)
	switch {
	case err == database.ErrNotFound:
		return ids.Empty, errNoTxAtThatIndex
	case err != nil:
		i.log.Error("couldn't read transaction ID from database: %w", err)
		return ids.Empty, fmt.Errorf("couldn't read from database: %w", err)
	case len(txIDBytes) != 32:
		// Should never happen
		i.log.Error("expected tx ID to be 32 bytes but is %d", len(txIDBytes))
		return ids.Empty, fmt.Errorf("expected tx ID to be 32 bytes but is %d", len(txIDBytes))
	}
	var txID ids.ID
	copy(txID[:], txIDBytes)
	return txID, nil
}

// Returns:
// 1) The index of the most recently accepted transaction,
//    or 0 if no transactions have been accepted
// 2) Whether at least 1 transaction has been accepted
func (i *indexer) lastAcceptedIndex() (uint64, bool) {
	if i.nextAcceptedIndex == 0 {
		return 0, false
	}
	return i.nextAcceptedIndex - 1, true
}

// Return the transactions at index [start], [start+1], ... , [start+n-1]
// If [n] == 0, returns nil, nil
// If [n] > [maxFetchedByRange], returns an error
// If the [start] > the last accepted index, returns an error (unless the above applies)
// If we run out of transactions, returns the ones fetched before running out
func (i *indexer) getAcceptedTxRange(startIndex uint64, n uint64) ([]ids.ID, error) {
	// Check arguments for validity
	if n == 0 {
		return nil, nil
	} else if n > maxFetchedByRange {
		return nil, fmt.Errorf("requested %d but maximum page size is %d", n, maxFetchedByRange)
	}
	lastAcceptedIndex, ok := i.lastAcceptedIndex()
	if !ok {
		return nil, fmt.Errorf("start index (%d) > last accepted index (-1)", startIndex)
	} else if startIndex > lastAcceptedIndex {
		return nil, fmt.Errorf("start index (%d) > last accepted index (%d)", startIndex, lastAcceptedIndex)
	}

	// Convert start index to bytes
	p := wrappers.Packer{MaxSize: wrappers.LongLen}
	p.PackLong(startIndex)
	if p.Err != nil {
		return nil, fmt.Errorf("couldn't convert start index to bytes: %w", p.Err)
	}

	// Calculate the size of the needed slice
	var txIDs []ids.ID
	lastIndex := math.Min64(startIndex+n, lastAcceptedIndex)
	// [lastIndex] is always >= [startIndex] so this is safe.
	// [n] is limited to [maxFetchedByRange] so [txIDs] can't be crazy big.
	txIDs = make([]ids.ID, 0, int(lastIndex)-int(startIndex)+1)

	iter := i.db.NewIteratorWithStart(p.Bytes)
	defer iter.Release()

	for len(txIDs) < int(n) && iter.Next() {
		txIDBytes := iter.Value()
		if len(txIDBytes) != 32 {
			if bytes.Equal(txIDBytes, nextAcceptedIndexKey) {
				// Ignore the one non-index value in the database
				continue
			}
			// Should never happen
			return nil, fmt.Errorf("expected tx ID to be 32 bytes but is %d", len(txIDBytes))
		}
		var txID ids.ID
		copy(txID[:], txIDBytes)
		txIDs = append(txIDs, txID)
	}
	return txIDs, nil
}

// IndexerService returns data about accepted transactions
type IndexerService struct {
	indexer *indexer
}

// GetAcceptedTxByIndexArgs ...
type GetAcceptedTxIDByIndexArgs struct {
	Index json.Uint64 `json:"index"`
}

// GetAcceptedTxIDByIndex returns the ID of the transaction at [args.Index]
func (is *IndexerService) GetAcceptedTxIDByIndex(r *http.Request, args *GetAcceptedTxIDByIndexArgs, reply *api.JSONTxID) error {
	txID, err := is.indexer.getAcceptedTxByIndex(uint64(args.Index))
	if err != nil {
		return err
	}
	if txID == ids.Empty {
		return errNoTxAtThatIndex
	}
	reply.TxID = txID
	return nil
}

// LastAcceptedIndexReply ...
type LastAcceptedIndexReply struct {
	Index json.Uint64 `json:"index"`
}

// LastAcceptedIndex returns the index of the most recently accepted transaction.
// This is 1 less than the total number of accepted transactions.
// Returns [errNoTxs] if no transactions have been accepted.
func (is *IndexerService) LastAcceptedIndex(r *http.Request, args *GetAcceptedTxIDByIndexArgs, reply *LastAcceptedIndexReply) error {
	lastIndex, ok := is.indexer.lastAcceptedIndex()
	if !ok {
		return errNoTxs
	}
	reply.Index = json.Uint64(lastIndex)
	return nil
}

// GetAcceptedTxIDRangeArgs ...
type GetAcceptedTxIDRangeArgs struct {
	StartIndex json.Uint64 `json:"index"`
	NumToFetch json.Uint64 `json:"numToFetch"`
}

// GetAcceptedTxIDRangeArgs ...
type GetAcceptedTxIDRangeReply struct {
	TxIDs []struct {
		json.Uint64 `json:"index"`
		ids.ID      `json:"txID"`
	} `json:"txs"`
}

// GetAcceptedTxIDByIndex returns the transactions at index [startIndex], [startIndex+1], ... , [startIndex+n-1]
// If [n] == 0, returns an empty response (i.e. null).
// If [startIndex] > the last accepted index, returns an error (unless the above apply.)
// If [n] > [maxFetchedByRange], returns an error.
// If we run out of transactions, returns the ones fetched before running out.
func (is *IndexerService) GetAcceptedTxIDRange(r *http.Request, args *GetAcceptedTxIDRangeArgs, reply *GetAcceptedTxIDRangeReply) error {
	txIDs, err := is.indexer.getAcceptedTxRange(uint64(args.StartIndex), uint64(args.NumToFetch))
	if err != nil {
		return err
	}
	nextIndex := args.StartIndex
	for _, txID := range txIDs {
		reply.TxIDs = append(
			reply.TxIDs,
			struct {
				json.Uint64 `json:"index"`
				ids.ID      `json:"txID"`
			}{
				nextIndex,
				txID,
			},
		)
		nextIndex++
	}
	return nil
}
<commit_msg>fix bug in iteration over range<commit_after>package avm

import (
	"bytes"
	"errors"
	"fmt"
	"net/http"

	"github.com/ava-labs/avalanchego/api"
	"github.com/ava-labs/avalanchego/database"
	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/utils/json"
	"github.com/ava-labs/avalanchego/utils/logging"
	"github.com/ava-labs/avalanchego/utils/math"
	"github.com/ava-labs/avalanchego/utils/wrappers"
)

const (
	// Maximum number of transaction IDs that can be fetched at a time in
	// GetAcceptedTxIDRange
	maxFetchedByRange = 4096
)

var (
	errNoTxAtThatIndex = errors.New("no accepted transaction at that index")
	errNoTxs           = errors.New("no transactions have been accepted")
	indexDbPrefix      = []byte("index")

	// Maps to the byte representation of the next accepted index
	nextAcceptedIndexKey []byte = []byte("next")
)

// indexer indexes all accepted transactions by the order in which they were accepted
type indexer struct {
	// The index of the next accepted transaction
	nextAcceptedIndex uint64
	db                database.Database
	log               logging.Logger
}

func newIndexer(db database.Database, log logging.Logger) (*indexer, error) {
	i := &indexer{
		db:  db,
		log: log,
	}
	// Find out what the next accepted index is
	nextAcceptedIndexBytes, err := i.db.Get(nextAcceptedIndexKey)
	if err == database.ErrNotFound {
		// Couldn't find it in the database. Must not have accepted any txs in previous runs.
		i.log.Info("next accepted index %d", i.nextAcceptedIndex)
		return i, nil
	} else if err != nil {
		return nil, fmt.Errorf("couldn't get next accepted index from database: %w", err)
	}
	p := wrappers.Packer{Bytes: nextAcceptedIndexBytes}
	i.nextAcceptedIndex = p.UnpackLong()
	if p.Err != nil {
		return nil, fmt.Errorf("couldn't parse next accepted index from bytes: %w", err)
	}
	i.log.Info("next accepted index %d", i.nextAcceptedIndex)
	return i, nil
}

// Index that the given transaction is accepted
// Returned error should be treated as fatal
func (i *indexer) markAccepted(txID ids.ID) error {
	batch := i.db.NewBatch()

	p := wrappers.Packer{MaxSize: wrappers.LongLen}
	p.PackLong(i.nextAcceptedIndex)
	if p.Err != nil {
		return fmt.Errorf("couldn't convert next accepted index to bytes: %w", p.Err)
	}
	// Next accepted index --> tx at that index
	err := batch.Put(p.Bytes, txID[:])
	if err != nil {
		return fmt.Errorf("couldn't put accepted transaction %s into index: %w", txID, err)
	}

	// Increment the next accepted index and persist last accepted index
	i.nextAcceptedIndex++
	p = wrappers.Packer{MaxSize: wrappers.LongLen}
	p.PackLong(i.nextAcceptedIndex)
	if p.Err != nil {
		return fmt.Errorf("couldn't convert next accepted index to bytes: %w", p.Err)
	}
	err = batch.Put(nextAcceptedIndexKey, p.Bytes)
	if err != nil {
		return fmt.Errorf("couldn't put accepted transaction %s into index: %w", txID, err)
	}

	// Write to underlying data store
	err = batch.Write()
	if err != nil {
		return fmt.Errorf("couldn't write batch: %w", err)
	}
	return nil
}

// Returns the [index]th accepted transaction
// For example, if [index] == 0, returns the first accepted transaction
// If [index] == 1, returns the second accepted transaction, etc.
func (i *indexer) getAcceptedTxByIndex(index uint64) (ids.ID, error) {
	lastAcceptedIndex, ok := i.lastAcceptedIndex()
	if !ok || index > lastAcceptedIndex {
		return ids.Empty, errNoTxAtThatIndex
	}

	p := wrappers.Packer{MaxSize: wrappers.LongLen}
	p.PackLong(index)
	if p.Err != nil {
		return ids.Empty, fmt.Errorf("couldn't convert index to bytes: %w", p.Err)
	}

	txIDBytes, err := i.db.Get(p.Bytes)
	switch {
	case err == database.ErrNotFound:
		return ids.Empty, errNoTxAtThatIndex
	case err != nil:
		i.log.Error("couldn't read transaction ID from database: %w", err)
		return ids.Empty, fmt.Errorf("couldn't read from database: %w", err)
	case len(txIDBytes) != 32:
		// Should never happen
		i.log.Error("expected tx ID to be 32 bytes but is %d", len(txIDBytes))
		return ids.Empty, fmt.Errorf("expected tx ID to be 32 bytes but is %d", len(txIDBytes))
	}
	var txID ids.ID
	copy(txID[:], txIDBytes)
	return txID, nil
}

// Returns:
// 1) The index of the most recently accepted transaction,
//    or 0 if no transactions have been accepted
// 2) Whether at least 1 transaction has been accepted
func (i *indexer) lastAcceptedIndex() (uint64, bool) {
	if i.nextAcceptedIndex == 0 {
		return 0, false
	}
	return i.nextAcceptedIndex - 1, true
}

// Return the transactions at index [start], [start+1], ... , [start+n-1]
// If [n] == 0, returns nil, nil
// If [n] > [maxFetchedByRange], returns an error
// If the [start] > the last accepted index, returns an error (unless the above applies)
// If we run out of transactions, returns the ones fetched before running out
func (i *indexer) getAcceptedTxRange(startIndex uint64, n uint64) ([]ids.ID, error) {
	// Check arguments for validity
	if n == 0 {
		return nil, nil
	} else if n > maxFetchedByRange {
		return nil, fmt.Errorf("requested %d but maximum page size is %d", n, maxFetchedByRange)
	}
	lastAcceptedIndex, ok := i.lastAcceptedIndex()
	if !ok {
		return nil, fmt.Errorf("start index (%d) > last accepted index (-1)", startIndex)
	} else if startIndex > lastAcceptedIndex {
		return nil, fmt.Errorf("start index (%d) > last accepted index (%d)", startIndex, lastAcceptedIndex)
	}

	// Convert start index to bytes
	p := wrappers.Packer{MaxSize: wrappers.LongLen}
	p.PackLong(startIndex)
	if p.Err != nil {
		return nil, fmt.Errorf("couldn't convert start index to bytes: %w", p.Err)
	}

	// Calculate the size of the needed slice
	var txIDs []ids.ID
	lastIndex := math.Min64(startIndex+n, lastAcceptedIndex)
	// [lastIndex] is always >= [startIndex] so this is safe.
	// [n] is limited to [maxFetchedByRange] so [txIDs] can't be crazy big.
	txIDs = make([]ids.ID, 0, int(lastIndex)-int(startIndex)+1)

	iter := i.db.NewIteratorWithStart(p.Bytes)
	defer iter.Release()

	for len(txIDs) < int(n) && iter.Next() {
		if bytes.Equal(iter.Key(), nextAcceptedIndexKey) {
			// Ignore the one non-index value in the database
			continue
		}
		txIDBytes := iter.Value()
		if len(txIDBytes) != 32 {
			// Should never happen
			return nil, fmt.Errorf("expected tx ID to be 32 bytes but is %d", len(txIDBytes))
		}
		var txID ids.ID
		copy(txID[:], txIDBytes)
		txIDs = append(txIDs, txID)
	}
	return txIDs, nil
}

// IndexerService returns data about accepted transactions
type IndexerService struct {
	indexer *indexer
}

// GetAcceptedTxByIndexArgs ...
type GetAcceptedTxIDByIndexArgs struct {
	Index json.Uint64 `json:"index"`
}

// GetAcceptedTxIDByIndex returns the ID of the transaction at [args.Index]
func (is *IndexerService) GetAcceptedTxIDByIndex(r *http.Request, args *GetAcceptedTxIDByIndexArgs, reply *api.JSONTxID) error {
	txID, err := is.indexer.getAcceptedTxByIndex(uint64(args.Index))
	if err != nil {
		return err
	}
	if txID == ids.Empty {
		return errNoTxAtThatIndex
	}
	reply.TxID = txID
	return nil
}

// LastAcceptedIndexReply ...
type LastAcceptedIndexReply struct {
	Index json.Uint64 `json:"index"`
}

// LastAcceptedIndex returns the index of the most recently accepted transaction.
// This is 1 less than the total number of accepted transactions.
// Returns [errNoTxs] if no transactions have been accepted.
func (is *IndexerService) LastAcceptedIndex(r *http.Request, args *GetAcceptedTxIDByIndexArgs, reply *LastAcceptedIndexReply) error {
	lastIndex, ok := is.indexer.lastAcceptedIndex()
	if !ok {
		return errNoTxs
	}
	reply.Index = json.Uint64(lastIndex)
	return nil
}

// GetAcceptedTxIDRangeArgs ...
type GetAcceptedTxIDRangeArgs struct {
	StartIndex json.Uint64 `json:"index"`
	NumToFetch json.Uint64 `json:"numToFetch"`
}

// GetAcceptedTxIDRangeArgs ...
type GetAcceptedTxIDRangeReply struct {
	TxIDs []struct {
		json.Uint64 `json:"index"`
		ids.ID      `json:"txID"`
	} `json:"txs"`
}

// GetAcceptedTxIDByIndex returns the transactions at index [startIndex], [startIndex+1], ... , [startIndex+n-1]
// If [n] == 0, returns an empty response (i.e. null).
// If [startIndex] > the last accepted index, returns an error (unless the above apply.)
// If [n] > [maxFetchedByRange], returns an error.
// If we run out of transactions, returns the ones fetched before running out.
func (is *IndexerService) GetAcceptedTxIDRange(r *http.Request, args *GetAcceptedTxIDRangeArgs, reply *GetAcceptedTxIDRangeReply) error {
	txIDs, err := is.indexer.getAcceptedTxRange(uint64(args.StartIndex), uint64(args.NumToFetch))
	if err != nil {
		return err
	}
	nextIndex := args.StartIndex
	for _, txID := range txIDs {
		reply.TxIDs = append(
			reply.TxIDs,
			struct {
				json.Uint64 `json:"index"`
				ids.ID      `json:"txID"`
			}{
				nextIndex,
				txID,
			},
		)
		nextIndex++
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2015 The Serviced Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package volume

import (
	"archive/tar"
	"io"
	"os"
	"path/filepath"
	"syscall"

	"github.com/zenoss/glog"
)

// ExportDirectory recursively writes its contents into a tar Writer.
func ExportDirectory(tarfile *tar.Writer, path, name string) error {
	dir, err := os.Open(path)
	if err != nil {
		glog.Errorf("Could not open %s: %s", path, err)
		return err
	}
	defer dir.Close()
	fstat, err := dir.Stat()
	if err != nil {
		glog.Errorf("Could not stat %s: %s", path, err)
		return err
	}
	header, err := getHeader(name, "", fstat)
	if err != nil {
		return err
	}
	if err := tarfile.WriteHeader(header); err != nil {
		glog.Errorf("Could not write header for directory %s: %s", path, err)
		return err
	}
	files, err := dir.Readdir(0)
	if err != nil {
		glog.Errorf("Could not list directory for %s: %s", path, err)
		return err
	}
	for _, finfo := range files {
		fullpath, relpath := filepath.Join(path, finfo.Name()), filepath.Join(name, finfo.Name())
		if finfo.IsDir() {
			if err := ExportDirectory(tarfile, fullpath, relpath); err != nil {
				return err
			}
		} else {
			if err := ExportFile(tarfile, fullpath, relpath); err != nil {
				return err
			}
		}
	}
	return nil
}

// ExportFile writes a file into a tar Writer.
func ExportFile(tarfile *tar.Writer, path, name string) error {
	file, err := os.Open(path)
	if err != nil {
		glog.Errorf("Could not open %s: %s", path, err)
		return err
	}
	defer file.Close()
	fstat, err := file.Stat()
	if err != nil {
		glog.Errorf("Could not stat %s: %s", path, err)
		return err
	}

	link, err := filepath.EvalSymlinks(path)
	if err != nil {
		glog.Errorf("Could not check link for %s: %s", path, err)
		return err
	}
	header, err := getHeader(name, link, fstat)
	if err != nil {
		glog.Errorf("Could not create file header %s: %s", path, err)
		return err
	}
	if err := tarfile.WriteHeader(header); err != nil {
		glog.Errorf("Could not write file header %s: %s", path, err)
		return err
	}
	if link == path {
		if _, err := io.Copy(tarfile, file); err != nil {
			glog.Errorf("Could not write file %s: %s", path, err)
			return err
		}
	}
	return nil
}

// ImportArchive reads from a tar Reader and writes the contents into a path
// preserving file permissions and ownership.
func ImportArchive(tarfile *tar.Reader, path string) error {
	for {
		header, err := tarfile.Next()
		if err == io.EOF {
			break
		} else if err != nil {
			glog.Errorf("Could not import archive to %s: %s", path, err)
			return err
		}
		if err := ImportArchiveHeader(header, tarfile, path); err != nil {
			return err
		}
	}
	return nil
}

// ImportArchiveHeader imports a tarfile header to a particular path
func ImportArchiveHeader(header *tar.Header, reader io.Reader, path string) error {
	filename := filepath.Join(path, header.Name)
	switch header.Typeflag {
	case tar.TypeDir:
		if err := os.MkdirAll(filename, 0755); err != nil {
			glog.Errorf("Could not create directory at %s: %s", filename, err)
			return err
		}
	case tar.TypeSymlink:
		if err := os.Symlink(filename, header.Linkname); err != nil {
			glog.Errorf("Could not create symlink at %s: %s", filename, err)
			return err
		}
	case tar.TypeReg:
		err := func() error {
			writer, err := os.Create(filename)
			if err != nil {
				glog.Errorf("Could not create file at %s: %s", filename, err)
				return err
			}
			defer writer.Close()
			if _, err := io.Copy(writer, reader); err != nil {
				glog.Errorf("Could not copy file %s: %s", filename, err)
				return err
			}
			return nil
		}()
		if err != nil {
			return err
		}
	default:
		glog.Errorf("Found unxepected file type %b: will not import %s", header.Typeflag, filename)
		return nil
	}
	if err := os.Chown(filename, header.Uid, header.Gid); err != nil {
		glog.Warningf("Could not change file ownership for %s: %s", filename, err)
	}
	if err := os.Chmod(filename, header.FileInfo().Mode()); err != nil {
		glog.Warningf("Could not set permissions for file %s: %s", filename, err)
	}
	return nil
}

func getHeader(name, link string, fstat os.FileInfo) (*tar.Header, error) {
	header, err := tar.FileInfoHeader(fstat, link)
	if err != nil {
		return nil, err
	}
	header.Name = name
	header.Uid = int(fstat.Sys().(*syscall.Stat_t).Uid)
	header.Gid = int(fstat.Sys().(*syscall.Stat_t).Gid)
	header.ModTime = fstat.ModTime()
	return header, nil
}
<commit_msg>Skip exportFile for Unix domain sockets during backups.<commit_after>// Copyright 2015 The Serviced Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package volume

import (
	"archive/tar"
	"io"
	"os"
	"path/filepath"
	"syscall"

	"github.com/zenoss/glog"
)

// ExportDirectory recursively writes its contents into a tar Writer.
func ExportDirectory(tarfile *tar.Writer, path, name string) error {
	dir, err := os.Open(path)
	if err != nil {
		glog.Errorf("Could not open %s: %s", path, err)
		return err
	}
	defer dir.Close()
	fstat, err := dir.Stat()
	if err != nil {
		glog.Errorf("Could not stat %s: %s", path, err)
		return err
	}
	header, err := getHeader(name, "", fstat)
	if err != nil {
		return err
	}
	if err := tarfile.WriteHeader(header); err != nil {
		glog.Errorf("Could not write header for directory %s: %s", path, err)
		return err
	}
	files, err := dir.Readdir(0)
	if err != nil {
		glog.Errorf("Could not list directory for %s: %s", path, err)
		return err
	}
	for _, finfo := range files {
		fullpath, relpath := filepath.Join(path, finfo.Name()), filepath.Join(name, finfo.Name())
		if finfo.IsDir() {
			if err := ExportDirectory(tarfile, fullpath, relpath); err != nil {
				return err
			}
		} else if isSocket := finfo.Mode() & os.ModeSocket; isSocket != os.ModeSocket {
			if err := ExportFile(tarfile, fullpath, relpath); err != nil {
				return err
			}
		}
	}
	return nil
}

// ExportFile writes a file into a tar Writer.
func ExportFile(tarfile *tar.Writer, path, name string) error {
	file, err := os.Open(path)
	if err != nil {
		glog.Errorf("Could not open %s: %s", path, err)
		return err
	}
	defer file.Close()
	fstat, err := file.Stat()
	if err != nil {
		glog.Errorf("Could not stat %s: %s", path, err)
		return err
	}

	link, err := filepath.EvalSymlinks(path)
	if err != nil {
		glog.Errorf("Could not check link for %s: %s", path, err)
		return err
	}
	header, err := getHeader(name, link, fstat)
	if err != nil {
		glog.Errorf("Could not create file header %s: %s", path, err)
		return err
	}
	if err := tarfile.WriteHeader(header); err != nil {
		glog.Errorf("Could not write file header %s: %s", path, err)
		return err
	}
	if link == path {
		if _, err := io.Copy(tarfile, file); err != nil {
			glog.Errorf("Could not write file %s: %s", path, err)
			return err
		}
	}
	return nil
}

// ImportArchive reads from a tar Reader and writes the contents into a path
// preserving file permissions and ownership.
func ImportArchive(tarfile *tar.Reader, path string) error {
	for {
		header, err := tarfile.Next()
		if err == io.EOF {
			break
		} else if err != nil {
			glog.Errorf("Could not import archive to %s: %s", path, err)
			return err
		}
		if err := ImportArchiveHeader(header, tarfile, path); err != nil {
			return err
		}
	}
	return nil
}

// ImportArchiveHeader imports a tarfile header to a particular path
func ImportArchiveHeader(header *tar.Header, reader io.Reader, path string) error {
	filename := filepath.Join(path, header.Name)
	switch header.Typeflag {
	case tar.TypeDir:
		if err := os.MkdirAll(filename, 0755); err != nil {
			glog.Errorf("Could not create directory at %s: %s", filename, err)
			return err
		}
	case tar.TypeSymlink:
		if err := os.Symlink(filename, header.Linkname); err != nil {
			glog.Errorf("Could not create symlink at %s: %s", filename, err)
			return err
		}
	case tar.TypeReg:
		err := func() error {
			writer, err := os.Create(filename)
			if err != nil {
				glog.Errorf("Could not create file at %s: %s", filename, err)
				return err
			}
			defer writer.Close()
			if _, err := io.Copy(writer, reader); err != nil {
				glog.Errorf("Could not copy file %s: %s", filename, err)
				return err
			}
			return nil
		}()
		if err != nil {
			return err
		}
	default:
		glog.Errorf("Found unxepected file type %b: will not import %s", header.Typeflag, filename)
		return nil
	}
	if err := os.Chown(filename, header.Uid, header.Gid); err != nil {
		glog.Warningf("Could not change file ownership for %s: %s", filename, err)
	}
	if err := os.Chmod(filename, header.FileInfo().Mode()); err != nil {
		glog.Warningf("Could not set permissions for file %s: %s", filename, err)
	}
	return nil
}

func getHeader(name, link string, fstat os.FileInfo) (*tar.Header, error) {
	header, err := tar.FileInfoHeader(fstat, link)
	if err != nil {
		return nil, err
	}
	header.Name = name
	header.Uid = int(fstat.Sys().(*syscall.Stat_t).Uid)
	header.Gid = int(fstat.Sys().(*syscall.Stat_t).Gid)
	header.ModTime = fstat.ModTime()
	return header, nil
}
<|endoftext|>


<commit_before>package que

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"testing"
)

func init() {
	log.SetOutput(ioutil.Discard)
}

func TestWorkerWorkOne(t *testing.T) {
	c := openTestClient(t)
	defer truncateAndClose(c.pool)

	success := false
	wm := WorkMap{
		"MyJob": func(j *Job) error {
			success = true
			return nil
		},
	}
	w := NewWorker(c, wm)

	didWork := w.WorkOne()
	if didWork {
		t.Errorf("want didWork=false when no job was queued")
	}

	if err := c.Enqueue(&Job{Type: "MyJob"}); err != nil {
		t.Fatal(err)
	}

	didWork = w.WorkOne()
	if !didWork {
		t.Errorf("want didWork=true")
	}
	if !success {
		t.Errorf("want success=true")
	}
}

func TestWorkerShutdown(t *testing.T) {
	c := openTestClient(t)
	defer truncateAndClose(c.pool)

	w := NewWorker(c, WorkMap{})
	finished := false
	go func() {
		w.Work()
		finished = true
	}()
	w.Shutdown()
	if !finished {
		t.Errorf("want finished=true")
	}
	if !w.done {
		t.Errorf("want w.done=true")
	}
}

func BenchmarkWorker(b *testing.B) {
	c := openTestClient(b)
	log.SetOutput(ioutil.Discard)
	defer func() {
		log.SetOutput(os.Stdout)
	}()
	defer truncateAndClose(c.pool)

	w := NewWorker(c, WorkMap{"Nil": nilWorker})

	for i := 0; i < b.N; i++ {
		if err := c.Enqueue(&Job{Type: "Nil"}); err != nil {
			log.Fatal(err)
		}
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		w.WorkOne()
	}
}

func nilWorker(j *Job) error {
	return nil
}

func TestWorkerWorkReturnsError(t *testing.T) {
	c := openTestClient(t)
	defer truncateAndClose(c.pool)

	called := 0
	wm := WorkMap{
		"MyJob": func(j *Job) error {
			called++
			return fmt.Errorf("the error msg")
		},
	}
	w := NewWorker(c, wm)

	didWork := w.WorkOne()
	if didWork {
		t.Errorf("want didWork=false when no job was queued")
	}

	if err := c.Enqueue(&Job{Type: "MyJob"}); err != nil {
		t.Fatal(err)
	}

	didWork = w.WorkOne()
	if !didWork {
		t.Errorf("want didWork=true")
	}
	if called != 1 {
		t.Errorf("want called=1 was: %d", called)
	}

	tx, err := c.pool.Begin()
	if err != nil {
		t.Fatal(err)
	}
	defer tx.Rollback()

	j, err := findOneJob(tx)
	if err != nil {
		t.Fatal(err)
	}
	if j.ErrorCount != 1 {
		t.Errorf("want ErrorCount=1 was %d", j.ErrorCount)
	}
	if !j.LastError.Valid {
		t.Errorf("want LastError IS NOT NULL")
	}
	if j.LastError.String != "the error msg" {
		t.Errorf("want LastError=\"the error msg\" was: %q", j.LastError.String)
	}
}

func TestWorkerWorkRescuesPanic(t *testing.T) {
	c := openTestClient(t)
	defer truncateAndClose(c.pool)

	called := 0
	wm := WorkMap{
		"MyJob": func(j *Job) error {
			called++
			panic("the panic msg")
			return nil
		},
	}
	w := NewWorker(c, wm)

	if err := c.Enqueue(&Job{Type: "MyJob"}); err != nil {
		t.Fatal(err)
	}

	w.WorkOne()
	if called != 1 {
		t.Errorf("want called=1 was: %d", called)
	}

	tx, err := c.pool.Begin()
	if err != nil {
		t.Fatal(err)
	}
	defer tx.Rollback()

	j, err := findOneJob(tx)
	if err != nil {
		t.Fatal(err)
	}
	if j.ErrorCount != 1 {
		t.Errorf("want ErrorCount=1 was %d", j.ErrorCount)
	}
	if !j.LastError.Valid {
		t.Errorf("want LastError IS NOT NULL")
	}
	if !strings.Contains(j.LastError.String, "the panic msg\n") {
		t.Errorf("want LastError contains \"the panic msg\\n\" was: %q", j.LastError.String)
	}
	// basic check if a stacktrace is there - not the stacktrace format itself
	if !strings.Contains(j.LastError.String, "worker.go:") {
		t.Errorf("want LastError contains \"worker.go:\" was: %q", j.LastError.String)
	}
	if !strings.Contains(j.LastError.String, "worker_test.go:") {
		t.Errorf("want LastError contains \"worker_test.go:\" was: %q", j.LastError.String)
	}
}

func TestWorkerWorkOneTypeNotInMap(t *testing.T) {
	c := openTestClient(t)
	defer truncateAndClose(c.pool)

	currentConns := c.pool.Stat().CurrentConnections
	availConns := c.pool.Stat().AvailableConnections

	success := false
	wm := WorkMap{}
	w := NewWorker(c, wm)

	didWork := w.WorkOne()
	if didWork {
		t.Errorf("want didWork=false when no job was queued")
	}

	if err := c.Enqueue(&Job{Type: "MyJob"}); err != nil {
		t.Fatal(err)
	}

	didWork = w.WorkOne()
	if !didWork {
		t.Errorf("want didWork=true")
	}
	if success {
		t.Errorf("want success=false")
	}

	if currentConns != c.pool.Stat().CurrentConnections {
		t.Errorf("want currentConns euqual: before=%d  after=%d", currentConns, c.pool.Stat().CurrentConnections)
	}
	if availConns != c.pool.Stat().AvailableConnections {
		t.Errorf("want availConns euqual: before=%d  after=%d", availConns, c.pool.Stat().AvailableConnections)
	}

	tx, err := c.pool.Begin()
	if err != nil {
		t.Fatal(err)
	}
	defer tx.Rollback()

	j, err := findOneJob(tx)
	if err != nil {
		t.Fatal(err)
	}
	if j.ErrorCount != 1 {
		t.Errorf("want ErrorCount=1 was %d", j.ErrorCount)
	}
	if !j.LastError.Valid {
		t.Fatal("want non-nil LastError")
	}
	if want := "unknown job type: \"MyJob\""; j.LastError.String != want {
		t.Errorf("want LastError=%q, got %q", want, j.LastError.String)
	}

}
<commit_msg>Fix race in TestWorkerShutdown<commit_after>package que

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"testing"
	"time"
)

func init() {
	log.SetOutput(ioutil.Discard)
}

func TestWorkerWorkOne(t *testing.T) {
	c := openTestClient(t)
	defer truncateAndClose(c.pool)

	success := false
	wm := WorkMap{
		"MyJob": func(j *Job) error {
			success = true
			return nil
		},
	}
	w := NewWorker(c, wm)

	didWork := w.WorkOne()
	if didWork {
		t.Errorf("want didWork=false when no job was queued")
	}

	if err := c.Enqueue(&Job{Type: "MyJob"}); err != nil {
		t.Fatal(err)
	}

	didWork = w.WorkOne()
	if !didWork {
		t.Errorf("want didWork=true")
	}
	if !success {
		t.Errorf("want success=true")
	}
}

func TestWorkerShutdown(t *testing.T) {
	c := openTestClient(t)
	defer truncateAndClose(c.pool)

	w := NewWorker(c, WorkMap{})
	done := make(chan struct{})
	go func() {
		w.Work()
		close(done)
	}()
	w.Shutdown()

	select {
	case <-done:
	case <-time.After(time.Second):
		t.Fatal("timed out waiting for worker to shutdown")
	}

	if !w.done {
		t.Errorf("want w.done=true")
	}
}

func BenchmarkWorker(b *testing.B) {
	c := openTestClient(b)
	log.SetOutput(ioutil.Discard)
	defer func() {
		log.SetOutput(os.Stdout)
	}()
	defer truncateAndClose(c.pool)

	w := NewWorker(c, WorkMap{"Nil": nilWorker})

	for i := 0; i < b.N; i++ {
		if err := c.Enqueue(&Job{Type: "Nil"}); err != nil {
			log.Fatal(err)
		}
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		w.WorkOne()
	}
}

func nilWorker(j *Job) error {
	return nil
}

func TestWorkerWorkReturnsError(t *testing.T) {
	c := openTestClient(t)
	defer truncateAndClose(c.pool)

	called := 0
	wm := WorkMap{
		"MyJob": func(j *Job) error {
			called++
			return fmt.Errorf("the error msg")
		},
	}
	w := NewWorker(c, wm)

	didWork := w.WorkOne()
	if didWork {
		t.Errorf("want didWork=false when no job was queued")
	}

	if err := c.Enqueue(&Job{Type: "MyJob"}); err != nil {
		t.Fatal(err)
	}

	didWork = w.WorkOne()
	if !didWork {
		t.Errorf("want didWork=true")
	}
	if called != 1 {
		t.Errorf("want called=1 was: %d", called)
	}

	tx, err := c.pool.Begin()
	if err != nil {
		t.Fatal(err)
	}
	defer tx.Rollback()

	j, err := findOneJob(tx)
	if err != nil {
		t.Fatal(err)
	}
	if j.ErrorCount != 1 {
		t.Errorf("want ErrorCount=1 was %d", j.ErrorCount)
	}
	if !j.LastError.Valid {
		t.Errorf("want LastError IS NOT NULL")
	}
	if j.LastError.String != "the error msg" {
		t.Errorf("want LastError=\"the error msg\" was: %q", j.LastError.String)
	}
}

func TestWorkerWorkRescuesPanic(t *testing.T) {
	c := openTestClient(t)
	defer truncateAndClose(c.pool)

	called := 0
	wm := WorkMap{
		"MyJob": func(j *Job) error {
			called++
			panic("the panic msg")
			return nil
		},
	}
	w := NewWorker(c, wm)

	if err := c.Enqueue(&Job{Type: "MyJob"}); err != nil {
		t.Fatal(err)
	}

	w.WorkOne()
	if called != 1 {
		t.Errorf("want called=1 was: %d", called)
	}

	tx, err := c.pool.Begin()
	if err != nil {
		t.Fatal(err)
	}
	defer tx.Rollback()

	j, err := findOneJob(tx)
	if err != nil {
		t.Fatal(err)
	}
	if j.ErrorCount != 1 {
		t.Errorf("want ErrorCount=1 was %d", j.ErrorCount)
	}
	if !j.LastError.Valid {
		t.Errorf("want LastError IS NOT NULL")
	}
	if !strings.Contains(j.LastError.String, "the panic msg\n") {
		t.Errorf("want LastError contains \"the panic msg\\n\" was: %q", j.LastError.String)
	}
	// basic check if a stacktrace is there - not the stacktrace format itself
	if !strings.Contains(j.LastError.String, "worker.go:") {
		t.Errorf("want LastError contains \"worker.go:\" was: %q", j.LastError.String)
	}
	if !strings.Contains(j.LastError.String, "worker_test.go:") {
		t.Errorf("want LastError contains \"worker_test.go:\" was: %q", j.LastError.String)
	}
}

func TestWorkerWorkOneTypeNotInMap(t *testing.T) {
	c := openTestClient(t)
	defer truncateAndClose(c.pool)

	currentConns := c.pool.Stat().CurrentConnections
	availConns := c.pool.Stat().AvailableConnections

	success := false
	wm := WorkMap{}
	w := NewWorker(c, wm)

	didWork := w.WorkOne()
	if didWork {
		t.Errorf("want didWork=false when no job was queued")
	}

	if err := c.Enqueue(&Job{Type: "MyJob"}); err != nil {
		t.Fatal(err)
	}

	didWork = w.WorkOne()
	if !didWork {
		t.Errorf("want didWork=true")
	}
	if success {
		t.Errorf("want success=false")
	}

	if currentConns != c.pool.Stat().CurrentConnections {
		t.Errorf("want currentConns euqual: before=%d  after=%d", currentConns, c.pool.Stat().CurrentConnections)
	}
	if availConns != c.pool.Stat().AvailableConnections {
		t.Errorf("want availConns euqual: before=%d  after=%d", availConns, c.pool.Stat().AvailableConnections)
	}

	tx, err := c.pool.Begin()
	if err != nil {
		t.Fatal(err)
	}
	defer tx.Rollback()

	j, err := findOneJob(tx)
	if err != nil {
		t.Fatal(err)
	}
	if j.ErrorCount != 1 {
		t.Errorf("want ErrorCount=1 was %d", j.ErrorCount)
	}
	if !j.LastError.Valid {
		t.Fatal("want non-nil LastError")
	}
	if want := "unknown job type: \"MyJob\""; j.LastError.String != want {
		t.Errorf("want LastError=%q, got %q", want, j.LastError.String)
	}

}
<|endoftext|>


<commit_before>// Package auth implements Basic authentication.
package auth

import (
	"encoding/base64"
	"net/http"
	"strings"
	"time"

	"github.com/codegangsta/negroni"
	"github.com/pmylund/go-cache"
	"golang.org/x/crypto/bcrypt"

	"github.com/nabeken/negroni-auth/datastore"
)

const (
	defaultCacheExpireTime = 10 * time.Minute
	defaultCachePurseTime  = 60 * time.Second
	bcryptCost             = 12
)

// NewSimpleBasic returns *datastore.Simple built from userid, password.
func NewSimpleBasic(userId, password string) (*datastore.Simple, error) {
	hashedPassword, err := Hash(password)
	if err != nil {
		return nil, err
	}

	return &datastore.Simple{
		Key:   userId,
		Value: hashedPassword,
	}, nil
}

// requireAuth writes error to client which initiates the authentication process
// or requires reauthentication.
func requireAuth(w http.ResponseWriter) {
	w.Header().Set("WWW-Authenticate", "Basic realm=\"Authorization Required\"")
	http.Error(w, "Not Authorized", http.StatusUnauthorized)
}

// getCred get userid, password from request.
func getCred(req *http.Request) (string, string) {
	// Split authorization header.
	s := strings.SplitN(req.Header.Get("Authorization"), " ", 2)
	if len(s) != 2 || s[0] != "Basic" {
		return "", ""
	}

	// Decode credential.
	cred, err := base64.StdEncoding.DecodeString(s[1])
	if err != nil {
		return "", ""
	}

	// Split credential into userid, password.
	pair := strings.SplitN(string(cred), ":", 2)
	if len(pair) != 2 {
		return "", ""
	}

	return pair[0], pair[1]
}

// Hash returns a hashed password.
func Hash(password string) ([]byte, error) {
	return bcrypt.GenerateFromPassword([]byte(password), bcryptCost)
}

// NewBasic returns a negroni.HandlerFunc that authenticates via Basic auth using data store.
// Writes a http.StatusUnauthorized if authentication fails.
func NewBasic(datastore datastore.Datastore) negroni.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request, next http.HandlerFunc) {
		// Extract userid, password from request.
		userId, password := getCred(req)

		if userId == "" {
			requireAuth(w)
			return
		}

		// Extract hashed passwor from credentials.
		hashedPassword, found := datastore.Get(userId)
		if !found {
			requireAuth(w)
			return
		}

		// Check if the password is correct.
		err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
		// Password not correct. Fail.
		if err != nil {
			requireAuth(w)
			return
		}

		r := w.(negroni.ResponseWriter)

		// Password correct.
		if r.Status() != http.StatusUnauthorized {
			next(w, req)
		}
	}
}

// Basic returns a negroni.HandlerFunc that authenticates via Basic Auth.
// Writes a http.StatusUnauthorized if authentication fails.
func Basic(userid, password string) negroni.HandlerFunc {
	datastore, err := NewSimpleBasic(userid, password)
	if err != nil {
		panic(err)
	}

	return NewBasic(datastore)
}

// CacheBasic returns a negroni.HandlerFunc that authenticates via Basic auth using cache.
// Writes a http.StatusUnauthorized if authentication fails.
func CacheBasic(datastore datastore.Datastore, cacheExpireTime, cachePurseTime time.Duration) negroni.HandlerFunc {
	var basic = NewBasic(datastore)
	var c = cache.New(cacheExpireTime, cachePurseTime)

	return func(w http.ResponseWriter, req *http.Request, next http.HandlerFunc) {
		// Get credential from request header.
		credential := req.Header.Get("Authorization")
		// Get authentication status by credential.
		authenticated, found := c.Get(credential)

		// Cache hit
		if found && (authenticated == "true") {
			next(w, req)
		} else { // Cache miss. Unauthenticated.
			basic(w, req, next)
			r := w.(negroni.ResponseWriter)

			// Password correct.
			if r.Status() != http.StatusUnauthorized {
				c.Set(credential, "true", cache.DefaultExpiration)
			}
		}
	}
}

// CacheBasicDefault returns a negroni.HandlerFunc that authenticates via Basic auth using cache.
// with default cache configuration. Writes a http.StatusUnauthorized if authentication fails.
func CacheBasicDefault(datastore datastore.Datastore) negroni.HandlerFunc {
	return CacheBasic(datastore, defaultCacheExpireTime, defaultCachePurseTime)
}
<commit_msg>Export bcryptCost and reduce default cost from 12 to 10<commit_after>// Package auth implements Basic authentication.
package auth

import (
	"encoding/base64"
	"net/http"
	"strings"
	"time"

	"github.com/codegangsta/negroni"
	"github.com/pmylund/go-cache"
	"golang.org/x/crypto/bcrypt"

	"github.com/nabeken/negroni-auth/datastore"
)

const (
	defaultCacheExpireTime = 10 * time.Minute
	defaultCachePurseTime  = 60 * time.Second
)

// See https://godoc.org/golang.org/x/crypto/bcrypt#pkg-constants for more details.
var BcryptCost = 10

// NewSimpleBasic returns *datastore.Simple built from userid, password.
func NewSimpleBasic(userId, password string) (*datastore.Simple, error) {
	hashedPassword, err := Hash(password)
	if err != nil {
		return nil, err
	}

	return &datastore.Simple{
		Key:   userId,
		Value: hashedPassword,
	}, nil
}

// requireAuth writes error to client which initiates the authentication process
// or requires reauthentication.
func requireAuth(w http.ResponseWriter) {
	w.Header().Set("WWW-Authenticate", "Basic realm=\"Authorization Required\"")
	http.Error(w, "Not Authorized", http.StatusUnauthorized)
}

// getCred get userid, password from request.
func getCred(req *http.Request) (string, string) {
	// Split authorization header.
	s := strings.SplitN(req.Header.Get("Authorization"), " ", 2)
	if len(s) != 2 || s[0] != "Basic" {
		return "", ""
	}

	// Decode credential.
	cred, err := base64.StdEncoding.DecodeString(s[1])
	if err != nil {
		return "", ""
	}

	// Split credential into userid, password.
	pair := strings.SplitN(string(cred), ":", 2)
	if len(pair) != 2 {
		return "", ""
	}

	return pair[0], pair[1]
}

// Hash returns a hashed password.
func Hash(password string) ([]byte, error) {
	return bcrypt.GenerateFromPassword([]byte(password), BcryptCost)
}

// NewBasic returns a negroni.HandlerFunc that authenticates via Basic auth using data store.
// Writes a http.StatusUnauthorized if authentication fails.
func NewBasic(datastore datastore.Datastore) negroni.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request, next http.HandlerFunc) {
		// Extract userid, password from request.
		userId, password := getCred(req)

		if userId == "" {
			requireAuth(w)
			return
		}

		// Extract hashed passwor from credentials.
		hashedPassword, found := datastore.Get(userId)
		if !found {
			requireAuth(w)
			return
		}

		// Check if the password is correct.
		err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
		// Password not correct. Fail.
		if err != nil {
			requireAuth(w)
			return
		}

		r := w.(negroni.ResponseWriter)

		// Password correct.
		if r.Status() != http.StatusUnauthorized {
			next(w, req)
		}
	}
}

// Basic returns a negroni.HandlerFunc that authenticates via Basic Auth.
// Writes a http.StatusUnauthorized if authentication fails.
func Basic(userid, password string) negroni.HandlerFunc {
	datastore, err := NewSimpleBasic(userid, password)
	if err != nil {
		panic(err)
	}

	return NewBasic(datastore)
}

// CacheBasic returns a negroni.HandlerFunc that authenticates via Basic auth using cache.
// Writes a http.StatusUnauthorized if authentication fails.
func CacheBasic(datastore datastore.Datastore, cacheExpireTime, cachePurseTime time.Duration) negroni.HandlerFunc {
	var basic = NewBasic(datastore)
	var c = cache.New(cacheExpireTime, cachePurseTime)

	return func(w http.ResponseWriter, req *http.Request, next http.HandlerFunc) {
		// Get credential from request header.
		credential := req.Header.Get("Authorization")
		// Get authentication status by credential.
		authenticated, found := c.Get(credential)

		// Cache hit
		if found && (authenticated == "true") {
			next(w, req)
		} else { // Cache miss. Unauthenticated.
			basic(w, req, next)
			r := w.(negroni.ResponseWriter)

			// Password correct.
			if r.Status() != http.StatusUnauthorized {
				c.Set(credential, "true", cache.DefaultExpiration)
			}
		}
	}
}

// CacheBasicDefault returns a negroni.HandlerFunc that authenticates via Basic auth using cache.
// with default cache configuration. Writes a http.StatusUnauthorized if authentication fails.
func CacheBasicDefault(datastore datastore.Datastore) negroni.HandlerFunc {
	return CacheBasic(datastore, defaultCacheExpireTime, defaultCachePurseTime)
}
<|endoftext|>


<commit_before>package quicktemplate

import (
	"testing"
)

func TestWriter(t *testing.T) {
	bb := AcquireByteBuffer()
	qw := AcquireWriter(bb)
	w := qw.W()
	bbNew, ok := w.(*ByteBuffer)
	if !ok {
		t.Fatalf("W() must return ByteBuffer, not %T", w)
	}
	if bbNew != bb {
		t.Fatalf("unexpected ByteBuffer returned: %p. Expecting %p", bbNew, bb)
	}

	wn := qw.N()
	we := qw.E()

	wn.S("")
	wn.D(123)
	wn.Z([]byte("'"))
	wn.Q("foo")
	wn.J("ds")
	wn.F(1.23)
	we.V(struct{}{})

	we.S("")
	we.D(321)
	we.Z([]byte("'"))
	we.Q("foo")
	we.J("ds")
	we.F(1.23)
	we.V(struct{}{})

	ReleaseWriter(qw)

	expectedS := `<a></a>123'"foo"ds1.23{}<a></a>321'"foo"ds1.23{}`
	if string(bb.B) != expectedS {
		t.Fatalf("unexpected output: %q. Expecting %q", bb.B, expectedS)
	}

	ReleaseByteBuffer(bb)
}
<commit_msg>added a test for QWriter.U<commit_after>package quicktemplate

import (
	"testing"
)

func TestWriter(t *testing.T) {
	bb := AcquireByteBuffer()
	qw := AcquireWriter(bb)
	w := qw.W()
	bbNew, ok := w.(*ByteBuffer)
	if !ok {
		t.Fatalf("W() must return ByteBuffer, not %T", w)
	}
	if bbNew != bb {
		t.Fatalf("unexpected ByteBuffer returned: %p. Expecting %p", bbNew, bb)
	}

	wn := qw.N()
	we := qw.E()

	wn.S("")
	wn.D(123)
	wn.Z([]byte("'"))
	wn.Q("foo")
	wn.J("ds")
	wn.F(1.23)
	wn.U("абв")
	we.V(struct{}{})

	we.S("")
	we.D(321)
	we.Z([]byte("'"))
	we.Q("foo")
	we.J("ds")
	we.F(1.23)
	we.U("абв")
	we.V(struct{}{})

	ReleaseWriter(qw)

	expectedS := `<a></a>123'"foo"ds1.23%D0%B0%D0%B1%D0%B2{}<a></a>321'"foo"ds1.23%D0%B0%D0%B1%D0%B2{}`
	if string(bb.B) != expectedS {
		t.Fatalf("unexpected output: %q. Expecting %q", bb.B, expectedS)
	}

	ReleaseByteBuffer(bb)
}
<|endoftext|>


<commit_before>package iobit

import (
	"bytes"
	"encoding/binary"
	"encoding/hex"
	"math/rand"
	"testing"
)

func getNumBits(read, max, align int) int {
	bits := 1
	if align != 32 {
		bits += rand.Intn(32 / align)
	}
	bits *= align
	if read+bits > max {
		bits = max - read
	}
	if bits > 32 {
		panic("too many bits")
	}
	return bits
}

func makeSource(size int) []uint8 {
	src := make([]uint8, size)
	for i := range src {
		src[i] = uint8(rand.Intn(0xFF))
	}
	return src[:]
}

func flushCheck(t *testing.T, w *Writer) {
	err := w.Flush()
	if err != nil {
		t.Fatal("unexpected error during flush", err)
	}
}

func compare(t *testing.T, src, dst []uint8) {
	if bytes.Equal(src, dst) {
		return
	}
	t.Log(hex.Dump(src))
	t.Log(hex.Dump(dst))
	t.Fatal("invalid output")
}

func testWrites(w *Writer, t *testing.T, align int, src []uint8) {
	max := len(src) * 8
	for read := 0; read < max; {
		bits := getNumBits(read, max, align)
		idx := read >> 3
		fill := read - idx*8
		if idx*8 > max-64 {
			rewind := max - 64
			fill += idx*8 - rewind
			idx = rewind >> 3
		}
		block := binary.BigEndian.Uint64(src[idx:])
		block >>= uint(64 - bits - fill)
		value := uint32(block & 0xFFFFFFFF)
		BigEndian.PutUint32(w, uint(bits), value)
		read += bits
	}
	flushCheck(t, w)
}

func TestWrites(t *testing.T) {
	src := makeSource(512)
	dst := make([]uint8, len(src))
	for i := 32; i > 0; i >>= 1 {
		w := NewWriter(dst)
		testWrites(w, t, i, src)
		compare(t, src, dst)
	}
}

func TestSmall64BigEndianWrite(t *testing.T) {
	buf := make([]uint8, 5)
	w := NewWriter(buf)
	BigEndian.PutUint64(w, 33, 0xFFFFFFFE00000001)
	BigEndian.PutUint32(w, 7, 0)
	w.Flush()
	compare(t, buf, []uint8{0x00, 0x00, 0x00, 0x00, 0x80})
}

func TestSmall64LittleEndianWrite(t *testing.T) {
	buf := make([]uint8, 5)
	w := NewWriter(buf)
	LittleEndian.PutUint64(w, 33, 0xFFFFFFFE00000001)
	LittleEndian.PutUint32(w, 7, 0)
	w.Flush()
	compare(t, buf, []uint8{0x01, 0x00, 0x00, 0x00, 0x00})
}

func TestBigEndianWrite(t *testing.T) {
	buf := make([]uint8, 8)
	w := NewWriter(buf)
	BigEndian.PutUint64(w, 64, 0x0123456789ABCDEF)
	w.Flush()
	compare(t, buf, []uint8{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF})
}

func TestLittleEndianWrite(t *testing.T) {
	buf := make([]uint8, 8)
	w := NewWriter(buf)
	LittleEndian.PutUint64(w, 64, 0x0123456789ABCDEF)
	w.Flush()
	compare(t, buf, []uint8{0xEF, 0xCD, 0xAB, 0x89, 0x67, 0x45, 0x23, 0x01})
}

func benchmarkWrites(b *testing.B, align int) {
	b.StopTimer()
	size := 1 << 12
	buf := make([]uint8, size)
	bits := make([]uint, size)
	values := make([]uint32, size)
	idx := 0
	last := 0
	for i := 0; i < size; i++ {
		val := getNumBits(idx, size*8, align)
		idx += val
		if val != 0 {
			last = i + 1
		}
		bits[i] = uint(val)
		values[i] = rand.Uint32()
	}
	b.SetBytes(int64(size))
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		w := NewWriter(buf)
		for j := 0; j < last; j++ {
			BigEndian.PutUint32(w, bits[j], values[j])
		}
	}
}

func BenchmarkBigEndian1(b *testing.B)  { benchmarkWrites(b, 1) }
func BenchmarkBigEndian8(b *testing.B)  { benchmarkWrites(b, 8) }
func BenchmarkBigEndian32(b *testing.B) { benchmarkWrites(b, 32) }

func checkError(t *testing.T, expected, actual error) {
	if actual != expected {
		t.Fatal("expecting", expected, "got", actual)
	}
}

func TestFlushErrors(t *testing.T) {
	buf := make([]uint8, 2)

	w := NewWriter(buf)
	BigEndian.PutUint32(w, 9, 0)
	checkError(t, ErrUnderflow, w.Flush())

	w = NewWriter(buf)
	BigEndian.PutUint32(w, 16, 0)
	checkError(t, nil, w.Flush())

	w = NewWriter(buf)
	BigEndian.PutUint32(w, 17, 0)
	checkError(t, ErrOverflow, w.Flush())
}
<commit_msg>writer: add better benchmarks<commit_after>package iobit

import (
	"bytes"
	"encoding/binary"
	"encoding/hex"
	"math/rand"
	"testing"
)

func getNumBits(read, max, chunk, align int) int {
	bits := 1
	if align != chunk {
		bits += rand.Intn(chunk / align)
	}
	bits *= align
	if read+bits > max {
		bits = max - read
	}
	if bits > chunk {
		panic("too many bits")
	}
	return bits
}

func makeSource(size int) []uint8 {
	src := make([]uint8, size)
	for i := range src {
		src[i] = uint8(rand.Intn(0xFF))
	}
	return src[:]
}

func flushCheck(t *testing.T, w *Writer) {
	err := w.Flush()
	if err != nil {
		t.Fatal("unexpected error during flush", err)
	}
}

func compare(t *testing.T, src, dst []uint8) {
	if bytes.Equal(src, dst) {
		return
	}
	t.Log(hex.Dump(src))
	t.Log(hex.Dump(dst))
	t.Fatal("invalid output")
}

func testWrites(w *Writer, t *testing.T, align int, src []uint8) {
	max := len(src) * 8
	for read := 0; read < max; {
		bits := getNumBits(read, max, 32, align)
		idx := read >> 3
		fill := read - idx*8
		if idx*8 > max-64 {
			rewind := max - 64
			fill += idx*8 - rewind
			idx = rewind >> 3
		}
		block := binary.BigEndian.Uint64(src[idx:])
		block >>= uint(64 - bits - fill)
		value := uint32(block & 0xFFFFFFFF)
		BigEndian.PutUint32(w, uint(bits), value)
		read += bits
	}
	flushCheck(t, w)
}

func TestWrites(t *testing.T) {
	src := makeSource(512)
	dst := make([]uint8, len(src))
	for i := 32; i > 0; i >>= 1 {
		w := NewWriter(dst)
		testWrites(w, t, i, src)
		compare(t, src, dst)
	}
}

func TestSmall64BigEndianWrite(t *testing.T) {
	buf := make([]uint8, 5)
	w := NewWriter(buf)
	BigEndian.PutUint64(w, 33, 0xFFFFFFFE00000001)
	BigEndian.PutUint32(w, 7, 0)
	w.Flush()
	compare(t, buf, []uint8{0x00, 0x00, 0x00, 0x00, 0x80})
}

func TestSmall64LittleEndianWrite(t *testing.T) {
	buf := make([]uint8, 5)
	w := NewWriter(buf)
	LittleEndian.PutUint64(w, 33, 0xFFFFFFFE00000001)
	LittleEndian.PutUint32(w, 7, 0)
	w.Flush()
	compare(t, buf, []uint8{0x01, 0x00, 0x00, 0x00, 0x00})
}

func TestBigEndianWrite(t *testing.T) {
	buf := make([]uint8, 8)
	w := NewWriter(buf)
	BigEndian.PutUint64(w, 64, 0x0123456789ABCDEF)
	w.Flush()
	compare(t, buf, []uint8{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF})
}

func TestLittleEndianWrite(t *testing.T) {
	buf := make([]uint8, 8)
	w := NewWriter(buf)
	LittleEndian.PutUint64(w, 64, 0x0123456789ABCDEF)
	w.Flush()
	compare(t, buf, []uint8{0xEF, 0xCD, 0xAB, 0x89, 0x67, 0x45, 0x23, 0x01})
}

func checkError(t *testing.T, expected, actual error) {
	if actual != expected {
		t.Fatal("expecting", expected, "got", actual)
	}
}

func TestFlushErrors(t *testing.T) {
	buf := make([]uint8, 2)

	w := NewWriter(buf)
	BigEndian.PutUint32(w, 9, 0)
	checkError(t, ErrUnderflow, w.Flush())

	w = NewWriter(buf)
	BigEndian.PutUint32(w, 16, 0)
	checkError(t, nil, w.Flush())

	w = NewWriter(buf)
	BigEndian.PutUint32(w, 17, 0)
	checkError(t, ErrOverflow, w.Flush())
}

func prepareBenchmark(size, chunk, align int) ([]uint8, []uint, []uint64, int) {
	buf := make([]uint8, size)
	bits := make([]uint, size)
	values := make([]uint64, size)
	idx := 0
	last := 0
	for i := 0; i < size; i++ {
		val := getNumBits(idx, size*8, chunk, align)
		idx += val
		if val != 0 {
			last = i + 1
		}
		bits[i] = uint(val)
		values[i] = uint64(rand.Uint32())<<32 + uint64(rand.Uint32())
	}
	return buf, bits, values, last
}

func bigWrite32(w *Writer, bits []uint, values []uint64, last int) {
	for j := 0; j < last; j++ {
		BigEndian.PutUint32(w, bits[j], uint32(values[j]))
	}
}

func bigWrite64(w *Writer, bits []uint, values []uint64, last int) {
	for j := 0; j < last; j++ {
		BigEndian.PutUint64(w, bits[j], values[j])
	}
}

func littleWrite32(w *Writer, bits []uint, values []uint64, last int) {
	for j := 0; j < last; j++ {
		LittleEndian.PutUint32(w, bits[j], uint32(values[j]))
	}
}

func littleWrite64(w *Writer, bits []uint, values []uint64, last int) {
	for j := 0; j < last; j++ {
		LittleEndian.PutUint64(w, bits[j], values[j])
	}
}

type WriteOp func(*Writer, []uint, []uint64, int)

func benchmarkWrites(b *testing.B, op WriteOp, chunk, align int) {
	b.StopTimer()
	size := 1 << 12
	buf, bits, values, last := prepareBenchmark(size, chunk, align)
	b.SetBytes(int64(len(buf)))
	b.StartTimer()
	for i := 0; i < b.N; i++ {
		w := NewWriter(buf)
		op(w, bits, values, last)
	}
}

func BenchmarkBigEndianWriteUint32Align1(b *testing.B)     { benchmarkWrites(b, bigWrite32, 32, 1) }
func BenchmarkBigEndianWriteUint32Align32(b *testing.B)    { benchmarkWrites(b, bigWrite32, 32, 32) }
func BenchmarkBigEndianWriteUint64Align1(b *testing.B)     { benchmarkWrites(b, bigWrite64, 64, 1) }
func BenchmarkBigEndianWriteUint64Align32(b *testing.B)    { benchmarkWrites(b, bigWrite64, 64, 32) }
func BenchmarkBigEndianWriteUint64Align64(b *testing.B)    { benchmarkWrites(b, bigWrite64, 64, 64) }
func BenchmarkLittleEndianWriteUint32Align1(b *testing.B)  { benchmarkWrites(b, littleWrite32, 32, 1) }
func BenchmarkLittleEndianWriteUint32Align32(b *testing.B) { benchmarkWrites(b, littleWrite32, 32, 32) }
func BenchmarkLittleEndianWriteUint64Align1(b *testing.B)  { benchmarkWrites(b, littleWrite64, 64, 1) }
func BenchmarkLittleEndianWriteUint64Align32(b *testing.B) { benchmarkWrites(b, littleWrite64, 64, 32) }
func BenchmarkLittleEndianWriteUint64Align64(b *testing.B) { benchmarkWrites(b, littleWrite64, 64, 64) }
<|endoftext|>