<commit_before>package util

import (
	"context"
	"crypto/rand"
	"encoding/base64"
	"errors"
	"fmt"
	"os"
	"runtime/debug"
	"sync"
	"time"

	log "github.com/sirupsen/logrus"
)

var (
	// location to use for generating temporary files, such as the kubeconfig needed by kubectl
	TempDir string
)

func init() {
	fileInfo, err := os.Stat("/dev/shm")
	if err == nil && fileInfo.IsDir() {
		TempDir = "/dev/shm"
	}
}

type Closer interface {
	Close() error
}

// Close is a convenience function to close a object that has a Close() method, ignoring any errors
// Used to satisfy errcheck lint
func Close(c Closer) {
	if err := c.Close(); err != nil {
		log.Warnf("failed to close %v: %v", c, err)
	}
}

// DeleteFile is best effort deletion of a file
func DeleteFile(path string) {
	if _, err := os.Stat(path); os.IsNotExist(err) {
		return
	}
	_ = os.Remove(path)
}

// Wait takes a check interval and timeout and waits for a function to return `true`.
// Wait will return `true` on success and `false` on timeout.
// The passed function, in turn, should pass `true` (or anything, really) to the channel when it's done.
// Pass `0` as the timeout to run infinitely until completion.
func Wait(timeout uint, f func(chan<- bool)) bool {
	done := make(chan bool)
	go f(done)

	// infinite
	if timeout == 0 {
		return <-done
	}

	timedOut := time.After(time.Duration(timeout) * time.Second)
	for {
		select {
		case <-done:
			return true
		case <-timedOut:
			return false
		}
	}
}

// MakeSignature generates a cryptographically-secure pseudo-random token, based on a given number of random bytes, for signing purposes.
func MakeSignature(size int) ([]byte, error) {
	b := make([]byte, size)
	_, err := rand.Read(b)
	if err != nil {
		b = nil
	}
	// base64 encode it so signing key can be typed into validation utilities
	b = []byte(base64.StdEncoding.EncodeToString(b))
	return b, err
}

// RetryUntilSucceed keep retrying given action with specified timeout until action succeed or specified context is done.
func RetryUntilSucceed(action func() error, desc string, ctx context.Context, timeout time.Duration) {
	ctxCompleted := false
	stop := make(chan bool)
	defer close(stop)
	go func() {
		select {
		case <-ctx.Done():
			ctxCompleted = true
		case <-stop:
		}
	}()
	for {
		log.Debugf("Start %s", desc)
		err := action()
		if err == nil {
			log.Infof("Completed %s", desc)
			return
		}
		if ctxCompleted {
			log.Debugf("Stop retrying %s", desc)
			return
		}
		log.Debugf("Failed to %s: %+v, retrying in %v", desc, err, timeout)
		time.Sleep(timeout)

	}
}

func FirstNonEmpty(args ...string) string {
	for _, value := range args {
		if len(value) > 0 {
			return value
		}
	}
	return ""
}

func RunAllAsync(count int, action func(i int) error) (err error) {
	defer func() {
		if r := recover(); r != nil {
			message := fmt.Sprintf("Recovered from panic: %+v\n%s", r, debug.Stack())
			log.Error(message)
			err = errors.New(message)
		}
	}()
	var wg sync.WaitGroup
	for i := 0; i < count; i++ {
		wg.Add(1)
		go func(index int) {
			defer wg.Done()
			actionErr := action(index)
			if actionErr != nil {
				err = actionErr
			}
		}(i)
		if err != nil {
			break
		}
	}
	wg.Wait()
	return err
}
<commit_msg>Change loggin level in util function to Debug (#1488)<commit_after>package util

import (
	"context"
	"crypto/rand"
	"encoding/base64"
	"errors"
	"fmt"
	"os"
	"runtime/debug"
	"sync"
	"time"

	log "github.com/sirupsen/logrus"
)

var (
	// location to use for generating temporary files, such as the kubeconfig needed by kubectl
	TempDir string
)

func init() {
	fileInfo, err := os.Stat("/dev/shm")
	if err == nil && fileInfo.IsDir() {
		TempDir = "/dev/shm"
	}
}

type Closer interface {
	Close() error
}

// Close is a convenience function to close a object that has a Close() method, ignoring any errors
// Used to satisfy errcheck lint
func Close(c Closer) {
	if err := c.Close(); err != nil {
		log.Warnf("failed to close %v: %v", c, err)
	}
}

// DeleteFile is best effort deletion of a file
func DeleteFile(path string) {
	if _, err := os.Stat(path); os.IsNotExist(err) {
		return
	}
	_ = os.Remove(path)
}

// Wait takes a check interval and timeout and waits for a function to return `true`.
// Wait will return `true` on success and `false` on timeout.
// The passed function, in turn, should pass `true` (or anything, really) to the channel when it's done.
// Pass `0` as the timeout to run infinitely until completion.
func Wait(timeout uint, f func(chan<- bool)) bool {
	done := make(chan bool)
	go f(done)

	// infinite
	if timeout == 0 {
		return <-done
	}

	timedOut := time.After(time.Duration(timeout) * time.Second)
	for {
		select {
		case <-done:
			return true
		case <-timedOut:
			return false
		}
	}
}

// MakeSignature generates a cryptographically-secure pseudo-random token, based on a given number of random bytes, for signing purposes.
func MakeSignature(size int) ([]byte, error) {
	b := make([]byte, size)
	_, err := rand.Read(b)
	if err != nil {
		b = nil
	}
	// base64 encode it so signing key can be typed into validation utilities
	b = []byte(base64.StdEncoding.EncodeToString(b))
	return b, err
}

// RetryUntilSucceed keep retrying given action with specified timeout until action succeed or specified context is done.
func RetryUntilSucceed(action func() error, desc string, ctx context.Context, timeout time.Duration) {
	ctxCompleted := false
	stop := make(chan bool)
	defer close(stop)
	go func() {
		select {
		case <-ctx.Done():
			ctxCompleted = true
		case <-stop:
		}
	}()
	for {
		log.Debugf("Start %s", desc)
		err := action()
		if err == nil {
			log.Debugf("Completed %s", desc)
			return
		}
		if ctxCompleted {
			log.Debugf("Stop retrying %s", desc)
			return
		}
		log.Debugf("Failed to %s: %+v, retrying in %v", desc, err, timeout)
		time.Sleep(timeout)

	}
}

func FirstNonEmpty(args ...string) string {
	for _, value := range args {
		if len(value) > 0 {
			return value
		}
	}
	return ""
}

func RunAllAsync(count int, action func(i int) error) (err error) {
	defer func() {
		if r := recover(); r != nil {
			message := fmt.Sprintf("Recovered from panic: %+v\n%s", r, debug.Stack())
			log.Error(message)
			err = errors.New(message)
		}
	}()
	var wg sync.WaitGroup
	for i := 0; i < count; i++ {
		wg.Add(1)
		go func(index int) {
			defer wg.Done()
			actionErr := action(index)
			if actionErr != nil {
				err = actionErr
			}
		}(i)
		if err != nil {
			break
		}
	}
	wg.Wait()
	return err
}
<|endoftext|>


<commit_before>package ws

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"math/rand"
	"net/textproto"
	"strings"
	"testing"
)

var compareWithStd = flag.Bool("std", false, "compare with standard library implementation (if exists)")

func TestReadLine(t *testing.T) {
	for _, test := range []struct {
		label   string
		in      string
		bufSize int
	}{
		{
			label:   "simple",
			in:      "hello, world!",
			bufSize: 1024,
		},
		{
			label:   "chunked",
			in:      "hello, world! this is a long long line!",
			bufSize: 16,
		},
	} {
		t.Run(test.label, func(t *testing.T) {
			br := bufio.NewReaderSize(strings.NewReader(test.in), test.bufSize)
			bts, err := readLine(br)
			if err != nil {
				t.Errorf("unexpected error: %s", err)
			}
			if act, exp := string(bts), test.in; act != exp {
				t.Errorf("readLine() returned %#q; want %#q", act, exp)
			}
		})
	}
}

func TestHasToken(t *testing.T) {
	for i, test := range []struct {
		header string
		token  string
		exp    bool
	}{
		{"Keep-Alive, Close, Upgrade", "upgrade", true},
		{"Keep-Alive, Close, upgrade, hello", "upgrade", true},
		{"Keep-Alive, Close,  hello", "upgrade", false},
	} {
		t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) {
			if has := strHasToken(test.header, test.token); has != test.exp {
				t.Errorf("hasToken(%q, %q) = %v; want %v", test.header, test.token, has, test.exp)
			}
		})
	}
}

func BenchmarkHasToken(b *testing.B) {
	for i, bench := range []struct {
		header string
		token  string
	}{
		{"Keep-Alive, Close, Upgrade", "upgrade"},
		{"Keep-Alive, Close, upgrade, hello", "upgrade"},
		{"Keep-Alive, Close,  hello", "upgrade"},
	} {
		b.Run(fmt.Sprintf("#%d", i), func(b *testing.B) {
			for i := 0; i < b.N; i++ {
				_ = strHasToken(bench.header, bench.token)
			}
		})
	}
}

type equalFoldCase struct {
	label string
	a, b  string
}

var equalFoldCases = []equalFoldCase{
	{"websocket", "WebSocket", "websocket"},
	{"upgrade", "Upgrade", "upgrade"},
	randomEqualLetters(512),
	inequalAt(randomEqualLetters(512), 256),
}

func TestStrEqualFold(t *testing.T) {
	for i, test := range equalFoldCases {
		t.Run(fmt.Sprintf("%s#%d", test.label, i), func(t *testing.T) {
			if len(test.a) < 100 && len(test.b) < 100 {
				t.Logf("\n\ta: %s\n\tb: %s\n", test.a, test.b)
			}
			exp := strings.EqualFold(test.a, test.b)
			if act := strEqualFold(test.a, test.b); act != exp {
				t.Errorf("strEqualFold(%q, %q) = %v; want %v", test.a, test.b, act, exp)
			}
		})
	}
}

func BenchmarkStrEqualFold(b *testing.B) {
	for i, bench := range equalFoldCases {
		b.Run(fmt.Sprintf("%s#%d", bench.label, i), func(b *testing.B) {
			for i := 0; i < b.N; i++ {
				_ = strEqualFold(bench.a, bench.b)
			}
		})
	}
	if *compareWithStd {
		for i, bench := range equalFoldCases {
			b.Run(fmt.Sprintf("%s#%d_std", bench.label, i), func(b *testing.B) {
				for i := 0; i < b.N; i++ {
					_ = strings.EqualFold(bench.a, bench.b)
				}
			})
		}
	}
}

func BenchmarkBtsEqualFold(b *testing.B) {
	for i, bench := range equalFoldCases {
		ab, bb := []byte(bench.a), []byte(bench.b)
		b.Run(fmt.Sprintf("%s#%d", bench.label, i), func(b *testing.B) {
			for i := 0; i < b.N; i++ {
				_ = btsEqualFold(ab, bb)
			}
		})
	}
	if *compareWithStd {
		for i, bench := range equalFoldCases {
			ab, bb := []byte(bench.a), []byte(bench.b)
			b.Run(fmt.Sprintf("%s#%d_std", bench.label, i), func(b *testing.B) {
				for i := 0; i < b.N; i++ {
					_ = bytes.EqualFold(ab, bb)
				}
			})
		}
	}
}

func TestAsciiToInt(t *testing.T) {
	for _, test := range []struct {
		bts []byte
		exp int
		err bool
	}{
		{[]byte{'0'}, 0, false},
		{[]byte{'1'}, 1, false},
		{[]byte("42"), 42, false},
		{[]byte("420"), 420, false},
		{[]byte("010050042"), 10050042, false},
	} {
		t.Run(fmt.Sprintf("%s", string(test.bts)), func(t *testing.T) {
			act, err := asciiToInt(test.bts)
			if (test.err && err == nil) || (!test.err && err != nil) {
				t.Errorf("unexpected error: %v", err)
			}
			if act != test.exp {
				t.Errorf("asciiToInt(%v) = %v; want %v", test.bts, act, test.exp)
			}
		})
	}
}

func TestBtrim(t *testing.T) {
	for _, test := range []struct {
		bts []byte
		exp []byte
	}{
		{[]byte("abc"), []byte("abc")},
		{[]byte(" abc"), []byte("abc")},
		{[]byte("abc "), []byte("abc")},
		{[]byte(" abc "), []byte("abc")},
	} {
		t.Run(fmt.Sprintf("%s", string(test.bts)), func(t *testing.T) {
			if act := btrim(test.bts); !bytes.Equal(act, test.exp) {
				t.Errorf("btrim(%v) = %v; want %v", test.bts, act, test.exp)
			}
		})
	}
}

func TestBSplit3(t *testing.T) {
	for _, test := range []struct {
		bts  []byte
		sep  byte
		exp1 []byte
		exp2 []byte
		exp3 []byte
	}{
		{[]byte(""), ' ', []byte{}, nil, nil},
		{[]byte("GET / HTTP/1.1"), ' ', []byte("GET"), []byte("/"), []byte("HTTP/1.1")},
	} {
		t.Run(fmt.Sprintf("%s", string(test.bts)), func(t *testing.T) {
			b1, b2, b3 := bsplit3(test.bts, test.sep)
			if !bytes.Equal(b1, test.exp1) || !bytes.Equal(b2, test.exp2) || !bytes.Equal(b3, test.exp3) {
				t.Errorf(
					"bsplit3(%q) = %q, %q, %q; want %q, %q, %q",
					string(test.bts), string(b1), string(b2), string(b3),
					string(test.exp1), string(test.exp2), string(test.exp3),
				)
			}
		})
	}
}

var canonicalHeaderCases = [][]byte{
	[]byte("foo-"),
	[]byte("-foo"),
	[]byte("-"),
	[]byte("foo----bar"),
	[]byte("foo-bar"),
	[]byte("FoO-BaR"),
	[]byte("Foo-Bar"),
	[]byte("sec-websocket-extensions"),
}

func TestCanonicalizeHeaderKey(t *testing.T) {
	for _, bts := range canonicalHeaderCases {
		t.Run(fmt.Sprintf("%s", string(bts)), func(t *testing.T) {
			act := append([]byte(nil), bts...)
			canonicalizeHeaderKey(act)

			exp := strToBytes(textproto.CanonicalMIMEHeaderKey(string(bts)))

			if !bytes.Equal(act, exp) {
				t.Errorf(
					"canonicalizeHeaderKey(%v) = %v; want %v",
					string(bts), string(act), string(exp),
				)
			}
		})
	}
}

func BenchmarkCanonicalizeHeaderKey(b *testing.B) {
	for _, bts := range canonicalHeaderCases {
		b.Run(fmt.Sprintf("%s", string(bts)), func(b *testing.B) {
			for i := 0; i < b.N; i++ {
				canonicalizeHeaderKey(bts)
			}
		})
	}
}

func randomEqualLetters(n int) (c equalFoldCase) {
	c.label = fmt.Sprintf("rnd_eq_%d", n)

	a, b := make([]byte, n), make([]byte, n)

	for i := 0; i < n; i++ {
		c := byte(rand.Intn('Z'-'A'+1) + 'A') // Random character from 'A' to 'Z'.
		a[i] = c
		b[i] = c | ('a' - 'A') // Swap fold.
	}

	c.a = string(a)
	c.b = string(b)

	return
}

func inequalAt(c equalFoldCase, i int) equalFoldCase {
	bts := make([]byte, len(c.a))
	copy(bts, c.a)
	for {
		b := byte(rand.Intn('z'-'a'+1) + 'a')
		if bts[i] != b {
			bts[i] = b
			c.a = string(bts)
			c.label = fmt.Sprintf("rnd_ineq_%d_%d", len(c.a), i)
			return c
		}
	}
}
<commit_msg>util: readLine more tests and benchmarks<commit_after>package ws

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"io"
	"math/rand"
	"net/textproto"
	"strings"
	"testing"
)

var compareWithStd = flag.Bool("std", false, "compare with standard library implementation (if exists)")

var readLineCases = []struct {
	label   string
	in      string
	line    []byte
	err     error
	bufSize int
}{
	{
		label:   "simple",
		in:      "hello, world!",
		line:    []byte("hello, world!"),
		err:     io.EOF,
		bufSize: 1024,
	},
	{
		label:   "simple",
		in:      "hello, world!\r\n",
		line:    []byte("hello, world!"),
		bufSize: 1024,
	},
	{
		label:   "simple",
		in:      "hello, world!\n",
		line:    []byte("hello, world!"),
		bufSize: 1024,
	},
	{
		// The case where "\r\n" straddles the buffer.
		label:   "straddle",
		in:      "hello, world!!!\r\n...",
		line:    []byte("hello, world!!!"),
		bufSize: 16,
	},
	{
		label:   "chunked",
		in:      "hello, world! this is a long long line!",
		line:    []byte("hello, world! this is a long long line!"),
		err:     io.EOF,
		bufSize: 16,
	},
	{
		label:   "chunked",
		in:      "hello, world! this is a long long line!\r\n",
		line:    []byte("hello, world! this is a long long line!"),
		bufSize: 16,
	},
}

func TestReadLine(t *testing.T) {
	for _, test := range readLineCases {
		t.Run(test.label, func(t *testing.T) {
			br := bufio.NewReaderSize(strings.NewReader(test.in), test.bufSize)
			bts, err := readLine(br)
			if err != test.err {
				t.Errorf("unexpected error: %v; want %v", err, test.err)
			}
			if act, exp := bts, test.line; !bytes.Equal(act, exp) {
				t.Errorf("readLine() result is %#q; want %#q", act, exp)
			}
		})
	}
}

func BenchmarkReadLine(b *testing.B) {
	for _, test := range readLineCases {
		sr := strings.NewReader(test.in)
		br := bufio.NewReaderSize(sr, test.bufSize)
		b.Run(test.label, func(b *testing.B) {
			for i := 0; i < b.N; i++ {
				_, _ = readLine(br)
				sr.Reset(test.in)
				br.Reset(sr)
			}
		})
	}
}

func TestHasToken(t *testing.T) {
	for i, test := range []struct {
		header string
		token  string
		exp    bool
	}{
		{"Keep-Alive, Close, Upgrade", "upgrade", true},
		{"Keep-Alive, Close, upgrade, hello", "upgrade", true},
		{"Keep-Alive, Close,  hello", "upgrade", false},
	} {
		t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) {
			if has := strHasToken(test.header, test.token); has != test.exp {
				t.Errorf("hasToken(%q, %q) = %v; want %v", test.header, test.token, has, test.exp)
			}
		})
	}
}

func BenchmarkHasToken(b *testing.B) {
	for i, bench := range []struct {
		header string
		token  string
	}{
		{"Keep-Alive, Close, Upgrade", "upgrade"},
		{"Keep-Alive, Close, upgrade, hello", "upgrade"},
		{"Keep-Alive, Close,  hello", "upgrade"},
	} {
		b.Run(fmt.Sprintf("#%d", i), func(b *testing.B) {
			for i := 0; i < b.N; i++ {
				_ = strHasToken(bench.header, bench.token)
			}
		})
	}
}

type equalFoldCase struct {
	label string
	a, b  string
}

var equalFoldCases = []equalFoldCase{
	{"websocket", "WebSocket", "websocket"},
	{"upgrade", "Upgrade", "upgrade"},
	randomEqualLetters(512),
	inequalAt(randomEqualLetters(512), 256),
}

func TestStrEqualFold(t *testing.T) {
	for i, test := range equalFoldCases {
		t.Run(fmt.Sprintf("%s#%d", test.label, i), func(t *testing.T) {
			if len(test.a) < 100 && len(test.b) < 100 {
				t.Logf("\n\ta: %s\n\tb: %s\n", test.a, test.b)
			}
			exp := strings.EqualFold(test.a, test.b)
			if act := strEqualFold(test.a, test.b); act != exp {
				t.Errorf("strEqualFold(%q, %q) = %v; want %v", test.a, test.b, act, exp)
			}
		})
	}
}

func BenchmarkStrEqualFold(b *testing.B) {
	for i, bench := range equalFoldCases {
		b.Run(fmt.Sprintf("%s#%d", bench.label, i), func(b *testing.B) {
			for i := 0; i < b.N; i++ {
				_ = strEqualFold(bench.a, bench.b)
			}
		})
	}
	if *compareWithStd {
		for i, bench := range equalFoldCases {
			b.Run(fmt.Sprintf("%s#%d_std", bench.label, i), func(b *testing.B) {
				for i := 0; i < b.N; i++ {
					_ = strings.EqualFold(bench.a, bench.b)
				}
			})
		}
	}
}

func BenchmarkBtsEqualFold(b *testing.B) {
	for i, bench := range equalFoldCases {
		ab, bb := []byte(bench.a), []byte(bench.b)
		b.Run(fmt.Sprintf("%s#%d", bench.label, i), func(b *testing.B) {
			for i := 0; i < b.N; i++ {
				_ = btsEqualFold(ab, bb)
			}
		})
	}
	if *compareWithStd {
		for i, bench := range equalFoldCases {
			ab, bb := []byte(bench.a), []byte(bench.b)
			b.Run(fmt.Sprintf("%s#%d_std", bench.label, i), func(b *testing.B) {
				for i := 0; i < b.N; i++ {
					_ = bytes.EqualFold(ab, bb)
				}
			})
		}
	}
}

func TestAsciiToInt(t *testing.T) {
	for _, test := range []struct {
		bts []byte
		exp int
		err bool
	}{
		{[]byte{'0'}, 0, false},
		{[]byte{'1'}, 1, false},
		{[]byte("42"), 42, false},
		{[]byte("420"), 420, false},
		{[]byte("010050042"), 10050042, false},
	} {
		t.Run(fmt.Sprintf("%s", string(test.bts)), func(t *testing.T) {
			act, err := asciiToInt(test.bts)
			if (test.err && err == nil) || (!test.err && err != nil) {
				t.Errorf("unexpected error: %v", err)
			}
			if act != test.exp {
				t.Errorf("asciiToInt(%v) = %v; want %v", test.bts, act, test.exp)
			}
		})
	}
}

func TestBtrim(t *testing.T) {
	for _, test := range []struct {
		bts []byte
		exp []byte
	}{
		{[]byte("abc"), []byte("abc")},
		{[]byte(" abc"), []byte("abc")},
		{[]byte("abc "), []byte("abc")},
		{[]byte(" abc "), []byte("abc")},
	} {
		t.Run(fmt.Sprintf("%s", string(test.bts)), func(t *testing.T) {
			if act := btrim(test.bts); !bytes.Equal(act, test.exp) {
				t.Errorf("btrim(%v) = %v; want %v", test.bts, act, test.exp)
			}
		})
	}
}

func TestBSplit3(t *testing.T) {
	for _, test := range []struct {
		bts  []byte
		sep  byte
		exp1 []byte
		exp2 []byte
		exp3 []byte
	}{
		{[]byte(""), ' ', []byte{}, nil, nil},
		{[]byte("GET / HTTP/1.1"), ' ', []byte("GET"), []byte("/"), []byte("HTTP/1.1")},
	} {
		t.Run(fmt.Sprintf("%s", string(test.bts)), func(t *testing.T) {
			b1, b2, b3 := bsplit3(test.bts, test.sep)
			if !bytes.Equal(b1, test.exp1) || !bytes.Equal(b2, test.exp2) || !bytes.Equal(b3, test.exp3) {
				t.Errorf(
					"bsplit3(%q) = %q, %q, %q; want %q, %q, %q",
					string(test.bts), string(b1), string(b2), string(b3),
					string(test.exp1), string(test.exp2), string(test.exp3),
				)
			}
		})
	}
}

var canonicalHeaderCases = [][]byte{
	[]byte("foo-"),
	[]byte("-foo"),
	[]byte("-"),
	[]byte("foo----bar"),
	[]byte("foo-bar"),
	[]byte("FoO-BaR"),
	[]byte("Foo-Bar"),
	[]byte("sec-websocket-extensions"),
}

func TestCanonicalizeHeaderKey(t *testing.T) {
	for _, bts := range canonicalHeaderCases {
		t.Run(fmt.Sprintf("%s", string(bts)), func(t *testing.T) {
			act := append([]byte(nil), bts...)
			canonicalizeHeaderKey(act)

			exp := strToBytes(textproto.CanonicalMIMEHeaderKey(string(bts)))

			if !bytes.Equal(act, exp) {
				t.Errorf(
					"canonicalizeHeaderKey(%v) = %v; want %v",
					string(bts), string(act), string(exp),
				)
			}
		})
	}
}

func BenchmarkCanonicalizeHeaderKey(b *testing.B) {
	for _, bts := range canonicalHeaderCases {
		b.Run(fmt.Sprintf("%s", string(bts)), func(b *testing.B) {
			for i := 0; i < b.N; i++ {
				canonicalizeHeaderKey(bts)
			}
		})
	}
}

func randomEqualLetters(n int) (c equalFoldCase) {
	c.label = fmt.Sprintf("rnd_eq_%d", n)

	a, b := make([]byte, n), make([]byte, n)

	for i := 0; i < n; i++ {
		c := byte(rand.Intn('Z'-'A'+1) + 'A') // Random character from 'A' to 'Z'.
		a[i] = c
		b[i] = c | ('a' - 'A') // Swap fold.
	}

	c.a = string(a)
	c.b = string(b)

	return
}

func inequalAt(c equalFoldCase, i int) equalFoldCase {
	bts := make([]byte, len(c.a))
	copy(bts, c.a)
	for {
		b := byte(rand.Intn('z'-'a'+1) + 'a')
		if bts[i] != b {
			bts[i] = b
			c.a = string(bts)
			c.label = fmt.Sprintf("rnd_ineq_%d_%d", len(c.a), i)
			return c
		}
	}
}
<|endoftext|>


<commit_before>// Package urltitle is a bort IRC bot plugin that extracts titles for URLs
// posted in a channel
package urltitle

import (
	"log"
	"net/http"
	"regexp"

	"github.com/ianremmler/bort"
	"github.com/mvdan/xurls"
	"golang.org/x/net/html"
)

var (
	urlRE *regexp.Regexp
)

func findNode(path []string, node *html.Node) *html.Node {
	for i := range path {
		if node == nil {
			return nil
		}
		for node = node.FirstChild; node != nil; node = node.NextSibling {
			if node.Type == html.ElementNode && node.Data == path[i] {
				break
			}
		}
	}
	return node
}

func extractTitle(in, out *bort.Message) error {
	url := urlRE.FindString(in.Text)
	if url == "" {
		return nil
	}
	resp, err := http.Get(url)
	if err != nil {
		return nil
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		return nil
	}
	page, err := html.Parse(resp.Body)
	if err != nil {
		return nil
	}
	title := findNode([]string{"html", "head", "title"}, page)
	if title != nil && title.FirstChild != nil {
		out.Type = bort.PrivMsg
		out.Text = title.FirstChild.Data
	}
	return nil
}

func init() {
	var err error
	if urlRE, err = xurls.StrictMatchingScheme("http"); err != nil {
		log.Println("urltitle: error setting regexp")
		return
	}
	if _, err = bort.RegisterMatcher(bort.PrivMsg, urlRE.String(), extractTitle); err != nil {
		log.Println("urltitle: error registering plugin")
	}
}
<commit_msg>urltitle: we get the match from bort; don't do it again.<commit_after>// Package urltitle is a bort IRC bot plugin that extracts titles for URLs
// posted in a channel
package urltitle

import (
	"log"
	"net/http"

	"github.com/ianremmler/bort"
	"github.com/mvdan/xurls"
	"golang.org/x/net/html"
)

func findNode(path []string, node *html.Node) *html.Node {
	for i := range path {
		if node == nil {
			return nil
		}
		for node = node.FirstChild; node != nil; node = node.NextSibling {
			if node.Type == html.ElementNode && node.Data == path[i] {
				break
			}
		}
	}
	return node
}

func extractTitle(in, out *bort.Message) error {
	resp, err := http.Get(in.Match)
	if err != nil {
		return nil
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		return nil
	}
	page, err := html.Parse(resp.Body)
	if err != nil {
		return nil
	}
	title := findNode([]string{"html", "head", "title"}, page)
	if title != nil && title.FirstChild != nil {
		out.Type = bort.PrivMsg
		out.Text = title.FirstChild.Data
	}
	return nil
}

func init() {
	urlRE, err := xurls.StrictMatchingScheme("http")
	if err != nil {
		log.Println("urltitle: error setting regexp")
		return
	}
	pat := "(" + urlRE.String() + ")"
	if _, err = bort.RegisterMatcher(bort.PrivMsg, pat, extractTitle); err != nil {
		log.Println("urltitle: error registering plugin")
	}
}
<|endoftext|>


<commit_before>package goquery

import (
	"bytes"

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

var nodeNames = []string{
	html.ErrorNode:    "#error",
	html.TextNode:     "#text",
	html.DocumentNode: "#document",
	html.CommentNode:  "#comment",
}

// NodeName returns the node name of the first element in the selection.
// It tries to behave in a similar way as the DOM's nodeName property
// (https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeName).
//
// Go's net/html package defines the following node types, listed with
// the corresponding returned value from this function:
//
// ErrorNode : #error
// TextNode : #text
// DocumentNode : #document
// ElementNode : the element's tag name
// CommentNode : #comment
// DoctypeNode : the name of the document type
//
func NodeName(s *Selection) string {
	if s.Length() == 0 {
		return ""
	}
	switch n := s.Get(0); n.Type {
	case html.ElementNode, html.DoctypeNode:
		return n.Data
	default:
		if n.Type >= 0 && int(n.Type) < len(nodeNames) {
			return nodeNames[n.Type]
		}
		return ""
	}
}

// OuterHtml returns the outer HTML rendering of the first item in
// the selection - that is, the HTML including the first element's
// tag and attributes.
//
// Unlike InnerHtml, this is a function and not a method on the Selection,
// because this is not a jQuery method (in javascript-land, this is
// a property provided by the DOM).
func OuterHtml(s *Selection) (string, error) {
	var buf bytes.Buffer

	if s.Length() == 0 {
		return "", nil
	}
	n := s.Get(0)
	if err := html.Render(&buf, n); err != nil {
		return "", err
	}
	return buf.String(), nil
}

func getChildren(n *html.Node) (result []*html.Node) {
	for c := n.FirstChild; c != nil; c = c.NextSibling {
		result = append(result, c)
	}
	return result
}

// Loop through all container nodes to search for the target node.
func sliceContains(container []*html.Node, contained *html.Node) bool {
	for _, n := range container {
		if nodeContains(n, contained) {
			return true
		}
	}

	return false
}

// Checks if the contained node is within the container node.
func nodeContains(container *html.Node, contained *html.Node) bool {
	// Check if the parent of the contained node is the container node, traversing
	// upward until the top is reached, or the container is found.
	for contained = contained.Parent; contained != nil; contained = contained.Parent {
		if container == contained {
			return true
		}
	}
	return false
}

// Checks if the target node is in the slice of nodes.
func isInSlice(slice []*html.Node, node *html.Node) bool {
	return indexInSlice(slice, node) > -1
}

// Returns the index of the target node in the slice, or -1.
func indexInSlice(slice []*html.Node, node *html.Node) int {
	if node != nil {
		for i, n := range slice {
			if n == node {
				return i
			}
		}
	}
	return -1
}

// Appends the new nodes to the target slice, making sure no duplicate is added.
// There is no check to the original state of the target slice, so it may still
// contain duplicates. The target slice is returned because append() may create
// a new underlying array.
func appendWithoutDuplicates(target []*html.Node, nodes []*html.Node) []*html.Node {
	for _, n := range nodes {
		if !isInSlice(target, n) {
			target = append(target, n)
		}
	}

	return target
}

// Loop through a selection, returning only those nodes that pass the predicate
// function.
func grep(sel *Selection, predicate func(i int, s *Selection) bool) (result []*html.Node) {
	for i, n := range sel.Nodes {
		if predicate(i, newSingleSelection(n, sel.document)) {
			result = append(result, n)
		}
	}
	return result
}

// Creates a new Selection object based on the specified nodes, and keeps the
// source Selection object on the stack (linked list).
func pushStack(fromSel *Selection, nodes []*html.Node) *Selection {
	result := &Selection{nodes, fromSel.document, fromSel}
	return result
}
<commit_msg>doc: fix list for godoc<commit_after>package goquery

import (
	"bytes"

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

var nodeNames = []string{
	html.ErrorNode:    "#error",
	html.TextNode:     "#text",
	html.DocumentNode: "#document",
	html.CommentNode:  "#comment",
}

// NodeName returns the node name of the first element in the selection.
// It tries to behave in a similar way as the DOM's nodeName property
// (https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeName).
//
// Go's net/html package defines the following node types, listed with
// the corresponding returned value from this function:
//
//     ErrorNode : #error
//     TextNode : #text
//     DocumentNode : #document
//     ElementNode : the element's tag name
//     CommentNode : #comment
//     DoctypeNode : the name of the document type
//
func NodeName(s *Selection) string {
	if s.Length() == 0 {
		return ""
	}
	switch n := s.Get(0); n.Type {
	case html.ElementNode, html.DoctypeNode:
		return n.Data
	default:
		if n.Type >= 0 && int(n.Type) < len(nodeNames) {
			return nodeNames[n.Type]
		}
		return ""
	}
}

// OuterHtml returns the outer HTML rendering of the first item in
// the selection - that is, the HTML including the first element's
// tag and attributes.
//
// Unlike InnerHtml, this is a function and not a method on the Selection,
// because this is not a jQuery method (in javascript-land, this is
// a property provided by the DOM).
func OuterHtml(s *Selection) (string, error) {
	var buf bytes.Buffer

	if s.Length() == 0 {
		return "", nil
	}
	n := s.Get(0)
	if err := html.Render(&buf, n); err != nil {
		return "", err
	}
	return buf.String(), nil
}

func getChildren(n *html.Node) (result []*html.Node) {
	for c := n.FirstChild; c != nil; c = c.NextSibling {
		result = append(result, c)
	}
	return result
}

// Loop through all container nodes to search for the target node.
func sliceContains(container []*html.Node, contained *html.Node) bool {
	for _, n := range container {
		if nodeContains(n, contained) {
			return true
		}
	}

	return false
}

// Checks if the contained node is within the container node.
func nodeContains(container *html.Node, contained *html.Node) bool {
	// Check if the parent of the contained node is the container node, traversing
	// upward until the top is reached, or the container is found.
	for contained = contained.Parent; contained != nil; contained = contained.Parent {
		if container == contained {
			return true
		}
	}
	return false
}

// Checks if the target node is in the slice of nodes.
func isInSlice(slice []*html.Node, node *html.Node) bool {
	return indexInSlice(slice, node) > -1
}

// Returns the index of the target node in the slice, or -1.
func indexInSlice(slice []*html.Node, node *html.Node) int {
	if node != nil {
		for i, n := range slice {
			if n == node {
				return i
			}
		}
	}
	return -1
}

// Appends the new nodes to the target slice, making sure no duplicate is added.
// There is no check to the original state of the target slice, so it may still
// contain duplicates. The target slice is returned because append() may create
// a new underlying array.
func appendWithoutDuplicates(target []*html.Node, nodes []*html.Node) []*html.Node {
	for _, n := range nodes {
		if !isInSlice(target, n) {
			target = append(target, n)
		}
	}

	return target
}

// Loop through a selection, returning only those nodes that pass the predicate
// function.
func grep(sel *Selection, predicate func(i int, s *Selection) bool) (result []*html.Node) {
	for i, n := range sel.Nodes {
		if predicate(i, newSingleSelection(n, sel.document)) {
			result = append(result, n)
		}
	}
	return result
}

// Creates a new Selection object based on the specified nodes, and keeps the
// source Selection object on the stack (linked list).
func pushStack(fromSel *Selection, nodes []*html.Node) *Selection {
	result := &Selection{nodes, fromSel.document, fromSel}
	return result
}
<|endoftext|>


<commit_before>package commands

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"sort"

	cmds "github.com/ipfs/go-ipfs/commands"
	peer "github.com/ipfs/go-ipfs/p2p/peer"
	iaddr "github.com/ipfs/go-ipfs/util/ipfsaddr"

	ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
)

type stringList struct {
	Strings []string
}

type addrMap struct {
	Addrs map[string][]string
}

var SwarmCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "swarm inspection tool",
		Synopsis: `
ipfs swarm peers                - List peers with open connections
ipfs swarm addrs                - List known addresses. Useful to debug.
ipfs swarm connect <address>    - Open connection to a given address
ipfs swarm disconnect <address> - Close connection to a given address
`,
		ShortDescription: `
ipfs swarm is a tool to manipulate the network swarm. The swarm is the
component that opens, listens for, and maintains connections to other
ipfs peers in the internet.
`,
	},
	Subcommands: map[string]*cmds.Command{
		"peers":      swarmPeersCmd,
		"addrs":      swarmAddrsCmd,
		"connect":    swarmConnectCmd,
		"disconnect": swarmDisconnectCmd,
	},
}

var swarmPeersCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "List peers with open connections",
		ShortDescription: `
ipfs swarm peers lists the set of peers this node is connected to.
`,
	},
	Run: func(req cmds.Request, res cmds.Response) {

		log.Debug("ipfs swarm peers")
		n, err := req.Context().GetNode()
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		if n.PeerHost == nil {
			res.SetError(errNotOnline, cmds.ErrClient)
			return
		}

		conns := n.PeerHost.Network().Conns()
		addrs := make([]string, len(conns))
		for i, c := range conns {
			pid := c.RemotePeer()
			addr := c.RemoteMultiaddr()
			addrs[i] = fmt.Sprintf("%s/ipfs/%s", addr, pid.Pretty())
		}

		sort.Sort(sort.StringSlice(addrs))
		res.SetOutput(&stringList{addrs})
	},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: stringListMarshaler,
	},
	Type: stringList{},
}

var swarmAddrsCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "List known addresses. Useful to debug.",
		ShortDescription: `
ipfs swarm addrs lists all addresses this node is aware of.
`,
	},
	Run: func(req cmds.Request, res cmds.Response) {

		n, err := req.Context().GetNode()
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		if n.PeerHost == nil {
			res.SetError(errNotOnline, cmds.ErrClient)
			return
		}

		addrs := make(map[string][]string)
		ps := n.PeerHost.Network().Peerstore()
		for _, p := range ps.Peers() {
			s := p.Pretty()
			for _, a := range ps.Addrs(p) {
				addrs[s] = append(addrs[s], a.String())
			}
			sort.Sort(sort.StringSlice(addrs[s]))
		}

		res.SetOutput(&addrMap{Addrs: addrs})
	},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: func(res cmds.Response) (io.Reader, error) {
			m, ok := res.Output().(*addrMap)
			if !ok {
				return nil, errors.New("failed to cast map[string]string")
			}

			// sort the ids first
			ids := make([]string, 0, len(m.Addrs))
			for p := range m.Addrs {
				ids = append(ids, p)
			}
			sort.Sort(sort.StringSlice(ids))

			var buf bytes.Buffer
			for _, p := range ids {
				paddrs := m.Addrs[p]
				buf.WriteString(fmt.Sprintf("%s (%d)\n", p, len(paddrs)))
				for _, addr := range paddrs {
					buf.WriteString("\t" + addr + "\n")
				}
			}
			return &buf, nil
		},
	},
	Type: addrMap{},
}

var swarmConnectCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Open connection to a given address",
		ShortDescription: `
'ipfs swarm connect' opens a connection to a peer address. The address format
is an ipfs multiaddr:

ipfs swarm connect /ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ
`,
	},
	Arguments: []cmds.Argument{
		cmds.StringArg("address", true, true, "address of peer to connect to").EnableStdin(),
	},
	Run: func(req cmds.Request, res cmds.Response) {
		ctx := context.TODO()

		n, err := req.Context().GetNode()
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		addrs := req.Arguments()

		if n.PeerHost == nil {
			res.SetError(errNotOnline, cmds.ErrClient)
			return
		}

		pis, err := peersWithAddresses(addrs)
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		output := make([]string, len(pis))
		for i, pi := range pis {
			output[i] = "connect " + pi.ID.Pretty()

			err := n.PeerHost.Connect(ctx, pi)
			if err != nil {
				output[i] += " failure: " + err.Error()
			} else {
				output[i] += " success"
			}
		}

		res.SetOutput(&stringList{output})
	},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: stringListMarshaler,
	},
	Type: stringList{},
}

var swarmDisconnectCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Close connection to a given address",
		ShortDescription: `
'ipfs swarm disconnect' closes a connection to a peer address. The address format
is an ipfs multiaddr:

ipfs swarm disconnect /ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ
`,
	},
	Arguments: []cmds.Argument{
		cmds.StringArg("address", true, true, "address of peer to connect to").EnableStdin(),
	},
	Run: func(req cmds.Request, res cmds.Response) {
		n, err := req.Context().GetNode()
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		addrs := req.Arguments()

		if n.PeerHost == nil {
			res.SetError(errNotOnline, cmds.ErrClient)
			return
		}

		iaddrs, err := parseAddresses(addrs)
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		output := make([]string, len(iaddrs))
		for i, addr := range iaddrs {
			taddr := addr.Transport()
			output[i] = "disconnect " + addr.ID().Pretty()

			found := false
			conns := n.PeerHost.Network().ConnsToPeer(addr.ID())
			for _, conn := range conns {
				if !conn.RemoteMultiaddr().Equal(taddr) {
					log.Debug("it's not", conn.RemoteMultiaddr(), taddr)
					continue
				}

				if err := conn.Close(); err != nil {
					output[i] += " failure: " + err.Error()
				} else {
					output[i] += " success"
				}
				found = true
				break
			}

			if !found {
				output[i] += " failure: conn not found"
			}
		}
		res.SetOutput(&stringList{output})
	},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: stringListMarshaler,
	},
	Type: stringList{},
}

func stringListMarshaler(res cmds.Response) (io.Reader, error) {
	list, ok := res.Output().(*stringList)
	if !ok {
		return nil, errors.New("failed to cast []string")
	}

	var buf bytes.Buffer
	for _, s := range list.Strings {
		buf.WriteString(s)
		buf.WriteString("\n")
	}
	return &buf, nil
}

// parseAddresses is a function that takes in a slice of string peer addresses
// (multiaddr + peerid) and returns slices of multiaddrs and peerids.
func parseAddresses(addrs []string) (iaddrs []iaddr.IPFSAddr, err error) {
	iaddrs = make([]iaddr.IPFSAddr, len(addrs))
	for i, saddr := range addrs {
		iaddrs[i], err = iaddr.ParseString(saddr)
		if err != nil {
			return nil, cmds.ClientError("invalid peer address: " + err.Error())
		}
	}
	return
}

// peersWithAddresses is a function that takes in a slice of string peer addresses
// (multiaddr + peerid) and returns a slice of properly constructed peers
func peersWithAddresses(addrs []string) (pis []peer.PeerInfo, err error) {
	iaddrs, err := parseAddresses(addrs)
	if err != nil {
		return nil, err
	}

	for _, iaddr := range iaddrs {
		pis = append(pis, peer.PeerInfo{
			ID:    iaddr.ID(),
			Addrs: []ma.Multiaddr{iaddr.Transport()},
		})
	}
	return pis, nil
}
<commit_msg>Issue #873. Thought I might do a small change first to get my feet wet.<commit_after>package commands

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"sort"

	cmds "github.com/ipfs/go-ipfs/commands"
	peer "github.com/ipfs/go-ipfs/p2p/peer"
	iaddr "github.com/ipfs/go-ipfs/util/ipfsaddr"

	ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
)

type stringList struct {
	Strings []string
}

type addrMap struct {
	Addrs map[string][]string
}

var SwarmCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "swarm inspection tool",
		Synopsis: `
ipfs swarm peers                - List peers with open connections
ipfs swarm addrs                - List known addresses. Useful to debug.
ipfs swarm connect <address>    - Open connection to a given address
ipfs swarm disconnect <address> - Close connection to a given address
`,
		ShortDescription: `
ipfs swarm is a tool to manipulate the network swarm. The swarm is the
component that opens, listens for, and maintains connections to other
ipfs peers in the internet.
`,
	},
	Subcommands: map[string]*cmds.Command{
		"peers":      swarmPeersCmd,
		"addrs":      swarmAddrsCmd,
		"connect":    swarmConnectCmd,
		"disconnect": swarmDisconnectCmd,
	},
}

var swarmPeersCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "List peers with open connections",
		ShortDescription: `
ipfs swarm peers lists the set of peers this node is connected to.
`,
	},
	Run: func(req cmds.Request, res cmds.Response) {

		log.Debug("ipfs swarm peers")
		n, err := req.Context().GetNode()
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		if n.PeerHost == nil {
			res.SetError(errNotOnline, cmds.ErrClient)
			return
		}

		conns := n.PeerHost.Network().Conns()
		addrs := make([]string, len(conns))
		for i, c := range conns {
			pid := c.RemotePeer()
			addr := c.RemoteMultiaddr()
			addrs[i] = fmt.Sprintf("%s/ipfs/%s", addr, pid.Pretty())
		}

		sort.Sort(sort.StringSlice(addrs))
		res.SetOutput(&stringList{addrs})
	},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: stringListMarshaler,
	},
	Type: stringList{},
}

var swarmAddrsCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "List known addresses. Useful to debug.",
		ShortDescription: `
ipfs swarm addrs lists all addresses this node is aware of.
`,
	},
	Run: func(req cmds.Request, res cmds.Response) {

		n, err := req.Context().GetNode()
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		if n.PeerHost == nil {
			res.SetError(errNotOnline, cmds.ErrClient)
			return
		}

		addrs := make(map[string][]string)
		ps := n.PeerHost.Network().Peerstore()
		for _, p := range ps.Peers() {
			s := p.Pretty()
			for _, a := range ps.Addrs(p) {
				addrs[s] = append(addrs[s], a.String())
			}
			sort.Sort(sort.StringSlice(addrs[s]))
		}

		res.SetOutput(&addrMap{Addrs: addrs})
	},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: func(res cmds.Response) (io.Reader, error) {
			m, ok := res.Output().(*addrMap)
			if !ok {
				return nil, errors.New("failed to cast map[string]string")
			}

			// sort the ids first
			ids := make([]string, 0, len(m.Addrs))
			for p := range m.Addrs {
				ids = append(ids, p)
			}
			sort.Sort(sort.StringSlice(ids))

			var buf bytes.Buffer
			for _, p := range ids {
				paddrs := m.Addrs[p]
				buf.WriteString(fmt.Sprintf("%s (%d)\n", p, len(paddrs)))
				for _, addr := range paddrs {
					buf.WriteString("\t" + addr + "\n")
				}
			}
			return &buf, nil
		},
	},
	Type: addrMap{},
}

var swarmConnectCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Open connection to a given address",
		ShortDescription: `
'ipfs swarm connect' opens a new direct connection to a peer address.

The address format is an ipfs multiaddr:

ipfs swarm connect /ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ
`,
	},
	Arguments: []cmds.Argument{
		cmds.StringArg("address", true, true, "address of peer to connect to").EnableStdin(),
	},
	Run: func(req cmds.Request, res cmds.Response) {
		ctx := context.TODO()

		n, err := req.Context().GetNode()
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		addrs := req.Arguments()

		if n.PeerHost == nil {
			res.SetError(errNotOnline, cmds.ErrClient)
			return
		}

		pis, err := peersWithAddresses(addrs)
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		output := make([]string, len(pis))
		for i, pi := range pis {
			output[i] = "connect " + pi.ID.Pretty()

			err := n.PeerHost.Connect(ctx, pi)
			if err != nil {
				output[i] += " failure: " + err.Error()
			} else {
				output[i] += " success"
			}
		}

		res.SetOutput(&stringList{output})
	},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: stringListMarshaler,
	},
	Type: stringList{},
}

var swarmDisconnectCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Close connection to a given address",
		ShortDescription: `
'ipfs swarm disconnect' closes a connection to a peer address. The address format
is an ipfs multiaddr:

ipfs swarm disconnect /ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ
`,
	},
	Arguments: []cmds.Argument{
		cmds.StringArg("address", true, true, "address of peer to connect to").EnableStdin(),
	},
	Run: func(req cmds.Request, res cmds.Response) {
		n, err := req.Context().GetNode()
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		addrs := req.Arguments()

		if n.PeerHost == nil {
			res.SetError(errNotOnline, cmds.ErrClient)
			return
		}

		iaddrs, err := parseAddresses(addrs)
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		output := make([]string, len(iaddrs))
		for i, addr := range iaddrs {
			taddr := addr.Transport()
			output[i] = "disconnect " + addr.ID().Pretty()

			found := false
			conns := n.PeerHost.Network().ConnsToPeer(addr.ID())
			for _, conn := range conns {
				if !conn.RemoteMultiaddr().Equal(taddr) {
					log.Debug("it's not", conn.RemoteMultiaddr(), taddr)
					continue
				}

				if err := conn.Close(); err != nil {
					output[i] += " failure: " + err.Error()
				} else {
					output[i] += " success"
				}
				found = true
				break
			}

			if !found {
				output[i] += " failure: conn not found"
			}
		}
		res.SetOutput(&stringList{output})
	},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: stringListMarshaler,
	},
	Type: stringList{},
}

func stringListMarshaler(res cmds.Response) (io.Reader, error) {
	list, ok := res.Output().(*stringList)
	if !ok {
		return nil, errors.New("failed to cast []string")
	}

	var buf bytes.Buffer
	for _, s := range list.Strings {
		buf.WriteString(s)
		buf.WriteString("\n")
	}
	return &buf, nil
}

// parseAddresses is a function that takes in a slice of string peer addresses
// (multiaddr + peerid) and returns slices of multiaddrs and peerids.
func parseAddresses(addrs []string) (iaddrs []iaddr.IPFSAddr, err error) {
	iaddrs = make([]iaddr.IPFSAddr, len(addrs))
	for i, saddr := range addrs {
		iaddrs[i], err = iaddr.ParseString(saddr)
		if err != nil {
			return nil, cmds.ClientError("invalid peer address: " + err.Error())
		}
	}
	return
}

// peersWithAddresses is a function that takes in a slice of string peer addresses
// (multiaddr + peerid) and returns a slice of properly constructed peers
func peersWithAddresses(addrs []string) (pis []peer.PeerInfo, err error) {
	iaddrs, err := parseAddresses(addrs)
	if err != nil {
		return nil, err
	}

	for _, iaddr := range iaddrs {
		pis = append(pis, peer.PeerInfo{
			ID:    iaddr.ID(),
			Addrs: []ma.Multiaddr{iaddr.Transport()},
		})
	}
	return pis, nil
}
<|endoftext|>


<commit_before>/*
Copyright IBM Corp. 2018 All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package endorser

import (
	"github.com/hyperledger/fabric/core/handlers/endorsement/api/state"
	"github.com/hyperledger/fabric/core/ledger"
	"github.com/hyperledger/fabric/core/transientstore"
	"github.com/hyperledger/fabric/protos/ledger/rwset"
	"github.com/pkg/errors"
)

// go:generate mockery -dir core/endorser/ -name QueryCreator -case underscore -output core/endorser/mocks/
// QueryCreator creates new QueryExecutors
type QueryCreator interface {
	NewQueryExecutor() (ledger.QueryExecutor, error)
}

// ChannelState defines state operations
type ChannelState struct {
	transientstore.Store
	QueryCreator
}

// FetchState fetches state
func (cs *ChannelState) FetchState() (endorsement.State, error) {
	qe, err := cs.NewQueryExecutor()
	if err != nil {
		return nil, err
	}

	return &StateContext{
		QueryExecutor: qe,
		Store:         cs.Store,
	}, nil
}

// StateContext defines an execution context that interacts with the state
type StateContext struct {
	transientstore.Store
	ledger.QueryExecutor
}

// GetTransientByTXID returns the private data associated with this transaction ID.
func (sc *StateContext) GetTransientByTXID(txID string) ([]*rwset.TxPvtReadWriteSet, error) {
	scanner, err := sc.Store.GetTxPvtRWSetByTxid(txID, nil)
	if err != nil {
		return nil, errors.WithStack(err)
	}
	var data []*rwset.TxPvtReadWriteSet
	for {
		res, err := scanner.NextWithConfig()
		if err != nil {
			return nil, errors.WithStack(err)
		}
		if res == nil {
			break
		}
		if res.PvtSimulationResultsWithConfig == nil {
			return nil, errors.New("received nil private simulation results")
		}
		data = append(data, res.PvtSimulationResultsWithConfig.PvtRwset)
	}
	return data, nil
}
<commit_msg>[FAB-10153] Continue instead of erroring- endorserState<commit_after>/*
Copyright IBM Corp. 2018 All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package endorser

import (
	"github.com/hyperledger/fabric/core/handlers/endorsement/api/state"
	"github.com/hyperledger/fabric/core/ledger"
	"github.com/hyperledger/fabric/core/transientstore"
	"github.com/hyperledger/fabric/protos/ledger/rwset"
	"github.com/pkg/errors"
)

// go:generate mockery -dir core/endorser/ -name QueryCreator -case underscore -output core/endorser/mocks/
// QueryCreator creates new QueryExecutors
type QueryCreator interface {
	NewQueryExecutor() (ledger.QueryExecutor, error)
}

// ChannelState defines state operations
type ChannelState struct {
	transientstore.Store
	QueryCreator
}

// FetchState fetches state
func (cs *ChannelState) FetchState() (endorsement.State, error) {
	qe, err := cs.NewQueryExecutor()
	if err != nil {
		return nil, err
	}

	return &StateContext{
		QueryExecutor: qe,
		Store:         cs.Store,
	}, nil
}

// StateContext defines an execution context that interacts with the state
type StateContext struct {
	transientstore.Store
	ledger.QueryExecutor
}

// GetTransientByTXID returns the private data associated with this transaction ID.
func (sc *StateContext) GetTransientByTXID(txID string) ([]*rwset.TxPvtReadWriteSet, error) {
	scanner, err := sc.Store.GetTxPvtRWSetByTxid(txID, nil)
	if err != nil {
		return nil, errors.WithStack(err)
	}
	var data []*rwset.TxPvtReadWriteSet
	for {
		res, err := scanner.NextWithConfig()
		if err != nil {
			return nil, errors.WithStack(err)
		}
		if res == nil {
			break
		}
		if res.PvtSimulationResultsWithConfig == nil {
			continue
		}
		data = append(data, res.PvtSimulationResultsWithConfig.PvtRwset)
	}
	return data, nil
}
<|endoftext|>


<commit_before>package whoson

import (
	"fmt"
	"net"
	"net/textproto"
	"os"
	"strings"
	"syscall"
	"time"

	"github.com/pkg/errors"
)

type Session struct {
	protocol ProtocolType
	id       uint64

	udpconn    *net.UDPConn
	remoteAddr *net.UDPAddr
	b          *Buffer

	tcpserver *TCPServer
	conn      net.Conn
	tp        *textproto.Conn
	tpid      uint

	cmdMethod MethodType
	cmdIp     net.IP
	cmdArgs   string
}

func NewSessionUDP(c *net.UDPConn, r *net.UDPAddr, b *Buffer) (*Session, error) {
	id, err := IDGenerator.NextID()
	if err != nil {
		return nil, err
	}

	return &Session{
		protocol:   pUDP,
		id:         id,
		udpconn:    c,
		remoteAddr: r,
		b:          b,
	}, nil
}

func NewSessionTCP(s *TCPServer, c net.Conn) (*Session, error) {
	id, err := IDGenerator.NextID()
	if err != nil {
		return nil, err
	}

	return &Session{
		protocol:  pTCP,
		id:        id,
		tcpserver: s,
		conn:      c,
		tp:        textproto.NewConn(c),
	}, nil
}

func (ses *Session) setTpId() {
	ses.tpid = ses.tp.Next()
}

func (ses *Session) methodType(m string) MethodType {
	if v, ok := methodFromString[m]; ok {
		return v
	} else {
		return mUnkownMethod
	}
}

func (ses *Session) parseCmd(line string) error {
	var cmd []string

	if strings.TrimSpace(line) == "" {
		return errors.New("command parse error")
	}

	w := strings.Split(line, " ")
	for i := range w {
		if w[i] != "" {
			cmd = append(cmd, strings.TrimSpace(w[i]))
		}
	}

	ses.cmdMethod = ses.methodType(strings.ToUpper(cmd[0]))
	switch ses.cmdMethod {
	case mLogin, mLogout, mQuery:
		if ses.cmdIp = net.ParseIP(cmd[1]); ses.cmdIp == nil {
			return errors.New("command parse error")
		}
		ses.cmdArgs = strings.Join(cmd[2:], " ")
	case mQuit:
		//pp.Println("Quit")
		ses.cmdArgs = strings.Join(cmd[1:], " ")
	default:
		return errors.New("command not found")
	}
	return nil
}

func (ses *Session) readLine() (string, error) {
	ses.tp.StartRequest(ses.tpid)
	l1, err := ses.tp.ReadLine()
	if err != nil {
		return "", err
	}

	l2, err := ses.tp.ReadLine()
	if err != nil {
		return "", err
	}
	ses.tp.EndRequest(ses.tpid)

	if l1 != "" && l2 == "" {
		return l1, nil
	} else {
		return "", errors.New("session read error")
	}
}

func (ses *Session) sendLine(str string) error {
	var err error
	if ses.protocol == pTCP {
		ses.tp.StartResponse(ses.tpid)
		err = ses.tp.PrintfLine(str + CRLF)
		ses.tp.EndResponse(ses.tpid)
	} else {
		b := []byte(str + CRLF + CRLF)
		_, err = ses.udpconn.WriteToUDP(b, ses.remoteAddr)
	}
	return err
}

func (ses *Session) sendResponsePositive(str string) error {
	return ses.sendLine(fmt.Sprintf("%s%s", result[rPositive], str))
}

func (ses *Session) sendResponseNegative(str string) error {
	return ses.sendLine(fmt.Sprintf("%s%s", result[rNegative], str))
}

func (ses *Session) sendResponseBadRequest(str string) error {
	return ses.sendLine(fmt.Sprintf("%s%s", result[rBadRequest], str))
}

func (ses *Session) resetCmd() {
	ses.cmdMethod = mUnkownMethod
	ses.cmdIp = nil
	ses.cmdArgs = ""
}

func (ses *Session) startHandler() bool {
	defer ses.resetCmd()
	var err error

	if ses.protocol == pTCP {
		ses.setTpId()
		line, err := ses.readLine()
		if !ses.tcpErrorHandling(err) {
			return false
		}
		err = ses.parseCmd(line)
		if err != nil {
			Log("debug", "StartHandler", ses, err)
			ses.sendResponseBadRequest(err.Error())
			return true
		}
	} else {
		err = ses.parseCmd(string(ses.b.buf[:ses.b.count]))
		if err != nil {
			Log("debug", "StartHandler", ses, err)
			ses.sendResponseBadRequest(err.Error())
			return true
		}
	}

	switch ses.cmdMethod {
	case mLogin:
		expCommandLoginTotal.Add(1)
		ses.methodLogin()
	case mLogout:
		expCommandLogoutTotal.Add(1)
		ses.methodLogout()
	case mQuery:
		expCommandQueryTotal.Add(1)
		ses.methodQuery()
	case mQuit:
		expCommandQuitTotal.Add(1)
		ses.methodQuit()
		return false
	default:
		err := errors.New("handler error")
		expErrorsTotal.Add(1)
		Log("error", "StartHandler:Error", ses, err)
		ses.sendResponseBadRequest(err.Error())
	}
	Log("debug", "SessionHandler", ses, err)
	return true
}

func (ses *Session) tcpErrorHandling(err error) bool {
	if err != nil {
		if opError, ok := err.(*net.OpError); ok && opError.Timeout() {
			Log("debug", "StartHandler:Timeout", ses, err)
			return false
		} else if ok {
			if opError2, ok2 := opError.Err.(*os.SyscallError); ok2 && opError2.Err == syscall.ECONNRESET {
				Log("debug", "StartHandler:ResetByPeer", ses, err)
				return false
			}
		} else if err.Error() == "EOF" {
			Log("debug", "StartHandler:EOF", ses, err)
			return false
		}
		expErrorsTotal.Add(1)
		Log("error", "StartHandler:Error", ses, err)
		ses.sendResponseBadRequest(err.Error())
		return true
	}
	return true
}

func (ses *Session) methodLogin() {
	sd := &StoreData{
		Expire: time.Now().Add(StoreDataExpire),
		IP:     ses.cmdIp,
		Data:   ses.cmdArgs,
	}
	MainStore.Set(sd.Key(), sd)
	ses.sendResponsePositive("LOGIN OK")
}

func (ses *Session) methodLogout() {
	ok := MainStore.Del(ses.cmdIp.String())
	if ok {
		ses.sendResponsePositive("LOGOUT record deleted")
	} else {
		ses.sendResponsePositive("LOGOUT no such record, nothing done")
	}
}

func (ses *Session) methodQuery() {
	sd, err := MainStore.Get(ses.cmdIp.String())
	if err != nil {
		ses.sendResponseNegative("Not Logged in")
	} else {
		ses.sendResponsePositive(sd.Data)
	}
}

func (ses *Session) methodQuit() {
	ses.sendResponsePositive("QUIT OK")
	if ses.protocol == pTCP {
		ses.conn.Close()
	}
}

func (ses *Session) close() {
	if ses.protocol == pUDP {
		ses.b.Free()
	}
}
<commit_msg>Update Logging for whoson/protocol.go<commit_after>package whoson

import (
	"fmt"
	"net"
	"net/textproto"
	"os"
	"strings"
	"syscall"
	"time"

	"github.com/pkg/errors"
)

type Session struct {
	protocol ProtocolType
	id       uint64

	udpconn    *net.UDPConn
	remoteAddr *net.UDPAddr
	b          *Buffer

	tcpserver *TCPServer
	conn      net.Conn
	tp        *textproto.Conn
	tpid      uint

	cmdMethod MethodType
	cmdIp     net.IP
	cmdArgs   string
}

func NewSessionUDP(c *net.UDPConn, r *net.UDPAddr, b *Buffer) (*Session, error) {
	id, err := IDGenerator.NextID()
	if err != nil {
		return nil, err
	}

	return &Session{
		protocol:   pUDP,
		id:         id,
		udpconn:    c,
		remoteAddr: r,
		b:          b,
	}, nil
}

func NewSessionTCP(s *TCPServer, c net.Conn) (*Session, error) {
	id, err := IDGenerator.NextID()
	if err != nil {
		return nil, err
	}

	return &Session{
		protocol:  pTCP,
		id:        id,
		tcpserver: s,
		conn:      c,
		tp:        textproto.NewConn(c),
	}, nil
}

func (ses *Session) setTpId() {
	ses.tpid = ses.tp.Next()
}

func (ses *Session) methodType(m string) MethodType {
	if v, ok := methodFromString[m]; ok {
		return v
	} else {
		return mUnkownMethod
	}
}

func (ses *Session) parseCmd(line string) error {
	var cmd []string

	if strings.TrimSpace(line) == "" {
		return errors.New("command parse error")
	}

	w := strings.Split(line, " ")
	for i := range w {
		if w[i] != "" {
			cmd = append(cmd, strings.TrimSpace(w[i]))
		}
	}

	ses.cmdMethod = ses.methodType(strings.ToUpper(cmd[0]))
	switch ses.cmdMethod {
	case mLogin, mLogout, mQuery:
		if ses.cmdIp = net.ParseIP(cmd[1]); ses.cmdIp == nil {
			return errors.New("command parse error")
		}
		ses.cmdArgs = strings.Join(cmd[2:], " ")
	case mQuit:
		//pp.Println("Quit")
		ses.cmdArgs = strings.Join(cmd[1:], " ")
	default:
		return errors.New("command not found")
	}
	return nil
}

func (ses *Session) readLine() (string, error) {
	ses.tp.StartRequest(ses.tpid)
	l1, err := ses.tp.ReadLine()
	if err != nil {
		return "", err
	}

	l2, err := ses.tp.ReadLine()
	if err != nil {
		return "", err
	}
	ses.tp.EndRequest(ses.tpid)

	if l1 != "" && l2 == "" {
		return l1, nil
	} else {
		return "", errors.New("session read error")
	}
}

func (ses *Session) sendLine(str string) error {
	var err error
	if ses.protocol == pTCP {
		ses.tp.StartResponse(ses.tpid)
		err = ses.tp.PrintfLine(str + CRLF)
		ses.tp.EndResponse(ses.tpid)
	} else {
		b := []byte(str + CRLF + CRLF)
		_, err = ses.udpconn.WriteToUDP(b, ses.remoteAddr)
	}
	return err
}

func (ses *Session) sendResponsePositive(str string) error {
	return ses.sendLine(fmt.Sprintf("%s%s", result[rPositive], str))
}

func (ses *Session) sendResponseNegative(str string) error {
	return ses.sendLine(fmt.Sprintf("%s%s", result[rNegative], str))
}

func (ses *Session) sendResponseBadRequest(str string) error {
	return ses.sendLine(fmt.Sprintf("%s%s", result[rBadRequest], str))
}

func (ses *Session) resetCmd() {
	ses.cmdMethod = mUnkownMethod
	ses.cmdIp = nil
	ses.cmdArgs = ""
}

func (ses *Session) startHandler() bool {
	defer ses.resetCmd()
	var err error

	if ses.protocol == pTCP {
		ses.setTpId()
		line, err := ses.readLine()
		if !ses.tcpErrorHandling(err) {
			return false
		}
		err = ses.parseCmd(line)
		if err != nil {
			Log("debug", "StartHandler", ses, err)
			ses.sendResponseBadRequest(err.Error())
			return true
		}
	} else {
		err = ses.parseCmd(string(ses.b.buf[:ses.b.count]))
		if err != nil {
			Log("debug", "StartHandler", ses, err)
			ses.sendResponseBadRequest(err.Error())
			return true
		}
	}

	switch ses.cmdMethod {
	case mLogin:
		expCommandLoginTotal.Add(1)
		ses.methodLogin()
		Log("debug", "SessionHandler", ses, err)
	case mLogout:
		expCommandLogoutTotal.Add(1)
		ses.methodLogout()
		Log("debug", "SessionHandler", ses, err)
	case mQuery:
		expCommandQueryTotal.Add(1)
		ses.methodQuery()
		Log("debug", "SessionHandler", ses, err)
	case mQuit:
		expCommandQuitTotal.Add(1)
		ses.methodQuit()
		Log("debug", "SessionHandler", ses, err)
		return false
	default:
		err := errors.New("handler error")
		expErrorsTotal.Add(1)
		Log("error", "StartHandler:Error", ses, err)
		ses.sendResponseBadRequest(err.Error())
	}
	return true
}

func (ses *Session) tcpErrorHandling(err error) bool {
	if err != nil {
		if opError, ok := err.(*net.OpError); ok && opError.Timeout() {
			Log("debug", "StartHandler:Timeout", ses, err)
			return false
		} else if ok {
			if opError2, ok2 := opError.Err.(*os.SyscallError); ok2 && opError2.Err == syscall.ECONNRESET {
				Log("debug", "StartHandler:ResetByPeer", ses, err)
				return false
			}
		} else if err.Error() == "EOF" {
			Log("debug", "StartHandler:EOF", ses, err)
			return false
		}
		expErrorsTotal.Add(1)
		Log("error", "StartHandler:Error", ses, err)
		ses.sendResponseBadRequest(err.Error())
		return true
	}
	return true
}

func (ses *Session) methodLogin() {
	sd := &StoreData{
		Expire: time.Now().Add(StoreDataExpire),
		IP:     ses.cmdIp,
		Data:   ses.cmdArgs,
	}
	MainStore.Set(sd.Key(), sd)
	ses.sendResponsePositive("LOGIN OK")
}

func (ses *Session) methodLogout() {
	ok := MainStore.Del(ses.cmdIp.String())
	if ok {
		ses.sendResponsePositive("LOGOUT record deleted")
	} else {
		ses.sendResponsePositive("LOGOUT no such record, nothing done")
	}
}

func (ses *Session) methodQuery() {
	sd, err := MainStore.Get(ses.cmdIp.String())
	if err != nil {
		ses.sendResponseNegative("Not Logged in")
	} else {
		ses.sendResponsePositive(sd.Data)
	}
}

func (ses *Session) methodQuit() {
	ses.sendResponsePositive("QUIT OK")
	if ses.protocol == pTCP {
		ses.conn.Close()
	}
}

func (ses *Session) close() {
	if ses.protocol == pUDP {
		ses.b.Free()
	}
}
<|endoftext|>


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

package core

import (
	"sync"
	"testing"
)

type mockProducer struct {
	ProducerBase
}

func (prod *mockProducer) Produce(workers *sync.WaitGroup) {
	// does something.
}

func TestProducerConfigure(t *testing.T) {

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

package core

import (
	"github.com/trivago/gollum/shared"
	"sync"
	"testing"
	"time"
)

type mockProducer struct {
	ProducerBase
}

func (prod *mockProducer) Produce(workers *sync.WaitGroup) {
	// does something.
}

func TestProducerConfigure(t *testing.T) {
	expect := shared.NewExpect(t)

	mockProducer := mockProducer{}

	shared.TypeRegistry.Register(mockPlugin{})
	shared.TypeRegistry.Register(mockFormatter{})
	shared.TypeRegistry.Register(mockFilter{})
	mockConf := NewPluginConfig("core.mockPlugin")
	mockConf.ID = "testPluginConf"
	mockConf.Stream = []string{"testBoundStream"}

	err := mockProducer.Configure(mockConf)
	expect.NotNil(err)
	mockConf.Settings["Formatter"] = "core.mockFormatter"

	err = mockProducer.Configure(mockConf)
	expect.NotNil(err)
	mockConf.Settings["Filter"] = "core.mockFilter"

	err = mockProducer.Configure(mockConf)
	expect.Nil(err)
}

func TestProducerState(t *testing.T) {
	expect := shared.NewExpect(t)

	mockProducer := mockProducer{}
	mockProducer.runState = new(PluginRunState)
	mockProducer.setState(PluginStateActive)
	expect.Equal(PluginStateActive, mockProducer.GetState())
	expect.True(mockProducer.IsActive())

	mockProducer.setState(PluginStateWaiting)
	expect.True(mockProducer.IsBlocked())

	mockProducer.setState(PluginStateStopping)
	expect.True(mockProducer.IsStopping())

	expect.True(mockProducer.IsActiveOrStopping())
}

func TestProducerCallback(t *testing.T) {
	expect := shared.NewExpect(t)

	mockProducer := mockProducer{}
	rollBackCalled := false

	rollCallBack := func() {
		rollBackCalled = true
	}

	mockProducer.SetRollCallback(rollCallBack)
	mockProducer.onRoll()
	expect.True(rollBackCalled)

	stopCallBackCalled := false
	stopCallBack := func() {
		stopCallBackCalled = true
	}
	mockProducer.SetStopCallback(stopCallBack)
	mockProducer.onStop()
	expect.True(stopCallBackCalled)
}

func TestProducerWaitgroup(t *testing.T) {
	// TODO: Well, complete this obviously

}

func TestProducerDependency(t *testing.T) {
	expect := shared.NewExpect(t)
	mockP := mockProducer{}

	secondMockP := mockProducer{}
	thirdMockP := mockProducer{}

	// give dropStreamId to differentiate secondMockP and thirdMockP
	secondMockP.dropStreamID = 1
	thirdMockP.dropStreamID = 2

	secondMockP.AddDependency(&thirdMockP)
	mockP.dependencies = []Producer{}
	mockP.AddDependency(&secondMockP)

	//add secondMockP again. Shouldn't add it in its dependency list
	mockP.AddDependency(&secondMockP)
	expect.Equal(1, len(mockP.dependencies))

	expect.True(mockP.DependsOn(&secondMockP))
	expect.True(mockP.DependsOn(&thirdMockP))
}

func TestProducerEnqueue(t *testing.T) {
	// Fuck this. distribute for drop route not called! Dont know why :(
	expect := shared.NewExpect(t)
	mockP := mockProducer{
		ProducerBase{
			messages:     make(chan Message),
			control:      make(chan PluginControl),
			streams:      []MessageStreamID{},
			dropStreamID: 2,
			runState:     new(PluginRunState),
			timeout:      500 * time.Millisecond,
			filter:       &mockFilter{},
			format:       &mockFormatter{},
		},
	}
	mockDistribute := func(msg Message) {
		expect.Equal("ProdEanqueueTest", msg.String())
	}
	mockDropStream := getMockStream()
	StreamRegistry.Register(&mockDropStream, 2)
	mockDropStream.distribute = mockDistribute

	msg := Message{
		Data:     []byte("ProdEnqueueTest"),
		StreamID: 1,
	}
	enqTimeout := time.Second
	mockP.setState(PluginStateStopping)
	// cause panic and check if message is dropped
	mockP.Enqueue(msg, &enqTimeout)

	mockP.setState(PluginStateActive)
	mockP.Enqueue(msg, &enqTimeout)

	mockStream := getMockStream()
	mockStream.distribute = mockDistribute
	StreamRegistry.Register(&mockStream, 1)

	go func() {
		mockP.Enqueue(msg, &enqTimeout)
	}()
	//give time for message to enqueue in the channel
	time.Sleep(200 * time.Millisecond)

	ret := <-mockP.messages
	expect.Equal("ProdEnqueueTest", ret.String())

}

func TestProducerCloseMessageChannel(t *testing.T) {
	expect := shared.NewExpect(t)
	mockP := mockProducer{
		ProducerBase{
			messages:        make(chan Message, 10),
			control:         make(chan PluginControl),
			streams:         []MessageStreamID{},
			dropStreamID:    2,
			runState:        new(PluginRunState),
			timeout:         500 * time.Millisecond,
			filter:          &mockFilter{},
			format:          &mockFormatter{},
			shutdownTimeout: 10 * time.Millisecond,
		},
	}

	mockP.setState(PluginStateActive)

	handleMessage := func(msg Message) {
		expect.Equal("closeMessageChannel", msg.String())
	}

	msgToSend := Message{
		Data:     []byte("closeMessageChannel"),
		StreamID: 1,
	}
	mockP.messages <- msgToSend

	mockP.CloseMessageChannel(handleMessage)

}
<|endoftext|>


<commit_before>package router

import (
	"errors"

	pb_broker "github.com/TheThingsNetwork/ttn/api/broker"
	pb "github.com/TheThingsNetwork/ttn/api/router"
	"github.com/TheThingsNetwork/ttn/core/types"
	"github.com/apex/log"
	"github.com/brocaar/lorawan"
)

func (r *router) HandleUplink(gatewayEUI types.GatewayEUI, uplink *pb.UplinkMessage) error {
	ctx := r.Ctx.WithField("GatewayEUI", gatewayEUI)
	var err error
	defer func() {
		if err != nil {
			ctx.WithError(err).Warn("Could not handle uplink")
		}
	}()

	// LoRaWAN: Unmarshal
	var phyPayload lorawan.PHYPayload
	err = phyPayload.UnmarshalBinary(uplink.Payload)
	if err != nil {
		return err
	}

	if phyPayload.MHDR.MType == lorawan.JoinRequest {
		joinRequestPayload, ok := phyPayload.MACPayload.(*lorawan.JoinRequestPayload)
		if !ok {
			return errors.New("Join Request message does not contain a join payload.")
		}
		devEUI := types.DevEUI(joinRequestPayload.DevEUI)
		appEUI := types.AppEUI(joinRequestPayload.AppEUI)
		ctx.WithFields(log.Fields{
			"DevEUI": devEUI,
			"AppEUI": appEUI,
		}).Debug("Handle Uplink as Activation")
		_, err := r.HandleActivation(gatewayEUI, &pb.DeviceActivationRequest{
			Payload:          uplink.Payload,
			DevEui:           &devEUI,
			AppEui:           &appEUI,
			ProtocolMetadata: uplink.ProtocolMetadata,
			GatewayMetadata:  uplink.GatewayMetadata,
		})
		return err
	}

	macPayload, ok := phyPayload.MACPayload.(*lorawan.MACPayload)
	if !ok {
		return errors.New("Uplink message does not contain a MAC payload.")
	}
	devAddr := types.DevAddr(macPayload.FHDR.DevAddr)

	ctx = ctx.WithField("DevAddr", devAddr)

	gateway := r.getGateway(gatewayEUI)
	gateway.Schedule.Sync(uplink.GatewayMetadata.Timestamp)
	gateway.Utilization.AddRx(uplink)

	downlinkOptions := r.buildDownlinkOptions(uplink, false, gateway)

	// Find Broker
	brokers, err := r.brokerDiscovery.Discover(devAddr)
	if err != nil {
		return err
	}

	ctx = ctx.WithField("NumBrokers", len(brokers))
	ctx.Debug("Forward Uplink")

	// Forward to all brokers
	for _, broker := range brokers {
		broker, err := r.getBroker(broker)
		if err != nil {
			continue
		}
		broker.association.Send(&pb_broker.UplinkMessage{
			Payload:          uplink.Payload,
			ProtocolMetadata: uplink.ProtocolMetadata,
			GatewayMetadata:  uplink.GatewayMetadata,
			DownlinkOptions:  downlinkOptions,
		})
	}

	return nil
}
<commit_msg>Log the number of downlink options<commit_after>package router

import (
	"errors"

	pb_broker "github.com/TheThingsNetwork/ttn/api/broker"
	pb "github.com/TheThingsNetwork/ttn/api/router"
	"github.com/TheThingsNetwork/ttn/core/types"
	"github.com/apex/log"
	"github.com/brocaar/lorawan"
)

func (r *router) HandleUplink(gatewayEUI types.GatewayEUI, uplink *pb.UplinkMessage) error {
	ctx := r.Ctx.WithField("GatewayEUI", gatewayEUI)
	var err error
	defer func() {
		if err != nil {
			ctx.WithError(err).Warn("Could not handle uplink")
		}
	}()

	// LoRaWAN: Unmarshal
	var phyPayload lorawan.PHYPayload
	err = phyPayload.UnmarshalBinary(uplink.Payload)
	if err != nil {
		return err
	}

	if phyPayload.MHDR.MType == lorawan.JoinRequest {
		joinRequestPayload, ok := phyPayload.MACPayload.(*lorawan.JoinRequestPayload)
		if !ok {
			return errors.New("Join Request message does not contain a join payload.")
		}
		devEUI := types.DevEUI(joinRequestPayload.DevEUI)
		appEUI := types.AppEUI(joinRequestPayload.AppEUI)
		ctx.WithFields(log.Fields{
			"DevEUI": devEUI,
			"AppEUI": appEUI,
		}).Debug("Handle Uplink as Activation")
		_, err := r.HandleActivation(gatewayEUI, &pb.DeviceActivationRequest{
			Payload:          uplink.Payload,
			DevEui:           &devEUI,
			AppEui:           &appEUI,
			ProtocolMetadata: uplink.ProtocolMetadata,
			GatewayMetadata:  uplink.GatewayMetadata,
		})
		return err
	}

	macPayload, ok := phyPayload.MACPayload.(*lorawan.MACPayload)
	if !ok {
		return errors.New("Uplink message does not contain a MAC payload.")
	}
	devAddr := types.DevAddr(macPayload.FHDR.DevAddr)

	ctx = ctx.WithField("DevAddr", devAddr)

	gateway := r.getGateway(gatewayEUI)
	gateway.Schedule.Sync(uplink.GatewayMetadata.Timestamp)
	gateway.Utilization.AddRx(uplink)

	downlinkOptions := r.buildDownlinkOptions(uplink, false, gateway)

	ctx = ctx.WithField("DownlinkOptions", len(downlinkOptions))

	// Find Broker
	brokers, err := r.brokerDiscovery.Discover(devAddr)
	if err != nil {
		return err
	}

	ctx = ctx.WithField("NumBrokers", len(brokers))
	ctx.Debug("Forward Uplink")

	// Forward to all brokers
	for _, broker := range brokers {
		broker, err := r.getBroker(broker)
		if err != nil {
			continue
		}
		broker.association.Send(&pb_broker.UplinkMessage{
			Payload:          uplink.Payload,
			ProtocolMetadata: uplink.ProtocolMetadata,
			GatewayMetadata:  uplink.GatewayMetadata,
			DownlinkOptions:  downlinkOptions,
		})
	}

	return nil
}
<|endoftext|>


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

package main

import (
	"fmt"
	"go/token"
	"reflect"
	"strconv"
	"strings"

	"golang.org/x/tools/go/analysis"
)

// imageImportCheckAnalyzer is an analyzer to check whether unexpected `image/*` packages are imported.
// Importing `image/gif`, `image/jpeg`, and `image/png` registers their recorders at `init` functions, so
// have affect the result of `image.Decode`. Ebitengine should not have such side-effects.
var imageImportCheckAnalyzer = &analysis.Analyzer{
	Name:       "imageimportcheck",
	Doc:        "check importing image/gif, image/jpeg, and image/png packages",
	Run:        runImageImportCheck,
	ResultType: reflect.TypeOf(imageImportCheckResult{}),
}

type imageImportCheckResult struct {
	Errors []imageImportCheckError
}

type imageImportCheckError struct {
	Pos    token.Pos
	Import string
}

func runImageImportCheck(pass *analysis.Pass) (interface{}, error) {
	pkgPath := pass.Pkg.Path()
	if strings.HasPrefix(pkgPath, "github.com/hajimehoshi/ebiten/v2/example") {
		return nil, nil
	}
	if strings.HasSuffix(pkgPath, "_test") {
		return nil, nil
	}

	// TODO: Remove this exception after v3 is released (#2336).
	if strings.HasPrefix(pkgPath, "github.com/hajimehoshi/ebiten/v2/ebitenutil") {
		return nil, nil
	}

	var errs []imageImportCheckError
	for _, f := range pass.Files {
		for _, i := range f.Imports {
			path, err := strconv.Unquote(i.Path.Value)
			if err != nil {
				return nil, err
			}
			if path == "image/gif" || path == "image/jpeg" || path == "image/png" {
				err := imageImportCheckError{
					Pos:    pass.Fset.File(f.Pos()).Pos(int(i.Pos())),
					Import: path,
				}
				errs = append(errs, err)
				pass.Report(analysis.Diagnostic{
					Pos:     err.Pos,
					Message: fmt.Sprintf("unexpected import %q", err.Import),
				})
			}
		}
	}
	return imageImportCheckResult{
		Errors: errs,
	}, nil
}
<commit_msg>.github/workflows/vettools: fix import paths<commit_after>// Copyright 2022 The Ebitengine Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"
	"go/token"
	"reflect"
	"strconv"
	"strings"

	"golang.org/x/tools/go/analysis"
)

// imageImportCheckAnalyzer is an analyzer to check whether unexpected `image/*` packages are imported.
// Importing `image/gif`, `image/jpeg`, and `image/png` registers their recorders at `init` functions, so
// have affect the result of `image.Decode`. Ebitengine should not have such side-effects.
var imageImportCheckAnalyzer = &analysis.Analyzer{
	Name:       "imageimportcheck",
	Doc:        "check importing image/gif, image/jpeg, and image/png packages",
	Run:        runImageImportCheck,
	ResultType: reflect.TypeOf(imageImportCheckResult{}),
}

type imageImportCheckResult struct {
	Errors []imageImportCheckError
}

type imageImportCheckError struct {
	Pos    token.Pos
	Import string
}

func runImageImportCheck(pass *analysis.Pass) (interface{}, error) {
	pkgPath := pass.Pkg.Path()
	if strings.HasPrefix(pkgPath, "github.com/hajimehoshi/ebiten/v2/examples/") {
		return nil, nil
	}
	if strings.HasSuffix(pkgPath, "_test") {
		return nil, nil
	}

	// TODO: Remove this exception after v3 is released (#2336).
	if pkgPath == "github.com/hajimehoshi/ebiten/v2/ebitenutil" {
		return nil, nil
	}

	var errs []imageImportCheckError
	for _, f := range pass.Files {
		for _, i := range f.Imports {
			path, err := strconv.Unquote(i.Path.Value)
			if err != nil {
				return nil, err
			}
			if path == "image/gif" || path == "image/jpeg" || path == "image/png" {
				err := imageImportCheckError{
					Pos:    pass.Fset.File(f.Pos()).Pos(int(i.Pos())),
					Import: path,
				}
				errs = append(errs, err)
				pass.Report(analysis.Diagnostic{
					Pos:     err.Pos,
					Message: fmt.Sprintf("unexpected import %q", err.Import),
				})
			}
		}
	}
	return imageImportCheckResult{
		Errors: errs,
	}, nil
}
<|endoftext|>


<commit_before>package awsdeployer

import (
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"

	"github.com/cloudfoundry-incubator/candiedyaml"
	"github.com/cloudfoundry/mega-ci/scripts/ci/deploy-aws-manifests/clients"
	"github.com/cloudfoundry/mega-ci/scripts/ci/deploy-aws-manifests/manifests"
)

type SubnetChecker interface {
	CheckSubnets(manifestFilename string) (bool, error)
}

type AWSDeployer struct {
	bosh          clients.BOSH
	subnetChecker SubnetChecker
	stdout        io.Writer
}

func NewAWSDeployer(bosh clients.BOSH, subnetChecker SubnetChecker, stdout io.Writer) AWSDeployer {
	return AWSDeployer{
		bosh:          bosh,
		subnetChecker: subnetChecker,
		stdout:        stdout,
	}
}

func (a AWSDeployer) Deploy(manifestsDirectory string) error {
	manifestsToDeploy, err := manifestsInDirectory(manifestsDirectory)
	if err != nil {
		return err
	}

	for _, manifestFilename := range manifestsToDeploy {
		fmt.Fprintf(a.stdout, "found manifest: %s\n", manifestFilename)
		fmt.Fprintln(a.stdout, "checking subnets...")
		hasSubnets, err := a.subnetChecker.CheckSubnets(manifestFilename)
		if err != nil {
			return err
		}

		if !hasSubnets {
			return errors.New("manifest subnets not found on AWS")
		}

		err = a.deployManifest(manifestFilename)
		if err != nil {
			return err
		}
	}

	return nil
}

func (a AWSDeployer) deployManifest(manifestFilename string) error {
	fmt.Fprintln(a.stdout, "fetching director uuid...")
	manifest, err := a.replaceUUID(manifestFilename)
	if err != nil {
		return err
	}

	buf, err := candiedyaml.Marshal(manifest)
	if err != nil {
		return err
	}

	fmt.Fprintln(a.stdout, "deploying...")
	err = a.bosh.Deploy(buf)
	if err != nil {
		return err
	}

	fmt.Fprintln(a.stdout, "deleting deployment...")
	err = a.deleteDeployment(manifest)
	if err != nil {
		return err
	}

	return nil
}

func manifestsInDirectory(directory string) ([]string, error) {
	var manifests []string
	err := filepath.Walk(directory, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if filepath.Ext(path) == ".yml" {
			manifests = append(manifests, path)
		}

		return nil
	})

	if err != nil {
		return []string{}, err
	}

	return manifests, nil
}

func (a AWSDeployer) replaceUUID(manifestFilename string) (map[string]interface{}, error) {
	directorUUID, err := a.bosh.UUID()
	if err != nil {
		return nil, err
	}

	manifest, err := manifests.ReadManifest(manifestFilename)
	if err != nil {
		return nil, err
	}

	manifest["director_uuid"] = directorUUID

	return manifest, nil
}

func (a AWSDeployer) deleteDeployment(manifest map[string]interface{}) error {
	deploymentName, ok := manifest["name"].(string)
	if !ok {
		return errors.New("deployment name missing from manifest")
	}

	err := a.bosh.DeleteDeployment(deploymentName)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Add message when subnet checks succeed<commit_after>package awsdeployer

import (
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"

	"github.com/cloudfoundry-incubator/candiedyaml"
	"github.com/cloudfoundry/mega-ci/scripts/ci/deploy-aws-manifests/clients"
	"github.com/cloudfoundry/mega-ci/scripts/ci/deploy-aws-manifests/manifests"
)

type SubnetChecker interface {
	CheckSubnets(manifestFilename string) (bool, error)
}

type AWSDeployer struct {
	bosh          clients.BOSH
	subnetChecker SubnetChecker
	stdout        io.Writer
}

func NewAWSDeployer(bosh clients.BOSH, subnetChecker SubnetChecker, stdout io.Writer) AWSDeployer {
	return AWSDeployer{
		bosh:          bosh,
		subnetChecker: subnetChecker,
		stdout:        stdout,
	}
}

func (a AWSDeployer) Deploy(manifestsDirectory string) error {
	manifestsToDeploy, err := manifestsInDirectory(manifestsDirectory)
	if err != nil {
		return err
	}

	for _, manifestFilename := range manifestsToDeploy {
		fmt.Fprintf(a.stdout, "found manifest: %s\n", manifestFilename)
		fmt.Fprintln(a.stdout, "checking subnets...")
		hasSubnets, err := a.subnetChecker.CheckSubnets(manifestFilename)
		if err != nil {
			return err
		}

		if !hasSubnets {
			return errors.New("manifest subnets not found on AWS")
		}

		fmt.Fprintln(a.stdout, "found all manifest subnets on AWS")

		err = a.deployManifest(manifestFilename)
		if err != nil {
			return err
		}
	}

	return nil
}

func (a AWSDeployer) deployManifest(manifestFilename string) error {
	fmt.Fprintln(a.stdout, "fetching director uuid...")
	manifest, err := a.replaceUUID(manifestFilename)
	if err != nil {
		return err
	}

	buf, err := candiedyaml.Marshal(manifest)
	if err != nil {
		return err
	}

	fmt.Fprintln(a.stdout, "deploying...")
	err = a.bosh.Deploy(buf)
	if err != nil {
		return err
	}

	fmt.Fprintln(a.stdout, "deleting deployment...")
	err = a.deleteDeployment(manifest)
	if err != nil {
		return err
	}

	return nil
}

func manifestsInDirectory(directory string) ([]string, error) {
	var manifests []string
	err := filepath.Walk(directory, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if filepath.Ext(path) == ".yml" {
			manifests = append(manifests, path)
		}

		return nil
	})

	if err != nil {
		return []string{}, err
	}

	return manifests, nil
}

func (a AWSDeployer) replaceUUID(manifestFilename string) (map[string]interface{}, error) {
	directorUUID, err := a.bosh.UUID()
	if err != nil {
		return nil, err
	}

	manifest, err := manifests.ReadManifest(manifestFilename)
	if err != nil {
		return nil, err
	}

	manifest["director_uuid"] = directorUUID

	return manifest, nil
}

func (a AWSDeployer) deleteDeployment(manifest map[string]interface{}) error {
	deploymentName, ok := manifest["name"].(string)
	if !ok {
		return errors.New("deployment name missing from manifest")
	}

	err := a.bosh.DeleteDeployment(deploymentName)
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


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

package ebiten

import (
	"fmt"
	"math"
	"sync"
	"sync/atomic"

	"github.com/hajimehoshi/ebiten/v2/internal/buffered"
	"github.com/hajimehoshi/ebiten/v2/internal/clock"
	"github.com/hajimehoshi/ebiten/v2/internal/debug"
	"github.com/hajimehoshi/ebiten/v2/internal/driver"
	"github.com/hajimehoshi/ebiten/v2/internal/hooks"
)

type uiContext struct {
	game      Game
	offscreen *Image
	screen    *Image

	updateCalled bool

	outsideSizeUpdated bool
	outsideWidth       float64
	outsideHeight      float64

	err atomic.Value

	m sync.Mutex
}

var theUIContext = &uiContext{}

func (c *uiContext) set(game Game) {
	c.m.Lock()
	defer c.m.Unlock()
	c.game = game
}

func (c *uiContext) setError(err error) {
	c.err.Store(err)
}

func (c *uiContext) Layout(outsideWidth, outsideHeight float64) {
	c.outsideSizeUpdated = true
	c.outsideWidth = outsideWidth
	c.outsideHeight = outsideHeight
}

func (c *uiContext) updateOffscreen() {
	sw, sh := c.game.Layout(int(c.outsideWidth), int(c.outsideHeight))
	if sw <= 0 || sh <= 0 {
		panic("ebiten: Layout must return positive numbers")
	}

	if c.offscreen != nil && !c.outsideSizeUpdated {
		if w, h := c.offscreen.Size(); w == sw && h == sh {
			return
		}
	}
	c.outsideSizeUpdated = false

	if c.screen != nil {
		c.screen.Dispose()
		c.screen = nil
	}

	if c.offscreen != nil {
		if w, h := c.offscreen.Size(); w != sw || h != sh {
			c.offscreen.Dispose()
			c.offscreen = nil
		}
	}
	if c.offscreen == nil {
		c.offscreen = NewImage(sw, sh)
		c.offscreen.mipmap.SetVolatile(IsScreenClearedEveryFrame())
	}

	// TODO: This is duplicated with mobile/ebitenmobileview/funcs.go. Refactor this.
	d := uiDriver().DeviceScaleFactor()
	c.screen = newScreenFramebufferImage(int(c.outsideWidth*d), int(c.outsideHeight*d))
}

func (c *uiContext) setScreenClearedEveryFrame(cleared bool) {
	c.m.Lock()
	defer c.m.Unlock()

	if c.offscreen != nil {
		c.offscreen.mipmap.SetVolatile(cleared)
	}
}

func (c *uiContext) setWindowResizable(resizable bool) {
	c.m.Lock()
	defer c.m.Unlock()

	if w := uiDriver().Window(); w != nil {
		w.SetResizable(resizable)
	}
}

func (c *uiContext) screenScale(deviceScaleFactor float64) float64 {
	if c.offscreen == nil {
		return 0
	}
	sw, sh := c.offscreen.Size()
	scaleX := c.outsideWidth / float64(sw) * deviceScaleFactor
	scaleY := c.outsideHeight / float64(sh) * deviceScaleFactor
	return math.Min(scaleX, scaleY)
}

func (c *uiContext) offsets(deviceScaleFactor float64) (float64, float64) {
	if c.offscreen == nil {
		return 0, 0
	}
	sw, sh := c.offscreen.Size()
	s := c.screenScale(deviceScaleFactor)
	width := float64(sw) * s
	height := float64(sh) * s
	x := (c.outsideWidth*deviceScaleFactor - width) / 2
	y := (c.outsideHeight*deviceScaleFactor - height) / 2
	return x, y
}

func (c *uiContext) Update() error {
	// TODO: If updateCount is 0 and vsync is disabled, swapping buffers can be skipped.

	if err, ok := c.err.Load().(error); ok && err != nil {
		return err
	}
	if err := buffered.BeginFrame(); err != nil {
		return err
	}
	if err := c.update(clock.Update(MaxTPS())); err != nil {
		return err
	}
	if err := buffered.EndFrame(); err != nil {
		return err
	}
	return nil
}

func (c *uiContext) ForceUpdate() error {
	if err, ok := c.err.Load().(error); ok && err != nil {
		return err
	}
	if err := buffered.BeginFrame(); err != nil {
		return err
	}
	if err := c.update(1); err != nil {
		return err
	}
	if err := buffered.EndFrame(); err != nil {
		return err
	}
	return nil
}

func (c *uiContext) update(updateCount int) error {
	c.updateOffscreen()

	// Ensure that Update is called once before Draw so that Update can be used for initialization.
	if !c.updateCalled && updateCount == 0 {
		updateCount = 1
		c.updateCalled = true
	}
	debug.Logf("--\nUpdate count per frame: %d\n", updateCount)

	for i := 0; i < updateCount; i++ {
		if err := hooks.RunBeforeUpdateHooks(); err != nil {
			return err
		}
		if err := c.game.Update(); err != nil {
			return err
		}
		uiDriver().ResetForFrame()
	}

	if updateCount > 0 {
		if IsScreenClearedEveryFrame() {
			c.offscreen.Clear()
		}
		c.game.Draw(c.offscreen)
	}

	// This clear is needed for fullscreen mode or some mobile platforms (#622).
	c.screen.Clear()

	op := &DrawImageOptions{}

	s := c.screenScale(uiDriver().DeviceScaleFactor())
	switch vd := uiDriver().Graphics().FramebufferYDirection(); vd {
	case driver.Upward:
		op.GeoM.Scale(s, -s)
		_, h := c.offscreen.Size()
		op.GeoM.Translate(0, float64(h)*s)
	case driver.Downward:
		op.GeoM.Scale(s, s)
	default:
		panic(fmt.Sprintf("ebiten: invalid v-direction: %d", vd))
	}

	op.GeoM.Translate(c.offsets(uiDriver().DeviceScaleFactor()))
	op.CompositeMode = CompositeModeCopy

	// filterScreen works with >=1 scale, but does not well with <1 scale.
	// Use regular FilterLinear instead so far (#669).
	if s >= 1 {
		op.Filter = filterScreen
	} else {
		op.Filter = FilterLinear
	}
	c.screen.DrawImage(c.offscreen, op)
	return nil
}

func (c *uiContext) AdjustPosition(x, y float64, deviceScaleFactor float64) (float64, float64) {
	ox, oy := c.offsets(deviceScaleFactor)
	s := c.screenScale(deviceScaleFactor)
	return (x*deviceScaleFactor - ox) / s, (y*deviceScaleFactor - oy) / s
}
<commit_msg>Revert "ebiten: Skip calling Draw when the update count is 0"<commit_after>// Copyright 2014 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ebiten

import (
	"fmt"
	"math"
	"sync"
	"sync/atomic"

	"github.com/hajimehoshi/ebiten/v2/internal/buffered"
	"github.com/hajimehoshi/ebiten/v2/internal/clock"
	"github.com/hajimehoshi/ebiten/v2/internal/debug"
	"github.com/hajimehoshi/ebiten/v2/internal/driver"
	"github.com/hajimehoshi/ebiten/v2/internal/hooks"
)

type uiContext struct {
	game      Game
	offscreen *Image
	screen    *Image

	updateCalled bool

	outsideSizeUpdated bool
	outsideWidth       float64
	outsideHeight      float64

	err atomic.Value

	m sync.Mutex
}

var theUIContext = &uiContext{}

func (c *uiContext) set(game Game) {
	c.m.Lock()
	defer c.m.Unlock()
	c.game = game
}

func (c *uiContext) setError(err error) {
	c.err.Store(err)
}

func (c *uiContext) Layout(outsideWidth, outsideHeight float64) {
	c.outsideSizeUpdated = true
	c.outsideWidth = outsideWidth
	c.outsideHeight = outsideHeight
}

func (c *uiContext) updateOffscreen() {
	sw, sh := c.game.Layout(int(c.outsideWidth), int(c.outsideHeight))
	if sw <= 0 || sh <= 0 {
		panic("ebiten: Layout must return positive numbers")
	}

	if c.offscreen != nil && !c.outsideSizeUpdated {
		if w, h := c.offscreen.Size(); w == sw && h == sh {
			return
		}
	}
	c.outsideSizeUpdated = false

	if c.screen != nil {
		c.screen.Dispose()
		c.screen = nil
	}

	if c.offscreen != nil {
		if w, h := c.offscreen.Size(); w != sw || h != sh {
			c.offscreen.Dispose()
			c.offscreen = nil
		}
	}
	if c.offscreen == nil {
		c.offscreen = NewImage(sw, sh)
		c.offscreen.mipmap.SetVolatile(IsScreenClearedEveryFrame())
	}

	// TODO: This is duplicated with mobile/ebitenmobileview/funcs.go. Refactor this.
	d := uiDriver().DeviceScaleFactor()
	c.screen = newScreenFramebufferImage(int(c.outsideWidth*d), int(c.outsideHeight*d))
}

func (c *uiContext) setScreenClearedEveryFrame(cleared bool) {
	c.m.Lock()
	defer c.m.Unlock()

	if c.offscreen != nil {
		c.offscreen.mipmap.SetVolatile(cleared)
	}
}

func (c *uiContext) setWindowResizable(resizable bool) {
	c.m.Lock()
	defer c.m.Unlock()

	if w := uiDriver().Window(); w != nil {
		w.SetResizable(resizable)
	}
}

func (c *uiContext) screenScale(deviceScaleFactor float64) float64 {
	if c.offscreen == nil {
		return 0
	}
	sw, sh := c.offscreen.Size()
	scaleX := c.outsideWidth / float64(sw) * deviceScaleFactor
	scaleY := c.outsideHeight / float64(sh) * deviceScaleFactor
	return math.Min(scaleX, scaleY)
}

func (c *uiContext) offsets(deviceScaleFactor float64) (float64, float64) {
	if c.offscreen == nil {
		return 0, 0
	}
	sw, sh := c.offscreen.Size()
	s := c.screenScale(deviceScaleFactor)
	width := float64(sw) * s
	height := float64(sh) * s
	x := (c.outsideWidth*deviceScaleFactor - width) / 2
	y := (c.outsideHeight*deviceScaleFactor - height) / 2
	return x, y
}

func (c *uiContext) Update() error {
	// TODO: If updateCount is 0 and vsync is disabled, swapping buffers can be skipped.

	if err, ok := c.err.Load().(error); ok && err != nil {
		return err
	}
	if err := buffered.BeginFrame(); err != nil {
		return err
	}
	if err := c.update(clock.Update(MaxTPS())); err != nil {
		return err
	}
	if err := buffered.EndFrame(); err != nil {
		return err
	}
	return nil
}

func (c *uiContext) ForceUpdate() error {
	if err, ok := c.err.Load().(error); ok && err != nil {
		return err
	}
	if err := buffered.BeginFrame(); err != nil {
		return err
	}
	if err := c.update(1); err != nil {
		return err
	}
	if err := buffered.EndFrame(); err != nil {
		return err
	}
	return nil
}

func (c *uiContext) update(updateCount int) error {
	c.updateOffscreen()

	// Ensure that Update is called once before Draw so that Update can be used for initialization.
	if !c.updateCalled && updateCount == 0 {
		updateCount = 1
		c.updateCalled = true
	}
	debug.Logf("--\nUpdate count per frame: %d\n", updateCount)

	for i := 0; i < updateCount; i++ {
		if err := hooks.RunBeforeUpdateHooks(); err != nil {
			return err
		}
		if err := c.game.Update(); err != nil {
			return err
		}
		uiDriver().ResetForFrame()
	}

	// Even though updateCount == 0, the offscreen is cleared and Draw is called.
	// Draw should not update the game state and then the screen should not be updated without Update, but
	// users might want to process something at Draw with the time intervals of FPS.
	if IsScreenClearedEveryFrame() {
		c.offscreen.Clear()
	}
	c.game.Draw(c.offscreen)

	// This clear is needed for fullscreen mode or some mobile platforms (#622).
	c.screen.Clear()

	op := &DrawImageOptions{}

	s := c.screenScale(uiDriver().DeviceScaleFactor())
	switch vd := uiDriver().Graphics().FramebufferYDirection(); vd {
	case driver.Upward:
		op.GeoM.Scale(s, -s)
		_, h := c.offscreen.Size()
		op.GeoM.Translate(0, float64(h)*s)
	case driver.Downward:
		op.GeoM.Scale(s, s)
	default:
		panic(fmt.Sprintf("ebiten: invalid v-direction: %d", vd))
	}

	op.GeoM.Translate(c.offsets(uiDriver().DeviceScaleFactor()))
	op.CompositeMode = CompositeModeCopy

	// filterScreen works with >=1 scale, but does not well with <1 scale.
	// Use regular FilterLinear instead so far (#669).
	if s >= 1 {
		op.Filter = filterScreen
	} else {
		op.Filter = FilterLinear
	}
	c.screen.DrawImage(c.offscreen, op)
	return nil
}

func (c *uiContext) AdjustPosition(x, y float64, deviceScaleFactor float64) (float64, float64) {
	ox, oy := c.offsets(deviceScaleFactor)
	s := c.screenScale(deviceScaleFactor)
	return (x*deviceScaleFactor - ox) / s, (y*deviceScaleFactor - oy) / s
}
<|endoftext|>