<commit_before>// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris windows

package runtime

import "unsafe"

func gogetenv(key string) string {
	env := environ()
	if env == nil {
		throw("getenv before env init")
	}
	for _, s := range env {
		if len(s) > len(key) && s[len(key)] == '=' && s[:len(key)] == key {
			return s[len(key)+1:]
		}
	}
	return ""
}

var _cgo_setenv unsafe.Pointer   // pointer to C function
var _cgo_unsetenv unsafe.Pointer // pointer to C function

// Update the C environment if cgo is loaded.
// Called from syscall.Setenv.
//go:linkname syscall_setenv_c syscall.setenv_c
func syscall_setenv_c(k string, v string) {
	if _cgo_setenv == nil {
		return
	}
	arg := [2]unsafe.Pointer{cstring(k), cstring(v)}
	asmcgocall(_cgo_setenv, unsafe.Pointer(&arg))
}

// Update the C environment if cgo is loaded.
// Called from syscall.unsetenv.
//go:linkname syscall_unsetenv_c syscall.unsetenv_c
func syscall_unsetenv_c(k string) {
	if _cgo_unsetenv == nil {
		return
	}
	arg := [1]unsafe.Pointer{cstring(k)}
	asmcgocall(_cgo_unsetenv, unsafe.Pointer(&arg))
}

func cstring(s string) unsafe.Pointer {
	p := make([]byte, len(s)+1)
	copy(p, s)
	return unsafe.Pointer(&p[0])
}
<commit_msg>runtime: look up runtime env variables case insensitively on Windows<commit_after>// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris windows

package runtime

import "unsafe"

func gogetenv(key string) string {
	env := environ()
	if env == nil {
		throw("getenv before env init")
	}
	for _, s := range env {
		if len(s) > len(key) && s[len(key)] == '=' && envKeyEqual(s[:len(key)], key) {
			return s[len(key)+1:]
		}
	}
	return ""
}

// envKeyEqual reports whether a == b, with ASCII-only case insensitivity
// on Windows. The two strings must have the same length.
func envKeyEqual(a, b string) bool {
	if GOOS == "windows" { // case insensitive
		for i := 0; i < len(a); i++ {
			ca, cb := a[i], b[i]
			if ca == cb || lowerASCII(ca) == lowerASCII(cb) {
				continue
			}
			return false
		}
		return true
	}
	return a == b
}

func lowerASCII(c byte) byte {
	if 'A' <= c && c <= 'Z' {
		return c + ('a' - 'A')
	}
	return c
}

var _cgo_setenv unsafe.Pointer   // pointer to C function
var _cgo_unsetenv unsafe.Pointer // pointer to C function

// Update the C environment if cgo is loaded.
// Called from syscall.Setenv.
//go:linkname syscall_setenv_c syscall.setenv_c
func syscall_setenv_c(k string, v string) {
	if _cgo_setenv == nil {
		return
	}
	arg := [2]unsafe.Pointer{cstring(k), cstring(v)}
	asmcgocall(_cgo_setenv, unsafe.Pointer(&arg))
}

// Update the C environment if cgo is loaded.
// Called from syscall.unsetenv.
//go:linkname syscall_unsetenv_c syscall.unsetenv_c
func syscall_unsetenv_c(k string) {
	if _cgo_unsetenv == nil {
		return
	}
	arg := [1]unsafe.Pointer{cstring(k)}
	asmcgocall(_cgo_unsetenv, unsafe.Pointer(&arg))
}

func cstring(s string) unsafe.Pointer {
	p := make([]byte, len(s)+1)
	copy(p, s)
	return unsafe.Pointer(&p[0])
}
<|endoftext|>


<commit_before>package server

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"sync"

	"github.com/open-lambda/open-lambda/ol/common"
)

const (
	BOSS_STATUS_PATH = "/bstatus"
	SCALING_PATH     = "/scaling/worker_count"
)

type Boss struct {
	mutex      sync.Mutex
}

var m = map[string][]int{"workers": []int{}}

func (b *Boss) BossStatus(w http.ResponseWriter, r *http.Request) {
	b.mutex.Lock()
	defer b.mutex.Unlock()

	log.Printf("Receive request to %s\n", r.URL.Path)
	if b, err := json.MarshalIndent(m, "", "\t"); err != nil {
		panic(err)
	} else {
		w.Write(b)
	}
}

func (b *Boss) ScalingWorker(w http.ResponseWriter, r *http.Request) {
	b.mutex.Lock()
	defer b.mutex.Unlock()

	log.Printf("Receive request to %s\n", r.URL.Path)
	var s []int
	m["workers"] = append(s, 1)
	for k, v := range m {
		fmt.Println(k, "value is", v)
	}
	if b, err := json.MarshalIndent(m, "", "\t"); err != nil {
		panic(err)
	} else {
		w.Write(b)
	}
}

func BossMain() (err error) {
	boss := Boss{}

	// things shared by all servers
	http.HandleFunc(BOSS_STATUS_PATH, boss.BossStatus)
	http.HandleFunc(SCALING_PATH, boss.ScalingWorker)

	port := fmt.Sprintf(":%s", common.Conf.Worker_port)
	fmt.Printf("Listen on port %s\n", port)
	log.Fatal(http.ListenAndServe(port, nil))
	panic("ListenAndServe should never return")
}
<commit_msg>extract worker count from POST body<commit_after>package server

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"sync"
	"io"
	"strconv"

	"github.com/open-lambda/open-lambda/ol/common"
)

const (
	BOSS_STATUS_PATH = "/bstatus"
	SCALING_PATH     = "/scaling/worker_count"
)

type Boss struct {
	mutex      sync.Mutex
}

var m = map[string][]int{"workers": []int{}}

func (b *Boss) BossStatus(w http.ResponseWriter, r *http.Request) {
	b.mutex.Lock()
	defer b.mutex.Unlock()

	log.Printf("Receive request to %s\n", r.URL.Path)
	if b, err := json.MarshalIndent(m, "", "\t"); err != nil {
		panic(err)
	} else {
		w.Write(b)
	}
}

func (b *Boss) ScalingWorker(w http.ResponseWriter, r *http.Request) {
	b.mutex.Lock()
	defer b.mutex.Unlock()

	// STEP 1: get int (worker count) from POST body, or return an error
	if r.Method != "POST" {
		w.WriteHeader(http.StatusMethodNotAllowed)
		_, err := w.Write([]byte("POST a count to /scaling/worker_count\n"))
		if err != nil {
			log.Printf("(1) could not write web response: %s\n", err.Error())
		}
		return
	}

	contents, err := io.ReadAll(r.Body)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		_, err := w.Write([]byte("could not read body of web request\n"))
		if err != nil {
			log.Printf("(2) could not write web response: %s\n", err.Error())
		}
		return
	}

	worker_count, err := strconv.Atoi(string(contents))
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		_, err := w.Write([]byte("body of post to /scaling/worker_count should be an int\n"))
		if err != nil {
			log.Printf("(3) could not write web response: %s\n", err.Error())
		}
		return
	}

	// STEP 2: adjust worker count (TODO)

	log.Printf("Receive request to %s, worker_count of %d requested\n", r.URL.Path, worker_count)
	var s []int
	m["workers"] = append(s, 1)
	for k, v := range m {
		fmt.Println(k, "value is", v)
	}
	if b, err := json.MarshalIndent(m, "", "\t"); err != nil {
		panic(err)
	} else {
		w.Write(b)
	}
}

func BossMain() (err error) {
	boss := Boss{}

	// things shared by all servers
	http.HandleFunc(BOSS_STATUS_PATH, boss.BossStatus)
	http.HandleFunc(SCALING_PATH, boss.ScalingWorker)

	port := fmt.Sprintf(":%s", common.Conf.Worker_port)
	fmt.Printf("Listen on port %s\n", port)
	log.Fatal(http.ListenAndServe(port, nil))
	panic("ListenAndServe should never return")
}
<|endoftext|>


<commit_before>package main_test

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"

	. "ci.guzzler.io/guzzler/corcel"
	"ci.guzzler.io/guzzler/corcel/test"

	"github.com/guzzlerio/rizo"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = FDescribe("ExecutionPlanHttpRequest", func() {
	BeforeEach(func() {
		TestServer.Clear()
		factory := rizo.HTTPResponseFactory(func(w http.ResponseWriter) {
			w.WriteHeader(http.StatusOK)
		})

		TestServer.Use(factory).For(rizo.RequestWithPath("/people"))
	})

	It("Supplies a payload to the HTTP Request", func() {
		planBuilder := test.NewYamlPlanBuilder()

		path := "/people"
		body := "Zee Body"

		planBuilder.
			CreateJob().
			CreateStep().
			ToExecuteAction(planBuilder.HTTPRequestAction().URL(TestServer.CreateURL(path)).Body(body).Build())

		err := ExecutePlanBuilder(planBuilder)
		Expect(err).To(BeNil())
		Expect(TestServer.Find(rizo.RequestWithPath(path), rizo.RequestWithBody(body))).To(Equal(true))
	})

	It("Supplies a payload as a file reference to the HTTP Request", func() {
		content := []byte("temporary file's content")
		dir, err := ioutil.TempDir("", "ExecutionPlanHttpRequest")
		if err != nil {
			log.Fatal(err)
		}

		defer os.RemoveAll(dir) // clean up

		tmpfn := filepath.Join(dir, "tmpfile")
		if err := ioutil.WriteFile(tmpfn, content, 0666); err != nil {
			log.Fatal(err)
		}

		path := "/people"
		body := fmt.Sprintf("@%s", tmpfn)

		planBuilder := test.NewYamlPlanBuilder()

		planBuilder.
			CreateJob().
			CreateStep().
			ToExecuteAction(planBuilder.HTTPRequestAction().URL(TestServer.CreateURL(path)).Body(body).Build())

		err = ExecutePlanBuilder(planBuilder)
		Expect(err).To(BeNil())
		Expect(TestServer.Find(rizo.RequestWithPath(path), rizo.RequestWithBody(string(content)))).To(Equal(true))
	})
})
<commit_msg>Adding a pending test to remember to look at the effect of a path which does not exist<commit_after>package main_test

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"

	. "ci.guzzler.io/guzzler/corcel"
	"ci.guzzler.io/guzzler/corcel/test"

	"github.com/guzzlerio/rizo"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = FDescribe("ExecutionPlanHttpRequest", func() {
	BeforeEach(func() {
		TestServer.Clear()
		factory := rizo.HTTPResponseFactory(func(w http.ResponseWriter) {
			w.WriteHeader(http.StatusOK)
		})

		TestServer.Use(factory).For(rizo.RequestWithPath("/people"))
	})

	It("Supplies a payload to the HTTP Request", func() {
		planBuilder := test.NewYamlPlanBuilder()

		path := "/people"
		body := "Zee Body"

		planBuilder.
			CreateJob().
			CreateStep().
			ToExecuteAction(planBuilder.HTTPRequestAction().URL(TestServer.CreateURL(path)).Body(body).Build())

		err := ExecutePlanBuilder(planBuilder)
		Expect(err).To(BeNil())
		Expect(TestServer.Find(rizo.RequestWithPath(path), rizo.RequestWithBody(body))).To(Equal(true))
	})

	It("Supplies a payload as a file reference to the HTTP Request", func() {
		content := []byte("temporary file's content")
		dir, err := ioutil.TempDir("", "ExecutionPlanHttpRequest")
		if err != nil {
			log.Fatal(err)
		}

		defer os.RemoveAll(dir) // clean up

		tmpfn := filepath.Join(dir, "tmpfile")
		if err := ioutil.WriteFile(tmpfn, content, 0666); err != nil {
			log.Fatal(err)
		}

		path := "/people"
		body := fmt.Sprintf("@%s", tmpfn)

		planBuilder := test.NewYamlPlanBuilder()

		planBuilder.
			CreateJob().
			CreateStep().
			ToExecuteAction(planBuilder.HTTPRequestAction().URL(TestServer.CreateURL(path)).Body(body).Build())

		err = ExecutePlanBuilder(planBuilder)
		Expect(err).To(BeNil())
		Expect(TestServer.Find(rizo.RequestWithPath(path), rizo.RequestWithBody(string(content)))).To(Equal(true))
	})

	PIt("Returns an error when the file path does not exist", func() {})
})
<|endoftext|>


<commit_before>package singularity

// XXX I got started with this, but it needs a dummy implementation of the
// singularity client, which needs extension of go-singularity and
// swagger-client-maker
import (
	"testing"

	"github.com/opentable/go-singularity/dtos"
	"github.com/opentable/sous/lib"
)

func TestBuildingRequestID(t *testing.T) {
	cn := "test-cluster"
	db := &deploymentBuilder{
		clusters: make(sous.Clusters),
		request:  &dtos.SingularityRequest{},
	}
	db.clusters[cn] = &sous.Cluster{}
	if err := db.assignClusterName(); err != nil {
		t.Errorf("unexpect error: %v", err)
	}
	if db.Target.ClusterName != cn {
		t.Errorf("db.Target.ClusterName was %v expected %v", db.Target.ClusterName, cn)
	}
}

func TestBuildingRequestIDTwoClusters(t *testing.T) {
	cn := "test-cluster"
	cn2 := "test-other"
	url := "https://a.singularity.somewhere"
	db := &deploymentBuilder{
		clusters: make(sous.Clusters),
		request:  &dtos.SingularityRequest{Id: "::test-other"},
		req:      SingReq{SourceURL: url},
	}
	db.clusters[cn] = &sous.Cluster{BaseURL: url}
	db.clusters[cn2] = &sous.Cluster{BaseURL: url}
	if err := db.assignClusterName(); err != nil {
		t.Errorf("unexpect error: %v", err)
	}
	if db.Target.ClusterName != cn2 {
		t.Errorf("db.Target.ClusterName was %v expected %v", db.Target.ClusterName, cn2)
	}
}

/*
func TestConstructDeployment(t *testing.T) {
	assert := assert.New(t)

	im := NewDummyNameCache()
	cl := NewDummyRectificationClient(im)
	req := singReq{
		sourceURL: "source.url",
		sing:      &DummyClient{}, //XXX need a dummy client for singularity
		reqParent: &dtos.SingularityRequestParent{
			Request:            &dtos.SingularityRequest{},
			RequestDeployState: &dtos.SingularityRequestDeployState{},
			ActiveDeploy:       &dtos.SingularityDeploy{},
			PendingDeploy:      &dtos.SingularityDeploy{},

			//			ExpiringBounce           *SingularityExpiringBounce           `json:"expiringBounce"`
			//			ExpiringPause            *SingularityExpiringPause            `json:"expiringPause"`
			//			ExpiringScale            *SingularityExpiringScale            `json:"expiringScale"`
			//			ExpiringSkipHealthchecks *SingularityExpiringSkipHealthchecks `json:"expiringSkipHealthchecks"`
			//			PendingDeployState       *SingularityPendingDeploy            `json:"pendingDeployState"`
			//			State                    SingularityRequestParentRequestState `json:"state"`
		},
	}

	//func assembleDeployment(cl RectificationClient, req singReq) (*Deployment, error) {
	uc := newDeploymentBuilder(cl, req)
	err := uc.completeConstruction()
	if assert.NoError(err) {
		if assert.Len(uc.target.DeployConfig.Volumes, 1) {
			assert.Equal("RO", string(uc.target.DeployConfig.Volumes[0].Mode))
		}
	}
}
*/
<commit_msg>Slight expansion to depbuilder test<commit_after>package singularity

// XXX I got started with this, but it needs a dummy implementation of the
// singularity client, which needs extension of go-singularity and
// swagger-client-maker
import (
	"testing"

	"github.com/nyarly/testify/assert"
	"github.com/opentable/go-singularity/dtos"
	"github.com/opentable/sous/lib"
)

func TestBuildingRequestID(t *testing.T) {
	cn := "test-cluster"
	db := &deploymentBuilder{
		clusters: make(sous.Clusters),
		request:  &dtos.SingularityRequest{},
	}
	db.clusters[cn] = &sous.Cluster{}
	if err := db.assignClusterName(); err != nil {
		t.Errorf("unexpect error: %v", err)
	}
	if db.Target.ClusterName != cn {
		t.Errorf("db.Target.ClusterName was %v expected %v", db.Target.ClusterName, cn)
	}
}

func TestBuildingRequestIDTwoClusters(t *testing.T) {
	cn := "test-cluster"
	cn2 := "test-other"
	url := "https://a.singularity.somewhere"
	clusters := make(sous.Clusters)
	clusters[cn] = &sous.Cluster{BaseURL: url}
	clusters[cn2] = &sous.Cluster{BaseURL: url}

	db := &deploymentBuilder{
		clusters: clusters,
		request:  &dtos.SingularityRequest{Id: "::" + cn},
		req:      SingReq{SourceURL: url},
	}
	assert.NoError(t, db.assignClusterName())
	assert.Equal(t, db.Target.ClusterName, cn)

	db2 := &deploymentBuilder{
		clusters: clusters,
		request:  &dtos.SingularityRequest{Id: "::" + cn2},
		req:      SingReq{SourceURL: url},
	}
	assert.NoError(t, db2.assignClusterName())
	assert.Equal(t, db2.Target.ClusterName, cn2)
}

/*
func TestConstructDeployment(t *testing.T) {
	assert := assert.New(t)

	im := NewDummyNameCache()
	cl := NewDummyRectificationClient(im)
	req := singReq{
		sourceURL: "source.url",
		sing:      &DummyClient{}, //XXX need a dummy client for singularity
		reqParent: &dtos.SingularityRequestParent{
			Request:            &dtos.SingularityRequest{},
			RequestDeployState: &dtos.SingularityRequestDeployState{},
			ActiveDeploy:       &dtos.SingularityDeploy{},
			PendingDeploy:      &dtos.SingularityDeploy{},

			//			ExpiringBounce           *SingularityExpiringBounce           `json:"expiringBounce"`
			//			ExpiringPause            *SingularityExpiringPause            `json:"expiringPause"`
			//			ExpiringScale            *SingularityExpiringScale            `json:"expiringScale"`
			//			ExpiringSkipHealthchecks *SingularityExpiringSkipHealthchecks `json:"expiringSkipHealthchecks"`
			//			PendingDeployState       *SingularityPendingDeploy            `json:"pendingDeployState"`
			//			State                    SingularityRequestParentRequestState `json:"state"`
		},
	}

	//func assembleDeployment(cl RectificationClient, req singReq) (*Deployment, error) {
	uc := newDeploymentBuilder(cl, req)
	err := uc.completeConstruction()
	if assert.NoError(err) {
		if assert.Len(uc.target.DeployConfig.Volumes, 1) {
			assert.Equal("RO", string(uc.target.DeployConfig.Volumes[0].Mode))
		}
	}
}
*/
<|endoftext|>


<commit_before>/*
Package grpc provides a Handler and client for agent gRPC connections.
*/
package grpc

import (
	"fmt"
	"net"

	"google.golang.org/grpc"
)

// NewHandler returns a gRPC server that accepts connections from Handle(conn).
// The register function will be called with the grpc.Server to register
// gRPC services with the server.
func NewHandler(addr net.Addr, register func(server *grpc.Server)) *Handler {
	// We don't need to pass tls.Config to the server since it's multiplexed
	// behind the RPC listener, which already has TLS configured.
	srv := grpc.NewServer(
		grpc.StatsHandler(newStatsHandler()),
		grpc.StreamInterceptor((&activeStreamCounter{}).Intercept),
	)
	register(srv)

	lis := &chanListener{addr: addr, conns: make(chan net.Conn)}
	return &Handler{srv: srv, listener: lis}
}

// Handler implements a handler for the rpc server listener, and the
// agent.Component interface for managing the lifecycle of the grpc.Server.
type Handler struct {
	srv      *grpc.Server
	listener *chanListener
}

// Handle the connection by sending it to a channel for the grpc.Server to receive.
func (h *Handler) Handle(conn net.Conn) {
	h.listener.conns <- conn
}

func (h *Handler) Run() error {
	return h.srv.Serve(h.listener)
}

func (h *Handler) Shutdown() error {
	h.srv.Stop()
	return nil
}

// chanListener implements net.Listener for grpc.Server.
type chanListener struct {
	conns chan net.Conn
	addr  net.Addr
}

// Accept blocks until a connection is received from Handle, and then returns the
// connection. Accept implements part of the net.Listener interface for grpc.Server.
func (l *chanListener) Accept() (net.Conn, error) {
	select {
	case c, ok := <-l.conns:
		if !ok {
			return nil, &net.OpError{
				Op:   "accept",
				Net:  l.addr.Network(),
				Addr: l.addr,
				Err:  fmt.Errorf("listener closed"),
			}
		}
		return c, nil
	}
}

func (l *chanListener) Addr() net.Addr {
	return l.addr
}

func (l *chanListener) Close() error {
	close(l.conns)
	return nil
}

// NoOpHandler implements the same methods as Handler, but performs no handling.
// It may be used in place of Handler to disable the grpc server.
type NoOpHandler struct {
	Logger Logger
}

type Logger interface {
	Error(string, ...interface{})
}

func (h NoOpHandler) Handle(conn net.Conn) {
	h.Logger.Error("gRPC conn opened but gRPC RPC is disabled, closing",
		"conn", logConn(conn))
	_ = conn.Close()
}

func (h NoOpHandler) Run() error {
	return nil
}

func (h NoOpHandler) Shutdown() error {
	return nil
}

// logConn is a local copy of github.com/hashicorp/memberlist.LogConn, to avoid
// a large dependency for a minor formatting function.
// logConn is used to keep log formatting consistent.
func logConn(conn net.Conn) string {
	if conn == nil {
		return "from=<unknown address>"
	}
	addr := conn.RemoteAddr()
	if addr == nil {
		return "from=<unknown address>"
	}

	return fmt.Sprintf("from=%s", addr.String())
}
<commit_msg>agent/grpc: use a separate channel for closing the Accept<commit_after>/*
Package grpc provides a Handler and client for agent gRPC connections.
*/
package grpc

import (
	"fmt"
	"net"

	"google.golang.org/grpc"
)

// NewHandler returns a gRPC server that accepts connections from Handle(conn).
// The register function will be called with the grpc.Server to register
// gRPC services with the server.
func NewHandler(addr net.Addr, register func(server *grpc.Server)) *Handler {
	// We don't need to pass tls.Config to the server since it's multiplexed
	// behind the RPC listener, which already has TLS configured.
	srv := grpc.NewServer(
		grpc.StatsHandler(newStatsHandler()),
		grpc.StreamInterceptor((&activeStreamCounter{}).Intercept),
	)
	register(srv)

	lis := &chanListener{addr: addr, conns: make(chan net.Conn), done: make(chan struct{})}
	return &Handler{srv: srv, listener: lis}
}

// Handler implements a handler for the rpc server listener, and the
// agent.Component interface for managing the lifecycle of the grpc.Server.
type Handler struct {
	srv      *grpc.Server
	listener *chanListener
}

// Handle the connection by sending it to a channel for the grpc.Server to receive.
func (h *Handler) Handle(conn net.Conn) {
	h.listener.conns <- conn
}

func (h *Handler) Run() error {
	return h.srv.Serve(h.listener)
}

func (h *Handler) Shutdown() error {
	h.srv.Stop()
	return nil
}

// chanListener implements net.Listener for grpc.Server.
type chanListener struct {
	conns chan net.Conn
	addr  net.Addr
	done  chan struct{}
}

// Accept blocks until a connection is received from Handle, and then returns the
// connection. Accept implements part of the net.Listener interface for grpc.Server.
func (l *chanListener) Accept() (net.Conn, error) {
	select {
	case c := <-l.conns:
		return c, nil
	case <-l.done:
		return nil, &net.OpError{
			Op:   "accept",
			Net:  l.addr.Network(),
			Addr: l.addr,
			Err:  fmt.Errorf("listener closed"),
		}
	}
}

func (l *chanListener) Addr() net.Addr {
	return l.addr
}

func (l *chanListener) Close() error {
	close(l.done)
	return nil
}

// NoOpHandler implements the same methods as Handler, but performs no handling.
// It may be used in place of Handler to disable the grpc server.
type NoOpHandler struct {
	Logger Logger
}

type Logger interface {
	Error(string, ...interface{})
}

func (h NoOpHandler) Handle(conn net.Conn) {
	h.Logger.Error("gRPC conn opened but gRPC RPC is disabled, closing",
		"conn", logConn(conn))
	_ = conn.Close()
}

func (h NoOpHandler) Run() error {
	return nil
}

func (h NoOpHandler) Shutdown() error {
	return nil
}

// logConn is a local copy of github.com/hashicorp/memberlist.LogConn, to avoid
// a large dependency for a minor formatting function.
// logConn is used to keep log formatting consistent.
func logConn(conn net.Conn) string {
	if conn == nil {
		return "from=<unknown address>"
	}
	addr := conn.RemoteAddr()
	if addr == nil {
		return "from=<unknown address>"
	}

	return fmt.Sprintf("from=%s", addr.String())
}
<|endoftext|>


<commit_before>// Copyright 2017 The LUCI 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 streamserver

import (
	"context"
	"strings"
	"testing"

	"go.chromium.org/luci/logdog/client/butlerlib/streamclient"

	. "github.com/smartystreets/goconvey/convey"
	. "go.chromium.org/luci/common/testing/assertions"
)

func TestWindowsNamedPipeServer(t *testing.T) {
	t.Parallel()

	Convey(`A named pipe server`, t, func() {
		ctx := context.Background()

		Convey(`Will generate a prefix if none is provided.`, func() {
			srv, err := newStreamServer(ctx, "")
			So(err, ShouldBeNil)

			So(srv.Address(), ShouldStartWith, "net.pipe:"+defaultWinPipePrefix)
		})

		Convey(`Will refuse to create if longer than maximum length.`, func() {
			_, err := newStreamServer(ctx, strings.Repeat("A", maxWindowsNamedPipeLength+1))
			So(err, ShouldErrLike, "path exceeds maximum length")
		})

		Convey(`When created and listening.`, func() {
			svr, err := newStreamServer(ctx, "ButlerNamedPipeTest")
			So(err, ShouldBeNil)

			So(svr.Listen(), ShouldBeNil)
			defer svr.Close()

			client, err := streamclient.New(svr.Address(), "")
			So(err, ShouldBeNil)

			testClientServer(svr, client)
		})
	})
}
<commit_msg>[butler/streamserver] Disable windows tests again; still not fixed.<commit_after>// Copyright 2017 The LUCI 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 streamserver

import (
	"context"
	"strings"
	"testing"

	"go.chromium.org/luci/logdog/client/butlerlib/streamclient"

	. "github.com/smartystreets/goconvey/convey"
	. "go.chromium.org/luci/common/testing/assertions"
)

func TestWindowsNamedPipeServer(t *testing.T) {
	t.Skip("test disabled on windows for flake: crbug.com/998936")

	t.Parallel()

	Convey(`A named pipe server`, t, func() {
		ctx := context.Background()

		Convey(`Will generate a prefix if none is provided.`, func() {
			srv, err := newStreamServer(ctx, "")
			So(err, ShouldBeNil)

			So(srv.Address(), ShouldStartWith, "net.pipe:"+defaultWinPipePrefix)
		})

		Convey(`Will refuse to create if longer than maximum length.`, func() {
			_, err := newStreamServer(ctx, strings.Repeat("A", maxWindowsNamedPipeLength+1))
			So(err, ShouldErrLike, "path exceeds maximum length")
		})

		Convey(`When created and listening.`, func() {
			svr, err := newStreamServer(ctx, "ButlerNamedPipeTest")
			So(err, ShouldBeNil)

			So(svr.Listen(), ShouldBeNil)
			defer svr.Close()

			client, err := streamclient.New(svr.Address(), "")
			So(err, ShouldBeNil)

			testClientServer(svr, client)
		})
	})
}
<|endoftext|>


<commit_before>package net

import (
	"io"

	conn "github.com/jbenet/go-ipfs/net/conn"
	// swarm "github.com/jbenet/go-ipfs/net/swarm2"
	peer "github.com/jbenet/go-ipfs/peer"

	context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
	ctxgroup "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup"
	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
)

// ProtocolID is an identifier used to write protocol headers in streams.
type ProtocolID string

// These are the ProtocolIDs of the protocols running. It is useful
// to keep them in one place.
const (
	ProtocolTesting  ProtocolID = "/ipfs/testing"
	ProtocolBitswap  ProtocolID = "/ipfs/bitswap"
	ProtocolDHT      ProtocolID = "/ipfs/dht"
	ProtocolIdentify ProtocolID = "/ipfs/id"
	ProtocolDiag     ProtocolID = "/ipfs/diagnostics"
)

// MessageSizeMax is a soft (recommended) maximum for network messages.
// One can write more, as the interface is a stream. But it is useful
// to bunch it up into multiple read/writes when the whole message is
// a single, large serialized object.
const MessageSizeMax = 2 << 22 // 4MB

// Stream represents a bidirectional channel between two agents in
// the IPFS network. "agent" is as granular as desired, potentially
// being a "request -> reply" pair, or whole protocols.
// Streams are backed by SPDY streams underneath the hood.
type Stream interface {
	io.Reader
	io.Writer
	io.Closer

	// Conn returns the connection this stream is part of.
	Conn() Conn
}

// StreamHandler is the function protocols who wish to listen to
// incoming streams must implement.
type StreamHandler func(Stream)

type StreamHandlerMap map[ProtocolID]StreamHandler

// Conn is a connection to a remote peer. It multiplexes streams.
// Usually there is no need to use a Conn directly, but it may
// be useful to get information about the peer on the other side:
//  stream.Conn().RemotePeer()
type Conn interface {
	conn.PeerConn

	// NewStreamWithProtocol constructs a new Stream over this conn.
	NewStreamWithProtocol(pr ProtocolID) (Stream, error)
}

// Network is the interface IPFS uses for connecting to the world.
// It dials and listens for connections. it uses a Swarm to pool
// connnections (see swarm pkg, and peerstream.Swarm). Connections
// are encrypted with a TLS-like protocol.
type Network interface {
	Dialer
	io.Closer

	// SetHandler sets the protocol handler on the Network's Muxer.
	// This operation is threadsafe.
	SetHandler(ProtocolID, StreamHandler)

	// Protocols returns the list of protocols this network currently
	// has registered handlers for.
	Protocols() []ProtocolID

	// NewStream returns a new stream to given peer p.
	// If there is no connection to p, attempts to create one.
	// If ProtocolID is "", writes no header.
	NewStream(ProtocolID, peer.ID) (Stream, error)

	// Peers returns the peers connected
	Peers() []peer.ID

	// Conns returns the connections in this Netowrk
	Conns() []Conn

	// ConnsToPeer returns the connections in this Netowrk for given peer.
	ConnsToPeer(p peer.ID) []Conn

	// BandwidthTotals returns the total number of bytes passed through
	// the network since it was instantiated
	BandwidthTotals() (uint64, uint64)

	// ListenAddresses returns a list of addresses at which this network listens.
	ListenAddresses() []ma.Multiaddr

	// InterfaceListenAddresses returns a list of addresses at which this network
	// listens. It expands "any interface" addresses (/ip4/0.0.0.0, /ip6/::) to
	// use the known local interfaces.
	InterfaceListenAddresses() ([]ma.Multiaddr, error)

	// CtxGroup returns the network's contextGroup
	CtxGroup() ctxgroup.ContextGroup

	// IdentifyProtocol returns the instance of the object running the Identify
	// Protocol. This is what runs the ifps handshake-- this should be removed
	// if this abstracted out to its own package.
	IdentifyProtocol() *IDService
}

// Dialer represents a service that can dial out to peers
// (this is usually just a Network, but other services may not need the whole
// stack, and thus it becomes easier to mock)
type Dialer interface {

	// Peerstore returns the internal peerstore
	// This is useful to tell the dialer about a new address for a peer.
	// Or use one of the public keys found out over the network.
	Peerstore() peer.Peerstore

	// LocalPeer returns the local peer associated with this network
	LocalPeer() peer.ID

	// DialPeer attempts to establish a connection to a given peer
	DialPeer(context.Context, peer.ID) error

	// ClosePeer closes the connection to a given peer
	ClosePeer(peer.ID) error

	// Connectedness returns a state signaling connection capabilities
	Connectedness(peer.ID) Connectedness
}

// Connectedness signals the capacity for a connection with a given node.
// It is used to signal to services and other peers whether a node is reachable.
type Connectedness int

const (
	// NotConnected means no connection to peer, and no extra information (default)
	NotConnected Connectedness = iota

	// Connected means has an open, live connection to peer
	Connected

	// CanConnect means recently connected to peer, terminated gracefully
	CanConnect

	// CannotConnect means recently attempted connecting but failed to connect.
	// (should signal "made effort, failed")
	CannotConnect
)
<commit_msg>net/interface: move more to Dialer<commit_after>package net

import (
	"io"

	conn "github.com/jbenet/go-ipfs/net/conn"
	// swarm "github.com/jbenet/go-ipfs/net/swarm2"
	peer "github.com/jbenet/go-ipfs/peer"

	context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
	ctxgroup "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup"
	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
)

// ProtocolID is an identifier used to write protocol headers in streams.
type ProtocolID string

// These are the ProtocolIDs of the protocols running. It is useful
// to keep them in one place.
const (
	ProtocolTesting  ProtocolID = "/ipfs/testing"
	ProtocolBitswap  ProtocolID = "/ipfs/bitswap"
	ProtocolDHT      ProtocolID = "/ipfs/dht"
	ProtocolIdentify ProtocolID = "/ipfs/id"
	ProtocolDiag     ProtocolID = "/ipfs/diagnostics"
)

// MessageSizeMax is a soft (recommended) maximum for network messages.
// One can write more, as the interface is a stream. But it is useful
// to bunch it up into multiple read/writes when the whole message is
// a single, large serialized object.
const MessageSizeMax = 2 << 22 // 4MB

// Stream represents a bidirectional channel between two agents in
// the IPFS network. "agent" is as granular as desired, potentially
// being a "request -> reply" pair, or whole protocols.
// Streams are backed by SPDY streams underneath the hood.
type Stream interface {
	io.Reader
	io.Writer
	io.Closer

	// Conn returns the connection this stream is part of.
	Conn() Conn
}

// StreamHandler is the function protocols who wish to listen to
// incoming streams must implement.
type StreamHandler func(Stream)

type StreamHandlerMap map[ProtocolID]StreamHandler

// Conn is a connection to a remote peer. It multiplexes streams.
// Usually there is no need to use a Conn directly, but it may
// be useful to get information about the peer on the other side:
//  stream.Conn().RemotePeer()
type Conn interface {
	conn.PeerConn

	// NewStreamWithProtocol constructs a new Stream over this conn.
	NewStreamWithProtocol(pr ProtocolID) (Stream, error)
}

// Network is the interface IPFS uses for connecting to the world.
// It dials and listens for connections. it uses a Swarm to pool
// connnections (see swarm pkg, and peerstream.Swarm). Connections
// are encrypted with a TLS-like protocol.
type Network interface {
	Dialer
	io.Closer

	// SetHandler sets the protocol handler on the Network's Muxer.
	// This operation is threadsafe.
	SetHandler(ProtocolID, StreamHandler)

	// Protocols returns the list of protocols this network currently
	// has registered handlers for.
	Protocols() []ProtocolID

	// NewStream returns a new stream to given peer p.
	// If there is no connection to p, attempts to create one.
	// If ProtocolID is "", writes no header.
	NewStream(ProtocolID, peer.ID) (Stream, error)

	// BandwidthTotals returns the total number of bytes passed through
	// the network since it was instantiated
	BandwidthTotals() (uint64, uint64)

	// ListenAddresses returns a list of addresses at which this network listens.
	ListenAddresses() []ma.Multiaddr

	// InterfaceListenAddresses returns a list of addresses at which this network
	// listens. It expands "any interface" addresses (/ip4/0.0.0.0, /ip6/::) to
	// use the known local interfaces.
	InterfaceListenAddresses() ([]ma.Multiaddr, error)

	// CtxGroup returns the network's contextGroup
	CtxGroup() ctxgroup.ContextGroup

	// IdentifyProtocol returns the instance of the object running the Identify
	// Protocol. This is what runs the ifps handshake-- this should be removed
	// if this abstracted out to its own package.
	IdentifyProtocol() *IDService
}

// Dialer represents a service that can dial out to peers
// (this is usually just a Network, but other services may not need the whole
// stack, and thus it becomes easier to mock)
type Dialer interface {

	// Peerstore returns the internal peerstore
	// This is useful to tell the dialer about a new address for a peer.
	// Or use one of the public keys found out over the network.
	Peerstore() peer.Peerstore

	// LocalPeer returns the local peer associated with this network
	LocalPeer() peer.ID

	// DialPeer attempts to establish a connection to a given peer
	DialPeer(context.Context, peer.ID) error

	// ClosePeer closes the connection to a given peer
	ClosePeer(peer.ID) error

	// Connectedness returns a state signaling connection capabilities
	Connectedness(peer.ID) Connectedness

	// Peers returns the peers connected
	Peers() []peer.ID

	// Conns returns the connections in this Netowrk
	Conns() []Conn

	// ConnsToPeer returns the connections in this Netowrk for given peer.
	ConnsToPeer(p peer.ID) []Conn
}

// Connectedness signals the capacity for a connection with a given node.
// It is used to signal to services and other peers whether a node is reachable.
type Connectedness int

const (
	// NotConnected means no connection to peer, and no extra information (default)
	NotConnected Connectedness = iota

	// Connected means has an open, live connection to peer
	Connected

	// CanConnect means recently connected to peer, terminated gracefully
	CanConnect

	// CannotConnect means recently attempted connecting but failed to connect.
	// (should signal "made effort, failed")
	CannotConnect
)
<|endoftext|>


<commit_before>package testutil

import (
	"bytes"
	"fmt"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"veyron.io/tools/lib/envutil"
	"veyron.io/tools/lib/util"
)

const (
	DefaultIntegrationTestTimeout = 2 * time.Minute
)

// binPackages enumerates the Go commands used by veyron integration tests.
//
// TODO(jingjin): port the integration test scripts from shell to Go
// and make them use a build cache to share binaries.
var binPackages = []string{
	"veyron.io/apps/tunnel/tunneld",
	"veyron.io/apps/tunnel/vsh",
	"veyron.io/playground/builder",
	"veyron.io/veyron/veyron/security/agent/agentd",
	"veyron.io/veyron/veyron/security/agent/pingpong",
	"veyron.io/veyron/veyron/services/mgmt/application/applicationd",
	"veyron.io/veyron/veyron/services/mgmt/binary/binaryd",
	"veyron.io/veyron/veyron/services/mgmt/build/buildd",
	"veyron.io/veyron/veyron/services/mgmt/profile/profiled",
	"veyron.io/veyron/veyron/services/mounttable/mounttabled",
	"veyron.io/veyron/veyron/services/proxy/proxyd",
	"veyron.io/veyron/veyron/tools/application",
	"veyron.io/veyron/veyron/tools/binary",
	"veyron.io/veyron/veyron/tools/build",
	"veyron.io/veyron/veyron/tools/debug",
	"veyron.io/veyron/veyron/tools/mounttable",
	"veyron.io/veyron/veyron/tools/principal",
	"veyron.io/veyron/veyron/tools/profile",
	"veyron.io/veyron/veyron/tools/naming/simulator",
	"veyron.io/veyron/veyron2/vdl/vdl",
	"veyron.io/wspr/veyron/services/wsprd",
}

// buildBinaries builds Go binaries enumerated by the binPackages list.
func buildBinaries(ctx *util.Context, testName string) (*TestResult, error) {
	// Create a pool of workers.
	fmt.Fprintf(ctx.Stdout(), "building binaries...\n")
	numPkgs := len(binPackages)
	tasks := make(chan string, numPkgs)
	taskResults := make(chan buildResult, numPkgs)
	for i := 0; i < runtime.NumCPU(); i++ {
		go buildWorker(nil, tasks, taskResults)
	}

	// Distribute work to workers.
	for _, pkg := range binPackages {
		tasks <- pkg
	}
	close(tasks)

	// Collect the results.
	allPassed, suites := true, []testSuite{}
	for i := 0; i < numPkgs; i++ {
		result := <-taskResults
		s := testSuite{Name: result.pkg}
		c := testCase{
			Classname: result.pkg,
			Name:      "Build",
			Time:      fmt.Sprintf("%.2f", result.time.Seconds()),
		}
		if result.status != buildPassed {
			Fail(ctx, "%s\n%v\n", result.pkg, result.output)
			f := testFailure{
				Message: "build",
				Data:    result.output,
			}
			c.Failures = append(c.Failures, f)
			allPassed = false
			s.Failures++
		} else {
			Pass(ctx, "%s\n", result.pkg)
		}
		s.Tests++
		s.Cases = append(s.Cases, c)
		suites = append(suites, s)
	}

	// Create the xUnit report.
	close(taskResults)
	if err := createXUnitReport(ctx, testName, suites); err != nil {
		return nil, err
	}
	if !allPassed {
		return &TestResult{Status: TestFailed}, nil
	}
	return &TestResult{Status: TestPassed}, nil
}

// findTestScripts finds all test.sh file from the given root dirs.
func findTestScripts(ctx *util.Context, rootDirs []string) []string {
	if ctx.DryRun() {
		// In "dry run" mode, no test scripts are executed.
		return nil
	}
	matchedFiles := []string{}
	for _, rootDir := range rootDirs {
		filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error {
			// TODO(sjr): remove /test.sh check once support for shell-based integration tests is removed.
			if strings.HasSuffix(path, string(os.PathSeparator)+"test.sh") || strings.HasSuffix(path, filepath.Join("testdata", "integration_test.go")) {
				matchedFiles = append(matchedFiles, path)
			}
			return nil
		})
	}
	return matchedFiles
}

// runTestScripts runs all test.sh scripts found under
// $VEYRON_ROOT/roadmap/go/src and $VEYRON_ROOT/veyron/go/src.
func runTestScripts(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VeyronRoot()
	if err != nil {
		return nil, err
	}

	// Find all test.sh scripts.
	testScripts := findTestScripts(ctx, []string{
		filepath.Join(root, "veyron", "go", "src"),
		filepath.Join(root, "roadmap", "go", "src"),
		filepath.Join(root, "scripts"),
	})

	// Create a worker pool to run tests in parallel, passing the
	// location of binaries through shell_test_BIN_DIR.
	fmt.Fprintf(ctx.Stdout(), "running tests...\n")
	numTests := len(testScripts)
	tasks := make(chan string, numTests)
	taskResults := make(chan testResult, numTests)
	env := envutil.NewSnapshotFromOS()
	env.Set("shell_test_BIN_DIR", binDirPath())
	env.Set("VEYRON_INTEGRATION_BIN_DIR", binDirPath())
	for i := 0; i < runtime.NumCPU(); i++ {
		go testScriptWorker(root, env.Map(), tasks, taskResults)
	}

	// Send test scripts to free workers in the pool.
	for _, testScript := range testScripts {
		tasks <- testScript
	}
	close(tasks)

	// Collect the results.
	allPassed, suites := true, []testSuite{}
	for i := 0; i < numTests; i++ {
		result := <-taskResults
		s := testSuite{Name: result.pkg}
		c := testCase{
			Classname: result.pkg,
			Name:      "Integration Test",
			Time:      fmt.Sprintf("%.2f", result.time.Seconds()),
		}
		switch result.status {
		case testFailed:
			Fail(ctx, "%s\n%v\n", result.pkg, result.output)
			f := testFailure{
				Message: "test",
				Data:    result.output,
			}
			c.Failures = append(c.Failures, f)
			allPassed = false
			s.Failures++
		case testPassed:
			Pass(ctx, "%s\n", result.pkg)
		}
		s.Tests++
		s.Cases = append(s.Cases, c)
		suites = append(suites, s)
	}
	close(taskResults)

	// Create the xUnit report.
	if err := createXUnitReport(ctx, testName, suites); err != nil {
		return nil, err
	}
	if !allPassed {
		return &TestResult{Status: TestFailed}, nil
	}
	return &TestResult{Status: TestPassed}, nil
}

// testScriptWorker receives tasks from the <tasks> channel, runs
// them, and sends results to the <results> channel.
func testScriptWorker(root string, env map[string]string, tasks <-chan string, results chan<- testResult) {
	var out bytes.Buffer
	ctx := util.NewContext(env, os.Stdin, &out, &out, false, false, false)
	for script := range tasks {
		start := time.Now()
		var args []string
		if strings.HasSuffix(script, ".go") {
			args = []string{"go", "test", script}
		} else {
			// TODO(sjr): remove /test.sh check once support for shell-based integration tests is removed.
			args = []string{"run", script}
		}
		err := ctx.Run().TimedCommand(DefaultIntegrationTestTimeout, "veyron", args...)
		pkgName := path.Dir(script)
		if index := strings.Index(pkgName, "veyron.io"); index != -1 {
			pkgName = pkgName[index:]
		}
		result := testResult{
			pkg:    pkgName,
			time:   time.Now().Sub(start),
			output: out.String(),
		}
		if err != nil {
			result.status = testFailed
		} else {
			result.status = testPassed
		}
		results <- result
		out.Reset()
	}
}

// VeyronIntegrationTest runs veyron integration tests.
func VeyronIntegrationTest(ctx *util.Context, testName string) (*TestResult, error) {
	// Initialize the test.
	cleanup, err := initTest(ctx, testName, []string{"web"})
	if err != nil {
		return nil, err
	}
	defer cleanup()

	// Build all Go binaries used in intergartion test scripts and
	// then run the integration tests. We pre-build the binaries
	// used by multiple test scripts to speed things up.
	if ctx.DryRun() {
		binPackages = nil
	}
	result, err := buildBinaries(ctx, testName)
	if err != nil {
		return nil, err
	}
	if result.Status == TestFailed {
		return result, nil
	}
	result, err = runTestScripts(ctx, testName)
	if err != nil {
		return nil, err
	}
	return result, nil
}
<commit_msg>tools/lib/testutil: update comments in integration.go<commit_after>package testutil

import (
	"bytes"
	"fmt"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"veyron.io/tools/lib/envutil"
	"veyron.io/tools/lib/util"
)

const (
	DefaultIntegrationTestTimeout = 2 * time.Minute
)

// binPackages enumerates the Go commands used by veyron integration tests.
//
// TODO(jingjin): port the integration test scripts from shell to Go
// and make them use a build cache to share binaries.
var binPackages = []string{
	"veyron.io/apps/tunnel/tunneld",
	"veyron.io/apps/tunnel/vsh",
	"veyron.io/playground/builder",
	"veyron.io/veyron/veyron/security/agent/agentd",
	"veyron.io/veyron/veyron/security/agent/pingpong",
	"veyron.io/veyron/veyron/services/mgmt/application/applicationd",
	"veyron.io/veyron/veyron/services/mgmt/binary/binaryd",
	"veyron.io/veyron/veyron/services/mgmt/build/buildd",
	"veyron.io/veyron/veyron/services/mgmt/profile/profiled",
	"veyron.io/veyron/veyron/services/mounttable/mounttabled",
	"veyron.io/veyron/veyron/services/proxy/proxyd",
	"veyron.io/veyron/veyron/tools/application",
	"veyron.io/veyron/veyron/tools/binary",
	"veyron.io/veyron/veyron/tools/build",
	"veyron.io/veyron/veyron/tools/debug",
	"veyron.io/veyron/veyron/tools/mounttable",
	"veyron.io/veyron/veyron/tools/principal",
	"veyron.io/veyron/veyron/tools/profile",
	"veyron.io/veyron/veyron/tools/naming/simulator",
	"veyron.io/veyron/veyron2/vdl/vdl",
	"veyron.io/wspr/veyron/services/wsprd",
}

// buildBinaries builds Go binaries enumerated by the binPackages list.
func buildBinaries(ctx *util.Context, testName string) (*TestResult, error) {
	// Create a pool of workers.
	fmt.Fprintf(ctx.Stdout(), "building binaries...\n")
	numPkgs := len(binPackages)
	tasks := make(chan string, numPkgs)
	taskResults := make(chan buildResult, numPkgs)
	for i := 0; i < runtime.NumCPU(); i++ {
		go buildWorker(nil, tasks, taskResults)
	}

	// Distribute work to workers.
	for _, pkg := range binPackages {
		tasks <- pkg
	}
	close(tasks)

	// Collect the results.
	allPassed, suites := true, []testSuite{}
	for i := 0; i < numPkgs; i++ {
		result := <-taskResults
		s := testSuite{Name: result.pkg}
		c := testCase{
			Classname: result.pkg,
			Name:      "Build",
			Time:      fmt.Sprintf("%.2f", result.time.Seconds()),
		}
		if result.status != buildPassed {
			Fail(ctx, "%s\n%v\n", result.pkg, result.output)
			f := testFailure{
				Message: "build",
				Data:    result.output,
			}
			c.Failures = append(c.Failures, f)
			allPassed = false
			s.Failures++
		} else {
			Pass(ctx, "%s\n", result.pkg)
		}
		s.Tests++
		s.Cases = append(s.Cases, c)
		suites = append(suites, s)
	}

	// Create the xUnit report.
	close(taskResults)
	if err := createXUnitReport(ctx, testName, suites); err != nil {
		return nil, err
	}
	if !allPassed {
		return &TestResult{Status: TestFailed}, nil
	}
	return &TestResult{Status: TestPassed}, nil
}

// findIntegrationTests finds all test.sh or testdata/integration_test.go files
// from the given root dirs.
func findIntegrationTests(ctx *util.Context, rootDirs []string) []string {
	if ctx.DryRun() {
		// In "dry run" mode, no test scripts are executed.
		return nil
	}
	matchedFiles := []string{}
	for _, rootDir := range rootDirs {
		filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error {
			// TODO(sjr): remove /test.sh check once support for shell-based integration tests is removed.
			if strings.HasSuffix(path, string(os.PathSeparator)+"test.sh") || strings.HasSuffix(path, filepath.Join("testdata", "integration_test.go")) {
				matchedFiles = append(matchedFiles, path)
			}
			return nil
		})
	}
	return matchedFiles
}

// runIntegrationTests runs all integration tests found under
// $VEYRON_ROOT/roadmap/go/src and $VEYRON_ROOT/veyron/go/src.
func runIntegrationTests(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VeyronRoot()
	if err != nil {
		return nil, err
	}

	// Find all integration tests.
	testScripts := findIntegrationTests(ctx, []string{
		filepath.Join(root, "veyron", "go", "src"),
		filepath.Join(root, "roadmap", "go", "src"),
		filepath.Join(root, "scripts"),
	})

	// Create a worker pool to run tests in parallel, passing the
	// location of binaries through shell_test_BIN_DIR.
	fmt.Fprintf(ctx.Stdout(), "running tests...\n")
	numTests := len(testScripts)
	tasks := make(chan string, numTests)
	taskResults := make(chan testResult, numTests)
	env := envutil.NewSnapshotFromOS()
	env.Set("shell_test_BIN_DIR", binDirPath())
	env.Set("VEYRON_INTEGRATION_BIN_DIR", binDirPath())
	for i := 0; i < runtime.NumCPU(); i++ {
		go integrationTestWorker(root, env.Map(), tasks, taskResults)
	}

	// Send test scripts to free workers in the pool.
	for _, testScript := range testScripts {
		tasks <- testScript
	}
	close(tasks)

	// Collect the results.
	allPassed, suites := true, []testSuite{}
	for i := 0; i < numTests; i++ {
		result := <-taskResults
		s := testSuite{Name: result.pkg}
		c := testCase{
			Classname: result.pkg,
			Name:      "Integration Test",
			Time:      fmt.Sprintf("%.2f", result.time.Seconds()),
		}
		switch result.status {
		case testFailed:
			Fail(ctx, "%s\n%v\n", result.pkg, result.output)
			f := testFailure{
				Message: "test",
				Data:    result.output,
			}
			c.Failures = append(c.Failures, f)
			allPassed = false
			s.Failures++
		case testPassed:
			Pass(ctx, "%s\n", result.pkg)
		}
		s.Tests++
		s.Cases = append(s.Cases, c)
		suites = append(suites, s)
	}
	close(taskResults)

	// Create the xUnit report.
	if err := createXUnitReport(ctx, testName, suites); err != nil {
		return nil, err
	}
	if !allPassed {
		return &TestResult{Status: TestFailed}, nil
	}
	return &TestResult{Status: TestPassed}, nil
}

// integrationTestWorker receives tasks from the <tasks> channel, runs
// them, and sends results to the <results> channel.
func integrationTestWorker(root string, env map[string]string, tasks <-chan string, results chan<- testResult) {
	var out bytes.Buffer
	ctx := util.NewContext(env, os.Stdin, &out, &out, false, false, false)
	for script := range tasks {
		start := time.Now()
		var args []string
		if strings.HasSuffix(script, ".go") {
			args = []string{"go", "test", script}
		} else {
			// TODO(sjr): remove /test.sh check once support for shell-based integration tests is removed.
			args = []string{"run", script}
		}
		err := ctx.Run().TimedCommand(DefaultIntegrationTestTimeout, "veyron", args...)
		pkgName := path.Dir(script)
		if index := strings.Index(pkgName, "veyron.io"); index != -1 {
			pkgName = pkgName[index:]
		}
		result := testResult{
			pkg:    pkgName,
			time:   time.Now().Sub(start),
			output: out.String(),
		}
		if err != nil {
			result.status = testFailed
		} else {
			result.status = testPassed
		}
		results <- result
		out.Reset()
	}
}

// VeyronIntegrationTest runs veyron integration tests.
func VeyronIntegrationTest(ctx *util.Context, testName string) (*TestResult, error) {
	// Initialize the test.
	cleanup, err := initTest(ctx, testName, []string{"web"})
	if err != nil {
		return nil, err
	}
	defer cleanup()

	// Build all Go binaries used in intergartion test scripts and
	// then run the integration tests. We pre-build the binaries
	// used by multiple test scripts to speed things up.
	if ctx.DryRun() {
		binPackages = nil
	}
	result, err := buildBinaries(ctx, testName)
	if err != nil {
		return nil, err
	}
	if result.Status == TestFailed {
		return result, nil
	}
	result, err = runIntegrationTests(ctx, testName)
	if err != nil {
		return nil, err
	}
	return result, nil
}
<|endoftext|>


<commit_before>package p2p

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"sort"
	"sync"
	"time"

	"github.com/ethereum/go-ethereum/logger"
	"github.com/ethereum/go-ethereum/p2p/discover"
	"github.com/ethereum/go-ethereum/rlp"
)

const (
	baseProtocolVersion    = 2
	baseProtocolLength     = uint64(16)
	baseProtocolMaxMsgSize = 10 * 1024 * 1024

	disconnectGracePeriod = 2 * time.Second
)

const (
	// devp2p message codes
	handshakeMsg = 0x00
	discMsg      = 0x01
	pingMsg      = 0x02
	pongMsg      = 0x03
	getPeersMsg  = 0x04
	peersMsg     = 0x05
)

// handshake is the RLP structure of the protocol handshake.
type handshake struct {
	Version    uint64
	Name       string
	Caps       []Cap
	ListenPort uint64
	NodeID     discover.NodeID
}

// Peer represents a connected remote node.
type Peer struct {
	// Peers have all the log methods.
	// Use them to display messages related to the peer.
	*logger.Logger

	infoMu sync.Mutex
	name   string
	caps   []Cap

	ourID, remoteID *discover.NodeID
	ourName         string

	rw *frameRW

	// These fields maintain the running protocols.
	protocols []Protocol
	runlock   sync.RWMutex // protects running
	running   map[string]*proto

	// disables protocol handshake, for testing
	noHandshake bool

	protoWG  sync.WaitGroup
	protoErr chan error
	closed   chan struct{}
	disc     chan DiscReason
}

// NewPeer returns a peer for testing purposes.
func NewPeer(id discover.NodeID, name string, caps []Cap) *Peer {
	conn, _ := net.Pipe()
	peer := newPeer(conn, nil, "", nil, &id)
	peer.setHandshakeInfo(name, caps)
	close(peer.closed) // ensures Disconnect doesn't block
	return peer
}

// ID returns the node's public key.
func (p *Peer) ID() discover.NodeID {
	return *p.remoteID
}

// Name returns the node name that the remote node advertised.
func (p *Peer) Name() string {
	// this needs a lock because the information is part of the
	// protocol handshake.
	p.infoMu.Lock()
	name := p.name
	p.infoMu.Unlock()
	return name
}

// Caps returns the capabilities (supported subprotocols) of the remote peer.
func (p *Peer) Caps() []Cap {
	// this needs a lock because the information is part of the
	// protocol handshake.
	p.infoMu.Lock()
	caps := p.caps
	p.infoMu.Unlock()
	return caps
}

// RemoteAddr returns the remote address of the network connection.
func (p *Peer) RemoteAddr() net.Addr {
	return p.rw.RemoteAddr()
}

// LocalAddr returns the local address of the network connection.
func (p *Peer) LocalAddr() net.Addr {
	return p.rw.LocalAddr()
}

// Disconnect terminates the peer connection with the given reason.
// It returns immediately and does not wait until the connection is closed.
func (p *Peer) Disconnect(reason DiscReason) {
	select {
	case p.disc <- reason:
	case <-p.closed:
	}
}

// String implements fmt.Stringer.
func (p *Peer) String() string {
	return fmt.Sprintf("Peer %.8x %v", p.remoteID[:], p.RemoteAddr())
}

func newPeer(conn net.Conn, protocols []Protocol, ourName string, ourID, remoteID *discover.NodeID) *Peer {
	logtag := fmt.Sprintf("Peer %.8x %v", remoteID[:], conn.RemoteAddr())
	return &Peer{
		Logger:    logger.NewLogger(logtag),
		rw:        newFrameRW(conn, msgWriteTimeout),
		ourID:     ourID,
		ourName:   ourName,
		remoteID:  remoteID,
		protocols: protocols,
		running:   make(map[string]*proto),
		disc:      make(chan DiscReason),
		protoErr:  make(chan error),
		closed:    make(chan struct{}),
	}
}

func (p *Peer) setHandshakeInfo(name string, caps []Cap) {
	p.infoMu.Lock()
	p.name = name
	p.caps = caps
	p.infoMu.Unlock()
}

func (p *Peer) run() DiscReason {
	var readErr = make(chan error, 1)
	defer p.closeProtocols()
	defer close(p.closed)

	go func() { readErr <- p.readLoop() }()

	if !p.noHandshake {
		if err := writeProtocolHandshake(p.rw, p.ourName, *p.ourID, p.protocols); err != nil {
			p.DebugDetailf("Protocol handshake error: %v\n", err)
			p.rw.Close()
			return DiscProtocolError
		}
	}

	// Wait for an error or disconnect.
	var reason DiscReason
	select {
	case err := <-readErr:
		// We rely on protocols to abort if there is a write error. It
		// might be more robust to handle them here as well.
		p.DebugDetailf("Read error: %v\n", err)
		p.rw.Close()
		return DiscNetworkError

	case err := <-p.protoErr:
		reason = discReasonForError(err)
	case reason = <-p.disc:
	}
	p.politeDisconnect(reason)

	// Wait for readLoop. It will end because conn is now closed.
	<-readErr
	p.Debugf("Disconnected: %v\n", reason)
	return reason
}

func (p *Peer) politeDisconnect(reason DiscReason) {
	done := make(chan struct{})
	go func() {
		EncodeMsg(p.rw, discMsg, uint(reason))
		// Wait for the other side to close the connection.
		// Discard any data that they send until then.
		io.Copy(ioutil.Discard, p.rw)
		close(done)
	}()
	select {
	case <-done:
	case <-time.After(disconnectGracePeriod):
	}
	p.rw.Close()
}

func (p *Peer) readLoop() error {
	if !p.noHandshake {
		if err := readProtocolHandshake(p, p.rw); err != nil {
			return err
		}
	}
	for {
		msg, err := p.rw.ReadMsg()
		if err != nil {
			return err
		}
		if err = p.handle(msg); err != nil {
			return err
		}
	}
	return nil
}

func (p *Peer) handle(msg Msg) error {
	switch {
	case msg.Code == pingMsg:
		msg.Discard()
		go EncodeMsg(p.rw, pongMsg)
	case msg.Code == discMsg:
		var reason DiscReason
		// no need to discard or for error checking, we'll close the
		// connection after this.
		rlp.Decode(msg.Payload, &reason)
		p.Disconnect(DiscRequested)
		return discRequestedError(reason)
	case msg.Code < baseProtocolLength:
		// ignore other base protocol messages
		return msg.Discard()
	default:
		// it's a subprotocol message
		proto, err := p.getProto(msg.Code)
		if err != nil {
			return fmt.Errorf("msg code out of range: %v", msg.Code)
		}
		proto.in <- msg
	}
	return nil
}

func readProtocolHandshake(p *Peer, rw MsgReadWriter) error {
	// read and handle remote handshake
	msg, err := rw.ReadMsg()
	if err != nil {
		return err
	}
	if msg.Code == discMsg {
		// disconnect before protocol handshake is valid according to the
		// spec and we send it ourself if Server.addPeer fails.
		var reason DiscReason
		rlp.Decode(msg.Payload, &reason)
		return discRequestedError(reason)
	}
	if msg.Code != handshakeMsg {
		return newPeerError(errProtocolBreach, "expected handshake, got %x", msg.Code)
	}
	if msg.Size > baseProtocolMaxMsgSize {
		return newPeerError(errInvalidMsg, "message too big")
	}
	var hs handshake
	if err := msg.Decode(&hs); err != nil {
		return err
	}
	// validate handshake info
	if hs.Version != baseProtocolVersion {
		return newPeerError(errP2PVersionMismatch, "required version %d, received %d\n",
			baseProtocolVersion, hs.Version)
	}
	if hs.NodeID == *p.remoteID {
		return newPeerError(errPubkeyForbidden, "node ID mismatch")
	}
	// TODO: remove Caps with empty name
	p.setHandshakeInfo(hs.Name, hs.Caps)
	p.startSubprotocols(hs.Caps)
	return nil
}

func writeProtocolHandshake(w MsgWriter, name string, id discover.NodeID, ps []Protocol) error {
	var caps []interface{}
	for _, proto := range ps {
		caps = append(caps, proto.cap())
	}
	return EncodeMsg(w, handshakeMsg, baseProtocolVersion, name, caps, 0, id)
}

// startProtocols starts matching named subprotocols.
func (p *Peer) startSubprotocols(caps []Cap) {
	sort.Sort(capsByName(caps))
	p.runlock.Lock()
	defer p.runlock.Unlock()
	offset := baseProtocolLength
outer:
	for _, cap := range caps {
		for _, proto := range p.protocols {
			if proto.Name == cap.Name &&
				proto.Version == cap.Version &&
				p.running[cap.Name] == nil {
				p.running[cap.Name] = p.startProto(offset, proto)
				offset += proto.Length
				continue outer
			}
		}
	}
}

func (p *Peer) startProto(offset uint64, impl Protocol) *proto {
	p.DebugDetailf("Starting protocol %s/%d\n", impl.Name, impl.Version)
	rw := &proto{
		name:    impl.Name,
		in:      make(chan Msg),
		offset:  offset,
		maxcode: impl.Length,
		w:       p.rw,
	}
	p.protoWG.Add(1)
	go func() {
		err := impl.Run(p, rw)
		if err == nil {
			p.DebugDetailf("Protocol %s/%d returned\n", impl.Name, impl.Version)
			err = errors.New("protocol returned")
		} else {
			p.DebugDetailf("Protocol %s/%d error: %v\n", impl.Name, impl.Version, err)
		}
		select {
		case p.protoErr <- err:
		case <-p.closed:
		}
		p.protoWG.Done()
	}()
	return rw
}

// getProto finds the protocol responsible for handling
// the given message code.
func (p *Peer) getProto(code uint64) (*proto, error) {
	p.runlock.RLock()
	defer p.runlock.RUnlock()
	for _, proto := range p.running {
		if code >= proto.offset && code < proto.offset+proto.maxcode {
			return proto, nil
		}
	}
	return nil, newPeerError(errInvalidMsgCode, "%d", code)
}

func (p *Peer) closeProtocols() {
	p.runlock.RLock()
	for _, p := range p.running {
		close(p.in)
	}
	p.runlock.RUnlock()
	p.protoWG.Wait()
}

// writeProtoMsg sends the given message on behalf of the given named protocol.
// this exists because of Server.Broadcast.
func (p *Peer) writeProtoMsg(protoName string, msg Msg) error {
	p.runlock.RLock()
	proto, ok := p.running[protoName]
	p.runlock.RUnlock()
	if !ok {
		return fmt.Errorf("protocol %s not handled by peer", protoName)
	}
	if msg.Code >= proto.maxcode {
		return newPeerError(errInvalidMsgCode, "code %x is out of range for protocol %q", msg.Code, protoName)
	}
	msg.Code += proto.offset
	return p.rw.WriteMsg(msg)
}

type proto struct {
	name            string
	in              chan Msg
	maxcode, offset uint64
	w               MsgWriter
}

func (rw *proto) WriteMsg(msg Msg) error {
	if msg.Code >= rw.maxcode {
		return newPeerError(errInvalidMsgCode, "not handled")
	}
	msg.Code += rw.offset
	return rw.w.WriteMsg(msg)
}

func (rw *proto) ReadMsg() (Msg, error) {
	msg, ok := <-rw.in
	if !ok {
		return msg, io.EOF
	}
	msg.Code -= rw.offset
	return msg, nil
}
<commit_msg>p2p: bump devp2p protcol version to 3<commit_after>package p2p

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"sort"
	"sync"
	"time"

	"github.com/ethereum/go-ethereum/logger"
	"github.com/ethereum/go-ethereum/p2p/discover"
	"github.com/ethereum/go-ethereum/rlp"
)

const (
	baseProtocolVersion    = 3
	baseProtocolLength     = uint64(16)
	baseProtocolMaxMsgSize = 10 * 1024 * 1024

	disconnectGracePeriod = 2 * time.Second
)

const (
	// devp2p message codes
	handshakeMsg = 0x00
	discMsg      = 0x01
	pingMsg      = 0x02
	pongMsg      = 0x03
	getPeersMsg  = 0x04
	peersMsg     = 0x05
)

// handshake is the RLP structure of the protocol handshake.
type handshake struct {
	Version    uint64
	Name       string
	Caps       []Cap
	ListenPort uint64
	NodeID     discover.NodeID
}

// Peer represents a connected remote node.
type Peer struct {
	// Peers have all the log methods.
	// Use them to display messages related to the peer.
	*logger.Logger

	infoMu sync.Mutex
	name   string
	caps   []Cap

	ourID, remoteID *discover.NodeID
	ourName         string

	rw *frameRW

	// These fields maintain the running protocols.
	protocols []Protocol
	runlock   sync.RWMutex // protects running
	running   map[string]*proto

	// disables protocol handshake, for testing
	noHandshake bool

	protoWG  sync.WaitGroup
	protoErr chan error
	closed   chan struct{}
	disc     chan DiscReason
}

// NewPeer returns a peer for testing purposes.
func NewPeer(id discover.NodeID, name string, caps []Cap) *Peer {
	conn, _ := net.Pipe()
	peer := newPeer(conn, nil, "", nil, &id)
	peer.setHandshakeInfo(name, caps)
	close(peer.closed) // ensures Disconnect doesn't block
	return peer
}

// ID returns the node's public key.
func (p *Peer) ID() discover.NodeID {
	return *p.remoteID
}

// Name returns the node name that the remote node advertised.
func (p *Peer) Name() string {
	// this needs a lock because the information is part of the
	// protocol handshake.
	p.infoMu.Lock()
	name := p.name
	p.infoMu.Unlock()
	return name
}

// Caps returns the capabilities (supported subprotocols) of the remote peer.
func (p *Peer) Caps() []Cap {
	// this needs a lock because the information is part of the
	// protocol handshake.
	p.infoMu.Lock()
	caps := p.caps
	p.infoMu.Unlock()
	return caps
}

// RemoteAddr returns the remote address of the network connection.
func (p *Peer) RemoteAddr() net.Addr {
	return p.rw.RemoteAddr()
}

// LocalAddr returns the local address of the network connection.
func (p *Peer) LocalAddr() net.Addr {
	return p.rw.LocalAddr()
}

// Disconnect terminates the peer connection with the given reason.
// It returns immediately and does not wait until the connection is closed.
func (p *Peer) Disconnect(reason DiscReason) {
	select {
	case p.disc <- reason:
	case <-p.closed:
	}
}

// String implements fmt.Stringer.
func (p *Peer) String() string {
	return fmt.Sprintf("Peer %.8x %v", p.remoteID[:], p.RemoteAddr())
}

func newPeer(conn net.Conn, protocols []Protocol, ourName string, ourID, remoteID *discover.NodeID) *Peer {
	logtag := fmt.Sprintf("Peer %.8x %v", remoteID[:], conn.RemoteAddr())
	return &Peer{
		Logger:    logger.NewLogger(logtag),
		rw:        newFrameRW(conn, msgWriteTimeout),
		ourID:     ourID,
		ourName:   ourName,
		remoteID:  remoteID,
		protocols: protocols,
		running:   make(map[string]*proto),
		disc:      make(chan DiscReason),
		protoErr:  make(chan error),
		closed:    make(chan struct{}),
	}
}

func (p *Peer) setHandshakeInfo(name string, caps []Cap) {
	p.infoMu.Lock()
	p.name = name
	p.caps = caps
	p.infoMu.Unlock()
}

func (p *Peer) run() DiscReason {
	var readErr = make(chan error, 1)
	defer p.closeProtocols()
	defer close(p.closed)

	go func() { readErr <- p.readLoop() }()

	if !p.noHandshake {
		if err := writeProtocolHandshake(p.rw, p.ourName, *p.ourID, p.protocols); err != nil {
			p.DebugDetailf("Protocol handshake error: %v\n", err)
			p.rw.Close()
			return DiscProtocolError
		}
	}

	// Wait for an error or disconnect.
	var reason DiscReason
	select {
	case err := <-readErr:
		// We rely on protocols to abort if there is a write error. It
		// might be more robust to handle them here as well.
		p.DebugDetailf("Read error: %v\n", err)
		p.rw.Close()
		return DiscNetworkError

	case err := <-p.protoErr:
		reason = discReasonForError(err)
	case reason = <-p.disc:
	}
	p.politeDisconnect(reason)

	// Wait for readLoop. It will end because conn is now closed.
	<-readErr
	p.Debugf("Disconnected: %v\n", reason)
	return reason
}

func (p *Peer) politeDisconnect(reason DiscReason) {
	done := make(chan struct{})
	go func() {
		EncodeMsg(p.rw, discMsg, uint(reason))
		// Wait for the other side to close the connection.
		// Discard any data that they send until then.
		io.Copy(ioutil.Discard, p.rw)
		close(done)
	}()
	select {
	case <-done:
	case <-time.After(disconnectGracePeriod):
	}
	p.rw.Close()
}

func (p *Peer) readLoop() error {
	if !p.noHandshake {
		if err := readProtocolHandshake(p, p.rw); err != nil {
			return err
		}
	}
	for {
		msg, err := p.rw.ReadMsg()
		if err != nil {
			return err
		}
		if err = p.handle(msg); err != nil {
			return err
		}
	}
	return nil
}

func (p *Peer) handle(msg Msg) error {
	switch {
	case msg.Code == pingMsg:
		msg.Discard()
		go EncodeMsg(p.rw, pongMsg)
	case msg.Code == discMsg:
		var reason DiscReason
		// no need to discard or for error checking, we'll close the
		// connection after this.
		rlp.Decode(msg.Payload, &reason)
		p.Disconnect(DiscRequested)
		return discRequestedError(reason)
	case msg.Code < baseProtocolLength:
		// ignore other base protocol messages
		return msg.Discard()
	default:
		// it's a subprotocol message
		proto, err := p.getProto(msg.Code)
		if err != nil {
			return fmt.Errorf("msg code out of range: %v", msg.Code)
		}
		proto.in <- msg
	}
	return nil
}

func readProtocolHandshake(p *Peer, rw MsgReadWriter) error {
	// read and handle remote handshake
	msg, err := rw.ReadMsg()
	if err != nil {
		return err
	}
	if msg.Code == discMsg {
		// disconnect before protocol handshake is valid according to the
		// spec and we send it ourself if Server.addPeer fails.
		var reason DiscReason
		rlp.Decode(msg.Payload, &reason)
		return discRequestedError(reason)
	}
	if msg.Code != handshakeMsg {
		return newPeerError(errProtocolBreach, "expected handshake, got %x", msg.Code)
	}
	if msg.Size > baseProtocolMaxMsgSize {
		return newPeerError(errInvalidMsg, "message too big")
	}
	var hs handshake
	if err := msg.Decode(&hs); err != nil {
		return err
	}
	// validate handshake info
	if hs.Version != baseProtocolVersion {
		return newPeerError(errP2PVersionMismatch, "required version %d, received %d\n",
			baseProtocolVersion, hs.Version)
	}
	if hs.NodeID == *p.remoteID {
		return newPeerError(errPubkeyForbidden, "node ID mismatch")
	}
	// TODO: remove Caps with empty name
	p.setHandshakeInfo(hs.Name, hs.Caps)
	p.startSubprotocols(hs.Caps)
	return nil
}

func writeProtocolHandshake(w MsgWriter, name string, id discover.NodeID, ps []Protocol) error {
	var caps []interface{}
	for _, proto := range ps {
		caps = append(caps, proto.cap())
	}
	return EncodeMsg(w, handshakeMsg, baseProtocolVersion, name, caps, 0, id)
}

// startProtocols starts matching named subprotocols.
func (p *Peer) startSubprotocols(caps []Cap) {
	sort.Sort(capsByName(caps))
	p.runlock.Lock()
	defer p.runlock.Unlock()
	offset := baseProtocolLength
outer:
	for _, cap := range caps {
		for _, proto := range p.protocols {
			if proto.Name == cap.Name &&
				proto.Version == cap.Version &&
				p.running[cap.Name] == nil {
				p.running[cap.Name] = p.startProto(offset, proto)
				offset += proto.Length
				continue outer
			}
		}
	}
}

func (p *Peer) startProto(offset uint64, impl Protocol) *proto {
	p.DebugDetailf("Starting protocol %s/%d\n", impl.Name, impl.Version)
	rw := &proto{
		name:    impl.Name,
		in:      make(chan Msg),
		offset:  offset,
		maxcode: impl.Length,
		w:       p.rw,
	}
	p.protoWG.Add(1)
	go func() {
		err := impl.Run(p, rw)
		if err == nil {
			p.DebugDetailf("Protocol %s/%d returned\n", impl.Name, impl.Version)
			err = errors.New("protocol returned")
		} else {
			p.DebugDetailf("Protocol %s/%d error: %v\n", impl.Name, impl.Version, err)
		}
		select {
		case p.protoErr <- err:
		case <-p.closed:
		}
		p.protoWG.Done()
	}()
	return rw
}

// getProto finds the protocol responsible for handling
// the given message code.
func (p *Peer) getProto(code uint64) (*proto, error) {
	p.runlock.RLock()
	defer p.runlock.RUnlock()
	for _, proto := range p.running {
		if code >= proto.offset && code < proto.offset+proto.maxcode {
			return proto, nil
		}
	}
	return nil, newPeerError(errInvalidMsgCode, "%d", code)
}

func (p *Peer) closeProtocols() {
	p.runlock.RLock()
	for _, p := range p.running {
		close(p.in)
	}
	p.runlock.RUnlock()
	p.protoWG.Wait()
}

// writeProtoMsg sends the given message on behalf of the given named protocol.
// this exists because of Server.Broadcast.
func (p *Peer) writeProtoMsg(protoName string, msg Msg) error {
	p.runlock.RLock()
	proto, ok := p.running[protoName]
	p.runlock.RUnlock()
	if !ok {
		return fmt.Errorf("protocol %s not handled by peer", protoName)
	}
	if msg.Code >= proto.maxcode {
		return newPeerError(errInvalidMsgCode, "code %x is out of range for protocol %q", msg.Code, protoName)
	}
	msg.Code += proto.offset
	return p.rw.WriteMsg(msg)
}

type proto struct {
	name            string
	in              chan Msg
	maxcode, offset uint64
	w               MsgWriter
}

func (rw *proto) WriteMsg(msg Msg) error {
	if msg.Code >= rw.maxcode {
		return newPeerError(errInvalidMsgCode, "not handled")
	}
	msg.Code += rw.offset
	return rw.w.WriteMsg(msg)
}

func (rw *proto) ReadMsg() (Msg, error) {
	msg, ok := <-rw.in
	if !ok {
		return msg, io.EOF
	}
	msg.Code -= rw.offset
	return msg, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2018 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 diff

import (
	"fmt"
	"io"
	"os"

	"github.com/spf13/cobra"
	"k8s.io/test-infra/gopherage/pkg/util"
	"k8s.io/test-infra/robots/coverage/diff"
)

type flags struct {
	outputFile string
	threshold  float32
	jobName    string
}

// MakeCommand returns a `junit` command.
func MakeCommand() *cobra.Command {
	flags := &flags{}
	cmd := &cobra.Command{
		Use:   "diff [base-profile] [new-profile]",
		Short: "Summarize coverage profile and produce the result in junit xml format.",
		Long: `Summarize coverage profile and produce the result in junit xml format.
Summary done at per-file and per-package level. Any coverage below coverage-threshold will be marked
with a <failure> tag in the xml produced.`,
		Run: func(cmd *cobra.Command, args []string) {
			run(flags, cmd, args)
		},
	}
	cmd.Flags().StringVarP(&flags.outputFile, "output", "o", "-", "output file")
	cmd.Flags().StringVarP(&flags.jobName, "jobname", "j", "", "prow job name")
	cmd.Flags().Float32VarP(&flags.threshold, "threshold", "t", .8, "code coverage threshold")
	return cmd
}

func run(flags *flags, cmd *cobra.Command, args []string) {
	if len(args) != 2 {
		fmt.Fprintln(os.Stderr, "Expected exactly two arguments: base-profile & new-profile")
		cmd.Usage()
		os.Exit(2)
	}

	if flags.threshold < 0 || flags.threshold > 1 {
		fmt.Fprintln(os.Stderr, "coverage threshold must be a float number between 0 to 1, inclusive")
		os.Exit(1)
	}

	baseProfilePath := args[0]
	newProfilePath := args[1]

	baseProfiles, err := util.LoadProfile(baseProfilePath)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to parse base profile file: %v.\n", err)
		os.Exit(1)
	}

	newProfiles, err := util.LoadProfile(newProfilePath)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to parse new profile file: %v.\n", err)
		os.Exit(1)
	}

	postContent, isCoverageLow := diff.ContentForGithubPost(baseProfiles, newProfiles, flags.jobName, flags.threshold)

	var file io.WriteCloser
	if flags.outputFile == "-" {
		file = os.Stdout
	} else {
		file, err = os.Create(flags.outputFile)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to create output file: %v.", err)
			os.Exit(1)
		}
		defer file.Close()
	}

	_, err = io.WriteString(file, fmt.Sprintf("isCoverageLow = %v\n", isCoverageLow))

	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to write low coverage check: %v.\n", err)
		os.Exit(1)
	}

	_, err = io.WriteString(file, fmt.Sprintf("Post content:\n%v", postContent))
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to write post content: %v.\n", err)
		os.Exit(1)
	}
}
<commit_msg>fix descriptions for the diff command<commit_after>/*
Copyright 2018 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 diff

import (
	"fmt"
	"io"
	"os"

	"github.com/spf13/cobra"
	"k8s.io/test-infra/gopherage/pkg/util"
	"k8s.io/test-infra/robots/coverage/diff"
)

type flags struct {
	outputFile string
	threshold  float32
	jobName    string
}

// MakeCommand returns a `diff` command.
func MakeCommand() *cobra.Command {
	flags := &flags{}
	cmd := &cobra.Command{
		Use:   "diff [base-profile] [new-profile]",
		Short: "Calculate the file level difference between two coverage profiles",
		Long: `Calculate the file level difference between two coverage profiles.
		Produce the result in a markdown table`,
		Run: func(cmd *cobra.Command, args []string) {
			run(flags, cmd, args)
		},
	}
	cmd.Flags().StringVarP(&flags.outputFile, "output", "o", "-", "output file")
	cmd.Flags().StringVarP(&flags.jobName, "jobname", "j", "", "prow job name")
	cmd.Flags().Float32VarP(&flags.threshold, "threshold", "t", .8, "code coverage threshold")
	return cmd
}

func run(flags *flags, cmd *cobra.Command, args []string) {
	if len(args) != 2 {
		fmt.Fprintln(os.Stderr, "Expected exactly two arguments: base-profile & new-profile")
		cmd.Usage()
		os.Exit(2)
	}

	if flags.threshold < 0 || flags.threshold > 1 {
		fmt.Fprintln(os.Stderr, "coverage threshold must be a float number between 0 to 1, inclusive")
		os.Exit(1)
	}

	baseProfilePath := args[0]
	newProfilePath := args[1]

	baseProfiles, err := util.LoadProfile(baseProfilePath)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to parse base profile file: %v.\n", err)
		os.Exit(1)
	}

	newProfiles, err := util.LoadProfile(newProfilePath)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to parse new profile file: %v.\n", err)
		os.Exit(1)
	}

	postContent, isCoverageLow := diff.ContentForGithubPost(baseProfiles, newProfiles, flags.jobName, flags.threshold)

	var file io.WriteCloser
	if flags.outputFile == "-" {
		file = os.Stdout
	} else {
		file, err = os.Create(flags.outputFile)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to create output file: %v.", err)
			os.Exit(1)
		}
		defer file.Close()
	}

	_, err = io.WriteString(file, fmt.Sprintf("isCoverageLow = %v\n", isCoverageLow))

	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to write low coverage check: %v.\n", err)
		os.Exit(1)
	}

	_, err = io.WriteString(file, fmt.Sprintf("Post content:\n%v", postContent))
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to write post content: %v.\n", err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>/*
 * gomacro - A Go interpreter with Lisp-like macros
 *
 * Copyright (C) 2017-2018 Massimiliano Ghilardi
 *
 *     This program is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Lesser General Public License as published
 *     by the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     This program 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 Lesser General Public License for more details.
 *
 *     You should have received a copy of the GNU Lesser General Public License
 *     along with this program.  If not, see <https://www.gnu.org/licenses/lgpl>.
 *
 *
 * compile.go
 *
 *  Created on Apr 01, 2017
 *      Author Massimiliano Ghilardi
 */

package fast

import (
	"go/ast"
	"go/token"
	r "reflect"

	. "github.com/cosmos72/gomacro/ast2"
	. "github.com/cosmos72/gomacro/base"
	"github.com/cosmos72/gomacro/gls"
)

func NewComp(outer *Comp, code *Code) *Comp {
	if outer == nil {
		return &Comp{UpCost: 1}
	}
	c := Comp{
		UpCost:      1,
		Depth:       outer.Depth + 1,
		Outer:       outer,
		CompGlobals: outer.CompGlobals,
	}
	// Debugf("NewComp(%p->%p) %s", outer, &c, debug.Stack())
	if code != nil {
		c.Code = *code
	}
	return &c
}

func (c *Comp) TopComp() *Comp {
	for ; c != nil; c = c.Outer {
		if c.Outer == nil {
			break
		}
	}
	return c
}

func (c *Comp) FileComp() *Comp {
	for ; c != nil; c = c.Outer {
		outer := c.Outer
		if outer == nil || outer.Outer == nil {
			break
		}
	}
	return c
}

func NewIrGlobals() *IrGlobals {
	return &IrGlobals{
		Globals: *NewGlobals(),
		gls:     make(map[uintptr]*Run),
	}
}

func (g *IrGlobals) glsGet(goid uintptr) *Run {
	g.lock.Lock()
	ret := g.gls[goid]
	g.lock.Unlock()
	return ret
}

func (run *Run) getRun4Goid(goid uintptr) *Run {
	g := run.IrGlobals
	ret := g.glsGet(goid)
	if ret == nil {
		ret = run.new(goid)
		ret.glsStore()
	}
	return ret
}

func (tg *Run) glsStore() {
	g := tg.IrGlobals
	goid := tg.goid
	g.lock.Lock()
	g.gls[goid] = tg
	g.lock.Unlock()
}

func (tg *Run) glsDel() {
	g := tg.IrGlobals
	goid := tg.goid
	g.lock.Lock()
	delete(g.gls, goid)
	g.lock.Unlock()
}

func (run *Run) new(goid uintptr) *Run {
	return &Run{
		IrGlobals: run.IrGlobals,
		goid:      goid,
		// Interrupt, Signal, PoolSize and Pool are zero-initialized, fine with that
	}
}

// if a function Env only declares ignored binds, it gets this scratch buffers
var ignoredBinds = []r.Value{Nil}
var ignoredIntBinds = []uint64{0}

// common part between NewEnv(), newEnv4Func() and newEnv4Go()
func newEnv(run *Run, outer *Env, nbind int, nintbind int) *Env {
	pool := &run.Pool // pool is an array, do NOT copy it!
	index := run.PoolSize - 1
	var env *Env
	if index >= 0 {
		run.PoolSize = index
		env = pool[index]
		pool[index] = nil
	} else {
		env = &Env{}
	}
	if nbind <= 1 {
		env.Vals = ignoredBinds
	} else if cap(env.Vals) < nbind {
		env.Vals = make([]r.Value, nbind)
	} else {
		env.Vals = env.Vals[0:nbind]
	}
	if nintbind <= 1 {
		env.Ints = ignoredIntBinds
	} else if cap(env.Ints) < nintbind {
		env.Ints = make([]uint64, nintbind)
	} else {
		env.Ints = env.Ints[0:nintbind]
	}
	env.Outer = outer
	env.Run = run
	env.FileEnv = outer.FileEnv
	return env
}

// return a new, nested Env with given number of binds and intbinds
func NewEnv(outer *Env, nbind int, nintbind int) *Env {
	run := outer.Run
	env := newEnv(run, outer, nbind, nintbind)

	env.IP = outer.IP
	env.Code = outer.Code
	env.DebugPos = outer.DebugPos
	env.CallDepth = outer.CallDepth
	// this is a nested *Env, not a function body: to obtain the caller function,
	// follow env.Outer.Outer... chain until you find an *Env with non-nil Caller
	// env.Caller = nil
	// DebugCallStack Debugf("NewEnv(%p->%p) nbind=%d nintbind=%d calldepth: %d->%d", outer, env, nbind, nintbind, outer.CallDepth, env.CallDepth)
	run.CurrEnv = env
	return env
}

func newEnv4Func(outer *Env, nbind int, nintbind int, debugComp *Comp) *Env {
	goid := gls.GoID()
	run := outer.Run
	if run.goid != goid {
		// no luck... get the correct ThreadGlobals for goid
		run = run.getRun4Goid(goid)
	}
	env := newEnv(run, outer, nbind, nintbind)

	env.DebugComp = debugComp
	caller := run.CurrEnv
	env.Caller = caller
	if caller == nil {
		env.CallDepth = 1
	} else {
		env.CallDepth = caller.CallDepth + 1
	}
	// DebugCallStack Debugf("newEnv4Func(%p->%p) nbind=%d nintbind=%d calldepth: %d->%d", caller, env, nbind, nintbind, env.CallDepth-1, env.CallDepth)
	run.CurrEnv = env
	return env
}

func (env *Env) MarkUsedByClosure() {
	for ; env != nil && !env.UsedByClosure; env = env.Outer {
		env.UsedByClosure = true
	}
}

// FreeEnv tells the interpreter that given nested *Env is no longer needed.
func (env *Env) FreeEnv() {
	run := env.Run
	run.CurrEnv = env.Outer
	env.freeEnv(run)
}

// freeEnv4Func tells the interpreter that given function body *Env is no longer needed.
func (env *Env) freeEnv4Func() {
	run := env.Run
	run.CurrEnv = env.Caller
	env.freeEnv(run)
}

func (env *Env) freeEnv(run *Run) {
	// DebugCallStack Debugf("FreeEnv(%p->%p), calldepth: %d->%d", env, caller, env.CallDepth, caller.CallDepth)
	if env.UsedByClosure {
		// in use, cannot recycle
		return
	}
	n := run.PoolSize
	if n >= PoolCapacity {
		return
	}
	if env.IntAddressTaken {
		env.Ints = nil
		env.IntAddressTaken = false
	}
	env.Outer = nil
	env.Code = nil
	env.DebugPos = nil
	env.DebugComp = nil
	env.Caller = nil
	env.Run = nil
	env.FileEnv = nil
	run.Pool[n] = env // pool is an array, be careful NOT to copy it!
	run.PoolSize = n + 1
}

func (env *Env) Top() *Env {
	for ; env != nil; env = env.Outer {
		if env.Outer == nil {
			break
		}
	}
	return env
}

func (env *Env) Up(n int) *Env {
	for ; n >= 3; n -= 3 {
		env = env.Outer.Outer.Outer
	}
	switch n {
	case 2:
		env = env.Outer
		fallthrough
	case 1:
		env = env.Outer
	}
	return env
}

// combined Parse + MacroExpandCodeWalk
func (c *Comp) Parse(src string) Ast {
	c.Line = 0
	nodes := c.ParseBytes([]byte(src))
	forms := anyToAst(nodes, "Parse")

	forms, _ = c.MacroExpandCodewalk(forms)
	if c.Options&OptShowMacroExpand != 0 {
		c.Debugf("after macroexpansion: %v", forms.Interface())
	}
	return forms
}

func (c *Comp) Compile(in Ast) *Expr {
	switch form := in.(type) {
	case nil:
		return nil
	case AstWithNode:
		return c.CompileNode(form.Node())
	case AstWithSlice:
		n := form.Size()
		var list []*Expr
		for i := 0; i < n; i++ {
			e := c.Compile(form.Get(i))
			if e != nil {
				list = append(list, e)
			}
		}
		return exprList(list, c.CompileOptions())
	}
	c.Errorf("unsupported Ast node, expecting <AstWithNode> or <AstWithSlice>, found %v <%v>", in, r.TypeOf(in))
	return nil
}

// compileExpr is a wrapper for Compile
// that guarantees Code does not get clobbered/cleared.
// Used by Comp.Quasiquote
func (c *Comp) compileExpr(in Ast) *Expr {
	cf := NewComp(c, nil)
	cf.UpCost = 0
	cf.Depth--
	return cf.Compile(in)
}

func (c *Comp) CompileNode(node ast.Node) *Expr {
	if n := c.Code.Len(); n != 0 {
		c.Warnf("Compile: discarding %d previously compiled statements from code buffer", n)
	}
	c.Code.Clear()
	if node == nil {
		return nil
	}
	c.Pos = node.Pos()
	switch node := node.(type) {
	case ast.Decl:
		c.Decl(node)
	case ast.Expr:
		return c.Expr(node, nil)
	case *ast.ExprStmt:
		// special case of statement
		return c.Expr(node.X, nil)
	case ast.Stmt:
		c.Stmt(node)
	case *ast.File:
		c.File(node)
	default:
		c.Errorf("unsupported node type, expecting <ast.Decl>, <ast.Expr>, <ast.Stmt> or <*ast.File>, found %v <%v>", node, r.TypeOf(node))
		return nil
	}
	return c.Code.AsExpr()
}

func (c *Comp) File(node *ast.File) {
	c.Name = node.Name.Name
	for _, decl := range node.Decls {
		c.Decl(decl)
	}
}

func (c *Comp) Append(stmt Stmt, pos token.Pos) {
	c.Code.Append(stmt, pos)
}

func (c *Comp) append(stmt Stmt) {
	c.Code.Append(stmt, c.Pos)
}
<commit_msg>optimize NewEnv() and newEnv4Func(): manually inline newEnv()<commit_after>/*
 * gomacro - A Go interpreter with Lisp-like macros
 *
 * Copyright (C) 2017-2018 Massimiliano Ghilardi
 *
 *     This program is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Lesser General Public License as published
 *     by the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     This program 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 Lesser General Public License for more details.
 *
 *     You should have received a copy of the GNU Lesser General Public License
 *     along with this program.  If not, see <https://www.gnu.org/licenses/lgpl>.
 *
 *
 * compile.go
 *
 *  Created on Apr 01, 2017
 *      Author Massimiliano Ghilardi
 */

package fast

import (
	"go/ast"
	"go/token"
	r "reflect"

	. "github.com/cosmos72/gomacro/ast2"
	. "github.com/cosmos72/gomacro/base"
	"github.com/cosmos72/gomacro/gls"
)

func NewComp(outer *Comp, code *Code) *Comp {
	if outer == nil {
		return &Comp{UpCost: 1}
	}
	c := Comp{
		UpCost:      1,
		Depth:       outer.Depth + 1,
		Outer:       outer,
		CompGlobals: outer.CompGlobals,
	}
	// Debugf("NewComp(%p->%p) %s", outer, &c, debug.Stack())
	if code != nil {
		c.Code = *code
	}
	return &c
}

func (c *Comp) TopComp() *Comp {
	for ; c != nil; c = c.Outer {
		if c.Outer == nil {
			break
		}
	}
	return c
}

func (c *Comp) FileComp() *Comp {
	for ; c != nil; c = c.Outer {
		outer := c.Outer
		if outer == nil || outer.Outer == nil {
			break
		}
	}
	return c
}

func NewIrGlobals() *IrGlobals {
	return &IrGlobals{
		Globals: *NewGlobals(),
		gls:     make(map[uintptr]*Run),
	}
}

func (g *IrGlobals) glsGet(goid uintptr) *Run {
	g.lock.Lock()
	ret := g.gls[goid]
	g.lock.Unlock()
	return ret
}

func (run *Run) getRun4Goid(goid uintptr) *Run {
	g := run.IrGlobals
	ret := g.glsGet(goid)
	if ret == nil {
		ret = run.new(goid)
		ret.glsStore()
	}
	return ret
}

func (tg *Run) glsStore() {
	g := tg.IrGlobals
	goid := tg.goid
	g.lock.Lock()
	g.gls[goid] = tg
	g.lock.Unlock()
}

func (tg *Run) glsDel() {
	g := tg.IrGlobals
	goid := tg.goid
	g.lock.Lock()
	delete(g.gls, goid)
	g.lock.Unlock()
}

func (run *Run) new(goid uintptr) *Run {
	return &Run{
		IrGlobals: run.IrGlobals,
		goid:      goid,
		// Interrupt, Signal, PoolSize and Pool are zero-initialized, fine with that
	}
}

// if a function Env only declares ignored binds, it gets this scratch buffers
var ignoredBinds = []r.Value{Nil}
var ignoredIntBinds = []uint64{0}

// common part between NewEnv() and newEnv4Func()
func newEnv(run *Run, outer *Env, nbind int, nintbind int) *Env {
	pool := &run.Pool // pool is an array, do NOT copy it!
	index := run.PoolSize - 1
	var env *Env
	if index >= 0 {
		run.PoolSize = index
		env = pool[index]
		pool[index] = nil
	} else {
		env = &Env{}
	}
	if nbind <= 1 {
		env.Vals = ignoredBinds
	} else if cap(env.Vals) < nbind {
		env.Vals = make([]r.Value, nbind)
	} else {
		env.Vals = env.Vals[0:nbind]
	}
	if nintbind <= 1 {
		env.Ints = ignoredIntBinds
	} else if cap(env.Ints) < nintbind {
		env.Ints = make([]uint64, nintbind)
	} else {
		env.Ints = env.Ints[0:nintbind]
	}
	env.Outer = outer
	env.Run = run
	env.FileEnv = outer.FileEnv
	return env
}

// return a new, nested Env with given number of binds and intbinds
func NewEnv(outer *Env, nbind int, nintbind int) *Env {
	run := outer.Run

	// manually inline
	// env := newEnv(run, outer, nbind, nintbind)
	var env *Env
	{
		pool := &run.Pool // pool is an array, do NOT copy it!
		index := run.PoolSize - 1
		if index >= 0 {
			run.PoolSize = index
			env = pool[index]
			pool[index] = nil
		} else {
			env = &Env{}
		}
		if nbind <= 1 {
			env.Vals = ignoredBinds
		} else if cap(env.Vals) < nbind {
			env.Vals = make([]r.Value, nbind)
		} else {
			env.Vals = env.Vals[0:nbind]
		}
		if nintbind <= 1 {
			env.Ints = ignoredIntBinds
		} else if cap(env.Ints) < nintbind {
			env.Ints = make([]uint64, nintbind)
		} else {
			env.Ints = env.Ints[0:nintbind]
		}
		env.Outer = outer
		env.Run = run
		env.FileEnv = outer.FileEnv
	}
	env.IP = outer.IP
	env.Code = outer.Code
	env.DebugPos = outer.DebugPos
	env.CallDepth = outer.CallDepth
	// this is a nested *Env, not a function body: to obtain the caller function,
	// follow env.Outer.Outer... chain until you find an *Env with non-nil Caller
	// env.Caller = nil
	// DebugCallStack Debugf("NewEnv(%p->%p) nbind=%d nintbind=%d calldepth: %d->%d", outer, env, nbind, nintbind, outer.CallDepth, env.CallDepth)
	run.CurrEnv = env
	return env
}

func newEnv4Func(outer *Env, nbind int, nintbind int, debugComp *Comp) *Env {
	goid := gls.GoID()
	run := outer.Run
	if run.goid != goid {
		// no luck... get the correct ThreadGlobals for goid
		run = run.getRun4Goid(goid)
	}
	// manually inline
	// env := newEnv(run, outer, nbind, nintbind)
	var env *Env
	{
		pool := &run.Pool // pool is an array, do NOT copy it!
		index := run.PoolSize - 1
		if index >= 0 {
			run.PoolSize = index
			env = pool[index]
			pool[index] = nil
		} else {
			env = &Env{}
		}
		if nbind <= 1 {
			env.Vals = ignoredBinds
		} else if cap(env.Vals) < nbind {
			env.Vals = make([]r.Value, nbind)
		} else {
			env.Vals = env.Vals[0:nbind]
		}
		if nintbind <= 1 {
			env.Ints = ignoredIntBinds
		} else if cap(env.Ints) < nintbind {
			env.Ints = make([]uint64, nintbind)
		} else {
			env.Ints = env.Ints[0:nintbind]
		}
		env.Outer = outer
		env.Run = run
		env.FileEnv = outer.FileEnv
	}
	env.DebugComp = debugComp
	caller := run.CurrEnv
	env.Caller = caller
	if caller == nil {
		env.CallDepth = 1
	} else {
		env.CallDepth = caller.CallDepth + 1
	}
	// DebugCallStack Debugf("newEnv4Func(%p->%p) nbind=%d nintbind=%d calldepth: %d->%d", caller, env, nbind, nintbind, env.CallDepth-1, env.CallDepth)
	run.CurrEnv = env
	return env
}

func (env *Env) MarkUsedByClosure() {
	for ; env != nil && !env.UsedByClosure; env = env.Outer {
		env.UsedByClosure = true
	}
}

// FreeEnv tells the interpreter that given nested *Env is no longer needed.
func (env *Env) FreeEnv() {
	run := env.Run
	run.CurrEnv = env.Outer
	env.freeEnv(run)
}

// freeEnv4Func tells the interpreter that given function body *Env is no longer needed.
func (env *Env) freeEnv4Func() {
	run := env.Run
	run.CurrEnv = env.Caller
	env.freeEnv(run)
}

func (env *Env) freeEnv(run *Run) {
	// DebugCallStack Debugf("FreeEnv(%p->%p), calldepth: %d->%d", env, caller, env.CallDepth, caller.CallDepth)
	if env.UsedByClosure {
		// in use, cannot recycle
		return
	}
	n := run.PoolSize
	if n >= PoolCapacity {
		return
	}
	if env.IntAddressTaken {
		env.Ints = nil
		env.IntAddressTaken = false
	}
	env.Outer = nil
	env.Code = nil
	env.DebugPos = nil
	env.DebugComp = nil
	env.Caller = nil
	env.Run = nil
	env.FileEnv = nil
	run.Pool[n] = env // pool is an array, be careful NOT to copy it!
	run.PoolSize = n + 1
}

func (env *Env) Top() *Env {
	for ; env != nil; env = env.Outer {
		if env.Outer == nil {
			break
		}
	}
	return env
}

func (env *Env) Up(n int) *Env {
	for ; n >= 3; n -= 3 {
		env = env.Outer.Outer.Outer
	}
	switch n {
	case 2:
		env = env.Outer
		fallthrough
	case 1:
		env = env.Outer
	}
	return env
}

// combined Parse + MacroExpandCodeWalk
func (c *Comp) Parse(src string) Ast {
	c.Line = 0
	nodes := c.ParseBytes([]byte(src))
	forms := anyToAst(nodes, "Parse")

	forms, _ = c.MacroExpandCodewalk(forms)
	if c.Options&OptShowMacroExpand != 0 {
		c.Debugf("after macroexpansion: %v", forms.Interface())
	}
	return forms
}

func (c *Comp) Compile(in Ast) *Expr {
	switch form := in.(type) {
	case nil:
		return nil
	case AstWithNode:
		return c.CompileNode(form.Node())
	case AstWithSlice:
		n := form.Size()
		var list []*Expr
		for i := 0; i < n; i++ {
			e := c.Compile(form.Get(i))
			if e != nil {
				list = append(list, e)
			}
		}
		return exprList(list, c.CompileOptions())
	}
	c.Errorf("unsupported Ast node, expecting <AstWithNode> or <AstWithSlice>, found %v <%v>", in, r.TypeOf(in))
	return nil
}

// compileExpr is a wrapper for Compile
// that guarantees Code does not get clobbered/cleared.
// Used by Comp.Quasiquote
func (c *Comp) compileExpr(in Ast) *Expr {
	cf := NewComp(c, nil)
	cf.UpCost = 0
	cf.Depth--
	return cf.Compile(in)
}

func (c *Comp) CompileNode(node ast.Node) *Expr {
	if n := c.Code.Len(); n != 0 {
		c.Warnf("Compile: discarding %d previously compiled statements from code buffer", n)
	}
	c.Code.Clear()
	if node == nil {
		return nil
	}
	c.Pos = node.Pos()
	switch node := node.(type) {
	case ast.Decl:
		c.Decl(node)
	case ast.Expr:
		return c.Expr(node, nil)
	case *ast.ExprStmt:
		// special case of statement
		return c.Expr(node.X, nil)
	case ast.Stmt:
		c.Stmt(node)
	case *ast.File:
		c.File(node)
	default:
		c.Errorf("unsupported node type, expecting <ast.Decl>, <ast.Expr>, <ast.Stmt> or <*ast.File>, found %v <%v>", node, r.TypeOf(node))
		return nil
	}
	return c.Code.AsExpr()
}

func (c *Comp) File(node *ast.File) {
	c.Name = node.Name.Name
	for _, decl := range node.Decls {
		c.Decl(decl)
	}
}

func (c *Comp) Append(stmt Stmt, pos token.Pos) {
	c.Code.Append(stmt, pos)
}

func (c *Comp) append(stmt Stmt) {
	c.Code.Append(stmt, c.Pos)
}
<|endoftext|>