<commit_before>/*
   Copyright (c) 2014, Percona LLC and/or its affiliates. All rights reserved.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero 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 Affero General Public License for more details.

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

package cmd

import (
	"fmt"
	"os/exec"
	"time"
)

var (
	DefaultTimeout = 30 * time.Second
)

type Cmd struct {
	Timeout time.Duration
	name    string
	arg     []string
}

func New(name string, arg ...string) *Cmd {
	return &Cmd{
		name:    name,
		arg:     arg,
		Timeout: DefaultTimeout,
	}
}

func (c *Cmd) Run() (output string, err error) {
	cmd := exec.Command(c.name, c.arg...)
	outChan, errChan := runCmd(cmd)
	select {
	case <-time.After(c.Timeout):
		killErr := cmd.Process.Kill()
		if killErr != nil {
			// @todo:
			// If this happens that means leaving working process,
			// plus working goroutine waiting for that process to finish.
			// And since this command is going to be run over, and over again
			// we might end up with hundreds processes and goroutines hanging.
			// Maybe in such critical cases (or after n-cases) we should shutdown whole module (e.g. qan/mm/summary)
			// and notify us (developers), because this shouldn't happen in correct working program - but you never know
			err = fmt.Errorf("Failed to kill process after timeout in runPtSummary")
			return "", err
		}

		err = fmt.Errorf("Timeout in runPtSummary")
		return "", err
	case err = <-errChan:
		return "", err
	case output = <-outChan:
		return output, nil
	}

	err = fmt.Errorf("Unhandled case in runPtSummary")
	return "", err
}

func runCmd(cmd *exec.Cmd) (outChan chan string, errChan chan error) {
	// Below channels has buffer
	// because we might get data before we would be waiting on those channels
	outChan = make(chan string, 1)
	errChan = make(chan error, 1)
	go func() {
		output, err := cmd.Output()
		if err != nil {
			select {
			case errChan <- err:
			default:
			}
			return
		}

		select {
		case outChan <- string(output):
		default:
		}
	}()
	return outChan, errChan
}
<commit_msg>PCT-808: cleanup in cmd package, export errors<commit_after>/*
   Copyright (c) 2014, Percona LLC and/or its affiliates. All rights reserved.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero 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 Affero General Public License for more details.

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

package cmd

import (
	"errors"
	"os/exec"
	"time"
)

var (
	DefaultTimeout = 30 * time.Second
)

var (
	ErrTimeout                 = errors.New("Timeout")
	ErrKillProcessAfterTimeout = errors.New("Failed to kill process after timeout")
)

type Cmd struct {
	Timeout time.Duration
	name    string
	arg     []string
}

func New(name string, arg ...string) *Cmd {
	return &Cmd{
		name:    name,
		arg:     arg,
		Timeout: DefaultTimeout,
	}
}

func (c *Cmd) Run() (output string, err error) {
	cmd := exec.Command(c.name, c.arg...)
	outChan, errChan := runCmd(cmd)
	select {
	case <-time.After(c.Timeout):
		killErr := cmd.Process.Kill()
		if killErr != nil {
			// @todo:
			// If this happens that means leaving working process,
			// plus working goroutine waiting for that process to finish.
			// And since this command is going to be run over, and over again
			// we might end up with hundreds processes and goroutines hanging.
			// Maybe in such critical cases (or after n-cases) we should shutdown whole module (e.g. qan/mm/summary)
			// and notify us (developers), because this shouldn't happen in correct working program - but you never know
			return "", ErrKillProcessAfterTimeout
		}
		return "", ErrTimeout
	case err = <-errChan:
		return "", err
	case output = <-outChan:
		return output, nil
	}
}

func runCmd(cmd *exec.Cmd) (outChan chan string, errChan chan error) {
	// Below channels has buffer
	// because we might get data before we would be waiting on those channels
	outChan = make(chan string, 1)
	errChan = make(chan error, 1)
	go func() {
		output, err := cmd.Output()
		if err != nil {
			select {
			case errChan <- err:
			default:
			}
			return
		}

		select {
		case outChan <- string(output):
		default:
		}
	}()
	return outChan, errChan
}
<|endoftext|>


<commit_before>//    pubcontrol.go
//    ~~~~~~~~~
//    This module implements the PubControl struct and features.
//    :authors: Konstantin Bokarius.
//    :copyright: (c) 2015 by Fanout, Inc.
//    :license: MIT, see LICENSE for more details.

package pubcontrol

import (
	"fmt"
	"runtime"
	"strings"
	"sync"
)

// The PubControl struct allows a consumer to manage a set of publishing
// endpoints and to publish to all of those endpoints via a single publish
// method call. A PubControl instance can be configured either using a
// hash or array of hashes containing configuration information or by
// manually adding PubControlClient instances.
type PubControl struct {
	clients       []*PubControlClient
	clientsRWLock sync.RWMutex
}

// Initialize with or without a configuration. A configuration can be applied
// after initialization via the apply_config method.
func NewPubControl(config []map[string]interface{}) *PubControl {
	pc := new(PubControl)
	pc.clients = make([]*PubControlClient, 0)
	if config != nil && len(config) > 0 {
		pc.ApplyConfig(config)
	}
	return pc
}

// Remove all of the configured PubControlClient instances.
func (pc *PubControl) RemoveAllClients() {
	pc.clientsRWLock.Lock()
	defer pc.clientsRWLock.Unlock()
	pc.clients = make([]*PubControlClient, 0)
}

// Add the specified PubControlClient instance.
func (pc *PubControl) AddClient(pcc *PubControlClient) {
	pc.clientsRWLock.Lock()
	defer pc.clientsRWLock.Unlock()
	pc.clients = append(pc.clients, pcc)
}

// Apply the specified configuration to this PubControl instance. The
// configuration object can either be a hash or an array of hashes where
// each hash corresponds to a single PubControlClient instance. Each hash
// will be parsed and a PubControlClient will be created either using just
// a URI or a URI and JWT authentication information.
func (pc *PubControl) ApplyConfig(config []map[string]interface{}) {
	pc.clientsRWLock.Lock()
	defer pc.clientsRWLock.Unlock()
	for _, entry := range config {
		if _, ok := entry["uri"]; !ok {
			continue
		}
		pcc := NewPubControlClient(entry["uri"].(string))
		if _, ok := entry["iss"]; ok {
			claim := make(map[string]interface{})
			claim["iss"] = entry["iss"]
			switch entry["key"].(type) {
			case string:
				pcc.SetAuthJwt(claim, []byte(entry["key"].(string)))
			case []byte:
				pcc.SetAuthJwt(claim, entry["key"].([]byte))
			}
		}
		pc.clients = append(pc.clients, pcc)
	}
}

// The publish method for publishing the specified item to the specified
// channel on the configured endpoints. Different endpoints are published to in parallel,
// with this function waiting for them to finish. Any errors (including panics) are aggregated
// into one error.
func (pc *PubControl) Publish(channel string, item *Item) error {
	pc.clientsRWLock.RLock()
	defer pc.clientsRWLock.RUnlock()
	wg := sync.WaitGroup{}
	errCh := make(chan string, len(pc.clients))

	for _, pcc := range pc.clients {
		wg.Add(1)
		client := pcc
		go func() {
			defer func() {
				if err := recover(); err != nil {
					stack := make([]byte, 1024*8)
					stack = stack[:runtime.Stack(stack, false)]
					errCh <- fmt.Sprintf("%s: PANIC: %v\n%s", client.uri, err, stack)
				}
				wg.Done()
			}()

			err := client.Publish(channel, item)
			if err != nil {
				errCh <- fmt.Sprintf("%s: %s", client.uri, err.Error())
			}
		}()
	}
	wg.Wait()
	close(errCh)
	errs := make([]string, 0)
	for err := range errCh {
		errs = append(errs, err)
	}

	if len(errs) > 0 {
		return fmt.Errorf("%d/%d client(s) failed to publish to channel: %s Errors: [%s]",
			len(errs), len(pc.clients), channel, strings.Join(errs, "],["))
	}
	return nil
}
<commit_msg>Trim error message<commit_after>//    pubcontrol.go
//    ~~~~~~~~~
//    This module implements the PubControl struct and features.
//    :authors: Konstantin Bokarius.
//    :copyright: (c) 2015 by Fanout, Inc.
//    :license: MIT, see LICENSE for more details.

package pubcontrol

import (
	"fmt"
	"runtime"
	"strings"
	"sync"
)

// The PubControl struct allows a consumer to manage a set of publishing
// endpoints and to publish to all of those endpoints via a single publish
// method call. A PubControl instance can be configured either using a
// hash or array of hashes containing configuration information or by
// manually adding PubControlClient instances.
type PubControl struct {
	clients       []*PubControlClient
	clientsRWLock sync.RWMutex
}

// Initialize with or without a configuration. A configuration can be applied
// after initialization via the apply_config method.
func NewPubControl(config []map[string]interface{}) *PubControl {
	pc := new(PubControl)
	pc.clients = make([]*PubControlClient, 0)
	if config != nil && len(config) > 0 {
		pc.ApplyConfig(config)
	}
	return pc
}

// Remove all of the configured PubControlClient instances.
func (pc *PubControl) RemoveAllClients() {
	pc.clientsRWLock.Lock()
	defer pc.clientsRWLock.Unlock()
	pc.clients = make([]*PubControlClient, 0)
}

// Add the specified PubControlClient instance.
func (pc *PubControl) AddClient(pcc *PubControlClient) {
	pc.clientsRWLock.Lock()
	defer pc.clientsRWLock.Unlock()
	pc.clients = append(pc.clients, pcc)
}

// Apply the specified configuration to this PubControl instance. The
// configuration object can either be a hash or an array of hashes where
// each hash corresponds to a single PubControlClient instance. Each hash
// will be parsed and a PubControlClient will be created either using just
// a URI or a URI and JWT authentication information.
func (pc *PubControl) ApplyConfig(config []map[string]interface{}) {
	pc.clientsRWLock.Lock()
	defer pc.clientsRWLock.Unlock()
	for _, entry := range config {
		if _, ok := entry["uri"]; !ok {
			continue
		}
		pcc := NewPubControlClient(entry["uri"].(string))
		if _, ok := entry["iss"]; ok {
			claim := make(map[string]interface{})
			claim["iss"] = entry["iss"]
			switch entry["key"].(type) {
			case string:
				pcc.SetAuthJwt(claim, []byte(entry["key"].(string)))
			case []byte:
				pcc.SetAuthJwt(claim, entry["key"].([]byte))
			}
		}
		pc.clients = append(pc.clients, pcc)
	}
}

// The publish method for publishing the specified item to the specified
// channel on the configured endpoints. Different endpoints are published to in parallel,
// with this function waiting for them to finish. Any errors (including panics) are aggregated
// into one error.
func (pc *PubControl) Publish(channel string, item *Item) error {
	pc.clientsRWLock.RLock()
	defer pc.clientsRWLock.RUnlock()
	wg := sync.WaitGroup{}
	errCh := make(chan string, len(pc.clients))

	for _, pcc := range pc.clients {
		wg.Add(1)
		client := pcc
		go func() {
			defer func() {
				if err := recover(); err != nil {
					stack := make([]byte, 1024*8)
					stack = stack[:runtime.Stack(stack, false)]
					errCh <- fmt.Sprintf("%s: PANIC: %v\n%s", client.uri, err, stack)
				}
				wg.Done()
			}()

			err := client.Publish(channel, item)
			if err != nil {
				errCh <- fmt.Sprintf("%s: %s", client.uri, strings.TrimSpace(err.Error()))
			}
		}()
	}
	wg.Wait()
	close(errCh)
	errs := make([]string, 0)
	for err := range errCh {
		errs = append(errs, err)
	}

	if len(errs) > 0 {
		return fmt.Errorf("%d/%d client(s) failed to publish to channel: %s Errors: [%s]",
			len(errs), len(pc.clients), channel, strings.Join(errs, "],["))
	}
	return nil
}
<|endoftext|>


<commit_before>package quark_test

import (
	"encoding/base64"
	"errors"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os"
	"regexp"
	"testing"

	"github.com/gkarlik/quark-go"
	"github.com/gkarlik/quark-go/broker"
	"github.com/gkarlik/quark-go/logger"
	"github.com/gkarlik/quark-go/metrics"
	"github.com/gkarlik/quark-go/service/discovery"
	"github.com/gkarlik/quark-go/service/trace"
	"github.com/gkarlik/quark-go/service/trace/zipkin"
	opentracing "github.com/opentracing/opentracing-go"
	"github.com/stretchr/testify/assert"
	"golang.org/x/net/context"
	"google.golang.org/grpc/metadata"
)

type TestServiceDiscovery struct{}

func (sd *TestServiceDiscovery) RegisterService(options ...discovery.Option) error {
	return nil
}

func (sd *TestServiceDiscovery) DeregisterService(options ...discovery.Option) error {
	return nil
}

func (sd *TestServiceDiscovery) GetServiceAddress(options ...discovery.Option) (*url.URL, error) {
	return nil, nil
}

func (sd *TestServiceDiscovery) Dispose() {}

type TestBroker struct{}

func (b *TestBroker) PublishMessage(message broker.Message) error {
	return nil
}

func (b *TestBroker) Subscribe(key string) (<-chan broker.Message, error) {
	return nil, nil
}

func (b *TestBroker) Dispose() {}

type TestMetrics struct{}

func (m *TestMetrics) Report(ms ...metrics.Metric) error {
	return nil
}

func (m *TestMetrics) Dispose() {}

type TestTracer struct{}

func (t *TestTracer) StartSpan(name string) trace.Span {
	return nil
}

func (t *TestTracer) StartSpanFromContext(name string, ctx context.Context) (trace.Span, context.Context) {
	return nil, nil
}

func (t *TestTracer) StartSpanWithParent(name string, parent trace.Span) trace.Span {
	return nil
}

func (t *TestTracer) SpanFromContext(ctx context.Context) trace.Span {
	return nil
}

func (t *TestTracer) ContextWithSpan(ctx context.Context, span trace.Span) context.Context {
	return nil
}

func (t *TestTracer) InjectSpan(s trace.Span, format interface{}, carrier interface{}) error {
	return nil
}

func (t *TestTracer) ExtractSpan(name string, format interface{}, carrier interface{}) (trace.Span, error) {
	return nil, nil
}

func (t *TestTracer) Dispose() {}

type TestService struct {
	*quark.ServiceBase
}

func TestServiceBase(t *testing.T) {
	a, _ := quark.GetHostAddress(1234)

	discovery := &TestServiceDiscovery{}
	logger := logger.Log()
	broker := &TestBroker{}
	metrics := &TestMetrics{}
	tracer := &TestTracer{}

	ts := &TestService{
		ServiceBase: quark.NewService(
			quark.Name("TestService"),
			quark.Version("1.0"),
			quark.Tags("A", "B"),
			quark.Address(a),
			quark.Logger(logger),
			quark.Discovery(discovery),
			quark.Broker(broker),
			quark.Metrics(metrics),
			quark.Tracer(tracer)),
	}

	defer ts.Dispose()

	assert.Equal(t, "TestService", ts.Info().Name)
	assert.Equal(t, ts.Info(), ts.Options().Info)
	assert.Equal(t, a, ts.Info().Address)
	assert.Equal(t, "1.0", ts.Info().Version)
	assert.Equal(t, 2, len(ts.Info().Tags))
	assert.Equal(t, "A", ts.Info().Tags[0])
	assert.Equal(t, "B", ts.Info().Tags[1])
	assert.Equal(t, logger, ts.Log())
	assert.Equal(t, logger, ts.Options().Logger)
	assert.Equal(t, discovery, ts.Discovery())
	assert.Equal(t, discovery, ts.Options().Discovery)
	assert.Equal(t, broker, ts.Broker())
	assert.Equal(t, broker, ts.Options().Broker)
	assert.Equal(t, metrics, ts.Metrics())
	assert.Equal(t, metrics, ts.Options().Metrics)
	assert.Equal(t, tracer, ts.Tracer())
	assert.Equal(t, tracer, ts.Options().Tracer)

	addr, _ := quark.GetHostAddress(5678)
	// address will change on CI server - validate only IP address format
	matched, err := regexp.MatchString("\\d+\\.\\d+\\.\\d+.\\d+:5678", addr.String())
	assert.NoError(t, err, "Invalid IP address")
	assert.Equal(t, true, matched)

	addr, _ = quark.GetHostAddress(0)
	// address will change on CI server - validate only IP address format
	matched, err = regexp.MatchString("\\d+\\.\\d+\\.\\d+.\\d+", addr.String())
	assert.NoError(t, err, "Invalid IP address")
	assert.Equal(t, true, matched)
}

func TestLackOfName(t *testing.T) {
	a, _ := quark.GetHostAddress(1234)

	assert.Panics(t, func() {
		var _ = &TestService{
			ServiceBase: quark.NewService(
				quark.Version("1.0"),
				quark.Tags("A", "B"),
				quark.Address(a)),
		}
	})
}

func TestLackOfVersion(t *testing.T) {
	a, _ := quark.GetHostAddress(1234)

	assert.Panics(t, func() {
		var _ = &TestService{
			ServiceBase: quark.NewService(
				quark.Name("TestService"),
				quark.Tags("A", "B"),
				quark.Address(a)),
		}
	})
}

func TestLackOfAddress(t *testing.T) {
	assert.Panics(t, func() {
		var _ = &TestService{
			ServiceBase: quark.NewService(
				quark.Name("TestService"),
				quark.Version("1.0"),
				quark.Tags("A", "B")),
		}
	})
}

func TestGetEnvVar(t *testing.T) {
	key := "GET_ENV_TEST_VAR"

	os.Setenv(key, "Lorem ipsum")

	v := quark.GetEnvVar(key)

	assert.Equal(t, "Lorem ipsum", v)

	os.Setenv(key, "")

	assert.Panics(t, func() {
		quark.GetEnvVar(key)
	})

	os.Unsetenv(key)

	assert.Panics(t, func() {
		quark.GetEnvVar(key)
	})
}

func TestRPCMetadataCarrier(t *testing.T) {
	md := metadata.Pairs()
	mc := &quark.RPCMetadataCarrier{MD: &md}

	test_key := "test"
	test_val := "test_value"

	mc.Set(test_key, test_val)

	err := mc.ForeachKey(func(key string, value string) error {
		assert.Equal(t, test_key, key)
		assert.Equal(t, test_val, value)

		return nil
	})

	assert.NoError(t, err, "ForeachKey returned an error")
}

func TestRPCMetadataCarrierBinary(t *testing.T) {
	md := metadata.Pairs()
	mc := &quark.RPCMetadataCarrier{MD: &md}

	test_key := "test-bin"
	test_val := "test_value"
	test_val_bin := string(base64.StdEncoding.EncodeToString([]byte(test_val)))

	mc.Set(test_key, test_val)

	err := mc.ForeachKey(func(key string, value string) error {
		assert.Equal(t, test_key, key)
		assert.Equal(t, test_val_bin, value)

		return nil
	})

	assert.NoError(t, err, "ForeachKey returned an error")
}

func TestRPCMetadataCarrierError(t *testing.T) {
	md := metadata.Pairs()
	mc := &quark.RPCMetadataCarrier{MD: &md}

	mc.Set("test_key", "test_val")

	err := mc.ForeachKey(func(key string, value string) error {
		return errors.New("Key not found")
	})

	assert.Error(t, err, "ForeachKey should return an error")
}

type TestMetricRecorder struct {
	Metric metrics.Metric
}

func (sm *TestMetricRecorder) Report(m ...metrics.Metric) error {
	sm.Metric = m[0]

	return nil
}

func (sm *TestMetricRecorder) Dispose() {}

func TestReportServiceValue(t *testing.T) {
	a, _ := quark.GetHostAddress(1234)

	metrics := &TestMetricRecorder{}

	ts := &TestService{
		ServiceBase: quark.NewService(
			quark.Name("TestService"),
			quark.Version("1.0"),
			quark.Address(a),
			quark.Metrics(metrics)),
	}

	defer ts.Dispose()

	err := quark.ReportServiceValue(ts, "TestMetric", 1)

	assert.NoError(t, err, "ReportServiceValue returns an error")
	assert.Equal(t, "TestMetric", metrics.Metric.Name)
	assert.Equal(t, "TestService", metrics.Metric.Tags["service"])
	assert.Equal(t, 1, metrics.Metric.Values["value"])
}

func TestCallHTTPService(t *testing.T) {
	data := struct {
		url  string
		body string
	}{}

	// trace http server
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusNoContent)

		b, _ := ioutil.ReadAll(r.Body)

		data.url = r.URL.String()
		data.body = string(b)
	}))

	a, _ := quark.GetHostAddress(1234)
	tracer := zipkin.NewTracer(ts.URL, "TestService", a)

	s := &TestService{
		ServiceBase: quark.NewService(
			quark.Name("TestService"),
			quark.Version("1.0"),
			quark.Address(a),
			quark.Tracer(tracer)),
	}

	// service http server
	hs := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)

		span, err := tracer.ExtractSpan("extracted_span", opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header))
		assert.NoError(t, err, "ExtractSpan return an error")

		span.Finish()
	}))
	defer func() {
		s.Dispose()
		hs.Close()
		ts.Close()
	}()

	span := tracer.StartSpan("root_span")
	_, err := quark.CallHTTPService(s, "GET", hs.URL, nil, span)

	assert.NoError(t, err, "CallHTTPService returns an error")

	span.Finish()

	tracer.Dispose()

	assert.Contains(t, data.body, "root_span")
	assert.Contains(t, data.body, "extracted_span")
}

func TestMessageContextCarrierBinary(t *testing.T) {
	context := broker.MessageContext{}
	mc := &quark.MessageContextCarrier{Context: &context}

	test_key := "test-bin"
	test_val := "test_value"
	test_val_bin := string(base64.StdEncoding.EncodeToString([]byte(test_val)))

	mc.Set(test_key, test_val)

	err := mc.ForeachKey(func(key string, value string) error {
		assert.Equal(t, test_key, key)
		assert.Equal(t, test_val_bin, value)

		return nil
	})

	assert.NoError(t, err, "ForeachKey returned an error")
}

func TestMessageContextCarrierError(t *testing.T) {
	context := broker.MessageContext{}
	mc := &quark.MessageContextCarrier{Context: &context}

	mc.Set("test_key", "test_val")

	err := mc.ForeachKey(func(key string, value string) error {
		return errors.New("Key not found")
	})

	assert.Error(t, err, "ForeachKey should return an error")
}
<commit_msg>Add tests for StartRPCSpan.<commit_after>package quark_test

import (
	"encoding/base64"
	"errors"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os"
	"regexp"
	"testing"

	"github.com/gkarlik/quark-go"
	"github.com/gkarlik/quark-go/broker"
	"github.com/gkarlik/quark-go/logger"
	"github.com/gkarlik/quark-go/metrics"
	"github.com/gkarlik/quark-go/service/discovery"
	"github.com/gkarlik/quark-go/service/trace"
	"github.com/gkarlik/quark-go/service/trace/noop"
	"github.com/gkarlik/quark-go/service/trace/zipkin"
	opentracing "github.com/opentracing/opentracing-go"
	"github.com/stretchr/testify/assert"
	"golang.org/x/net/context"
	"google.golang.org/grpc/metadata"
)

type TestServiceDiscovery struct{}

func (sd *TestServiceDiscovery) RegisterService(options ...discovery.Option) error {
	return nil
}

func (sd *TestServiceDiscovery) DeregisterService(options ...discovery.Option) error {
	return nil
}

func (sd *TestServiceDiscovery) GetServiceAddress(options ...discovery.Option) (*url.URL, error) {
	return nil, nil
}

func (sd *TestServiceDiscovery) Dispose() {}

type TestBroker struct{}

func (b *TestBroker) PublishMessage(message broker.Message) error {
	return nil
}

func (b *TestBroker) Subscribe(key string) (<-chan broker.Message, error) {
	return nil, nil
}

func (b *TestBroker) Dispose() {}

type TestMetrics struct{}

func (m *TestMetrics) Report(ms ...metrics.Metric) error {
	return nil
}

func (m *TestMetrics) Dispose() {}

type TestTracer struct{}

func (t *TestTracer) StartSpan(name string) trace.Span {
	return nil
}

func (t *TestTracer) StartSpanFromContext(name string, ctx context.Context) (trace.Span, context.Context) {
	return nil, nil
}

func (t *TestTracer) StartSpanWithParent(name string, parent trace.Span) trace.Span {
	return nil
}

func (t *TestTracer) SpanFromContext(ctx context.Context) trace.Span {
	return nil
}

func (t *TestTracer) ContextWithSpan(ctx context.Context, span trace.Span) context.Context {
	return nil
}

func (t *TestTracer) InjectSpan(s trace.Span, format interface{}, carrier interface{}) error {
	return nil
}

func (t *TestTracer) ExtractSpan(name string, format interface{}, carrier interface{}) (trace.Span, error) {
	return nil, nil
}

func (t *TestTracer) Dispose() {}

type TestService struct {
	*quark.ServiceBase
}

func TestServiceBase(t *testing.T) {
	a, _ := quark.GetHostAddress(1234)

	discovery := &TestServiceDiscovery{}
	logger := logger.Log()
	broker := &TestBroker{}
	metrics := &TestMetrics{}
	tracer := &TestTracer{}

	ts := &TestService{
		ServiceBase: quark.NewService(
			quark.Name("TestService"),
			quark.Version("1.0"),
			quark.Tags("A", "B"),
			quark.Address(a),
			quark.Logger(logger),
			quark.Discovery(discovery),
			quark.Broker(broker),
			quark.Metrics(metrics),
			quark.Tracer(tracer)),
	}

	defer ts.Dispose()

	assert.Equal(t, "TestService", ts.Info().Name)
	assert.Equal(t, ts.Info(), ts.Options().Info)
	assert.Equal(t, a, ts.Info().Address)
	assert.Equal(t, "1.0", ts.Info().Version)
	assert.Equal(t, 2, len(ts.Info().Tags))
	assert.Equal(t, "A", ts.Info().Tags[0])
	assert.Equal(t, "B", ts.Info().Tags[1])
	assert.Equal(t, logger, ts.Log())
	assert.Equal(t, logger, ts.Options().Logger)
	assert.Equal(t, discovery, ts.Discovery())
	assert.Equal(t, discovery, ts.Options().Discovery)
	assert.Equal(t, broker, ts.Broker())
	assert.Equal(t, broker, ts.Options().Broker)
	assert.Equal(t, metrics, ts.Metrics())
	assert.Equal(t, metrics, ts.Options().Metrics)
	assert.Equal(t, tracer, ts.Tracer())
	assert.Equal(t, tracer, ts.Options().Tracer)

	addr, _ := quark.GetHostAddress(5678)
	// address will change on CI server - validate only IP address format
	matched, err := regexp.MatchString("\\d+\\.\\d+\\.\\d+.\\d+:5678", addr.String())
	assert.NoError(t, err, "Invalid IP address")
	assert.Equal(t, true, matched)

	addr, _ = quark.GetHostAddress(0)
	// address will change on CI server - validate only IP address format
	matched, err = regexp.MatchString("\\d+\\.\\d+\\.\\d+.\\d+", addr.String())
	assert.NoError(t, err, "Invalid IP address")
	assert.Equal(t, true, matched)
}

func TestLackOfName(t *testing.T) {
	a, _ := quark.GetHostAddress(1234)

	assert.Panics(t, func() {
		var _ = &TestService{
			ServiceBase: quark.NewService(
				quark.Version("1.0"),
				quark.Tags("A", "B"),
				quark.Address(a)),
		}
	})
}

func TestLackOfVersion(t *testing.T) {
	a, _ := quark.GetHostAddress(1234)

	assert.Panics(t, func() {
		var _ = &TestService{
			ServiceBase: quark.NewService(
				quark.Name("TestService"),
				quark.Tags("A", "B"),
				quark.Address(a)),
		}
	})
}

func TestLackOfAddress(t *testing.T) {
	assert.Panics(t, func() {
		var _ = &TestService{
			ServiceBase: quark.NewService(
				quark.Name("TestService"),
				quark.Version("1.0"),
				quark.Tags("A", "B")),
		}
	})
}

func TestGetEnvVar(t *testing.T) {
	key := "GET_ENV_TEST_VAR"

	os.Setenv(key, "Lorem ipsum")

	v := quark.GetEnvVar(key)

	assert.Equal(t, "Lorem ipsum", v)

	os.Setenv(key, "")

	assert.Panics(t, func() {
		quark.GetEnvVar(key)
	})

	os.Unsetenv(key)

	assert.Panics(t, func() {
		quark.GetEnvVar(key)
	})
}

func TestRPCMetadataCarrier(t *testing.T) {
	md := metadata.Pairs()
	mc := &quark.RPCMetadataCarrier{MD: &md}

	test_key := "test"
	test_val := "test_value"

	mc.Set(test_key, test_val)

	err := mc.ForeachKey(func(key string, value string) error {
		assert.Equal(t, test_key, key)
		assert.Equal(t, test_val, value)

		return nil
	})

	assert.NoError(t, err, "ForeachKey returned an error")
}

func TestRPCMetadataCarrierBinary(t *testing.T) {
	md := metadata.Pairs()
	mc := &quark.RPCMetadataCarrier{MD: &md}

	test_key := "test-bin"
	test_val := "test_value"
	test_val_bin := string(base64.StdEncoding.EncodeToString([]byte(test_val)))

	mc.Set(test_key, test_val)

	err := mc.ForeachKey(func(key string, value string) error {
		assert.Equal(t, test_key, key)
		assert.Equal(t, test_val_bin, value)

		return nil
	})

	assert.NoError(t, err, "ForeachKey returned an error")
}

func TestRPCMetadataCarrierError(t *testing.T) {
	md := metadata.Pairs()
	mc := &quark.RPCMetadataCarrier{MD: &md}

	mc.Set("test_key", "test_val")

	err := mc.ForeachKey(func(key string, value string) error {
		return errors.New("Key not found")
	})

	assert.Error(t, err, "ForeachKey should return an error")
}

type TestMetricRecorder struct {
	Metric metrics.Metric
}

func (sm *TestMetricRecorder) Report(m ...metrics.Metric) error {
	sm.Metric = m[0]

	return nil
}

func (sm *TestMetricRecorder) Dispose() {}

func TestReportServiceValue(t *testing.T) {
	a, _ := quark.GetHostAddress(1234)

	metrics := &TestMetricRecorder{}

	ts := &TestService{
		ServiceBase: quark.NewService(
			quark.Name("TestService"),
			quark.Version("1.0"),
			quark.Address(a),
			quark.Metrics(metrics)),
	}

	defer ts.Dispose()

	err := quark.ReportServiceValue(ts, "TestMetric", 1)

	assert.NoError(t, err, "ReportServiceValue returns an error")
	assert.Equal(t, "TestMetric", metrics.Metric.Name)
	assert.Equal(t, "TestService", metrics.Metric.Tags["service"])
	assert.Equal(t, 1, metrics.Metric.Values["value"])
}

func TestCallHTTPService(t *testing.T) {
	data := struct {
		url  string
		body string
	}{}

	// trace http server
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusNoContent)

		b, _ := ioutil.ReadAll(r.Body)

		data.url = r.URL.String()
		data.body = string(b)
	}))

	a, _ := quark.GetHostAddress(1234)
	tracer := zipkin.NewTracer(ts.URL, "TestService", a)

	s := &TestService{
		ServiceBase: quark.NewService(
			quark.Name("TestService"),
			quark.Version("1.0"),
			quark.Address(a),
			quark.Tracer(tracer)),
	}

	// service http server
	hs := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)

		span, err := tracer.ExtractSpan("extracted_span", opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header))
		assert.NoError(t, err, "ExtractSpan return an error")

		span.Finish()
	}))
	defer func() {
		s.Dispose()
		hs.Close()
		ts.Close()
	}()

	span := tracer.StartSpan("root_span")
	_, err := quark.CallHTTPService(s, "GET", hs.URL, nil, span)

	assert.NoError(t, err, "CallHTTPService returns an error")

	span.Finish()

	tracer.Dispose()

	assert.Contains(t, data.body, "root_span")
	assert.Contains(t, data.body, "extracted_span")
}

func TestMessageContextCarrierBinary(t *testing.T) {
	context := broker.MessageContext{}
	mc := &quark.MessageContextCarrier{Context: &context}

	test_key := "test-bin"
	test_val := "test_value"
	test_val_bin := string(base64.StdEncoding.EncodeToString([]byte(test_val)))

	mc.Set(test_key, test_val)

	err := mc.ForeachKey(func(key string, value string) error {
		assert.Equal(t, test_key, key)
		assert.Equal(t, test_val_bin, value)

		return nil
	})

	assert.NoError(t, err, "ForeachKey returned an error")
}

func TestMessageContextCarrierError(t *testing.T) {
	context := broker.MessageContext{}
	mc := &quark.MessageContextCarrier{Context: &context}

	mc.Set("test_key", "test_val")

	err := mc.ForeachKey(func(key string, value string) error {
		return errors.New("Key not found")
	})

	assert.Error(t, err, "ForeachKey should return an error")
}

func TestStartRPCSpan(t *testing.T) {
	a, _ := quark.GetHostAddress(1234)

	s := &TestService{
		ServiceBase: quark.NewService(
			quark.Name("TestService"),
			quark.Version("1.0"),
			quark.Address(a),
			quark.Tracer(noop.NewTracer())),
	}
	defer s.Dispose()

	var md metadata.MD
	ctx := metadata.NewContext(context.Background(), md)

	span := quark.StartRPCSpan(s, "Test", ctx)
	assert.NotNil(t, span, "Span is not nil")
}

func TestStartRPCSpanIncorrectContext(t *testing.T) {
	a, _ := quark.GetHostAddress(1234)

	s := &TestService{
		ServiceBase: quark.NewService(
			quark.Name("TestService"),
			quark.Version("1.0"),
			quark.Address(a),
			quark.Tracer(noop.NewTracer())),
	}
	defer s.Dispose()

	span := quark.StartRPCSpan(s, "Test", context.Background())
	assert.NotNil(t, span, "Span is not nil")
}
<|endoftext|>


<commit_before>package gountries

import (
	"fmt"
	"math/rand"
	"sort"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestFindCountryByName(t *testing.T) {

	var result Country
	var err error

	// Test for lowercase
	//

	result, err = query.FindCountryByName("sweden")

	if err != nil {
		t.Fail()
	}

	assert.Equal(t, result.Alpha2, "SE", "Lowercase country names should match")

	// Test for common name
	result, err = query.FindCountryByName("United States")

	if err != nil {
		t.Fail()
	}

	assert.Equal(t, result.Alpha2, "US", "Lowercase country names should match")

	// Test for official name
	result, err = query.FindCountryByName("United States of America")

	if err != nil {
		t.Fail()
	}

	assert.Equal(t, result.Alpha2, "US", "Lowercase country names should match")

	// Test for uppercase
	//

	result, err = query.FindCountryByName("SWEDEN")

	if err != nil {
		t.Fail()
	}

	assert.Equal(t, result.Alpha2, "SE", "Uppercase country names should match")

	// Test for invariants
	//

	invariants := []string{"Sweden", "SwEdEn", "SWEden"}

	for _, invariant := range invariants {

		result, err = query.FindCountryByName(invariant)

		if err != nil {
			t.Fail()
		}

		assert.Equal(t, result.Alpha2, "SE", fmt.Sprintf("Invariants of country names, eg sWeden,SWEDEN,swEdEn should match. %s did not match", invariant))

	}

}

func TestFindCountryByAlpha(t *testing.T) {

	var result Country
	var err error

	// Test for lowercase
	//

	result, err = query.FindCountryByAlpha("se")

	if err != nil {
		t.Fail()
	}

	assert.Equal(t, result.Alpha2, "SE", "Lowercase country names should match")

	// Test for uppercase
	//

	result, err = query.FindCountryByAlpha("SE")

	if err != nil {
		t.Fail()
	}

	assert.Equal(t, result.Alpha2, "SE", "Uppercase country names should match")

	// Test for invariants
	//

	result, err = query.FindCountryByAlpha("Se")

	if err != nil {
		t.Fail()
	}

	assert.Equal(t, result.Alpha2, "SE", "Invariants of country names, eg sWeden,SWEDEN,swEdEn should match")

	// Test for wrong code types (wrong length)
	//

	result, err = query.FindCountryByAlpha("SEE")

	if err != nil {
		assert.EqualError(t, err, "gountries error. Could not find country with code: SEE")

	} else {
		t.Fail()
	}

	// Test for wrong code types: too long
	//

	result, err = query.FindCountryByAlpha("SEEE")

	if err != nil {
		assert.EqualError(t, err, "gountries error. Invalid code format: SEEE")

	} else {
		t.Fail()
	}

	// Test for wrong code types: too short
	//

	result, err = query.FindCountryByAlpha("S")

	if err != nil {
		assert.EqualError(t, err, "gountries error. Invalid code format: S")

	} else {
		t.Fail()
	}

}

func TestFindAllCountries(t *testing.T) {

	assert.Len(t, query.FindAllCountries(), 247)

}

func TestFindCountries(t *testing.T) {

	country := Country{}
	country.Alpha2 = "SE"

	countries := query.FindCountries(country)

	assert.Len(t, countries, 1)

	assert.Equal(t, countries[0].Alpha2, "SE", fmt.Sprintf("Countries did not return expected result %s: %s", "SE", countries[0].Alpha2))

}

func TestFindCountriesByRegion(t *testing.T) {

	country := Country{}
	country.Geo.Region = "Europe"

	countries := query.FindCountries(country)

	assert.Len(t, countries, 52) // 52 is not the exact number of countries in Europe. Fix this later

}

func TestFindCountriesByContinent(t *testing.T) {

	country := Country{}
	country.Geo.Continent = "Europe"

	countries := query.FindCountries(country)

	assert.Len(t, countries, 52) // 52 is not the exact number of countries in Europe. Fix this later

}

func TestFindCountriesBySubRegion(t *testing.T) {

	country := Country{}
	country.Geo.SubRegion = "Eastern Asia"

	countries := query.FindCountries(country)

	assert.Len(t, countries, 8) // 8 is not the exact number of countries in Eastern Asia. Fix this later

}

func TestFindCountryByNativeName(t *testing.T) {

	var result Country
	var err error

	// Test for common name
	//

	result, err = query.FindCountryByNativeName("Sverige")

	if err != nil {
		t.Fail()
	}

	assert.Equal(t, result.Alpha2, "SE", "Common native country names should match")

	// Test for common name
	result, err = query.FindCountryByNativeName("Konungariket Sverige")

	if err != nil {
		t.Fail()
	}

	assert.Equal(t, result.Alpha2, "SE", "Official native country names should match")

	// Test for lowercase
	//

	result, err = query.FindCountryByNativeName("sverige")

	if err != nil {
		t.Fail()
	}

	assert.Equal(t, result.Alpha2, "SE", "Uppercase native country names should match")

	// Test for uppercase
	//

	result, err = query.FindCountryByNativeName("SVERIGE")

	if err != nil {
		t.Fail()
	}

	assert.Equal(t, result.Alpha2, "SE", "Uppercase native country names should match")

	// Test for invariants
	//

	invariants := []string{"sVEriGE", "SveRIge", "SVErige"}

	for _, invariant := range invariants {

		result, err = query.FindCountryByNativeName(invariant)

		if err != nil {
			t.Fail()
		}

		assert.Equal(t, result.Alpha2, "SE", fmt.Sprintf("Invariants of native country names, eg sVEriGE,SveRIge,SVErige should match. %s did not match", invariant))

	}

}

func ExampleFindCountriesBorderingCountries() {

	country := Country{}
	//country.Alpha3 = "AUT"
	country.Borders = []string{
		"DEU",
	}

	countries := query.FindCountries(country)
	var c []string
	for _, country := range countries {
		c = append(c, country.Name.Common)
	}
	sort.Strings(c)
	for _, name := range c {
		fmt.Println(name)
	}

	// Output:
	//Austria
	//Belgium
	//Czech Republic
	//Denmark
	//France
	//Luxembourg
	//Netherlands
	//Poland
	//Switzerland

}

func ExampleFindCountriesBorderingCountries2() {

	country := Country{
		Borders: []string{
			"DEU",
			"CHE",
		},
	}

	countries := query.FindCountries(country)
	var c []string
	for _, country := range countries {
		c = append(c, country.Name.Common)
	}
	sort.Strings(c)
	for _, name := range c {
		fmt.Println(name)
	}

	// Output:
	//Austria
	//France

}

var result Country

func BenchmarkCountryLookupByName(b *testing.B) {

	q := New()
	var names []string
	for key := range q.Countries {
		names = append(names, q.Countries[key].Name.Common)
	}
	for n := 0; n <= b.N; n++ {
		randIndex := rand.Intn(len(q.Countries))
		c, err := q.FindCountryByName(names[randIndex])
		if err != nil {
			b.Fail()
		}
		result = c
	}
}
<commit_msg>Fix TestFindAllCountries assert value<commit_after>package gountries

import (
	"fmt"
	"math/rand"
	"sort"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestFindCountryByName(t *testing.T) {

	var result Country
	var err error

	// Test for lowercase
	//

	result, err = query.FindCountryByName("sweden")

	if err != nil {
		t.Fail()
	}

	assert.Equal(t, result.Alpha2, "SE", "Lowercase country names should match")

	// Test for common name
	result, err = query.FindCountryByName("United States")

	if err != nil {
		t.Fail()
	}

	assert.Equal(t, result.Alpha2, "US", "Lowercase country names should match")

	// Test for official name
	result, err = query.FindCountryByName("United States of America")

	if err != nil {
		t.Fail()
	}

	assert.Equal(t, result.Alpha2, "US", "Lowercase country names should match")

	// Test for uppercase
	//

	result, err = query.FindCountryByName("SWEDEN")

	if err != nil {
		t.Fail()
	}

	assert.Equal(t, result.Alpha2, "SE", "Uppercase country names should match")

	// Test for invariants
	//

	invariants := []string{"Sweden", "SwEdEn", "SWEden"}

	for _, invariant := range invariants {

		result, err = query.FindCountryByName(invariant)

		if err != nil {
			t.Fail()
		}

		assert.Equal(t, result.Alpha2, "SE", fmt.Sprintf("Invariants of country names, eg sWeden,SWEDEN,swEdEn should match. %s did not match", invariant))

	}

}

func TestFindCountryByAlpha(t *testing.T) {

	var result Country
	var err error

	// Test for lowercase
	//

	result, err = query.FindCountryByAlpha("se")

	if err != nil {
		t.Fail()
	}

	assert.Equal(t, result.Alpha2, "SE", "Lowercase country names should match")

	// Test for uppercase
	//

	result, err = query.FindCountryByAlpha("SE")

	if err != nil {
		t.Fail()
	}

	assert.Equal(t, result.Alpha2, "SE", "Uppercase country names should match")

	// Test for invariants
	//

	result, err = query.FindCountryByAlpha("Se")

	if err != nil {
		t.Fail()
	}

	assert.Equal(t, result.Alpha2, "SE", "Invariants of country names, eg sWeden,SWEDEN,swEdEn should match")

	// Test for wrong code types (wrong length)
	//

	result, err = query.FindCountryByAlpha("SEE")

	if err != nil {
		assert.EqualError(t, err, "gountries error. Could not find country with code: SEE")

	} else {
		t.Fail()
	}

	// Test for wrong code types: too long
	//

	result, err = query.FindCountryByAlpha("SEEE")

	if err != nil {
		assert.EqualError(t, err, "gountries error. Invalid code format: SEEE")

	} else {
		t.Fail()
	}

	// Test for wrong code types: too short
	//

	result, err = query.FindCountryByAlpha("S")

	if err != nil {
		assert.EqualError(t, err, "gountries error. Invalid code format: S")

	} else {
		t.Fail()
	}

}

func TestFindAllCountries(t *testing.T) {

	assert.Len(t, query.FindAllCountries(), 249)

}

func TestFindCountries(t *testing.T) {

	country := Country{}
	country.Alpha2 = "SE"

	countries := query.FindCountries(country)

	assert.Len(t, countries, 1)

	assert.Equal(t, countries[0].Alpha2, "SE", fmt.Sprintf("Countries did not return expected result %s: %s", "SE", countries[0].Alpha2))

}

func TestFindCountriesByRegion(t *testing.T) {

	country := Country{}
	country.Geo.Region = "Europe"

	countries := query.FindCountries(country)

	assert.Len(t, countries, 52) // 52 is not the exact number of countries in Europe. Fix this later

}

func TestFindCountriesByContinent(t *testing.T) {

	country := Country{}
	country.Geo.Continent = "Europe"

	countries := query.FindCountries(country)

	assert.Len(t, countries, 52) // 52 is not the exact number of countries in Europe. Fix this later

}

func TestFindCountriesBySubRegion(t *testing.T) {

	country := Country{}
	country.Geo.SubRegion = "Eastern Asia"

	countries := query.FindCountries(country)

	assert.Len(t, countries, 8) // 8 is not the exact number of countries in Eastern Asia. Fix this later

}

func TestFindCountryByNativeName(t *testing.T) {

	var result Country
	var err error

	// Test for common name
	//

	result, err = query.FindCountryByNativeName("Sverige")

	if err != nil {
		t.Fail()
	}

	assert.Equal(t, result.Alpha2, "SE", "Common native country names should match")

	// Test for common name
	result, err = query.FindCountryByNativeName("Konungariket Sverige")

	if err != nil {
		t.Fail()
	}

	assert.Equal(t, result.Alpha2, "SE", "Official native country names should match")

	// Test for lowercase
	//

	result, err = query.FindCountryByNativeName("sverige")

	if err != nil {
		t.Fail()
	}

	assert.Equal(t, result.Alpha2, "SE", "Uppercase native country names should match")

	// Test for uppercase
	//

	result, err = query.FindCountryByNativeName("SVERIGE")

	if err != nil {
		t.Fail()
	}

	assert.Equal(t, result.Alpha2, "SE", "Uppercase native country names should match")

	// Test for invariants
	//

	invariants := []string{"sVEriGE", "SveRIge", "SVErige"}

	for _, invariant := range invariants {

		result, err = query.FindCountryByNativeName(invariant)

		if err != nil {
			t.Fail()
		}

		assert.Equal(t, result.Alpha2, "SE", fmt.Sprintf("Invariants of native country names, eg sVEriGE,SveRIge,SVErige should match. %s did not match", invariant))

	}

}

func ExampleFindCountriesBorderingCountries() {

	country := Country{}
	//country.Alpha3 = "AUT"
	country.Borders = []string{
		"DEU",
	}

	countries := query.FindCountries(country)
	var c []string
	for _, country := range countries {
		c = append(c, country.Name.Common)
	}
	sort.Strings(c)
	for _, name := range c {
		fmt.Println(name)
	}

	// Output:
	//Austria
	//Belgium
	//Czech Republic
	//Denmark
	//France
	//Luxembourg
	//Netherlands
	//Poland
	//Switzerland

}

func ExampleFindCountriesBorderingCountries2() {

	country := Country{
		Borders: []string{
			"DEU",
			"CHE",
		},
	}

	countries := query.FindCountries(country)
	var c []string
	for _, country := range countries {
		c = append(c, country.Name.Common)
	}
	sort.Strings(c)
	for _, name := range c {
		fmt.Println(name)
	}

	// Output:
	//Austria
	//France

}

var result Country

func BenchmarkCountryLookupByName(b *testing.B) {

	q := New()
	var names []string
	for key := range q.Countries {
		names = append(names, q.Countries[key].Name.Common)
	}
	for n := 0; n <= b.N; n++ {
		randIndex := rand.Intn(len(q.Countries))
		c, err := q.FindCountryByName(names[randIndex])
		if err != nil {
			b.Fail()
		}
		result = c
	}
}
<|endoftext|>


<commit_before>package queue

import (
	"errors"
	"fmt"
	"net"
	"net/smtp"
	"time"
)

// Persistent connection to an SMTP host.
type Host struct {
	// TODO
}

// Attempt to find the mail servers for the specified host.
func findMailServers(host string) []string {

	// First check for MX records - if one or more were found, convert the
	// records into a list of strings (already sorted by priority) - if none
	// were found, then simply return the host that was originally provided
	if mx, err := net.LookupMX(host); err == nil {
		servers := make([]string, len(mx))
		for i, record := range mx {
			servers[i] = record.Host
		}
		return servers
	} else {
		return []string{host}
	}
}

// Attempt to connect to the specified host.
func connectToMailServer(host string, stop chan bool) (*smtp.Client, error) {

	// Obtain the list of mail servers to try
	servers := findMailServers(host)

	// RFC 5321 (4.5.4) describes the process for retrying connections to a
	// mail server after failure. The recommended strategy is to retry twice
	// with 30 minute intervals and continue at 120 minute intervals until four
	// days have elapsed.
	for i := 0; i < 50; i++ {

		// Attempt to connect to each of the mail servers from the list in the
		// order that was provided - return immediately if a connection is made
		for _, server := range servers {
			if client, err := smtp.Dial(fmt.Sprintf("%s:25", server)); err == nil {
				return client, nil
			}
		}

		// None of the connections succeeded, so it is time to wait either for
		// the specified timeout duration or a receive on the stop channel
		var dur time.Duration
		if i < 2 {
			dur = 30 * time.Minute
		} else {
			dur = 2 * time.Hour
		}

		select {
		case <-time.After(dur):
		case <-stop:
			return nil, nil
		}
	}

	// All attempts have failed, let the caller know we tried :)
	return nil, errors.New("unable to connect to a mail server")
}
<commit_msg>Implemented more of Host.<commit_after>package queue

import (
	"errors"
	"fmt"
	"net"
	"net/smtp"
	"time"
)

// Persistent connection to an SMTP host.
type Host struct {
	name string
	stop chan bool
}

// Attempt to find the mail servers for the specified host.
func findMailServers(host string) []string {

	// First check for MX records - if one or more were found, convert the
	// records into a list of strings (already sorted by priority) - if none
	// were found, then simply return the host that was originally provided
	if mx, err := net.LookupMX(host); err == nil {
		servers := make([]string, len(mx))
		for i, record := range mx {
			servers[i] = record.Host
		}
		return servers
	} else {
		return []string{host}
	}
}

// Attempt to connect to the specified host.
func connectToMailServer(host string, stop chan bool) (*smtp.Client, error) {

	// Obtain the list of mail servers to try
	servers := findMailServers(host)

	// RFC 5321 (4.5.4) describes the process for retrying connections to a
	// mail server after failure. The recommended strategy is to retry twice
	// with 30 minute intervals and continue at 120 minute intervals until four
	// days have elapsed.
	for i := 0; i < 50; i++ {

		// Attempt to connect to each of the mail servers from the list in the
		// order that was provided - return immediately if a connection is made
		for _, server := range servers {
			if client, err := smtp.Dial(fmt.Sprintf("%s:25", server)); err == nil {
				return client, nil
			}
		}

		// None of the connections succeeded, so it is time to wait either for
		// the specified timeout duration or a receive on the stop channel
		var dur time.Duration
		if i < 2 {
			dur = 30 * time.Minute
		} else {
			dur = 2 * time.Hour
		}

		select {
		case <-time.After(dur):
		case <-stop:
			return nil, nil
		}
	}

	// All attempts have failed, let the caller know we tried :)
	return nil, errors.New("unable to connect to a mail server")
}

// Attempt to deliver the specified message to the server.
func deliverToMailServer(client *smtp.Client, from string, to []string, message []byte) error {
	if err := client.Mail(from); err != nil {
		return err
	}
	for _, recipient := range to {
		if err := client.Rcpt(recipient); err != nil {
			return err
		}
	}
	writer, err := client.Data()
	if err != nil {
		return err
	}
	defer writer.Close()
	if _, err = writer.Write(message); err != nil {
		return err
	}
	return nil
}

// Create a new host connection.
func NewHost(host string) *Host {

	// Create the host, including the channel used for stopping it
	h := &Host{
		name: host,
		stop: make(chan bool),
	}

	//...
	go func() {

		// Close the stop channel when the goroutine exits
		defer close(h.stop)
	}()

	return h
}

// Abort the connection.
func (h *Host) Stop() {

	// Send on the channel to stop it and wait for it to be closed
	h.stop <- true
	<-h.stop
}
<|endoftext|>


<commit_before>package flux

import "sync/atomic"

type (

	//Signal denotes a value received by a reactivestack
	Signal interface{}

	//ReactiveOp defines a reactive function operation
	ReactiveOp func(ReactiveStacks)

	//ReactiveStacks provides an interface for a stack implementation using channels
	ReactiveStacks interface {
		In() chan Signal
		Out() chan Signal
		Error() <-chan error
		Closed() <-chan struct{}
		Feed() ReactiveStacks
		HasChild() bool
		// Child() ReactiveStacks
		React(ReactiveOp) ReactiveStacks
		End()
	}

	//ReactiveStack provides a concrete implementation
	ReactiveStack struct {
		in, out           chan Signal
		closed            chan struct{}
		errs              chan error
		flow              bool
		op                ReactiveOp
		root              ReactiveStacks
		next              ReactiveStacks
		started, finished int64
	}
)

//ReactReceive returns a react operator
func ReactReceive() ReactiveOp {
	return func(self ReactiveStacks) {
		func() {
		iloop:
			for {
				select {
				case <-self.Feed().Closed():
					self.End()
					break iloop
				case <-self.Closed():
					break iloop
				case data := <-self.Feed().Out():
					self.Out() <- data
				case data := <-self.In():
					self.Out() <- data
				}
			}
		}()
	}
}

//ReactIdentity returns a reactor that only sends it in to its out
func ReactIdentity() ReactiveStacks {
	return Reactive(func(self ReactiveStacks) {
		func() {
		iloop:
			for {
				select {
				case <-self.Closed():
					break iloop
				case data := <-self.In():
					if self.HasChild() {
						go func() { self.Out() <- data }()
					} else {
						data = nil
					}
				}
			}
		}()
	}, nil)
}

//Reactive returns a ReactiveStacks,the process is not started immediately if no root exists,to force it,call .ForceRun()
func Reactive(fx ReactiveOp, root ReactiveStacks) *ReactiveStack {
	r := &ReactiveStack{
		in:     make(chan Signal),
		out:    make(chan Signal),
		closed: make(chan struct{}),
		errs:   make(chan error),
		op:     fx,
		root:   root,
	}

	r.boot()

	return r
}

//ForceRun forces the immediate start of the reactor
func (r *ReactiveStack) boot() {
	//bootup this reactor
	if r.started > 0 {
		return
	}

	GoDefer("StartReact", func() {
		atomic.StoreInt64(&r.started, 1)
		r.op(r)
	})
}

//In returns the in-put pipe
func (r *ReactiveStack) In() chan Signal {
	return r.in
}

//Out returns the out-put pipe
func (r *ReactiveStack) Out() chan Signal {
	return r.out
}

//Closed returns the error pipe
func (r *ReactiveStack) Closed() <-chan struct{} {
	return r.closed
}

//Error returns the error pipe
func (r *ReactiveStack) Error() <-chan error {
	return r.errs
}

//Feed returns the parent reativestack
func (r *ReactiveStack) Feed() ReactiveStacks {
	return r.root
}

//HasChild returns true/false if its has a chain
func (r *ReactiveStack) HasChild() bool {
	return r.next != nil
}

//Parent returns the parent reativestack
// func (r *ReactiveStack) Parent() ReactiveStacks {
// 	return r.root
// }
//Child returns the next reativestack
// func (r *ReactiveStack) Child() ReactiveStacks {
// 	return r.next
// }

//React creates a reactivestack from this current one
func (r *ReactiveStack) React(fx ReactiveOp) ReactiveStacks {

	if r.next != nil {
		return r.next.React(fx)
	}

	r.next = Reactive(fx, r)

	return r.next
}

//End signals to the next stack its closing
func (r *ReactiveStack) End() {

	if r.finished > 0 {
		return
	}

	GoDefer("CloseReact", func() {
		close(r.closed)
		atomic.StoreInt64(&r.finished, 1)
	})
}
<commit_msg>adding detach instruction<commit_after>package flux

import "sync/atomic"

type (

	//Signal denotes a value received by a reactivestack
	Signal interface{}

	//ReactiveOp defines a reactive function operation
	ReactiveOp func(ReactiveStacks)

	//ReactiveStacks provides an interface for a stack implementation using channels
	ReactiveStacks interface {
		In() chan Signal
		Out() chan Signal
		Error() <-chan error
		Closed() <-chan struct{}
		Feed() ReactiveStacks
		HasChild() bool
		// Child() ReactiveStacks
		React(ReactiveOp) ReactiveStacks
		End()
		detach()
	}

	//ReactiveStack provides a concrete implementation
	ReactiveStack struct {
		in, out           chan Signal
		closed            chan struct{}
		errs              chan error
		flow              bool
		op                ReactiveOp
		root              ReactiveStacks
		next              ReactiveStacks
		started, finished int64
	}
)

//ReactReceive returns a react operator
func ReactReceive() ReactiveOp {
	return func(self ReactiveStacks) {
		func() {
		iloop:
			for {
				select {
				case <-self.Feed().Closed():
					self.End()
					break iloop
				case <-self.Closed():
					break iloop
				case data := <-self.Feed().Out():
					self.Out() <- data
				case data := <-self.In():
					self.Out() <- data
				}
			}
		}()
	}
}

//ReactIdentity returns a reactor that only sends it in to its out
func ReactIdentity() ReactiveStacks {
	return Reactive(func(self ReactiveStacks) {
		func() {
		iloop:
			for {
				select {
				case <-self.Closed():
					break iloop
				case data := <-self.In():
					if self.HasChild() {
						go func() { self.Out() <- data }()
					} else {
						data = nil
					}
				}
			}
		}()
	}, nil)
}

//Reactive returns a ReactiveStacks,the process is not started immediately if no root exists,to force it,call .ForceRun()
func Reactive(fx ReactiveOp, root ReactiveStacks) *ReactiveStack {
	r := &ReactiveStack{
		in:     make(chan Signal),
		out:    make(chan Signal),
		closed: make(chan struct{}),
		errs:   make(chan error),
		op:     fx,
		root:   root,
	}

	r.boot()

	return r
}

func (r *ReactiveStack) detach() {
	r.next = nil
}

//ForceRun forces the immediate start of the reactor
func (r *ReactiveStack) boot() {
	//bootup this reactor
	if r.started > 0 {
		return
	}

	GoDefer("StartReact", func() {
		atomic.StoreInt64(&r.started, 1)
		r.op(r)
	})
}

//In returns the in-put pipe
func (r *ReactiveStack) In() chan Signal {
	return r.in
}

//Out returns the out-put pipe
func (r *ReactiveStack) Out() chan Signal {
	return r.out
}

//Closed returns the error pipe
func (r *ReactiveStack) Closed() <-chan struct{} {
	return r.closed
}

//Error returns the error pipe
func (r *ReactiveStack) Error() <-chan error {
	return r.errs
}

//Feed returns the parent reativestack
func (r *ReactiveStack) Feed() ReactiveStacks {
	return r.root
}

//HasChild returns true/false if its has a chain
func (r *ReactiveStack) HasChild() bool {
	return r.next != nil
}

//Parent returns the parent reativestack
// func (r *ReactiveStack) Parent() ReactiveStacks {
// 	return r.root
// }
//Child returns the next reativestack
// func (r *ReactiveStack) Child() ReactiveStacks {
// 	return r.next
// }

//React creates a reactivestack from this current one
func (r *ReactiveStack) React(fx ReactiveOp) ReactiveStacks {

	if r.next != nil {
		return r.next.React(fx)
	}

	r.next = Reactive(fx, r)

	return r.next
}

//End signals to the next stack its closing
func (r *ReactiveStack) End() {

	if r.finished > 0 {
		return
	}

	GoDefer("CloseReact", func() {
		r.root.detach()
		close(r.closed)
		atomic.StoreInt64(&r.finished, 1)
	})
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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.

// More information about Google Directions API is available on
// https://developers.google.com/maps/documentation/directions/

package maps // import "google.golang.org/maps"

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"strings"
	"time"

	"google.golang.org/maps/internal"

	"golang.org/x/net/context"
)

// Get issues the Directions request and retrieves the Response
func (r *DirectionsRequest) Get(ctx context.Context) ([]Route, error) {
	var response DirectionsResponse

	if r.Origin == "" {
		return nil, errors.New("directions: Origin required")
	}
	if r.Destination == "" {
		return nil, errors.New("directions: Destination required")
	}
	if r.Mode != "" && TravelModeDriving != r.Mode && TravelModeWalking != r.Mode && TravelModeBicycling != r.Mode && TravelModeTransit != r.Mode {
		return nil, fmt.Errorf("directions: unknown Mode: '%s'", r.Mode)
	}
	for _, avoid := range r.Avoid {
		if avoid != AvoidTolls && avoid != AvoidHighways && avoid != AvoidFerries {
			return nil, fmt.Errorf("directions: Unknown Avoid restriction '%s'", avoid)
		}
	}
	if r.Units != "" && r.Units != UnitsMetric && r.Units != UnitsImperial {
		return nil, fmt.Errorf("directions: Unknown Units '%s'", r.Units)
	}
	for _, transitMode := range r.TransitMode {
		if transitMode != TransitModeBus && transitMode != TransitModeSubway && transitMode != TransitModeTrain && transitMode != TransitModeTram && transitMode != TransitModeRail {
			return nil, fmt.Errorf("directions: Unknown TransitMode '%s'", r.TransitMode)
		}
	}
	if r.TransitRoutingPreference != "" && r.TransitRoutingPreference != TransitRoutingPreferenceLessWalking && r.TransitRoutingPreference != TransitRoutingPreferenceFewerTransfers {
		return nil, fmt.Errorf("directions: Unknown TransitRoutingPreference '%s'", r.TransitRoutingPreference)
	}
	if r.DepartureTime != "" && r.ArrivalTime != "" {
		return nil, errors.New("directions: must not specify both DepartureTime and ArrivalTime")
	}

	if r.DepartureTime != "" && r.ArrivalTime != "" {
		return nil, errors.New("directions: must not specify both DepartureTime and ArrivalTime")
	}
	if len(r.TransitMode) != 0 && r.Mode != TravelModeTransit {
		return nil, errors.New("directions: must specify mode of transit when specifying transitMode")
	}
	if r.TransitRoutingPreference != "" && r.Mode != TravelModeTransit {
		return nil, errors.New("directions: must specify mode of transit when specifying transitRoutingPreference")
	}

	req, err := http.NewRequest("GET", "https://maps.googleapis.com/maps/api/directions/json", nil)
	if err != nil {
		return nil, err
	}
	q := req.URL.Query()
	q.Set("origin", r.Origin)
	q.Set("destination", r.Destination)
	q.Set("key", internal.APIKey(ctx))
	if r.Mode != "" {
		q.Set("mode", string(r.Mode))
	}
	if len(r.Waypoints) != 0 {
		q.Set("waypoints", strings.Join(r.Waypoints, "|"))
	}
	if r.Alternatives {
		q.Set("alternatives", "true")
	}
	if len(r.Avoid) > 0 {
		var avoid []string
		for _, a := range r.Avoid {
			avoid = append(avoid, string(a))
		}
		q.Set("avoid", strings.Join(avoid, "|"))
	}
	if r.Language != "" {
		q.Set("language", r.Language)
	}
	if r.Units != "" {
		q.Set("units", string(r.Units))
	}
	if r.Region != "" {
		q.Set("region", r.Region)
	}
	if len(r.TransitMode) != 0 {
		var transitMode []string
		for _, t := range r.TransitMode {
			transitMode = append(transitMode, string(t))
		}
		q.Set("transit_mode", strings.Join(transitMode, "|"))
	}
	req.URL.RawQuery = q.Encode()

	log.Println("Request:", req)

	err = httpDo(ctx, req, func(resp *http.Response, err error) error {
		if err != nil {
			return err
		}
		defer resp.Body.Close()

		if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	if response.Status != "OK" {
		return nil, fmt.Errorf("directions: %s - %s", response.Status, response.ErrorMessage)
	}

	return response.Routes, nil
}

// DirectionsRequest is the functional options struct for directions.Get
type DirectionsRequest struct {
	// Origin is the address or textual latitude/longitude value from which you wish to calculate directions. Required.
	Origin string
	// Destination is the address or textual latitude/longitude value from which you wish to calculate directions. Required.
	Destination string
	// Mode specifies the mode of transport to use when calculating directions. Optional.
	Mode mode
	// DepartureTime specifies the desired time of departure. You can specify the time as an integer in seconds since midnight, January 1, 1970 UTC. Alternatively, you can specify a value of `"now"`. Optional.
	DepartureTime string
	// ArrivalTime specifies the desired time of arrival for transit directions, in seconds since midnight, January 1, 1970 UTC. Optional. You cannot specify both `DepartureTime` and `ArrivalTime`.
	ArrivalTime string
	// Waypoints specifies an array of points to add to a route. Optional.
	Waypoints []string
	// Alternatives specifies if Directions service may provide more than one route alternative in the response. Optional.
	Alternatives bool
	// Avoid indicates that the calculated route(s) should avoid the indicated features. Optional.
	Avoid []avoid
	// Language specifies the language in which to return results. Optional.
	Language string
	// Units specifies the unit system to use when displaying results. Optional.
	Units units
	// Region specifies the region code, specified as a ccTLD two-character value. Optional.
	Region string
	// TransitMode specifies one or more preferred modes of transit. This parameter may only be specified for transit directions. Optional.
	TransitMode []transitMode
	// TransitRoutingPreference specifies preferences for transit routes. Optional.
	TransitRoutingPreference transitRoutingPreference
}

// DirectionsResponse represents a Directions API response.
type DirectionsResponse struct {
	// Routes lists the found routes between origin and destination.
	Routes []Route `json:"routes"`

	// Status contains the status of the request, and may contain
	// debugging information to help you track down why the Directions
	// service failed.
	// See https://developers.google.com/maps/documentation/directions/#StatusCodes
	Status string `json:"status"`

	// ErrorMessage is the explanatory field added when Status is an error.
	ErrorMessage string `json:"error_message"`
}

// Route represents a single route between an origin and a destination.
type Route struct {
	// Summary contains a short textual description for the route, suitable for
	// naming and disambiguating the route from alternatives.
	Summary string `json:"summary"`

	// Legs contains information about a leg of the route, between two locations within the
	// given route. A separate leg will be present for each waypoint or destination specified.
	// (A route with no waypoints will contain exactly one leg within the legs array.)
	Legs []*Leg `json:"legs"`

	// WaypointOrder contains an array indicating the order of any waypoints in the calculated route.
	WaypointOrder []int `json:"waypoint_order"`

	// OverviewPolyline contains an approximate (smoothed) path of the resulting directions.
	OverviewPolyline Polyline `json:"overview_polyline"`

	// Bounds contains the viewport bounding box of the overview polyline.
	Bounds Bounds `json:"bounds"`

	// Copyrights contains the copyrights text to be displayed for this route. You must handle
	// and display this information yourself.
	Copyrights string `json:"copyrights"`

	// Warnings contains an array of warnings to be displayed when showing these directions.
	// You must handle and display these warnings yourself.
	Warnings []string `json:"warnings"`
}

// Leg represents a single leg of a route.
type Leg struct {
	// Steps contains an array of steps denoting information about each separate step of the
	// leg of the journey.
	Steps []*Step `json:"steps"`

	// Distance indicates the total distance covered by this leg.
	Distance `json:"distance"`

	// Duration indicates total time required for this leg.
	time.Duration `json:"duration"`

	// ArrivalTime contains the estimated time of arrival for this leg. This property is only
	// returned for transit directions.
	ArrivalTime time.Time `json:"arrival_time"`

	// DepartureTime contains the estimated time of departure for this leg. This property is
	// only returned for transit directions.
	DepartureTime time.Time `json:"departure_time"`

	// StartLocation contains the latitude/longitude coordinates of the origin of this leg.
	StartLocation LatLng `json:"start_location"`

	// EndLocation contains the latitude/longitude coordinates of the destination of this leg.
	EndLocation LatLng `json:"end_location"`

	// StartAddress contains the human-readable address (typically a street address)
	// reflecting the start location of this leg.
	StartAddress string `json:"start_address"`

	// EndAddress contains the human-readable address (typically a street address)
	// reflecting the end location of this leg.
	EndAddress string `json:"end_address"`
}

// Step represents a single step of a leg.
type Step struct {
	// HTMLInstructions contains formatted instructions for this step, presented as an HTML text string.
	HTMLInstructions string `json:"html_instructions"`

	// Distance contains the distance covered by this step until the next step.
	Distance `json:"distance"`

	// Duration contains the typical time required to perform the step, until the next step.
	time.Duration `json:"duration"`

	// StartLocation contains the location of the starting point of this step, as a single set of lat
	// and lng fields.
	StartLocation LatLng `json:"start_location"`

	// EndLocation contains the location of the last point of this step, as a single set of lat and
	// lng fields.
	EndLocation LatLng `json:"end_location"`

	// Polyline contains a single points object that holds an encoded polyline representation of the
	// step. This polyline is an approximate (smoothed) path of the step.
	Polyline `json:"polyline"`

	// Steps contains detailed directions for walking or driving steps in transit directions. Substeps
	// are only available when travel_mode is set to "transit". The inner steps array is of the same
	// type as steps.
	Steps []*Step `json:"steps"`

	// TransitDetails contains transit specific information. This field is only returned with travel
	// mode is set to "transit".
	TransitDetails *TransitDetails `json:"transit_details"`

	// TravelMode indicates the travel mode of this step.
	TravelMode string `json:"travel_mode"`
}

// TransitDetails contains additional information about the transit stop, transit line and transit agency.
type TransitDetails struct {
	// ArrivalStop contains information about the stop/station for this part of the trip.
	ArrivalStop TransitStop `json:"arrival_stop"`
	// DepartureStop contains information about the stop/station for this part of the trip.
	DepartureStop TransitStop `json:"departure_stop"`
	// ArrivalTime contains the arrival time for this leg of the journey
	ArrivalTime time.Time `json:"arrival_time"`
	// DepartureTime contains the departure time for this leg of the journey
	DepartureTime time.Time `json:"departure_time"`
	// Headsign specifies the direction in which to travel on this line, as it is marked on the vehicle or at the departure stop.
	Headsign string `json:"headsign"`
	// Headway specifies the expected number of seconds between departures from the same stop at this time
	Headway time.Duration `json:"headway"`
	// NumStops contains the number of stops in this step, counting the arrival stop, but not the departure stop
	NumStops uint `json:"num_stops"`
	// Line contains information about the transit line used in this step
	Line TransitLine `json:"line"`
}

// TransitStop contains information about the stop/station for this part of the trip.
type TransitStop struct {
	// Location of the transit station/stop.
	Location LatLng `json:"location"`
	// Name of the transit station/stop. eg. "Union Square".
	Name string `json:"name"`
}

// TransitLine contains information about the transit line used in this step
type TransitLine struct {
	// Name contains the full name of this transit line. eg. "7 Avenue Express".
	Name string `json:"name"`
	// ShortName contains the short name of this transit line.
	ShortName string `json:"short_name"`
	// Color contains the color commonly used in signage for this transit line.
	Color string `json:"color"`
	// Agencies contains information about the operator of the line
	Agencies []*TransitAgency `json:"agencies"`
	// URL contains the URL for this transit line as provided by the transit agency
	URL *url.URL `json:"url"`
	// Icon contains the URL for the icon associated with this line
	Icon *url.URL `json:"icon"`
	// TextColor contains the color of text commonly used for signage of this line
	TextColor string `json:"text_color"`
	// Vehicle contains the type of vehicle used on this line
	Vehicle TransitLineVehicle `json:"vehicle"`
}

// TransitAgency contains information about the operator of the line
type TransitAgency struct {
	// Name contains the name of the transit agency
	Name string `json:"name"`
	// URL contains the URL for the transit agency
	URL *url.URL `json:"url"`
	// Phone contains the phone number of the transit agency
	Phone string `json:"phone"`
}

// TransitLineVehicle contains the type of vehicle used on this line
type TransitLineVehicle struct {
	// Name contains the name of the vehicle on this line
	Name string `json:"name"`
	// Type contains the type of vehicle that runs on this line
	Type string `json:"type"`
	// Icon contains the URL for an icon associated with this vehicle type
	Icon *url.URL `json:"icon"`
}
<commit_msg>Bounds -> LatLngBounds<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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.

// More information about Google Directions API is available on
// https://developers.google.com/maps/documentation/directions/

package maps // import "google.golang.org/maps"

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"strings"
	"time"

	"google.golang.org/maps/internal"

	"golang.org/x/net/context"
)

// Get issues the Directions request and retrieves the Response
func (r *DirectionsRequest) Get(ctx context.Context) ([]Route, error) {
	var response DirectionsResponse

	if r.Origin == "" {
		return nil, errors.New("directions: Origin required")
	}
	if r.Destination == "" {
		return nil, errors.New("directions: Destination required")
	}
	if r.Mode != "" && TravelModeDriving != r.Mode && TravelModeWalking != r.Mode && TravelModeBicycling != r.Mode && TravelModeTransit != r.Mode {
		return nil, fmt.Errorf("directions: unknown Mode: '%s'", r.Mode)
	}
	for _, avoid := range r.Avoid {
		if avoid != AvoidTolls && avoid != AvoidHighways && avoid != AvoidFerries {
			return nil, fmt.Errorf("directions: Unknown Avoid restriction '%s'", avoid)
		}
	}
	if r.Units != "" && r.Units != UnitsMetric && r.Units != UnitsImperial {
		return nil, fmt.Errorf("directions: Unknown Units '%s'", r.Units)
	}
	for _, transitMode := range r.TransitMode {
		if transitMode != TransitModeBus && transitMode != TransitModeSubway && transitMode != TransitModeTrain && transitMode != TransitModeTram && transitMode != TransitModeRail {
			return nil, fmt.Errorf("directions: Unknown TransitMode '%s'", r.TransitMode)
		}
	}
	if r.TransitRoutingPreference != "" && r.TransitRoutingPreference != TransitRoutingPreferenceLessWalking && r.TransitRoutingPreference != TransitRoutingPreferenceFewerTransfers {
		return nil, fmt.Errorf("directions: Unknown TransitRoutingPreference '%s'", r.TransitRoutingPreference)
	}
	if r.DepartureTime != "" && r.ArrivalTime != "" {
		return nil, errors.New("directions: must not specify both DepartureTime and ArrivalTime")
	}

	if r.DepartureTime != "" && r.ArrivalTime != "" {
		return nil, errors.New("directions: must not specify both DepartureTime and ArrivalTime")
	}
	if len(r.TransitMode) != 0 && r.Mode != TravelModeTransit {
		return nil, errors.New("directions: must specify mode of transit when specifying transitMode")
	}
	if r.TransitRoutingPreference != "" && r.Mode != TravelModeTransit {
		return nil, errors.New("directions: must specify mode of transit when specifying transitRoutingPreference")
	}

	req, err := http.NewRequest("GET", "https://maps.googleapis.com/maps/api/directions/json", nil)
	if err != nil {
		return nil, err
	}
	q := req.URL.Query()
	q.Set("origin", r.Origin)
	q.Set("destination", r.Destination)
	q.Set("key", internal.APIKey(ctx))
	if r.Mode != "" {
		q.Set("mode", string(r.Mode))
	}
	if len(r.Waypoints) != 0 {
		q.Set("waypoints", strings.Join(r.Waypoints, "|"))
	}
	if r.Alternatives {
		q.Set("alternatives", "true")
	}
	if len(r.Avoid) > 0 {
		var avoid []string
		for _, a := range r.Avoid {
			avoid = append(avoid, string(a))
		}
		q.Set("avoid", strings.Join(avoid, "|"))
	}
	if r.Language != "" {
		q.Set("language", r.Language)
	}
	if r.Units != "" {
		q.Set("units", string(r.Units))
	}
	if r.Region != "" {
		q.Set("region", r.Region)
	}
	if len(r.TransitMode) != 0 {
		var transitMode []string
		for _, t := range r.TransitMode {
			transitMode = append(transitMode, string(t))
		}
		q.Set("transit_mode", strings.Join(transitMode, "|"))
	}
	req.URL.RawQuery = q.Encode()

	log.Println("Request:", req)

	err = httpDo(ctx, req, func(resp *http.Response, err error) error {
		if err != nil {
			return err
		}
		defer resp.Body.Close()

		if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	if response.Status != "OK" {
		return nil, fmt.Errorf("directions: %s - %s", response.Status, response.ErrorMessage)
	}

	return response.Routes, nil
}

// DirectionsRequest is the functional options struct for directions.Get
type DirectionsRequest struct {
	// Origin is the address or textual latitude/longitude value from which you wish to calculate directions. Required.
	Origin string
	// Destination is the address or textual latitude/longitude value from which you wish to calculate directions. Required.
	Destination string
	// Mode specifies the mode of transport to use when calculating directions. Optional.
	Mode mode
	// DepartureTime specifies the desired time of departure. You can specify the time as an integer in seconds since midnight, January 1, 1970 UTC. Alternatively, you can specify a value of `"now"`. Optional.
	DepartureTime string
	// ArrivalTime specifies the desired time of arrival for transit directions, in seconds since midnight, January 1, 1970 UTC. Optional. You cannot specify both `DepartureTime` and `ArrivalTime`.
	ArrivalTime string
	// Waypoints specifies an array of points to add to a route. Optional.
	Waypoints []string
	// Alternatives specifies if Directions service may provide more than one route alternative in the response. Optional.
	Alternatives bool
	// Avoid indicates that the calculated route(s) should avoid the indicated features. Optional.
	Avoid []avoid
	// Language specifies the language in which to return results. Optional.
	Language string
	// Units specifies the unit system to use when displaying results. Optional.
	Units units
	// Region specifies the region code, specified as a ccTLD two-character value. Optional.
	Region string
	// TransitMode specifies one or more preferred modes of transit. This parameter may only be specified for transit directions. Optional.
	TransitMode []transitMode
	// TransitRoutingPreference specifies preferences for transit routes. Optional.
	TransitRoutingPreference transitRoutingPreference
}

// DirectionsResponse represents a Directions API response.
type DirectionsResponse struct {
	// Routes lists the found routes between origin and destination.
	Routes []Route `json:"routes"`

	// Status contains the status of the request, and may contain
	// debugging information to help you track down why the Directions
	// service failed.
	// See https://developers.google.com/maps/documentation/directions/#StatusCodes
	Status string `json:"status"`

	// ErrorMessage is the explanatory field added when Status is an error.
	ErrorMessage string `json:"error_message"`
}

// Route represents a single route between an origin and a destination.
type Route struct {
	// Summary contains a short textual description for the route, suitable for
	// naming and disambiguating the route from alternatives.
	Summary string `json:"summary"`

	// Legs contains information about a leg of the route, between two locations within the
	// given route. A separate leg will be present for each waypoint or destination specified.
	// (A route with no waypoints will contain exactly one leg within the legs array.)
	Legs []*Leg `json:"legs"`

	// WaypointOrder contains an array indicating the order of any waypoints in the calculated route.
	WaypointOrder []int `json:"waypoint_order"`

	// OverviewPolyline contains an approximate (smoothed) path of the resulting directions.
	OverviewPolyline Polyline `json:"overview_polyline"`

	// Bounds contains the viewport bounding box of the overview polyline.
	Bounds LatLngBounds `json:"bounds"`

	// Copyrights contains the copyrights text to be displayed for this route. You must handle
	// and display this information yourself.
	Copyrights string `json:"copyrights"`

	// Warnings contains an array of warnings to be displayed when showing these directions.
	// You must handle and display these warnings yourself.
	Warnings []string `json:"warnings"`
}

// Leg represents a single leg of a route.
type Leg struct {
	// Steps contains an array of steps denoting information about each separate step of the
	// leg of the journey.
	Steps []*Step `json:"steps"`

	// Distance indicates the total distance covered by this leg.
	Distance `json:"distance"`

	// Duration indicates total time required for this leg.
	time.Duration `json:"duration"`

	// ArrivalTime contains the estimated time of arrival for this leg. This property is only
	// returned for transit directions.
	ArrivalTime time.Time `json:"arrival_time"`

	// DepartureTime contains the estimated time of departure for this leg. This property is
	// only returned for transit directions.
	DepartureTime time.Time `json:"departure_time"`

	// StartLocation contains the latitude/longitude coordinates of the origin of this leg.
	StartLocation LatLng `json:"start_location"`

	// EndLocation contains the latitude/longitude coordinates of the destination of this leg.
	EndLocation LatLng `json:"end_location"`

	// StartAddress contains the human-readable address (typically a street address)
	// reflecting the start location of this leg.
	StartAddress string `json:"start_address"`

	// EndAddress contains the human-readable address (typically a street address)
	// reflecting the end location of this leg.
	EndAddress string `json:"end_address"`
}

// Step represents a single step of a leg.
type Step struct {
	// HTMLInstructions contains formatted instructions for this step, presented as an HTML text string.
	HTMLInstructions string `json:"html_instructions"`

	// Distance contains the distance covered by this step until the next step.
	Distance `json:"distance"`

	// Duration contains the typical time required to perform the step, until the next step.
	time.Duration `json:"duration"`

	// StartLocation contains the location of the starting point of this step, as a single set of lat
	// and lng fields.
	StartLocation LatLng `json:"start_location"`

	// EndLocation contains the location of the last point of this step, as a single set of lat and
	// lng fields.
	EndLocation LatLng `json:"end_location"`

	// Polyline contains a single points object that holds an encoded polyline representation of the
	// step. This polyline is an approximate (smoothed) path of the step.
	Polyline `json:"polyline"`

	// Steps contains detailed directions for walking or driving steps in transit directions. Substeps
	// are only available when travel_mode is set to "transit". The inner steps array is of the same
	// type as steps.
	Steps []*Step `json:"steps"`

	// TransitDetails contains transit specific information. This field is only returned with travel
	// mode is set to "transit".
	TransitDetails *TransitDetails `json:"transit_details"`

	// TravelMode indicates the travel mode of this step.
	TravelMode string `json:"travel_mode"`
}

// TransitDetails contains additional information about the transit stop, transit line and transit agency.
type TransitDetails struct {
	// ArrivalStop contains information about the stop/station for this part of the trip.
	ArrivalStop TransitStop `json:"arrival_stop"`
	// DepartureStop contains information about the stop/station for this part of the trip.
	DepartureStop TransitStop `json:"departure_stop"`
	// ArrivalTime contains the arrival time for this leg of the journey
	ArrivalTime time.Time `json:"arrival_time"`
	// DepartureTime contains the departure time for this leg of the journey
	DepartureTime time.Time `json:"departure_time"`
	// Headsign specifies the direction in which to travel on this line, as it is marked on the vehicle or at the departure stop.
	Headsign string `json:"headsign"`
	// Headway specifies the expected number of seconds between departures from the same stop at this time
	Headway time.Duration `json:"headway"`
	// NumStops contains the number of stops in this step, counting the arrival stop, but not the departure stop
	NumStops uint `json:"num_stops"`
	// Line contains information about the transit line used in this step
	Line TransitLine `json:"line"`
}

// TransitStop contains information about the stop/station for this part of the trip.
type TransitStop struct {
	// Location of the transit station/stop.
	Location LatLng `json:"location"`
	// Name of the transit station/stop. eg. "Union Square".
	Name string `json:"name"`
}

// TransitLine contains information about the transit line used in this step
type TransitLine struct {
	// Name contains the full name of this transit line. eg. "7 Avenue Express".
	Name string `json:"name"`
	// ShortName contains the short name of this transit line.
	ShortName string `json:"short_name"`
	// Color contains the color commonly used in signage for this transit line.
	Color string `json:"color"`
	// Agencies contains information about the operator of the line
	Agencies []*TransitAgency `json:"agencies"`
	// URL contains the URL for this transit line as provided by the transit agency
	URL *url.URL `json:"url"`
	// Icon contains the URL for the icon associated with this line
	Icon *url.URL `json:"icon"`
	// TextColor contains the color of text commonly used for signage of this line
	TextColor string `json:"text_color"`
	// Vehicle contains the type of vehicle used on this line
	Vehicle TransitLineVehicle `json:"vehicle"`
}

// TransitAgency contains information about the operator of the line
type TransitAgency struct {
	// Name contains the name of the transit agency
	Name string `json:"name"`
	// URL contains the URL for the transit agency
	URL *url.URL `json:"url"`
	// Phone contains the phone number of the transit agency
	Phone string `json:"phone"`
}

// TransitLineVehicle contains the type of vehicle used on this line
type TransitLineVehicle struct {
	// Name contains the name of the vehicle on this line
	Name string `json:"name"`
	// Type contains the type of vehicle that runs on this line
	Type string `json:"type"`
	// Icon contains the URL for an icon associated with this vehicle type
	Icon *url.URL `json:"icon"`
}
<|endoftext|>


<commit_before>// Copyright 2016 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package libkbfs

import (
	"io"
	"sort"
	"sync"

	"github.com/keybase/client/go/logger"
	"github.com/pkg/errors"
	"golang.org/x/net/context"
)

const (
	defaultIndirectPointerPrefetchCount int = 20
	fileIndirectBlockPrefetchPriority   int = -100
	dirEntryPrefetchPriority            int = -200
	updatePointerPrefetchPriority       int = 0
	defaultPrefetchPriority             int = -1024
)

type prefetcherConfig interface {
	dataVersioner
	logMaker
	blockCacher
}

type prefetchRequest struct {
	priority int
	kmd      KeyMetadata
	ptr      BlockPointer
	block    Block
}

type blockPrefetcher struct {
	config prefetcherConfig
	log    logger.Logger
	// blockRetriever to retrieve blocks from the server
	retriever BlockRetriever
	// channel to synchronize prefetch requests with the prefetcher shutdown
	progressCh chan prefetchRequest
	// channel that is idempotently closed when a shutdown occurs
	shutdownCh chan struct{}
	// channel that is closed when a shutdown completes and all pending
	// prefetch requests are complete
	doneCh chan struct{}
}

var _ Prefetcher = (*blockPrefetcher)(nil)

func newBlockPrefetcher(retriever BlockRetriever, config prefetcherConfig) *blockPrefetcher {
	p := &blockPrefetcher{
		config:     config,
		retriever:  retriever,
		progressCh: make(chan prefetchRequest),
		shutdownCh: make(chan struct{}),
		doneCh:     make(chan struct{}),
	}
	if config != nil {
		p.log = config.MakeLogger("PRE")
	} else {
		p.log = logger.NewNull()
	}
	if retriever == nil {
		// If we pass in a nil retriever, this prefetcher shouldn't do
		// anything. Treat it as already shut down.
		p.Shutdown()
		close(p.doneCh)
	} else {
		go p.run()
	}
	return p
}

func (p *blockPrefetcher) run() {
	var wg sync.WaitGroup
	defer func() {
		wg.Wait()
		close(p.doneCh)
	}()
	for {
		select {
		case req := <-p.progressCh:
			ctx, cancel := context.WithCancel(context.TODO())
			errCh := p.retriever.Request(ctx, req.priority, req.kmd, req.ptr, req.block, TransientEntry)
			wg.Add(1)
			go func() {
				defer wg.Done()
				defer cancel()
				select {
				case err := <-errCh:
					if err != nil {
						p.log.CDebugf(ctx, "Done prefetch for block %s. Error: %+v", req.ptr.ID, err)
					}
				case <-p.shutdownCh:
					// Cancel but still wait so p.doneCh accurately represents
					// whether we still have requests pending.
					cancel()
					<-errCh
				}
			}()
		case <-p.shutdownCh:
			return
		}
	}
}

func (p *blockPrefetcher) request(priority int, kmd KeyMetadata, ptr BlockPointer, block Block, entryName string) error {
	if _, err := p.config.BlockCache().Get(ptr); err == nil {
		return nil
	}
	if err := checkDataVersion(p.config, path{}, ptr); err != nil {
		return err
	}
	select {
	case p.progressCh <- prefetchRequest{priority, kmd, ptr, block}:
		return nil
	case <-p.shutdownCh:
		return errors.Wrapf(io.EOF, "Skipping prefetch for block %v since the prefetcher is shutdown", ptr.ID)
	}
}

func (p *blockPrefetcher) prefetchIndirectFileBlock(b *FileBlock, kmd KeyMetadata) {
	// Prefetch the first <n> indirect block pointers.
	// TODO: do something smart with subsequent blocks.
	numIPtrs := len(b.IPtrs)
	if numIPtrs > defaultIndirectPointerPrefetchCount {
		numIPtrs = defaultIndirectPointerPrefetchCount
	}
	p.log.CDebugf(context.TODO(), "Prefetching pointers for indirect file block. Num pointers to prefetch: %d", numIPtrs)
	for _, ptr := range b.IPtrs[:numIPtrs] {
		p.request(fileIndirectBlockPrefetchPriority, kmd,
			ptr.BlockPointer, b.NewEmpty(), "")
	}
}

func (p *blockPrefetcher) prefetchIndirectDirBlock(b *DirBlock, kmd KeyMetadata) {
	// Prefetch the first <n> indirect block pointers.
	numIPtrs := len(b.IPtrs)
	if numIPtrs > defaultIndirectPointerPrefetchCount {
		numIPtrs = defaultIndirectPointerPrefetchCount
	}
	p.log.CDebugf(context.TODO(), "Prefetching pointers for indirect dir block. Num pointers to prefetch: %d", numIPtrs)
	for _, ptr := range b.IPtrs[:numIPtrs] {
		_ = p.request(fileIndirectBlockPrefetchPriority, kmd,
			ptr.BlockPointer, b.NewEmpty(), "")
	}
}

func (p *blockPrefetcher) prefetchDirectDirBlock(ptr BlockPointer, b *DirBlock, kmd KeyMetadata) {
	p.log.CDebugf(context.TODO(), "Prefetching entries for directory block ID %s. Num entries: %d", ptr.ID, len(b.Children))
	// Prefetch all DirEntry root blocks.
	dirEntries := dirEntriesBySizeAsc{dirEntryMapToDirEntries(b.Children)}
	sort.Sort(dirEntries)
	for i, entry := range dirEntries.dirEntries {
		// Prioritize small files
		priority := dirEntryPrefetchPriority - i
		var block Block
		switch entry.Type {
		case Dir:
			block = &DirBlock{}
		case File:
			block = &FileBlock{}
		case Exec:
			block = &FileBlock{}
		default:
			p.log.CDebugf(context.TODO(), "Skipping prefetch for entry of unknown type %d", entry.Type)
			continue
		}
		p.request(priority, kmd, entry.BlockPointer, block, entry.entryName)
	}
}

// PrefetchBlock implements the Prefetcher interface for blockPrefetcher.
func (p *blockPrefetcher) PrefetchBlock(
	block Block, ptr BlockPointer, kmd KeyMetadata, priority int) error {
	// TODO: Remove this log line.
	p.log.CDebugf(context.TODO(), "Prefetching block by request from upstream component. Priority: %d", priority)
	return p.request(priority, kmd, ptr, block, "")
}

// PrefetchAfterBlockRetrieved implements the Prefetcher interface for
// blockPrefetcher.
func (p *blockPrefetcher) PrefetchAfterBlockRetrieved(
	b Block, ptr BlockPointer, kmd KeyMetadata) {
	switch b := b.(type) {
	case *FileBlock:
		if b.IsInd {
			p.prefetchIndirectFileBlock(b, kmd)
		}
	case *DirBlock:
		if b.IsInd {
			p.prefetchIndirectDirBlock(b, kmd)
		} else {
			p.prefetchDirectDirBlock(ptr, b, kmd)
		}
	default:
		// Skipping prefetch for block of unknown type (likely CommonBlock)
	}
}

// Shutdown implements the Prefetcher interface for blockPrefetcher.
func (p *blockPrefetcher) Shutdown() <-chan struct{} {
	select {
	case <-p.shutdownCh:
	default:
		close(p.shutdownCh)
	}
	return p.doneCh
}
<commit_msg>prefetcher: Prefetch all child blocks for an indirect block, now that prefetch workers are less eager.<commit_after>// Copyright 2016 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package libkbfs

import (
	"io"
	"sort"
	"sync"

	"github.com/keybase/client/go/logger"
	"github.com/pkg/errors"
	"golang.org/x/net/context"
)

const (
	defaultIndirectPointerPrefetchCount int = 20
	fileIndirectBlockPrefetchPriority   int = -100
	dirEntryPrefetchPriority            int = -200
	updatePointerPrefetchPriority       int = 0
	defaultPrefetchPriority             int = -1024
)

type prefetcherConfig interface {
	dataVersioner
	logMaker
	blockCacher
}

type prefetchRequest struct {
	priority int
	kmd      KeyMetadata
	ptr      BlockPointer
	block    Block
}

type blockPrefetcher struct {
	config prefetcherConfig
	log    logger.Logger
	// blockRetriever to retrieve blocks from the server
	retriever BlockRetriever
	// channel to synchronize prefetch requests with the prefetcher shutdown
	progressCh chan prefetchRequest
	// channel that is idempotently closed when a shutdown occurs
	shutdownCh chan struct{}
	// channel that is closed when a shutdown completes and all pending
	// prefetch requests are complete
	doneCh chan struct{}
}

var _ Prefetcher = (*blockPrefetcher)(nil)

func newBlockPrefetcher(retriever BlockRetriever, config prefetcherConfig) *blockPrefetcher {
	p := &blockPrefetcher{
		config:     config,
		retriever:  retriever,
		progressCh: make(chan prefetchRequest),
		shutdownCh: make(chan struct{}),
		doneCh:     make(chan struct{}),
	}
	if config != nil {
		p.log = config.MakeLogger("PRE")
	} else {
		p.log = logger.NewNull()
	}
	if retriever == nil {
		// If we pass in a nil retriever, this prefetcher shouldn't do
		// anything. Treat it as already shut down.
		p.Shutdown()
		close(p.doneCh)
	} else {
		go p.run()
	}
	return p
}

func (p *blockPrefetcher) run() {
	var wg sync.WaitGroup
	defer func() {
		wg.Wait()
		close(p.doneCh)
	}()
	for {
		select {
		case req := <-p.progressCh:
			ctx, cancel := context.WithCancel(context.TODO())
			errCh := p.retriever.Request(ctx, req.priority, req.kmd, req.ptr, req.block, TransientEntry)
			wg.Add(1)
			go func() {
				defer wg.Done()
				defer cancel()
				select {
				case err := <-errCh:
					if err != nil {
						p.log.CDebugf(ctx, "Done prefetch for block %s. Error: %+v", req.ptr.ID, err)
					}
				case <-p.shutdownCh:
					// Cancel but still wait so p.doneCh accurately represents
					// whether we still have requests pending.
					cancel()
					<-errCh
				}
			}()
		case <-p.shutdownCh:
			return
		}
	}
}

func (p *blockPrefetcher) request(priority int, kmd KeyMetadata,
	ptr BlockPointer, block Block, entryName string) error {
	if _, err := p.config.BlockCache().Get(ptr); err == nil {
		return nil
	}
	if err := checkDataVersion(p.config, path{}, ptr); err != nil {
		return err
	}
	select {
	case p.progressCh <- prefetchRequest{priority, kmd, ptr, block}:
		return nil
	case <-p.shutdownCh:
		return errors.Wrapf(io.EOF, "Skipping prefetch for block %v since "+
			"the prefetcher is shutdown", ptr.ID)
	}
}

func (p *blockPrefetcher) prefetchIndirectFileBlock(b *FileBlock,
	kmd KeyMetadata) {
	// Prefetch indirect block pointers.
	p.log.CDebugf(context.TODO(), "Prefetching pointers for indirect file "+
		"block. Num pointers to prefetch: %d", len(b.IPtrs))
	for _, ptr := range b.IPtrs {
		p.request(fileIndirectBlockPrefetchPriority, kmd, ptr.BlockPointer,
			b.NewEmpty(), "")
	}
}

func (p *blockPrefetcher) prefetchIndirectDirBlock(b *DirBlock,
	kmd KeyMetadata) {
	// Prefetch indirect block pointers.
	p.log.CDebugf(context.TODO(), "Prefetching pointers for indirect dir "+
		"block. Num pointers to prefetch: %d", len(b.IPtrs))
	for _, ptr := range b.IPtrs {
		_ = p.request(fileIndirectBlockPrefetchPriority, kmd,
			ptr.BlockPointer, b.NewEmpty(), "")
	}
}

func (p *blockPrefetcher) prefetchDirectDirBlock(ptr BlockPointer, b *DirBlock, kmd KeyMetadata) {
	p.log.CDebugf(context.TODO(), "Prefetching entries for directory block "+
		"ID %s. Num entries: %d", ptr.ID, len(b.Children))
	// Prefetch all DirEntry root blocks.
	dirEntries := dirEntriesBySizeAsc{dirEntryMapToDirEntries(b.Children)}
	sort.Sort(dirEntries)
	for i, entry := range dirEntries.dirEntries {
		// Prioritize small files
		priority := dirEntryPrefetchPriority - i
		var block Block
		switch entry.Type {
		case Dir:
			block = &DirBlock{}
		case File:
			block = &FileBlock{}
		case Exec:
			block = &FileBlock{}
		default:
			p.log.CDebugf(context.TODO(), "Skipping prefetch for entry of unknown type %d", entry.Type)
			continue
		}
		p.request(priority, kmd, entry.BlockPointer, block, entry.entryName)
	}
}

// PrefetchBlock implements the Prefetcher interface for blockPrefetcher.
func (p *blockPrefetcher) PrefetchBlock(
	block Block, ptr BlockPointer, kmd KeyMetadata, priority int) error {
	// TODO: Remove this log line.
	p.log.CDebugf(context.TODO(), "Prefetching block by request from upstream component. Priority: %d", priority)
	return p.request(priority, kmd, ptr, block, "")
}

// PrefetchAfterBlockRetrieved implements the Prefetcher interface for
// blockPrefetcher.
func (p *blockPrefetcher) PrefetchAfterBlockRetrieved(
	b Block, ptr BlockPointer, kmd KeyMetadata) {
	switch b := b.(type) {
	case *FileBlock:
		if b.IsInd {
			p.prefetchIndirectFileBlock(b, kmd)
		}
	case *DirBlock:
		if b.IsInd {
			p.prefetchIndirectDirBlock(b, kmd)
		} else {
			p.prefetchDirectDirBlock(ptr, b, kmd)
		}
	default:
		// Skipping prefetch for block of unknown type (likely CommonBlock)
	}
}

// Shutdown implements the Prefetcher interface for blockPrefetcher.
func (p *blockPrefetcher) Shutdown() <-chan struct{} {
	select {
	case <-p.shutdownCh:
	default:
		close(p.shutdownCh)
	}
	return p.doneCh
}
<|endoftext|>