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

package integrations

import (
	"fmt"
	"net/http"
	"testing"
	"time"

	"code.gitea.io/gitea/models"
	"code.gitea.io/gitea/modules/setting"
	api "code.gitea.io/gitea/modules/structs"

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

func TestAPILFSLocksNotStarted(t *testing.T) {
	prepareTestEnv(t)
	setting.LFS.StartServer = false
	user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
	repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)

	req := NewRequestf(t, "GET", "/%s/%s.git/info/lfs/locks", user.Name, repo.Name)
	MakeRequest(t, req, http.StatusNotFound)
	req = NewRequestf(t, "POST", "/%s/%s.git/info/lfs/locks", user.Name, repo.Name)
	MakeRequest(t, req, http.StatusNotFound)
	req = NewRequestf(t, "GET", "/%s/%s.git/info/lfs/locks/verify", user.Name, repo.Name)
	MakeRequest(t, req, http.StatusNotFound)
	req = NewRequestf(t, "GET", "/%s/%s.git/info/lfs/locks/10/unlock", user.Name, repo.Name)
	MakeRequest(t, req, http.StatusNotFound)
}

func TestAPILFSLocksNotLogin(t *testing.T) {
	prepareTestEnv(t)
	setting.LFS.StartServer = true
	user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
	repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)

	req := NewRequestf(t, "GET", "/%s/%s.git/info/lfs/locks", user.Name, repo.Name)
	req.Header.Set("Accept", "application/vnd.git-lfs+json")
	resp := MakeRequest(t, req, http.StatusUnauthorized)
	var lfsLockError api.LFSLockError
	DecodeJSON(t, resp, &lfsLockError)
	assert.Equal(t, "Unauthorized", lfsLockError.Message)
}

func TestAPILFSLocksLogged(t *testing.T) {
	prepareTestEnv(t)
	setting.LFS.StartServer = true
	user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) //in org 3
	user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) //in org 3

	repo1 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
	repo3 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository) // own by org 3

	tests := []struct {
		user       *models.User
		repo       *models.Repository
		path       string
		httpResult int
		addTime    []int
	}{
		{user: user2, repo: repo1, path: "foo/bar.zip", httpResult: http.StatusCreated, addTime: []int{0}},
		{user: user2, repo: repo1, path: "path/test", httpResult: http.StatusCreated, addTime: []int{0}},
		{user: user2, repo: repo1, path: "path/test", httpResult: http.StatusConflict},
		{user: user2, repo: repo1, path: "Foo/BaR.zip", httpResult: http.StatusConflict},
		{user: user2, repo: repo1, path: "Foo/Test/../subFOlder/../Relative/../BaR.zip", httpResult: http.StatusConflict},
		{user: user4, repo: repo1, path: "FoO/BaR.zip", httpResult: http.StatusUnauthorized},
		{user: user4, repo: repo1, path: "path/test-user4", httpResult: http.StatusUnauthorized},
		{user: user2, repo: repo1, path: "patH/Test-user4", httpResult: http.StatusCreated, addTime: []int{0}},
		{user: user2, repo: repo1, path: "some/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/path", httpResult: http.StatusCreated, addTime: []int{0}},

		{user: user2, repo: repo3, path: "test/foo/bar.zip", httpResult: http.StatusCreated, addTime: []int{1, 2}},
		{user: user4, repo: repo3, path: "test/foo/bar.zip", httpResult: http.StatusConflict},
		{user: user4, repo: repo3, path: "test/foo/bar.bin", httpResult: http.StatusCreated, addTime: []int{1, 2}},
	}

	resultsTests := []struct {
		user        *models.User
		repo        *models.Repository
		totalCount  int
		oursCount   int
		theirsCount int
		locksOwners []*models.User
		locksTimes  []time.Time
	}{
		{user: user2, repo: repo1, totalCount: 4, oursCount: 4, theirsCount: 0, locksOwners: []*models.User{user2, user2, user2, user2}, locksTimes: []time.Time{}},
		{user: user2, repo: repo3, totalCount: 2, oursCount: 1, theirsCount: 1, locksOwners: []*models.User{user2, user4}, locksTimes: []time.Time{}},
		{user: user4, repo: repo3, totalCount: 2, oursCount: 1, theirsCount: 1, locksOwners: []*models.User{user2, user4}, locksTimes: []time.Time{}},
	}

	deleteTests := []struct {
		user   *models.User
		repo   *models.Repository
		lockID string
	}{}

	//create locks
	for _, test := range tests {
		session := loginUser(t, test.user.Name)
		req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/%s.git/info/lfs/locks", test.repo.FullName()), map[string]string{"path": test.path})
		req.Header.Set("Accept", "application/vnd.git-lfs+json")
		req.Header.Set("Content-Type", "application/vnd.git-lfs+json")
		resp := session.MakeRequest(t, req, test.httpResult)
		if len(test.addTime) > 0 {
			var lfsLock api.LFSLockResponse
			DecodeJSON(t, resp, &lfsLock)
			assert.EqualValues(t, lfsLock.Lock.LockedAt.Format(time.RFC3339), lfsLock.Lock.LockedAt.Format(time.RFC3339Nano)) //locked at should be rounded to second
			for _, id := range test.addTime {
				resultsTests[id].locksTimes = append(resultsTests[id].locksTimes, time.Now())
			}
		}
	}

	//check creation
	for _, test := range resultsTests {
		session := loginUser(t, test.user.Name)
		req := NewRequestf(t, "GET", "/%s.git/info/lfs/locks", test.repo.FullName())
		req.Header.Set("Accept", "application/vnd.git-lfs+json")
		resp := session.MakeRequest(t, req, http.StatusOK)
		var lfsLocks api.LFSLockList
		DecodeJSON(t, resp, &lfsLocks)
		assert.Len(t, lfsLocks.Locks, test.totalCount)
		for i, lock := range lfsLocks.Locks {
			assert.EqualValues(t, test.locksOwners[i].DisplayName(), lock.Owner.Name)
			assert.WithinDuration(t, test.locksTimes[i], lock.LockedAt, 3*time.Second)
			assert.EqualValues(t, lock.LockedAt.Format(time.RFC3339), lock.LockedAt.Format(time.RFC3339Nano)) //locked at should be rounded to second
		}

		req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/%s.git/info/lfs/locks/verify", test.repo.FullName()), map[string]string{})
		req.Header.Set("Accept", "application/vnd.git-lfs+json")
		req.Header.Set("Content-Type", "application/vnd.git-lfs+json")
		resp = session.MakeRequest(t, req, http.StatusOK)
		var lfsLocksVerify api.LFSLockListVerify
		DecodeJSON(t, resp, &lfsLocksVerify)
		assert.Len(t, lfsLocksVerify.Ours, test.oursCount)
		assert.Len(t, lfsLocksVerify.Theirs, test.theirsCount)
		for _, lock := range lfsLocksVerify.Ours {
			assert.EqualValues(t, test.user.DisplayName(), lock.Owner.Name)
			deleteTests = append(deleteTests, struct {
				user   *models.User
				repo   *models.Repository
				lockID string
			}{test.user, test.repo, lock.ID})
		}
		for _, lock := range lfsLocksVerify.Theirs {
			assert.NotEqual(t, test.user.DisplayName(), lock.Owner.Name)
		}
	}

	//remove all locks
	for _, test := range deleteTests {
		session := loginUser(t, test.user.Name)
		req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/%s.git/info/lfs/locks/%s/unlock", test.repo.FullName(), test.lockID), map[string]string{})
		req.Header.Set("Accept", "application/vnd.git-lfs+json")
		req.Header.Set("Content-Type", "application/vnd.git-lfs+json")
		resp := session.MakeRequest(t, req, http.StatusOK)
		var lfsLockRep api.LFSLockResponse
		DecodeJSON(t, resp, &lfsLockRep)
		assert.Equal(t, test.lockID, lfsLockRep.Lock.ID)
		assert.Equal(t, test.user.DisplayName(), lfsLockRep.Lock.Owner.Name)
	}

	// check that we don't have any lock
	for _, test := range resultsTests {
		session := loginUser(t, test.user.Name)
		req := NewRequestf(t, "GET", "/%s.git/info/lfs/locks", test.repo.FullName())
		req.Header.Set("Accept", "application/vnd.git-lfs+json")
		resp := session.MakeRequest(t, req, http.StatusOK)
		var lfsLocks api.LFSLockList
		DecodeJSON(t, resp, &lfsLocks)
		assert.Len(t, lfsLocks.Locks, 0)
	}
}
<commit_msg>Fix lfs locks (#8361)<commit_after>// Copyright 2017 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package integrations

import (
	"fmt"
	"net/http"
	"testing"
	"time"

	"code.gitea.io/gitea/models"
	"code.gitea.io/gitea/modules/setting"
	api "code.gitea.io/gitea/modules/structs"

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

func TestAPILFSLocksNotStarted(t *testing.T) {
	prepareTestEnv(t)
	setting.LFS.StartServer = false
	user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
	repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)

	req := NewRequestf(t, "GET", "/%s/%s.git/info/lfs/locks", user.Name, repo.Name)
	MakeRequest(t, req, http.StatusNotFound)
	req = NewRequestf(t, "POST", "/%s/%s.git/info/lfs/locks", user.Name, repo.Name)
	MakeRequest(t, req, http.StatusNotFound)
	req = NewRequestf(t, "GET", "/%s/%s.git/info/lfs/locks/verify", user.Name, repo.Name)
	MakeRequest(t, req, http.StatusNotFound)
	req = NewRequestf(t, "GET", "/%s/%s.git/info/lfs/locks/10/unlock", user.Name, repo.Name)
	MakeRequest(t, req, http.StatusNotFound)
}

func TestAPILFSLocksNotLogin(t *testing.T) {
	prepareTestEnv(t)
	setting.LFS.StartServer = true
	user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
	repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)

	req := NewRequestf(t, "GET", "/%s/%s.git/info/lfs/locks", user.Name, repo.Name)
	req.Header.Set("Accept", "application/vnd.git-lfs+json")
	resp := MakeRequest(t, req, http.StatusUnauthorized)
	var lfsLockError api.LFSLockError
	DecodeJSON(t, resp, &lfsLockError)
	assert.Equal(t, "Unauthorized", lfsLockError.Message)
}

func TestAPILFSLocksLogged(t *testing.T) {
	prepareTestEnv(t)
	setting.LFS.StartServer = true
	user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) //in org 3
	user4 := models.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) //in org 3

	repo1 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
	repo3 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository) // own by org 3

	tests := []struct {
		user       *models.User
		repo       *models.Repository
		path       string
		httpResult int
		addTime    []int
	}{
		{user: user2, repo: repo1, path: "foo/bar.zip", httpResult: http.StatusCreated, addTime: []int{0}},
		{user: user2, repo: repo1, path: "path/test", httpResult: http.StatusCreated, addTime: []int{0}},
		{user: user2, repo: repo1, path: "path/test", httpResult: http.StatusConflict},
		{user: user2, repo: repo1, path: "Foo/BaR.zip", httpResult: http.StatusConflict},
		{user: user2, repo: repo1, path: "Foo/Test/../subFOlder/../Relative/../BaR.zip", httpResult: http.StatusConflict},
		{user: user4, repo: repo1, path: "FoO/BaR.zip", httpResult: http.StatusUnauthorized},
		{user: user4, repo: repo1, path: "path/test-user4", httpResult: http.StatusUnauthorized},
		{user: user2, repo: repo1, path: "patH/Test-user4", httpResult: http.StatusCreated, addTime: []int{0}},
		{user: user2, repo: repo1, path: "some/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/long/path", httpResult: http.StatusCreated, addTime: []int{0}},

		{user: user2, repo: repo3, path: "test/foo/bar.zip", httpResult: http.StatusCreated, addTime: []int{1, 2}},
		{user: user4, repo: repo3, path: "test/foo/bar.zip", httpResult: http.StatusConflict},
		{user: user4, repo: repo3, path: "test/foo/bar.bin", httpResult: http.StatusCreated, addTime: []int{1, 2}},
	}

	resultsTests := []struct {
		user        *models.User
		repo        *models.Repository
		totalCount  int
		oursCount   int
		theirsCount int
		locksOwners []*models.User
		locksTimes  []time.Time
	}{
		{user: user2, repo: repo1, totalCount: 4, oursCount: 4, theirsCount: 0, locksOwners: []*models.User{user2, user2, user2, user2}, locksTimes: []time.Time{}},
		{user: user2, repo: repo3, totalCount: 2, oursCount: 1, theirsCount: 1, locksOwners: []*models.User{user2, user4}, locksTimes: []time.Time{}},
		{user: user4, repo: repo3, totalCount: 2, oursCount: 1, theirsCount: 1, locksOwners: []*models.User{user2, user4}, locksTimes: []time.Time{}},
	}

	deleteTests := []struct {
		user   *models.User
		repo   *models.Repository
		lockID string
	}{}

	//create locks
	for _, test := range tests {
		session := loginUser(t, test.user.Name)
		req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/%s.git/info/lfs/locks", test.repo.FullName()), map[string]string{"path": test.path})
		req.Header.Set("Accept", "application/vnd.git-lfs+json")
		req.Header.Set("Content-Type", "application/vnd.git-lfs+json")
		resp := session.MakeRequest(t, req, test.httpResult)
		if len(test.addTime) > 0 {
			var lfsLock api.LFSLockResponse
			DecodeJSON(t, resp, &lfsLock)
			assert.EqualValues(t, lfsLock.Lock.LockedAt.Format(time.RFC3339), lfsLock.Lock.LockedAt.Format(time.RFC3339Nano)) //locked at should be rounded to second
			for _, id := range test.addTime {
				resultsTests[id].locksTimes = append(resultsTests[id].locksTimes, time.Now())
			}
		}
	}

	//check creation
	for _, test := range resultsTests {
		session := loginUser(t, test.user.Name)
		req := NewRequestf(t, "GET", "/%s.git/info/lfs/locks", test.repo.FullName())
		req.Header.Set("Accept", "application/vnd.git-lfs+json")
		resp := session.MakeRequest(t, req, http.StatusOK)
		var lfsLocks api.LFSLockList
		DecodeJSON(t, resp, &lfsLocks)
		assert.Len(t, lfsLocks.Locks, test.totalCount)
		for i, lock := range lfsLocks.Locks {
			assert.EqualValues(t, test.locksOwners[i].DisplayName(), lock.Owner.Name)
			assert.WithinDuration(t, test.locksTimes[i], lock.LockedAt, 10*time.Second)
			assert.EqualValues(t, lock.LockedAt.Format(time.RFC3339), lock.LockedAt.Format(time.RFC3339Nano)) //locked at should be rounded to second
		}

		req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/%s.git/info/lfs/locks/verify", test.repo.FullName()), map[string]string{})
		req.Header.Set("Accept", "application/vnd.git-lfs+json")
		req.Header.Set("Content-Type", "application/vnd.git-lfs+json")
		resp = session.MakeRequest(t, req, http.StatusOK)
		var lfsLocksVerify api.LFSLockListVerify
		DecodeJSON(t, resp, &lfsLocksVerify)
		assert.Len(t, lfsLocksVerify.Ours, test.oursCount)
		assert.Len(t, lfsLocksVerify.Theirs, test.theirsCount)
		for _, lock := range lfsLocksVerify.Ours {
			assert.EqualValues(t, test.user.DisplayName(), lock.Owner.Name)
			deleteTests = append(deleteTests, struct {
				user   *models.User
				repo   *models.Repository
				lockID string
			}{test.user, test.repo, lock.ID})
		}
		for _, lock := range lfsLocksVerify.Theirs {
			assert.NotEqual(t, test.user.DisplayName(), lock.Owner.Name)
		}
	}

	//remove all locks
	for _, test := range deleteTests {
		session := loginUser(t, test.user.Name)
		req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/%s.git/info/lfs/locks/%s/unlock", test.repo.FullName(), test.lockID), map[string]string{})
		req.Header.Set("Accept", "application/vnd.git-lfs+json")
		req.Header.Set("Content-Type", "application/vnd.git-lfs+json")
		resp := session.MakeRequest(t, req, http.StatusOK)
		var lfsLockRep api.LFSLockResponse
		DecodeJSON(t, resp, &lfsLockRep)
		assert.Equal(t, test.lockID, lfsLockRep.Lock.ID)
		assert.Equal(t, test.user.DisplayName(), lfsLockRep.Lock.Owner.Name)
	}

	// check that we don't have any lock
	for _, test := range resultsTests {
		session := loginUser(t, test.user.Name)
		req := NewRequestf(t, "GET", "/%s.git/info/lfs/locks", test.repo.FullName())
		req.Header.Set("Accept", "application/vnd.git-lfs+json")
		resp := session.MakeRequest(t, req, http.StatusOK)
		var lfsLocks api.LFSLockList
		DecodeJSON(t, resp, &lfsLocks)
		assert.Len(t, lfsLocks.Locks, 0)
	}
}
<|endoftext|>


<commit_before>package interceptor

import (
	"log"
	"net"
	"strings"
	"sync"
	"github.com/datawire/teleproxy/internal/pkg/nat"
	rt "github.com/datawire/teleproxy/internal/pkg/route"
)

type Interceptor struct {
	port string
	tables map[string]rt.Table
	translator *nat.Translator
	domains sync.Map
	work chan func()
	done chan empty
}

type empty interface {}

func NewInterceptor(name string) *Interceptor {
	return &Interceptor{
		tables: make(map[string]rt.Table),
		translator: nat.NewTranslator(name),
		work: make(chan func()),
		done: make(chan empty),
	}
}

func (i *Interceptor) Start() {
	go func() {
		defer close(i.done)
		i.translator.Enable()
		defer i.translator.Disable()
		for {
			action, ok := <- i.work
			if ok {
				action()
			} else {
				return
			}
		}
	}()
}

func (i *Interceptor) Stop() {
	close(i.work)
	<- i.done
}

func (i *Interceptor) Resolve(name string) *rt.Route {
	value, ok := i.domains.Load(strings.ToLower(name))
	if ok {
		return value.(*rt.Route)
	} else {
		return nil
	}
}

func (i *Interceptor) Destination(conn *net.TCPConn) (string, error) {
	_, host, err := i.translator.GetOriginalDst(conn)
	return host, err
}

func (i *Interceptor) Update(table rt.Table) {
	i.work <- func() {
		old, ok := i.tables[table.Name]

		routes := make(map[string]rt.Route)
		if ok {
			for _, route := range old.Routes {
				routes[route.Name] = route
			}
		}

		for _, route := range table.Routes {
			existing, ok := routes[route.Name]
			if ok && route != existing {

				switch route.Proto {
				case "tcp":
					i.translator.ClearTCP(existing.Ip)
				case "udp":
					i.translator.ClearUDP(existing.Ip)
				default:
					panic("unrecognized protocol")
				}

			}

			if !ok || route != existing {

				if route.Target != "" {
					switch route.Proto {
					case "tcp":
						i.translator.ForwardTCP(route.Ip, route.Target)
					case "udp":
						i.translator.ForwardUDP(route.Ip, route.Target)
					default:
						panic("unrecognized protocol")
					}
				}

				if route.Name != "" {
					log.Printf("Storing %v->%v", route.Domain(), route)
					copy := route
					i.domains.Store(route.Domain(), &copy)
				}

			}

			if ok {
				// remove the route from our map of
				// old routes so we don't end up
				// deleting it below
				delete(routes, route.Name)
			}
		}

		for _, route := range routes {
			log.Printf("Clearing %v->%v", route.Domain(), route)
			i.domains.Delete(route.Domain())

			switch route.Proto {
			case "tcp":
				i.translator.ClearTCP(route.Ip)
			case "udp":
				i.translator.ClearUDP(route.Ip)
			default:
				panic("unrecognized protocol")
			}

		}

		i.tables[table.Name] = table
	}
}
<commit_msg>Add Render and Delete.<commit_after>package interceptor

import (
	"encoding/json"
	"log"
	"net"
	"strings"
	"sync"
	"github.com/datawire/teleproxy/internal/pkg/nat"
	rt "github.com/datawire/teleproxy/internal/pkg/route"
)

type Interceptor struct {
	port string
	tables map[string]rt.Table
	translator *nat.Translator
	domains sync.Map
	work chan func()
	done chan empty
}

type empty interface {}

func NewInterceptor(name string) *Interceptor {
	return &Interceptor{
		tables: make(map[string]rt.Table),
		translator: nat.NewTranslator(name),
		work: make(chan func()),
		done: make(chan empty),
	}
}

func (i *Interceptor) Start() {
	go func() {
		defer close(i.done)
		i.translator.Enable()
		defer i.translator.Disable()
		for {
			action, ok := <- i.work
			if ok {
				action()
			} else {
				return
			}
		}
	}()
}

func (i *Interceptor) Stop() {
	close(i.work)
	<- i.done
}

func (i *Interceptor) Resolve(name string) *rt.Route {
	value, ok := i.domains.Load(strings.ToLower(name))
	if ok {
		return value.(*rt.Route)
	} else {
		return nil
	}
}

func (i *Interceptor) Destination(conn *net.TCPConn) (string, error) {
	_, host, err := i.translator.GetOriginalDst(conn)
	return host, err
}

func (i *Interceptor) Render(table string) string {
	result := make(chan string, 1)
	i.work <- func() {
		var obj interface{}

		if table == "" {
			var tables []rt.Table
			for _, t := range i.tables {
				tables = append(tables, t)
			}
			obj = tables
		} else {
			var ok bool
			obj, ok = i.tables[table]
			if !ok {
				result <- ""
				return
			}
		}

		bytes, err := json.MarshalIndent(obj, "", "  ")
		if err != nil {
			result <- err.Error()
		} else {
			result <- string(bytes)
		}
	}
	return <- result
}

func (i *Interceptor) Delete(table string) bool {
	result := make(chan bool, 1)
	i.work <- func() {
		var names []string
		if table == "" {
			for name := range i.tables {
				names = append(names, name)
			}
		} else if _, ok := i.tables[table]; ok {
			names = []string{table}
		} else {
			result <- false
		}

		for _, name := range names {
			if name != "bootstrap" {
				i.update(rt.Table{Name: name})
			}
		}

		result <- true
	}
	return <- result
}

func (i *Interceptor) Update(table rt.Table) {
	i.work <- func() {
		i.update(table)
	}
}

func (i *Interceptor) update(table rt.Table) {
	old, ok := i.tables[table.Name]

	routes := make(map[string]rt.Route)
	if ok {
		for _, route := range old.Routes {
			routes[route.Name] = route
		}
	}

	for _, route := range table.Routes {
		existing, ok := routes[route.Name]
		if ok && route != existing {

			switch route.Proto {
			case "tcp":
				i.translator.ClearTCP(existing.Ip)
			case "udp":
				i.translator.ClearUDP(existing.Ip)
			default:
				log.Printf("unrecognized protocol: %v", route)
			}

		}

		if !ok || route != existing {

			if route.Target != "" {
				switch route.Proto {
				case "tcp":
					i.translator.ForwardTCP(route.Ip, route.Target)
				case "udp":
					i.translator.ForwardUDP(route.Ip, route.Target)
				default:
					log.Printf("unrecognized protocol: %v", route)
				}
			}

			if route.Name != "" {
				log.Printf("Storing %v->%v", route.Domain(), route)
				copy := route
				i.domains.Store(route.Domain(), &copy)
			}

		}

		if ok {
			// remove the route from our map of
			// old routes so we don't end up
			// deleting it below
			delete(routes, route.Name)
		}
	}

	for _, route := range routes {
		log.Printf("Clearing %v->%v", route.Domain(), route)
		i.domains.Delete(route.Domain())

		switch route.Proto {
		case "tcp":
			i.translator.ClearTCP(route.Ip)
		case "udp":
			i.translator.ClearUDP(route.Ip)
		default:
			log.Printf("unrecognized protocol: %v", route)
		}

	}

	if table.Routes == nil || len(table.Routes) == 0 {
		delete(i.tables, table.Name)
	} else {
		i.tables[table.Name] = table
	}
}
<|endoftext|>


<commit_before>package main

import (
    "fmt"
    "flag"
)

const MAXREADS = 3

type State int

type PCell struct {
    next_state chan State
    state chan State
    idx int
    generation int
}

func (self* PCell) birth(N int) {
    /*
        N - Number of nodes
     */
    for {
        // Block on read of next_state
        next_state := <-self.next_state

        // Write appropriate number of states to state channel
        //  - 3 if inner node, 2 if edge node
        start, end, _ := calcStates(self.idx, N)
        for i := start; i < end + 1; i++ {
            self.state <- next_state
        }
        self.generation += 1
    }
}

type F1Cell struct {
    out chan State
    clk chan string
    idx int
    generation int
}

func (self* F1Cell) birth(state State, ps []PCell, rules []State, N int) {
    /*
        state - Initial State
        ps - array of PCells
        N - Number of nodes
     */
    var state_lookup State
    for {
        <-self.clk // Block here

        // Output state
        self.out <- state

        // Write state to P1 cells
        ps[self.idx].next_state <- state

        // Read Pcells into new state
        start, end, edge := calcStates(self.idx, N)
        state_lookup = 0
        for i := start; i < end + 1; i ++ {
            state_lookup = state_lookup << 1
            state_lookup = state_lookup + <-(ps[i].state)
        }
        if edge {
            // Edges are always zero
            state_lookup = (state_lookup << 1)
        }
        state = rules[state_lookup]
        self.generation += 1
    }
}

func calcStates (idx int, N int) (start int, end int, edge bool) {
    if idx == 0 {
        return 0, 1, true
    } else if idx == N - 1 {
        return idx - 1, idx, true
    } else {
        return idx - 1, idx + 1, false
    }
}

func InitCells (N int) ([]F1Cell, []PCell) {
    f1cells := make([]F1Cell, N)
    pcells := make([]PCell, N)

    for i := 0; i < N; i ++ {
        f1cells[i].idx = i
        f1cells[i].out = make(chan State)
        f1cells[i].clk = make(chan string)

        start, end, _ := calcStates(i, N)
        pcells[i].idx = i
        pcells[i].next_state = make(chan State)
        num_reads := end - start + 1
        pcells[i].state = make(chan State, num_reads)

    }
    fmt.Printf("\n")
    return f1cells, pcells
}

func consumeOut(f1cells []F1Cell, max_gen int) {
    to_consume := 0
    f1cell_states := make([]State, len(f1cells))
    for {
        if to_consume <= 0 {
            to_consume = len(f1cells)
            for i := range f1cells {
                f1cells[i].clk <- "go!"
            }
            printStateArray(f1cell_states)
        }
        for i := range f1cells {
            select {
            case out := <-f1cells[i].out:
                f1cell_states[i] = out
                to_consume -= 1
            default:
            }
        }

        if f1cells[0].generation == max_gen {
            return
        }
    }
}

func printStateArray(sa []State) {
    fmt.Printf("State array: ")
    for i := range sa {
        fmt.Printf("%d ", sa[i])
    }
    fmt.Printf("\n")
}

func InitStateRules(rule int) []State {
    m := make([]State, 8)
    for i := range m {
        if rule % 2 == 1 {
            m[i] = 1
        } else {
            m[i] = 0
        }
        rule = rule >> 1
    }
    return m
}

func main () {
    /*
    N := 32
    rule := 30
    seed := 1 << 16
    */
    //var N int
    //var rule int
    //var seed State

    var N = flag.Int("N", 32, "Number of nodes")
    var rule = flag.Int("rule", 30, "Rule to be used for calculating state: i.e. rule 90 http://en.wikipedia.org/wiki/Rule_90")
    var seed = flag.Int("seed", 65536, "A seed value (powers of 2 are nice)")
    var max_gen = flag.Int("gen", 30, "Number of generations to iterate")
    flag.Parse()

    var init_state State

    /* Build structures */
    rules := InitStateRules(*rule)
    fmt.Println("Rule:", *rule)
    fmt.Println("Seed (decimal):", *seed)
    fmt.Println("Generations:", *max_gen)
    printStateArray(rules)
    f1cells, pcells := InitCells(*N)

    /* Initialize soft state and run cells */
    fmt.Printf("Set seed to: ")
    for i := range f1cells {
        if *seed % 2 == 1 {
            init_state = 1
        } else {
            init_state = 0
        }
        *seed = *seed >> 1
        fmt.Printf("%d ", init_state)
        go f1cells[i].birth(init_state, pcells, rules, *N)
    }
    fmt.Printf("\n\n\n")

    for i := range pcells {
        go pcells[i].birth(*N)
    }
    consumeOut(f1cells, *max_gen)
}
<commit_msg>:sparkles: Parallelism :sparkles:<commit_after>package main

import (
    "fmt"
    "flag"
    "runtime"
)

const MAXREADS = 3

type State int

type PCell struct {
    next_state chan State
    state chan State
    idx int
    generation int
}

func (self* PCell) birth(N int) {
    /*
        N - Number of nodes
     */
    for {
        // Block on read of next_state
        next_state := <-self.next_state

        // Write appropriate number of states to state channel
        //  - 3 if inner node, 2 if edge node
        start, end, _ := calcStates(self.idx, N)
        for i := start; i < end + 1; i++ {
            self.state <- next_state
        }
        self.generation += 1
    }
}

type F1Cell struct {
    out chan State
    clk chan string
    idx int
    generation int
}

func (self* F1Cell) birth(state State, ps []PCell, rules []State, N int) {
    /*
        state - Initial State
        ps - array of PCells
        N - Number of nodes
     */
    var state_lookup State
    for {
        <-self.clk // Block here

        // Output state
        self.out <- state

        // Write state to P1 cells
        ps[self.idx].next_state <- state

        // Read Pcells into new state
        start, end, edge := calcStates(self.idx, N)
        state_lookup = 0
        for i := start; i < end + 1; i ++ {
            state_lookup = state_lookup << 1
            state_lookup = state_lookup + <-(ps[i].state)
        }
        if edge {
            // Edges are always zero
            state_lookup = (state_lookup << 1)
        }
        state = rules[state_lookup]
        self.generation += 1
    }
}

func calcStates (idx int, N int) (start int, end int, edge bool) {
    if idx == 0 {
        return 0, 1, true
    } else if idx == N - 1 {
        return idx - 1, idx, true
    } else {
        return idx - 1, idx + 1, false
    }
}

func InitCells (N int) ([]F1Cell, []PCell) {
    f1cells := make([]F1Cell, N)
    pcells := make([]PCell, N)

    for i := 0; i < N; i ++ {
        f1cells[i].idx = i
        f1cells[i].out = make(chan State)
        f1cells[i].clk = make(chan string)

        start, end, _ := calcStates(i, N)
        pcells[i].idx = i
        pcells[i].next_state = make(chan State)
        num_reads := end - start + 1
        pcells[i].state = make(chan State, num_reads)

    }
    fmt.Printf("\n")
    return f1cells, pcells
}

func consumeOut(f1cells []F1Cell, max_gen int) {
    to_consume := 0
    f1cell_states := make([]State, len(f1cells))
    for {
        if to_consume <= 0 {
            to_consume = len(f1cells)
            for i := range f1cells {
                f1cells[i].clk <- "go!"
            }
            printStateArray(f1cell_states)
        }
        for i := range f1cells {
            select {
            case out := <-f1cells[i].out:
                f1cell_states[i] = out
                to_consume -= 1
            default:
            }
        }

        if f1cells[0].generation == max_gen {
            return
        }
    }
}

func printStateArray(sa []State) {
    fmt.Printf("State array: ")
    for i := range sa {
        fmt.Printf("%d ", sa[i])
    }
    fmt.Printf("\n")
}

func InitStateRules(rule int) []State {
    m := make([]State, 8)
    for i := range m {
        if rule % 2 == 1 {
            m[i] = 1
        } else {
            m[i] = 0
        }
        rule = rule >> 1
    }
    return m
}

func main () {
    /*
    N := 32
    rule := 30
    seed := 1 << 16
    */
    //var N int
    //var rule int
    //var seed State

    var N = flag.Int("N", 32, "Number of nodes")
    var rule = flag.Int("rule", 30, "Rule to be used for calculating state: i.e. rule 90 http://en.wikipedia.org/wiki/Rule_90")
    var seed = flag.Int("seed", 65536, "A seed value (powers of 2 are nice)")
    var max_gen = flag.Int("gen", 30, "Number of generations to iterate")
    var ncpu = flag.Int(
        "ncpu", runtime.NumCPU(), "GOMAXPROCS knob. Defaults to runtime.NumCPU() (the number of cores exposed to your native kernel")
    flag.Parse()

    var init_state State

    /* Set number of CPUs */
    runtime.GOMAXPROCS(*ncpu)

    /* Build structures */
    rules := InitStateRules(*rule)
    fmt.Println("Rule:", *rule)
    fmt.Println("Seed (decimal):", *seed)
    fmt.Println("Generations:", *max_gen)
    printStateArray(rules)
    f1cells, pcells := InitCells(*N)

    /* Initialize soft state and run cells */
    fmt.Printf("Set seed to: ")
    for i := range f1cells {
        if *seed % 2 == 1 {
            init_state = 1
        } else {
            init_state = 0
        }
        *seed = *seed >> 1
        fmt.Printf("%d ", init_state)
        go f1cells[i].birth(init_state, pcells, rules, *N)
    }
    fmt.Printf("\n\n\n")

    for i := range pcells {
        go pcells[i].birth(*N)
    }
    consumeOut(f1cells, *max_gen)
}
<|endoftext|>


<commit_before>package vault

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"regexp"
	"strconv"
	"time"

	"github.com/99designs/keyring"
	"github.com/aws/aws-sdk-go/service/sts"
)

var sessionKeyPattern = regexp.MustCompile(`^(.+?) session \((\d+)\)$`)

func IsSessionKey(s string) bool {
	return sessionKeyPattern.MatchString(s)
}

func parseKeyringSession(s string, conf *Config) (KeyringSession, error) {
	matches := sessionKeyPattern.FindStringSubmatch(s)
	if len(matches) == 0 {
		return KeyringSession{}, errors.New("Failed to parse session name")
	}
	profile, _ := conf.Profile(matches[1])
	return KeyringSession{Profile: profile, Name: s, SessionID: matches[2]}, nil
}

type KeyringSession struct {
	Profile
	Name      string
	SessionID string
}

func (ks KeyringSession) IsExpired() bool {
	// Older sessions were 20 characters long and opaque identifiers
	if len(ks.SessionID) == 20 {
		return true
	}
	// Now our session id's are timestamps
	tsInt, err := strconv.ParseInt(ks.SessionID, 10, 64)
	if err != nil {
		return true
	}
	log.Printf("Session %q expires in %v", ks.Name, time.Unix(tsInt, 0).Sub(time.Now()).String())
	return time.Now().After(time.Unix(tsInt, 0))
}

type KeyringSessions struct {
	Keyring keyring.Keyring
	Config  *Config
}

func NewKeyringSessions(k keyring.Keyring, cfg *Config) (*KeyringSessions, error) {
	return &KeyringSessions{
		Keyring: k,
		Config:  cfg,
	}, nil
}

func (s *KeyringSessions) Sessions() ([]KeyringSession, error) {
	log.Printf("Looking up all keys in keyring")
	keys, err := s.Keyring.Keys()
	if err != nil {
		return nil, err
	}

	var sessions []KeyringSession

	for _, k := range keys {
		if IsSessionKey(k) {
			log.Printf("%s is a session", k)
			ks, _ := parseKeyringSession(k, s.Config)
			if ks.IsExpired() {
				log.Printf("Session %s is expired", ks.Name)
				continue
			}

			sessions = append(sessions, ks)
		}
	}

	return sessions, nil
}

// Retrieve searches sessions for specific profile, expects the source profile to be provided
func (s *KeyringSessions) Retrieve(profile string) (creds sts.Credentials, err error) {
	log.Printf("Looking for sessions for %s", profile)
	sessions, err := s.Sessions()
	if err != nil {
		return creds, err
	}

	for _, session := range sessions {
		log.Printf("Comparing %s and %s", session.Profile.Name, profile)
		if session.Profile.Name == profile {
			log.Printf("Matched session %s", session.Name)

			item, err := s.Keyring.Get(session.Name)
			if err != nil {
				return creds, err
			}

			if err = json.Unmarshal(item.Data, &creds); err != nil {
				return creds, err
			}

			// double check the actual expiry time
			if creds.Expiration.Before(time.Now()) {
				log.Printf("Session %q is expired, deleting", session.Name)
				if err = s.Keyring.Remove(session.Profile.Name); err != nil {
					return creds, err
				}
			}

			// success!
			return creds, nil
		}
	}

	return creds, keyring.ErrKeyNotFound
}

// Store stores a sessions for a specific profile, expects the source profile to be provided
func (s *KeyringSessions) Store(profile string, session sts.Credentials, expires time.Time) error {
	bytes, err := json.Marshal(session)
	if err != nil {
		return err
	}

	key := fmt.Sprintf("%s session (%d)", profile, expires.Unix())
	log.Printf("Writing session for %s to keyring: %q", profile, key)

	return s.Keyring.Set(keyring.Item{
		Key:         key,
		Label:       "aws-vault session for " + profile,
		Description: "aws-vault session for " + profile,
		Data:        bytes,

		// specific Keychain settings
		KeychainNotTrustApplication: true,
	})
}

// Delete deletes any sessions for a specific profile, expects the source profile to be provided
func (s *KeyringSessions) Delete(profile string) (n int, err error) {
	log.Printf("Looking for sessions for %s", profile)
	sessions, err := s.Sessions()
	if err != nil {
		return n, err
	}

	for _, session := range sessions {
		if session.Profile.Name == profile {
			log.Printf("Session %q matches profile %q", session.Name, profile)
			if err = s.Keyring.Remove(session.Profile.Name); err != nil {
				return n, err
			}
			n++
		}
	}

	return
}
<commit_msg>Delete sessions, not credentials 😨<commit_after>package vault

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"regexp"
	"strconv"
	"time"

	"github.com/99designs/keyring"
	"github.com/aws/aws-sdk-go/service/sts"
)

var sessionKeyPattern = regexp.MustCompile(`^(.+?) session \((\d+)\)$`)

func IsSessionKey(s string) bool {
	return sessionKeyPattern.MatchString(s)
}

func parseKeyringSession(s string, conf *Config) (KeyringSession, error) {
	matches := sessionKeyPattern.FindStringSubmatch(s)
	if len(matches) == 0 {
		return KeyringSession{}, errors.New("Failed to parse session name")
	}
	profile, _ := conf.Profile(matches[1])
	return KeyringSession{Profile: profile, Name: s, SessionID: matches[2]}, nil
}

type KeyringSession struct {
	Profile
	Name      string
	SessionID string
}

func (ks KeyringSession) IsExpired() bool {
	// Older sessions were 20 characters long and opaque identifiers
	if len(ks.SessionID) == 20 {
		return true
	}
	// Now our session id's are timestamps
	tsInt, err := strconv.ParseInt(ks.SessionID, 10, 64)
	if err != nil {
		return true
	}
	log.Printf("Session %q expires in %v", ks.Name, time.Unix(tsInt, 0).Sub(time.Now()).String())
	return time.Now().After(time.Unix(tsInt, 0))
}

type KeyringSessions struct {
	Keyring keyring.Keyring
	Config  *Config
}

func NewKeyringSessions(k keyring.Keyring, cfg *Config) (*KeyringSessions, error) {
	return &KeyringSessions{
		Keyring: k,
		Config:  cfg,
	}, nil
}

func (s *KeyringSessions) Sessions() ([]KeyringSession, error) {
	log.Printf("Looking up all keys in keyring")
	keys, err := s.Keyring.Keys()
	if err != nil {
		return nil, err
	}

	var sessions []KeyringSession

	for _, k := range keys {
		if IsSessionKey(k) {
			log.Printf("%s is a session", k)
			ks, _ := parseKeyringSession(k, s.Config)
			if ks.IsExpired() {
				log.Printf("Session %s is expired, deleting", k)
				if err := s.Keyring.Remove(k); err != nil {
					return nil, err
				}
				continue
			}

			sessions = append(sessions, ks)
		}
	}

	return sessions, nil
}

// Retrieve searches sessions for specific profile, expects the source profile to be provided
func (s *KeyringSessions) Retrieve(profile string) (creds sts.Credentials, err error) {
	log.Printf("Looking for sessions for %s", profile)
	sessions, err := s.Sessions()
	if err != nil {
		return creds, err
	}

	for _, session := range sessions {
		log.Printf("Comparing %s and %s", session.Profile.Name, profile)
		if session.Profile.Name == profile {
			log.Printf("Matched session %s", session.Name)

			item, err := s.Keyring.Get(session.Name)
			if err != nil {
				return creds, err
			}

			if err = json.Unmarshal(item.Data, &creds); err != nil {
				return creds, err
			}

			// double check the actual expiry time
			if creds.Expiration.Before(time.Now()) {
				log.Printf("Session %q is expired, deleting", session.Name)
				if err = s.Keyring.Remove(session.Profile.Name); err != nil {
					return creds, err
				}
			}

			// success!
			return creds, nil
		}
	}

	return creds, keyring.ErrKeyNotFound
}

// Store stores a sessions for a specific profile, expects the source profile to be provided
func (s *KeyringSessions) Store(profile string, session sts.Credentials, expires time.Time) error {
	bytes, err := json.Marshal(session)
	if err != nil {
		return err
	}

	key := fmt.Sprintf("%s session (%d)", profile, expires.Unix())
	log.Printf("Writing session for %s to keyring: %q", profile, key)

	return s.Keyring.Set(keyring.Item{
		Key:         key,
		Label:       "aws-vault session for " + profile,
		Description: "aws-vault session for " + profile,
		Data:        bytes,

		// specific Keychain settings
		KeychainNotTrustApplication: true,
	})
}

// Delete deletes any sessions for a specific profile, expects the source profile to be provided
func (s *KeyringSessions) Delete(profile string) (n int, err error) {
	log.Printf("Looking for sessions for %s", profile)
	sessions, err := s.Sessions()
	if err != nil {
		return n, err
	}

	for _, session := range sessions {
		if session.Profile.Name == profile {
			log.Printf("Session %q matches profile %q", session.Name, profile)
			if err = s.Keyring.Remove(session.Name); err != nil {
				return n, err
			}
			n++
		}
	}

	return
}
<|endoftext|>


<commit_before>package torrent

import (
	"encoding/binary"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"os"
	"testing"
	"time"

	_ "github.com/anacrolix/envpprof"
	"github.com/anacrolix/utp"
	"github.com/bradfitz/iter"
	"gopkg.in/check.v1"

	"github.com/anacrolix/torrent/bencode"
	"github.com/anacrolix/torrent/data/blob"
	"github.com/anacrolix/torrent/internal/testutil"
	"github.com/anacrolix/torrent/util"
)

func init() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
}

var TestingConfig = Config{
	ListenAddr:           "localhost:0",
	NoDHT:                true,
	DisableTrackers:      true,
	NoDefaultBlocklist:   true,
	DisableMetainfoCache: true,
}

func TestClientDefault(t *testing.T) {
	cl, err := NewClient(&TestingConfig)
	if err != nil {
		t.Fatal(err)
	}
	cl.Close()
}

func TestAddDropTorrent(t *testing.T) {
	cl, err := NewClient(&TestingConfig)
	if err != nil {
		t.Fatal(err)
	}
	defer cl.Close()
	dir, mi := testutil.GreetingTestTorrent()
	defer os.RemoveAll(dir)
	tt, new, err := cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
	if err != nil {
		t.Fatal(err)
	}
	if !new {
		t.FailNow()
	}
	tt.Drop()
}

func TestAddTorrentNoSupportedTrackerSchemes(t *testing.T) {
	t.SkipNow()
}

func TestAddTorrentNoUsableURLs(t *testing.T) {
	t.SkipNow()
}

func TestAddPeersToUnknownTorrent(t *testing.T) {
	t.SkipNow()
}

func TestPieceHashSize(t *testing.T) {
	if pieceHash.Size() != 20 {
		t.FailNow()
	}
}

func TestTorrentInitialState(t *testing.T) {
	dir, mi := testutil.GreetingTestTorrent()
	defer os.RemoveAll(dir)
	tor, err := newTorrent(func() (ih InfoHash) {
		util.CopyExact(ih[:], mi.Info.Hash)
		return
	}())
	if err != nil {
		t.Fatal(err)
	}
	err = tor.setMetadata(&mi.Info.Info, mi.Info.Bytes, nil)
	if err != nil {
		t.Fatal(err)
	}
	if len(tor.Pieces) != 3 {
		t.Fatal("wrong number of pieces")
	}
	p := tor.Pieces[0]
	tor.pendAllChunkSpecs(0)
	if p.numPendingChunks() != 1 {
		t.Fatalf("should only be 1 chunk: %v", p.PendingChunkSpecs)
	}
	// TODO: Set chunkSize to 2, to test odd/even silliness.
	if chunkIndexSpec(0, tor.pieceLength(0)).Length != 5 {
		t.Fatal("pending chunk spec is incorrect")
	}
}

func TestUnmarshalPEXMsg(t *testing.T) {
	var m peerExchangeMessage
	if err := bencode.Unmarshal([]byte("d5:added12:\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0ce"), &m); err != nil {
		t.Fatal(err)
	}
	if len(m.Added) != 2 {
		t.FailNow()
	}
	if m.Added[0].Port != 0x506 {
		t.FailNow()
	}
}

func TestReducedDialTimeout(t *testing.T) {
	for _, _case := range []struct {
		Max             time.Duration
		HalfOpenLimit   int
		PendingPeers    int
		ExpectedReduced time.Duration
	}{
		{nominalDialTimeout, 40, 0, nominalDialTimeout},
		{nominalDialTimeout, 40, 1, nominalDialTimeout},
		{nominalDialTimeout, 40, 39, nominalDialTimeout},
		{nominalDialTimeout, 40, 40, nominalDialTimeout / 2},
		{nominalDialTimeout, 40, 80, nominalDialTimeout / 3},
		{nominalDialTimeout, 40, 4000, nominalDialTimeout / 101},
	} {
		reduced := reducedDialTimeout(_case.Max, _case.HalfOpenLimit, _case.PendingPeers)
		expected := _case.ExpectedReduced
		if expected < minDialTimeout {
			expected = minDialTimeout
		}
		if reduced != expected {
			t.Fatalf("expected %s, got %s", _case.ExpectedReduced, reduced)
		}
	}
}

func TestUTPRawConn(t *testing.T) {
	l, err := utp.NewSocket("")
	if err != nil {
		t.Fatal(err)
	}
	defer l.Close()
	go func() {
		for {
			_, err := l.Accept()
			if err != nil {
				break
			}
		}
	}()
	// Connect a UTP peer to see if the RawConn will still work.
	utpPeer, err := func() *utp.Socket {
		s, _ := utp.NewSocket("")
		return s
	}().Dial(fmt.Sprintf("localhost:%d", util.AddrPort(l.Addr())))
	if err != nil {
		t.Fatalf("error dialing utp listener: %s", err)
	}
	defer utpPeer.Close()
	peer, err := net.ListenPacket("udp", ":0")
	if err != nil {
		t.Fatal(err)
	}
	defer peer.Close()

	msgsReceived := 0
	const N = 5000 // How many messages to send.
	readerStopped := make(chan struct{})
	// The reader goroutine.
	go func() {
		defer close(readerStopped)
		b := make([]byte, 500)
		for i := 0; i < N; i++ {
			n, _, err := l.PacketConn().ReadFrom(b)
			if err != nil {
				t.Fatalf("error reading from raw conn: %s", err)
			}
			msgsReceived++
			var d int
			fmt.Sscan(string(b[:n]), &d)
			if d != i {
				log.Printf("got wrong number: expected %d, got %d", i, d)
			}
		}
	}()
	udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", util.AddrPort(l.Addr())))
	if err != nil {
		t.Fatal(err)
	}
	for i := 0; i < N; i++ {
		_, err := peer.WriteTo([]byte(fmt.Sprintf("%d", i)), udpAddr)
		if err != nil {
			t.Fatal(err)
		}
		time.Sleep(time.Microsecond)
	}
	select {
	case <-readerStopped:
	case <-time.After(time.Second):
		t.Fatal("reader timed out")
	}
	if msgsReceived != N {
		t.Fatalf("messages received: %d", msgsReceived)
	}
}

func TestTwoClientsArbitraryPorts(t *testing.T) {
	for i := 0; i < 2; i++ {
		cl, err := NewClient(&TestingConfig)
		if err != nil {
			t.Fatal(err)
		}
		defer cl.Close()
	}
}

func TestAddDropManyTorrents(t *testing.T) {
	cl, _ := NewClient(&TestingConfig)
	defer cl.Close()
	for i := range iter.N(1000) {
		var spec TorrentSpec
		binary.PutVarint(spec.InfoHash[:], int64(i))
		tt, new, err := cl.AddTorrentSpec(&spec)
		if err != nil {
			t.Error(err)
		}
		if !new {
			t.FailNow()
		}
		defer tt.Drop()
	}
}

func TestClientTransfer(t *testing.T) {
	greetingTempDir, mi := testutil.GreetingTestTorrent()
	defer os.RemoveAll(greetingTempDir)
	cfg := TestingConfig
	cfg.DataDir = greetingTempDir
	seeder, err := NewClient(&cfg)
	if err != nil {
		t.Fatal(err)
	}
	defer seeder.Close()
	seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
	leecherDataDir, err := ioutil.TempDir("", "")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(leecherDataDir)
	// cfg.TorrentDataOpener = func(info *metainfo.Info) (data.Data, error) {
	// 	return blob.TorrentData(info, leecherDataDir), nil
	// }
	cfg.TorrentDataOpener = blob.NewStore(leecherDataDir).OpenTorrent
	leecher, _ := NewClient(&cfg)
	defer leecher.Close()
	leecherGreeting, _, _ := leecher.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
	leecherGreeting.AddPeers([]Peer{
		Peer{
			IP:   util.AddrIP(seeder.ListenAddr()),
			Port: util.AddrPort(seeder.ListenAddr()),
		},
	})
	r := leecherGreeting.NewReader()
	defer r.Close()
	_greeting, err := ioutil.ReadAll(r)
	if err != nil {
		t.Fatalf("%q %s", string(_greeting), err)
	}
	greeting := string(_greeting)
	if greeting != testutil.GreetingFileContents {
		t.Fatal(":(")
	}
}

func TestReadaheadPieces(t *testing.T) {
	for _, case_ := range []struct {
		readaheadBytes, pieceLength int64
		readaheadPieces             int
	}{
		{5 * 1024 * 1024, 256 * 1024, 19},
		{5 * 1024 * 1024, 5 * 1024 * 1024, 0},
		{5*1024*1024 - 1, 5 * 1024 * 1024, 0},
		{5 * 1024 * 1024, 5*1024*1024 - 1, 1},
		{0, 5 * 1024 * 1024, -1},
		{5 * 1024 * 1024, 1048576, 4},
	} {
		if readaheadPieces(case_.readaheadBytes, case_.pieceLength) != case_.readaheadPieces {
			t.Fatalf("case failed: %v", case_)
		}
	}
}

func (suite) TestMergingTrackersByAddingSpecs(c *check.C) {
	cl, _ := NewClient(&TestingConfig)
	defer cl.Close()
	spec := TorrentSpec{}
	T, new, _ := cl.AddTorrentSpec(&spec)
	if !new {
		c.FailNow()
	}
	spec.Trackers = [][]string{{"http://a"}, {"udp://b"}}
	_, new, _ = cl.AddTorrentSpec(&spec)
	if new {
		c.FailNow()
	}
	c.Assert(T.Trackers[0][0].URL(), check.Equals, "http://a")
	c.Assert(T.Trackers[1][0].URL(), check.Equals, "udp://b")
}
<commit_msg>Reduce number of messages sent in one test, the OS nondeterministically fails the test sometimes otherwise<commit_after>package torrent

import (
	"encoding/binary"
	"fmt"
	"io/ioutil"
	"log"
	"net"
	"os"
	"testing"
	"time"

	_ "github.com/anacrolix/envpprof"
	"github.com/anacrolix/utp"
	"github.com/bradfitz/iter"
	"gopkg.in/check.v1"

	"github.com/anacrolix/torrent/bencode"
	"github.com/anacrolix/torrent/data/blob"
	"github.com/anacrolix/torrent/internal/testutil"
	"github.com/anacrolix/torrent/util"
)

func init() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
}

var TestingConfig = Config{
	ListenAddr:           "localhost:0",
	NoDHT:                true,
	DisableTrackers:      true,
	NoDefaultBlocklist:   true,
	DisableMetainfoCache: true,
}

func TestClientDefault(t *testing.T) {
	cl, err := NewClient(&TestingConfig)
	if err != nil {
		t.Fatal(err)
	}
	cl.Close()
}

func TestAddDropTorrent(t *testing.T) {
	cl, err := NewClient(&TestingConfig)
	if err != nil {
		t.Fatal(err)
	}
	defer cl.Close()
	dir, mi := testutil.GreetingTestTorrent()
	defer os.RemoveAll(dir)
	tt, new, err := cl.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
	if err != nil {
		t.Fatal(err)
	}
	if !new {
		t.FailNow()
	}
	tt.Drop()
}

func TestAddTorrentNoSupportedTrackerSchemes(t *testing.T) {
	t.SkipNow()
}

func TestAddTorrentNoUsableURLs(t *testing.T) {
	t.SkipNow()
}

func TestAddPeersToUnknownTorrent(t *testing.T) {
	t.SkipNow()
}

func TestPieceHashSize(t *testing.T) {
	if pieceHash.Size() != 20 {
		t.FailNow()
	}
}

func TestTorrentInitialState(t *testing.T) {
	dir, mi := testutil.GreetingTestTorrent()
	defer os.RemoveAll(dir)
	tor, err := newTorrent(func() (ih InfoHash) {
		util.CopyExact(ih[:], mi.Info.Hash)
		return
	}())
	if err != nil {
		t.Fatal(err)
	}
	err = tor.setMetadata(&mi.Info.Info, mi.Info.Bytes, nil)
	if err != nil {
		t.Fatal(err)
	}
	if len(tor.Pieces) != 3 {
		t.Fatal("wrong number of pieces")
	}
	p := tor.Pieces[0]
	tor.pendAllChunkSpecs(0)
	if p.numPendingChunks() != 1 {
		t.Fatalf("should only be 1 chunk: %v", p.PendingChunkSpecs)
	}
	// TODO: Set chunkSize to 2, to test odd/even silliness.
	if chunkIndexSpec(0, tor.pieceLength(0)).Length != 5 {
		t.Fatal("pending chunk spec is incorrect")
	}
}

func TestUnmarshalPEXMsg(t *testing.T) {
	var m peerExchangeMessage
	if err := bencode.Unmarshal([]byte("d5:added12:\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0ce"), &m); err != nil {
		t.Fatal(err)
	}
	if len(m.Added) != 2 {
		t.FailNow()
	}
	if m.Added[0].Port != 0x506 {
		t.FailNow()
	}
}

func TestReducedDialTimeout(t *testing.T) {
	for _, _case := range []struct {
		Max             time.Duration
		HalfOpenLimit   int
		PendingPeers    int
		ExpectedReduced time.Duration
	}{
		{nominalDialTimeout, 40, 0, nominalDialTimeout},
		{nominalDialTimeout, 40, 1, nominalDialTimeout},
		{nominalDialTimeout, 40, 39, nominalDialTimeout},
		{nominalDialTimeout, 40, 40, nominalDialTimeout / 2},
		{nominalDialTimeout, 40, 80, nominalDialTimeout / 3},
		{nominalDialTimeout, 40, 4000, nominalDialTimeout / 101},
	} {
		reduced := reducedDialTimeout(_case.Max, _case.HalfOpenLimit, _case.PendingPeers)
		expected := _case.ExpectedReduced
		if expected < minDialTimeout {
			expected = minDialTimeout
		}
		if reduced != expected {
			t.Fatalf("expected %s, got %s", _case.ExpectedReduced, reduced)
		}
	}
}

func TestUTPRawConn(t *testing.T) {
	l, err := utp.NewSocket("")
	if err != nil {
		t.Fatal(err)
	}
	defer l.Close()
	go func() {
		for {
			_, err := l.Accept()
			if err != nil {
				break
			}
		}
	}()
	// Connect a UTP peer to see if the RawConn will still work.
	utpPeer, err := func() *utp.Socket {
		s, _ := utp.NewSocket("")
		return s
	}().Dial(fmt.Sprintf("localhost:%d", util.AddrPort(l.Addr())))
	if err != nil {
		t.Fatalf("error dialing utp listener: %s", err)
	}
	defer utpPeer.Close()
	peer, err := net.ListenPacket("udp", ":0")
	if err != nil {
		t.Fatal(err)
	}
	defer peer.Close()

	msgsReceived := 0
	// How many messages to send. I've set this to double the channel buffer
	// size in the raw packetConn.
	const N = 200
	readerStopped := make(chan struct{})
	// The reader goroutine.
	go func() {
		defer close(readerStopped)
		b := make([]byte, 500)
		for i := 0; i < N; i++ {
			n, _, err := l.PacketConn().ReadFrom(b)
			if err != nil {
				t.Fatalf("error reading from raw conn: %s", err)
			}
			msgsReceived++
			var d int
			fmt.Sscan(string(b[:n]), &d)
			if d != i {
				log.Printf("got wrong number: expected %d, got %d", i, d)
			}
		}
	}()
	udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", util.AddrPort(l.Addr())))
	if err != nil {
		t.Fatal(err)
	}
	for i := 0; i < N; i++ {
		_, err := peer.WriteTo([]byte(fmt.Sprintf("%d", i)), udpAddr)
		if err != nil {
			t.Fatal(err)
		}
		time.Sleep(time.Microsecond)
	}
	select {
	case <-readerStopped:
	case <-time.After(time.Second):
		t.Fatal("reader timed out")
	}
	if msgsReceived != N {
		t.Fatalf("messages received: %d", msgsReceived)
	}
}

func TestTwoClientsArbitraryPorts(t *testing.T) {
	for i := 0; i < 2; i++ {
		cl, err := NewClient(&TestingConfig)
		if err != nil {
			t.Fatal(err)
		}
		defer cl.Close()
	}
}

func TestAddDropManyTorrents(t *testing.T) {
	cl, _ := NewClient(&TestingConfig)
	defer cl.Close()
	for i := range iter.N(1000) {
		var spec TorrentSpec
		binary.PutVarint(spec.InfoHash[:], int64(i))
		tt, new, err := cl.AddTorrentSpec(&spec)
		if err != nil {
			t.Error(err)
		}
		if !new {
			t.FailNow()
		}
		defer tt.Drop()
	}
}

func TestClientTransfer(t *testing.T) {
	greetingTempDir, mi := testutil.GreetingTestTorrent()
	defer os.RemoveAll(greetingTempDir)
	cfg := TestingConfig
	cfg.DataDir = greetingTempDir
	seeder, err := NewClient(&cfg)
	if err != nil {
		t.Fatal(err)
	}
	defer seeder.Close()
	seeder.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
	leecherDataDir, err := ioutil.TempDir("", "")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(leecherDataDir)
	// cfg.TorrentDataOpener = func(info *metainfo.Info) (data.Data, error) {
	// 	return blob.TorrentData(info, leecherDataDir), nil
	// }
	cfg.TorrentDataOpener = blob.NewStore(leecherDataDir).OpenTorrent
	leecher, _ := NewClient(&cfg)
	defer leecher.Close()
	leecherGreeting, _, _ := leecher.AddTorrentSpec(TorrentSpecFromMetaInfo(mi))
	leecherGreeting.AddPeers([]Peer{
		Peer{
			IP:   util.AddrIP(seeder.ListenAddr()),
			Port: util.AddrPort(seeder.ListenAddr()),
		},
	})
	r := leecherGreeting.NewReader()
	defer r.Close()
	_greeting, err := ioutil.ReadAll(r)
	if err != nil {
		t.Fatalf("%q %s", string(_greeting), err)
	}
	greeting := string(_greeting)
	if greeting != testutil.GreetingFileContents {
		t.Fatal(":(")
	}
}

func TestReadaheadPieces(t *testing.T) {
	for _, case_ := range []struct {
		readaheadBytes, pieceLength int64
		readaheadPieces             int
	}{
		{5 * 1024 * 1024, 256 * 1024, 19},
		{5 * 1024 * 1024, 5 * 1024 * 1024, 0},
		{5*1024*1024 - 1, 5 * 1024 * 1024, 0},
		{5 * 1024 * 1024, 5*1024*1024 - 1, 1},
		{0, 5 * 1024 * 1024, -1},
		{5 * 1024 * 1024, 1048576, 4},
	} {
		if readaheadPieces(case_.readaheadBytes, case_.pieceLength) != case_.readaheadPieces {
			t.Fatalf("case failed: %v", case_)
		}
	}
}

func (suite) TestMergingTrackersByAddingSpecs(c *check.C) {
	cl, _ := NewClient(&TestingConfig)
	defer cl.Close()
	spec := TorrentSpec{}
	T, new, _ := cl.AddTorrentSpec(&spec)
	if !new {
		c.FailNow()
	}
	spec.Trackers = [][]string{{"http://a"}, {"udp://b"}}
	_, new, _ = cl.AddTorrentSpec(&spec)
	if new {
		c.FailNow()
	}
	c.Assert(T.Trackers[0][0].URL(), check.Equals, "http://a")
	c.Assert(T.Trackers[1][0].URL(), check.Equals, "udp://b")
}
<|endoftext|>


<commit_before>package stun

import (
	"errors"
	"io"
	"log"
	"testing"
	"time"
)

type TestAgent struct {
	f chan Handler
}

func (n *TestAgent) Close() error {
	close(n.f)
	return nil
}

func (TestAgent) Collect(time.Time) error { return nil }

func (TestAgent) Process(m *Message) error { return nil }

func (n *TestAgent) Start(id [TransactionIDSize]byte, deadline time.Time, f Handler) error {
	n.f <- f
	return nil
}

func (n *TestAgent) Stop([TransactionIDSize]byte) error {
	return nil
}

type noopConnection struct{}

func (noopConnection) Write(b []byte) (int, error) {
	return len(b), nil
}

func (noopConnection) Read(b []byte) (int, error) {
	time.Sleep(time.Millisecond)
	return 0, io.EOF
}

func (noopConnection) Close() error {
	return nil
}

func BenchmarkClient_Do(b *testing.B) {
	b.ReportAllocs()
	agent := &TestAgent{
		f: make(chan Handler, 1000),
	}
	client, err := NewClient(ClientOptions{
		Agent:      agent,
		Connection: noopConnection{},
	})
	if err != nil {
		log.Fatal(err)
	}
	defer client.Close()
	go func() {
		e := Event{
			Error:   nil,
			Message: nil,
		}
		for f := range agent.f {
			f.HandleEvent(e)
		}
	}()
	m := new(Message)
	m.Encode()
	noopF := func(event Event) {
		// pass
	}
	for i := 0; i < b.N; i++ {
		if err := client.Do(m, time.Time{}, noopF); err != nil {
			b.Fatal(err)
		}
	}
}

type testConnection struct {
	write   func([]byte) (int, error)
	b       []byte
	stopped bool
}

func (t *testConnection) Write(b []byte) (int, error) {
	return t.write(b)
}

func (t *testConnection) Close() error {
	if t.stopped {
		return errors.New("already stopped")
	}
	t.stopped = true
	return nil
}

func (t *testConnection) Read(b []byte) (int, error) {
	if t.stopped {
		return 0, io.EOF
	}
	return copy(b, t.b), nil
}

func TestClosedOrPanic(t *testing.T) {
	closedOrPanic(nil)
	closedOrPanic(ErrAgentClosed)
	func() {
		defer func() {
			r := recover()
			if r != io.EOF {
				t.Error(r)
			}
		}()
		closedOrPanic(io.EOF)
	}()
}

func TestClient_Do(t *testing.T) {
	response := MustBuild(TransactionID, BindingSuccess)
	response.Encode()
	conn := &testConnection{
		b: response.Raw,
		write: func(bytes []byte) (int, error) {
			return len(bytes), nil
		},
	}
	c, err := NewClient(ClientOptions{
		Connection: conn,
	})
	if err != nil {
		log.Fatal(err)
	}
	defer func() {
		if err := c.Close(); err != nil {
			t.Error(err)
		}
		if err := c.Close(); err == nil {
			t.Error("second close should fail")
		}
		if err := c.Do(MustBuild(TransactionID), time.Time{}, nil); err == nil {
			t.Error("Do after Close should fail")
		}
	}()
	m := MustBuild(
		NewTransactionIDSetter(response.TransactionID),
	)
	d := time.Now().Add(time.Second)
	if err := c.Do(m, d, func(event Event) {
		if event.Error != nil {
			t.Error(event.Error)
		}
	}); err != nil {
		t.Error(err)
	}
	m = MustBuild(TransactionID)
	if err := c.Do(m, d, nil); err != nil {
		t.Error(err)
	}
}

func TestCloseErr_Error(t *testing.T) {
	for id, c := range []struct {
		Err CloseErr
		Out string
	}{
		{CloseErr{}, "failed to close: <nil> (connection), <nil> (agent)"},
		{CloseErr{
			AgentErr: io.ErrUnexpectedEOF,
		}, "failed to close: <nil> (connection), unexpected EOF (agent)"},
		{CloseErr{
			ConnectionErr: io.ErrUnexpectedEOF,
		}, "failed to close: unexpected EOF (connection), <nil> (agent)"},
	} {
		if out := c.Err.Error(); out != c.Out {
			t.Errorf("[%d]: Error(%#v) %q (got) != %q (expected)",
				id, c.Err, out, c.Out,
			)
		}
	}
}

func TestStopErr_Error(t *testing.T) {
	for id, c := range []struct {
		Err StopErr
		Out string
	}{
		{StopErr{}, "error while stopping due to <nil>: <nil>"},
		{StopErr{
			Err: io.ErrUnexpectedEOF,
		}, "error while stopping due to <nil>: unexpected EOF"},
		{StopErr{
			Cause: io.ErrUnexpectedEOF,
		}, "error while stopping due to unexpected EOF: <nil>"},
	} {
		if out := c.Err.Error(); out != c.Out {
			t.Errorf("[%d]: Error(%#v) %q (got) != %q (expected)",
				id, c.Err, out, c.Out,
			)
		}
	}
}

type errorAgent struct {
	startErr error
	stopErr  error
}

func (errorAgent) Close() error { return nil }

func (errorAgent) Collect(time.Time) error { return nil }

func (errorAgent) Process(m *Message) error { return nil }

func (a errorAgent) Start(id [TransactionIDSize]byte, deadline time.Time, f Handler) error {
	return a.startErr
}

func (a errorAgent) Stop([TransactionIDSize]byte) error {
	return a.stopErr
}

func TestClientAgentError(t *testing.T) {
	response := MustBuild(TransactionID, BindingSuccess)
	response.Encode()
	conn := &testConnection{
		b: response.Raw,
		write: func(bytes []byte) (int, error) {
			return len(bytes), nil
		},
	}
	c, err := NewClient(ClientOptions{
		Agent: errorAgent{
			startErr: io.ErrUnexpectedEOF,
		},
		Connection: conn,
	})
	if err != nil {
		log.Fatal(err)
	}
	defer func() {
		if err := c.Close(); err != nil {
			t.Error(err)
		}
	}()
	m := MustBuild(NewTransactionIDSetter(response.TransactionID))
	if err := c.Do(m, time.Time{}, nil); err != nil {
		t.Error(err)
	}
	if err := c.Do(m, time.Time{}, func(event Event) {
		if event.Error == nil {
			t.Error("error expected")
		}
	}); err != io.ErrUnexpectedEOF {
		t.Error("error expected")
	}
}

func TestClientConnErr(t *testing.T) {
	conn := &testConnection{
		write: func(bytes []byte) (int, error) {
			return 0, io.ErrClosedPipe
		},
	}
	c, err := NewClient(ClientOptions{
		Connection: conn,
	})
	if err != nil {
		log.Fatal(err)
	}
	defer func() {
		if err := c.Close(); err != nil {
			t.Error(err)
		}
	}()
	m := MustBuild(TransactionID)
	if err := c.Do(m, time.Time{}, nil); err == nil {
		t.Error("error expected")
	}
	if err := c.Do(m, time.Time{}, noopHandler); err == nil {
		t.Error("error expected")
	}
}

func TestClientConnErrStopErr(t *testing.T) {
	conn := &testConnection{
		write: func(bytes []byte) (int, error) {
			return 0, io.ErrClosedPipe
		},
	}
	c, err := NewClient(ClientOptions{
		Connection: conn,
		Agent: errorAgent{
			stopErr: io.ErrUnexpectedEOF,
		},
	})
	if err != nil {
		log.Fatal(err)
	}
	defer func() {
		if err := c.Close(); err != nil {
			t.Error(err)
		}
	}()
	m := MustBuild(TransactionID)
	if err := c.Do(m, time.Time{}, noopHandler); err == nil {
		t.Error("error expected")
	}
}

func TestCallbackWaitHandler_setCallback(t *testing.T) {
	c := callbackWaitHandler{}
	defer func() {
		if err := recover(); err == nil {
			t.Error("should panic")
		}
	}()
	c.setCallback(nil)
}

func TestCallbackWaitHandler_HandleEvent(t *testing.T) {
	c := callbackWaitHandler{}
	defer func() {
		if err := recover(); err == nil {
			t.Error("should panic")
		}
	}()
	c.HandleEvent(Event{})
}

func TestNewClientNoConnection(t *testing.T) {
	c, err := NewClient(ClientOptions{
		Connection: nil,
	})
	if c != nil {
		t.Error("c should be nil")
	}
	if err != ErrNoConnection {
		t.Error("bad error")
	}
}

func TestDial(t *testing.T) {
	c, err := Dial("udp4", "localhost:0")
	if err != nil {
		t.Fatal(err)
	}
	defer func() {
		if err = c.Close(); err != nil {
			t.Error(err)
		}
	}()
}
<commit_msg>client: add TestDialError<commit_after>package stun

import (
	"errors"
	"io"
	"log"
	"testing"
	"time"
)

type TestAgent struct {
	f chan Handler
}

func (n *TestAgent) Close() error {
	close(n.f)
	return nil
}

func (TestAgent) Collect(time.Time) error { return nil }

func (TestAgent) Process(m *Message) error { return nil }

func (n *TestAgent) Start(id [TransactionIDSize]byte, deadline time.Time, f Handler) error {
	n.f <- f
	return nil
}

func (n *TestAgent) Stop([TransactionIDSize]byte) error {
	return nil
}

type noopConnection struct{}

func (noopConnection) Write(b []byte) (int, error) {
	return len(b), nil
}

func (noopConnection) Read(b []byte) (int, error) {
	time.Sleep(time.Millisecond)
	return 0, io.EOF
}

func (noopConnection) Close() error {
	return nil
}

func BenchmarkClient_Do(b *testing.B) {
	b.ReportAllocs()
	agent := &TestAgent{
		f: make(chan Handler, 1000),
	}
	client, err := NewClient(ClientOptions{
		Agent:      agent,
		Connection: noopConnection{},
	})
	if err != nil {
		log.Fatal(err)
	}
	defer client.Close()
	go func() {
		e := Event{
			Error:   nil,
			Message: nil,
		}
		for f := range agent.f {
			f.HandleEvent(e)
		}
	}()
	m := new(Message)
	m.Encode()
	noopF := func(event Event) {
		// pass
	}
	for i := 0; i < b.N; i++ {
		if err := client.Do(m, time.Time{}, noopF); err != nil {
			b.Fatal(err)
		}
	}
}

type testConnection struct {
	write   func([]byte) (int, error)
	b       []byte
	stopped bool
}

func (t *testConnection) Write(b []byte) (int, error) {
	return t.write(b)
}

func (t *testConnection) Close() error {
	if t.stopped {
		return errors.New("already stopped")
	}
	t.stopped = true
	return nil
}

func (t *testConnection) Read(b []byte) (int, error) {
	if t.stopped {
		return 0, io.EOF
	}
	return copy(b, t.b), nil
}

func TestClosedOrPanic(t *testing.T) {
	closedOrPanic(nil)
	closedOrPanic(ErrAgentClosed)
	func() {
		defer func() {
			r := recover()
			if r != io.EOF {
				t.Error(r)
			}
		}()
		closedOrPanic(io.EOF)
	}()
}

func TestClient_Do(t *testing.T) {
	response := MustBuild(TransactionID, BindingSuccess)
	response.Encode()
	conn := &testConnection{
		b: response.Raw,
		write: func(bytes []byte) (int, error) {
			return len(bytes), nil
		},
	}
	c, err := NewClient(ClientOptions{
		Connection: conn,
	})
	if err != nil {
		log.Fatal(err)
	}
	defer func() {
		if err := c.Close(); err != nil {
			t.Error(err)
		}
		if err := c.Close(); err == nil {
			t.Error("second close should fail")
		}
		if err := c.Do(MustBuild(TransactionID), time.Time{}, nil); err == nil {
			t.Error("Do after Close should fail")
		}
	}()
	m := MustBuild(
		NewTransactionIDSetter(response.TransactionID),
	)
	d := time.Now().Add(time.Second)
	if err := c.Do(m, d, func(event Event) {
		if event.Error != nil {
			t.Error(event.Error)
		}
	}); err != nil {
		t.Error(err)
	}
	m = MustBuild(TransactionID)
	if err := c.Do(m, d, nil); err != nil {
		t.Error(err)
	}
}

func TestCloseErr_Error(t *testing.T) {
	for id, c := range []struct {
		Err CloseErr
		Out string
	}{
		{CloseErr{}, "failed to close: <nil> (connection), <nil> (agent)"},
		{CloseErr{
			AgentErr: io.ErrUnexpectedEOF,
		}, "failed to close: <nil> (connection), unexpected EOF (agent)"},
		{CloseErr{
			ConnectionErr: io.ErrUnexpectedEOF,
		}, "failed to close: unexpected EOF (connection), <nil> (agent)"},
	} {
		if out := c.Err.Error(); out != c.Out {
			t.Errorf("[%d]: Error(%#v) %q (got) != %q (expected)",
				id, c.Err, out, c.Out,
			)
		}
	}
}

func TestStopErr_Error(t *testing.T) {
	for id, c := range []struct {
		Err StopErr
		Out string
	}{
		{StopErr{}, "error while stopping due to <nil>: <nil>"},
		{StopErr{
			Err: io.ErrUnexpectedEOF,
		}, "error while stopping due to <nil>: unexpected EOF"},
		{StopErr{
			Cause: io.ErrUnexpectedEOF,
		}, "error while stopping due to unexpected EOF: <nil>"},
	} {
		if out := c.Err.Error(); out != c.Out {
			t.Errorf("[%d]: Error(%#v) %q (got) != %q (expected)",
				id, c.Err, out, c.Out,
			)
		}
	}
}

type errorAgent struct {
	startErr error
	stopErr  error
}

func (errorAgent) Close() error { return nil }

func (errorAgent) Collect(time.Time) error { return nil }

func (errorAgent) Process(m *Message) error { return nil }

func (a errorAgent) Start(id [TransactionIDSize]byte, deadline time.Time, f Handler) error {
	return a.startErr
}

func (a errorAgent) Stop([TransactionIDSize]byte) error {
	return a.stopErr
}

func TestClientAgentError(t *testing.T) {
	response := MustBuild(TransactionID, BindingSuccess)
	response.Encode()
	conn := &testConnection{
		b: response.Raw,
		write: func(bytes []byte) (int, error) {
			return len(bytes), nil
		},
	}
	c, err := NewClient(ClientOptions{
		Agent: errorAgent{
			startErr: io.ErrUnexpectedEOF,
		},
		Connection: conn,
	})
	if err != nil {
		log.Fatal(err)
	}
	defer func() {
		if err := c.Close(); err != nil {
			t.Error(err)
		}
	}()
	m := MustBuild(NewTransactionIDSetter(response.TransactionID))
	if err := c.Do(m, time.Time{}, nil); err != nil {
		t.Error(err)
	}
	if err := c.Do(m, time.Time{}, func(event Event) {
		if event.Error == nil {
			t.Error("error expected")
		}
	}); err != io.ErrUnexpectedEOF {
		t.Error("error expected")
	}
}

func TestClientConnErr(t *testing.T) {
	conn := &testConnection{
		write: func(bytes []byte) (int, error) {
			return 0, io.ErrClosedPipe
		},
	}
	c, err := NewClient(ClientOptions{
		Connection: conn,
	})
	if err != nil {
		log.Fatal(err)
	}
	defer func() {
		if err := c.Close(); err != nil {
			t.Error(err)
		}
	}()
	m := MustBuild(TransactionID)
	if err := c.Do(m, time.Time{}, nil); err == nil {
		t.Error("error expected")
	}
	if err := c.Do(m, time.Time{}, noopHandler); err == nil {
		t.Error("error expected")
	}
}

func TestClientConnErrStopErr(t *testing.T) {
	conn := &testConnection{
		write: func(bytes []byte) (int, error) {
			return 0, io.ErrClosedPipe
		},
	}
	c, err := NewClient(ClientOptions{
		Connection: conn,
		Agent: errorAgent{
			stopErr: io.ErrUnexpectedEOF,
		},
	})
	if err != nil {
		log.Fatal(err)
	}
	defer func() {
		if err := c.Close(); err != nil {
			t.Error(err)
		}
	}()
	m := MustBuild(TransactionID)
	if err := c.Do(m, time.Time{}, noopHandler); err == nil {
		t.Error("error expected")
	}
}

func TestCallbackWaitHandler_setCallback(t *testing.T) {
	c := callbackWaitHandler{}
	defer func() {
		if err := recover(); err == nil {
			t.Error("should panic")
		}
	}()
	c.setCallback(nil)
}

func TestCallbackWaitHandler_HandleEvent(t *testing.T) {
	c := callbackWaitHandler{}
	defer func() {
		if err := recover(); err == nil {
			t.Error("should panic")
		}
	}()
	c.HandleEvent(Event{})
}

func TestNewClientNoConnection(t *testing.T) {
	c, err := NewClient(ClientOptions{
		Connection: nil,
	})
	if c != nil {
		t.Error("c should be nil")
	}
	if err != ErrNoConnection {
		t.Error("bad error")
	}
}

func TestDial(t *testing.T) {
	c, err := Dial("udp4", "localhost:0")
	if err != nil {
		t.Fatal(err)
	}
	defer func() {
		if err = c.Close(); err != nil {
			t.Error(err)
		}
	}()
}

func TestDialError(t *testing.T) {
	_, err := Dial("bad?network", "?????")
	if err == nil {
		t.Fatal("error expected")
	}
}
<|endoftext|>


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

package dns

import (
	"testing"
	"time"
)

func TestClientSync(t *testing.T) {
	m := new(Msg)
	m.SetQuestion("miek.nl.", TypeSOA)

	c := new(Client)
	r, _, _ := c.Exchange(m, "37.251.95.53:53")

	if r != nil && r.Rcode != RcodeSuccess {
		t.Log("Failed to get an valid answer")
		t.Fail()
		t.Logf("%v\n", r)
	}
}

func TestClientEDNS0(t *testing.T) {
	m := new(Msg)
	m.SetQuestion("miek.nl.", TypeDNSKEY)

	m.SetEdns0(2048, true)
	//edns.Option = make([]Option, 1)
	//edns.SetNsid("") // Empty to request it

	c := new(Client)
	r, _, _ := c.Exchange(m, "37.251.95.53:53")

	if r != nil && r.Rcode != RcodeSuccess {
		t.Log("Failed to get an valid answer")
		t.Fail()
		t.Logf("%v\n", r)
	}
}

func TestSingleSingleInflight(t *testing.T) {
	m := new(Msg)
	m.SetQuestion("miek.nl.", TypeDNSKEY)

	c := new(Client)
	c.SingleInflight = true
	nr := 10
	ch := make(chan time.Duration)
	for i := 0; i < nr; i++ {
		go func() {
			_, rtt, _ := c.Exchange(m, "37.251.95.53:53")
			ch <- rtt
		}()
	}
	i := 0
	var first time.Duration
	// With inflight *all* rtt are identical, and by doing actual lookups
	// the changes that this is a coincidence is small.
Loop:
	for {
		select {
		case rtt := <-ch:
			if i == 0 {
				first = rtt
			} else {
				if first != rtt {
					t.Log("All rtt should be equal")
					t.Fail()
				}
			}
			i++
			if i == 10 {
				break Loop
			}
		}
	}
}

func TestClientTsigAXFR(t *testing.T) {
	m := new(Msg)
	m.SetAxfr("miek.nl.")
	m.SetTsig("axfr.", HmacMD5, 300, time.Now().Unix())

	tr := new(Transfer)
	tr.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}

	if a, err := tr.n(m, "176.58.119.54:53"); err != nil {
		t.Log("Failed to setup axfr: " + err.Error())
		t.Fatal()
	} else {
		for ex := range a {
			if ex.Error != nil {
				t.Logf("Error %s\n", ex.Error.Error())
				t.Fail()
				break
			}
			for _, rr := range ex.RR {
				t.Logf("%s\n", rr.String())
			}
		}
	}
}
*/

/*
func TestClientAXFRMultipleMessages(t *testing.T) {
	m := new(Msg)
	m.SetAxfr("dnsex.nl.")

	c := new(Client)
	c.Net = "tcp"

	if a, err := c.TransferIn(m, "37.251.95.53:53"); err != nil {
		t.Log("Failed to setup axfr" + err.Error())
		t.Fail()
		return
	} else {
		for ex := range a {
			if ex.Error != nil {
				t.Logf("Error %s\n", ex.Error.Error())
				t.Fail()
				break
			}
		}
	}
}
*/

// not really a test, but shows how to use update leases
func TestUpdateLeaseTSIG(t *testing.T) {
	m := new(Msg)
	m.SetUpdate("t.local.ip6.io.")
	rr, _ := NewRR("t.local.ip6.io. 30 A 127.0.0.1")
	rrs := make([]RR, 1)
	rrs[0] = rr
	m.Insert(rrs)

	lease_rr := new(OPT)
	lease_rr.Hdr.Name = "."
	lease_rr.Hdr.Rrtype = TypeOPT
	e := new(EDNS0_UL)
	e.Code = EDNS0UL
	e.Lease = 120
	lease_rr.Option = append(lease_rr.Option, e)
	m.Extra = append(m.Extra, lease_rr)

	c := new(Client)
	m.SetTsig("polvi.", HmacMD5, 300, time.Now().Unix())
	c.TsigSecret = map[string]string{"polvi.": "pRZgBrBvI4NAHZYhxmhs/Q=="}

	_, _, err := c.Exchange(m, "127.0.0.1:53")
	if err != nil {
		t.Log(err.Error())
		t.Fail()
	}
}
<commit_msg>Fix tsig tests and tweak q<commit_after>// Copyright 2011 Miek Gieben. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package dns

import (
	"testing"
	"time"
)

func TestClientSync(t *testing.T) {
	m := new(Msg)
	m.SetQuestion("miek.nl.", TypeSOA)

	c := new(Client)
	r, _, _ := c.Exchange(m, "37.251.95.53:53")

	if r != nil && r.Rcode != RcodeSuccess {
		t.Log("Failed to get an valid answer")
		t.Fail()
		t.Logf("%v\n", r)
	}
}

func TestClientEDNS0(t *testing.T) {
	m := new(Msg)
	m.SetQuestion("miek.nl.", TypeDNSKEY)

	m.SetEdns0(2048, true)
	//edns.Option = make([]Option, 1)
	//edns.SetNsid("") // Empty to request it

	c := new(Client)
	r, _, _ := c.Exchange(m, "37.251.95.53:53")

	if r != nil && r.Rcode != RcodeSuccess {
		t.Log("Failed to get an valid answer")
		t.Fail()
		t.Logf("%v\n", r)
	}
}

func TestSingleSingleInflight(t *testing.T) {
	m := new(Msg)
	m.SetQuestion("miek.nl.", TypeDNSKEY)

	c := new(Client)
	c.SingleInflight = true
	nr := 10
	ch := make(chan time.Duration)
	for i := 0; i < nr; i++ {
		go func() {
			_, rtt, _ := c.Exchange(m, "37.251.95.53:53")
			ch <- rtt
		}()
	}
	i := 0
	var first time.Duration
	// With inflight *all* rtt are identical, and by doing actual lookups
	// the changes that this is a coincidence is small.
Loop:
	for {
		select {
		case rtt := <-ch:
			if i == 0 {
				first = rtt
			} else {
				if first != rtt {
					t.Log("All rtt should be equal")
					t.Fail()
				}
			}
			i++
			if i == 10 {
				break Loop
			}
		}
	}
}

func TestClientTsigAXFR(t *testing.T) {
	m := new(Msg)
	m.SetAxfr("example.nl.")
	m.SetTsig("axfr.", HmacMD5, 300, time.Now().Unix())

	tr := new(Transfer)
	tr.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}

	if a, err := tr.In(m, "176.58.119.54:53"); err != nil {
		t.Log("Failed to setup axfr: " + err.Error())
		t.Fatal()
	} else {
		for ex := range a {
			if ex.Error != nil {
				t.Logf("Error %s\n", ex.Error.Error())
				t.Fail()
				break
			}
			for _, rr := range ex.RR {
				t.Logf("%s\n", rr.String())
			}
		}
	}
}

func TestClientAXFRMultipleEnvelopes(t *testing.T) {
	m := new(Msg)
	m.SetAxfr("nlnetlabs.nl.")

	tr := new(Transfer)
	if a, err := tr.In(m, "213.154.224.1:53"); err != nil {
		t.Log("Failed to setup axfr" + err.Error())
		t.Fail()
		return
	} else {
		for ex := range a {
			if ex.Error != nil {
				t.Logf("Error %s\n", ex.Error.Error())
				t.Fail()
				break
			}
		}
	}
}

// not really a test, but shows how to use update leases
func ExampleUpdateLeaseTSIG(t *testing.T) {
	m := new(Msg)
	m.SetUpdate("t.local.ip6.io.")
	rr, _ := NewRR("t.local.ip6.io. 30 A 127.0.0.1")
	rrs := make([]RR, 1)
	rrs[0] = rr
	m.Insert(rrs)

	lease_rr := new(OPT)
	lease_rr.Hdr.Name = "."
	lease_rr.Hdr.Rrtype = TypeOPT
	e := new(EDNS0_UL)
	e.Code = EDNS0UL
	e.Lease = 120
	lease_rr.Option = append(lease_rr.Option, e)
	m.Extra = append(m.Extra, lease_rr)

	c := new(Client)
	m.SetTsig("polvi.", HmacMD5, 300, time.Now().Unix())
	c.TsigSecret = map[string]string{"polvi.": "pRZgBrBvI4NAHZYhxmhs/Q=="}

	_, _, err := c.Exchange(m, "127.0.0.1:53")
	if err != nil {
		t.Log(err.Error())
		t.Fail()
	}
}
<|endoftext|>


<commit_before>package twitch

import (
	"bufio"
	"fmt"
	"net"
	"net/textproto"
	"reflect"
	"strings"
	"testing"
	"time"
)

func TestCanCreateClient(t *testing.T) {
	client := NewClient("justinfan123123", "oauth:1123123")

	if reflect.TypeOf(*client) != reflect.TypeOf(Client{}) {
		t.Error("client is not of type Client")
	}
}

func TestCanConnectAndAuthenticate(t *testing.T) {
	var oauthMsg string
	wait := make(chan struct{})
	waitPass := make(chan struct{})
	go func() {
		ln, err := net.Listen("tcp", ":4321")
		if err != nil {
			t.Fatal(err)
		}
		close(wait)
		conn, err := ln.Accept()
		if err != nil {
			t.Fatal(err)
		}
		defer ln.Close()
		defer conn.Close()

		reader := bufio.NewReader(conn)
		tp := textproto.NewReader(reader)

		for {
			message, err := tp.ReadLine()
			if err != nil {
				t.Fatal(err)
			}
			message = strings.Replace(message, "\r\n", "", 1)
			if strings.HasPrefix(message, "PASS") {
				oauthMsg = message
				close(waitPass)
			}
		}
	}()

	// wait for server to start
	select {
	case <-wait:
	case <-time.After(time.Second * 3):
		t.Fatal("client didn't connect")
	}

	client := NewClient("justinfan123123", "oauth:123123132")
	client.SetIrcAddress(":4321")
	go client.Connect()

	select {
	case <-waitPass:
	case <-time.After(time.Second * 3):
		t.Fatal("no oauth read")
	}

	if oauthMsg != "PASS oauth:123123132" {
		t.Fatalf("invalid authentication data: oauth: %s", oauthMsg)
	}
}

func TestCanReceivePRIVMSGMessage(t *testing.T) {
	testMessage := "@badges=subscriber/6,premium/1;color=#FF0000;display-name=Redflamingo13;emotes=;id=2a31a9df-d6ff-4840-b211-a2547c7e656e;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1490382457309;turbo=0;user-id=78424343;user-type= :redflamingo13!redflamingo13@redflamingo13.tmi.twitch.tv PRIVMSG #pajlada :Thrashh5, FeelsWayTooAmazingMan kinda"
	wait := make(chan struct{})

	go func() {
		ln, err := net.Listen("tcp", ":4322")
		if err != nil {
			t.Fatal(err)
		}
		close(wait)
		conn, err := ln.Accept()
		if err != nil {
			t.Fatal(err)
		}
		defer ln.Close()
		defer conn.Close()

		fmt.Fprintf(conn, "%s\r\n", testMessage)
	}()

	// wait for server to start
	select {
	case <-wait:
	case <-time.After(time.Second * 3):
		t.Fatal("client didn't connect")
	}

	client := NewClient("justinfan123123", "oauth:123123132")
	client.SetIrcAddress(":4322")
	go client.Connect()

	waitMsg := make(chan string)
	var receivedMsg string

	client.OnNewMessage(func(channel string, user User, message Message) {
		receivedMsg = message.Text
		close(waitMsg)
	})

	// wait for server to start
	select {
	case <-waitMsg:
	case <-time.After(time.Second * 3):
		t.Fatal("no message sent")
	}

	if receivedMsg != "Thrashh5, FeelsWayTooAmazingMan kinda" {
		t.Fatal("invalid message text received")
	}
}

func TestCanReceiveCLEARCHATMessage(t *testing.T) {
	testMessage := `@ban-duration=1;ban-reason=testing\sxd;room-id=11148817;target-user-id=40910607 :tmi.twitch.tv CLEARCHAT #pajlada :ampzyh`
	wait := make(chan struct{})

	go func() {
		ln, err := net.Listen("tcp", ":4323")
		if err != nil {
			t.Fatal(err)
		}
		close(wait)
		conn, err := ln.Accept()
		if err != nil {
			t.Fatal(err)
		}
		defer ln.Close()
		defer conn.Close()

		fmt.Fprintf(conn, "%s\r\n", testMessage)
	}()

	// wait for server to start
	select {
	case <-wait:
	case <-time.After(time.Second * 3):
		t.Fatal("client didn't connect")
	}

	client := NewClient("justinfan123123", "oauth:123123132")
	client.SetIrcAddress(":4323")
	go client.Connect()

	waitMsg := make(chan string)
	var receivedMsg string

	client.OnNewClearchatMessage(func(channel string, user User, message Message) {
		receivedMsg = message.Text
		close(waitMsg)
	})

	// wait for server to start
	select {
	case <-waitMsg:
	case <-time.After(time.Second * 3):
		t.Fatal("no message sent")
	}

	assertStringsEqual(t, "ampzyh was timed out for 1s: testing xd", receivedMsg)
}

func TestCanReceiveROOMSTATEMessage(t *testing.T) {
	testMessage := `@slow=10 :tmi.twitch.tv ROOMSTATE #gempir`
	wait := make(chan struct{})

	go func() {
		ln, err := net.Listen("tcp", ":4324")
		if err != nil {
			t.Fatal(err)
		}
		close(wait)
		conn, err := ln.Accept()
		if err != nil {
			t.Fatal(err)
		}
		defer ln.Close()
		defer conn.Close()

		fmt.Fprintf(conn, "%s\r\n", testMessage)
	}()

	// wait for server to start
	select {
	case <-wait:
	case <-time.After(time.Second * 3):
		t.Fatal("client didn't connect")
	}

	client := NewClient("justinfan123123", "oauth:123123132")
	client.SetIrcAddress(":4324")
	go client.Connect()

	waitMsg := make(chan string)
	var receivedTag string

	client.OnNewRoomstateMessage(func(channel string, user User, message Message) {
		receivedTag = message.Tags["slow"]
		close(waitMsg)
	})

	// wait for server to start
	select {
	case <-waitMsg:
	case <-time.After(time.Second * 3):
		t.Fatal("no message sent")
	}

	assertStringsEqual(t, "10", receivedTag)
}

func TestCanSayMessage(t *testing.T) {
	testMessage := "Do not go gentle into that good night."
	wait := make(chan struct{})

	waitEnd := make(chan struct{})
	var receivedMsg string

	go func() {
		ln, err := net.Listen("tcp", ":4325")
		if err != nil {
			t.Fatal(err)
		}
		close(wait)
		conn, err := ln.Accept()
		if err != nil {
			t.Fatal(err)
		}
		defer ln.Close()
		defer conn.Close()

		reader := bufio.NewReader(conn)
		tp := textproto.NewReader(reader)

		for {
			message, err := tp.ReadLine()
			if err != nil {
				t.Fatal(err)
			}
			message = strings.Replace(message, "\r\n", "", 1)
			if strings.HasPrefix(message, "NICK") {
				fmt.Fprintf(conn, ":tmi.twitch.tv 001 justinfan123123 :Welcome, GLHF!\r\n")
			}
			if strings.HasPrefix(message, "PRIVMSG") {
				receivedMsg = message
				close(waitEnd)
			}
		}
	}()

	// wait for server to start
	select {
	case <-wait:
	case <-time.After(time.Second * 3):
		t.Fatal("testserver didn't start")
	}

	client := NewClient("justinfan123123", "oauth:123123132")
	client.SetIrcAddress(":4325")

	go client.Connect()

	client.Say("gempir", testMessage)

	// wait for server to receive message
	select {
	case <-waitEnd:
	case <-time.After(time.Second * 3):
		t.Fatal("no privmsg received")
	}

	assertStringsEqual(t, "PRIVMSG #gempir :"+testMessage, receivedMsg)
}

func TestCanJoinChannel(t *testing.T) {
	wait := make(chan struct{})

	waitEnd := make(chan struct{})
	var receivedMsg string

	go func() {
		ln, err := net.Listen("tcp", ":4326")
		if err != nil {
			t.Fatal(err)
		}
		close(wait)
		conn, err := ln.Accept()
		if err != nil {
			t.Fatal(err)
		}
		defer ln.Close()
		defer conn.Close()

		reader := bufio.NewReader(conn)
		tp := textproto.NewReader(reader)

		for {
			message, err := tp.ReadLine()
			if err != nil {
				t.Fatal(err)
			}
			message = strings.Replace(message, "\r\n", "", 1)
			if strings.HasPrefix(message, "NICK") {
				fmt.Fprintf(conn, ":tmi.twitch.tv 001 justinfan123123 :Welcome, GLHF!\r\n")
			}
			if strings.HasPrefix(message, "JOIN") {
				receivedMsg = message
				close(waitEnd)
			}
		}
	}()

	// wait for server to start
	select {
	case <-wait:
	case <-time.After(time.Second * 3):
		t.Fatal("testserver didn't start")
	}

	client := NewClient("justinfan123123", "oauth:123123132")
	client.SetIrcAddress(":4326")

	go client.Connect()

	client.Join("gempir")

	// wait for server to receive message
	select {
	case <-waitEnd:
	case <-time.After(time.Second * 3):
		t.Fatal("no join message received")
	}

	assertStringsEqual(t, "JOIN #gempir", receivedMsg)
}

func TestCanPong(t *testing.T) {
	wait := make(chan struct{})

	waitEnd := make(chan struct{})
	var receivedMsg string

	go func() {
		ln, err := net.Listen("tcp", ":4327")
		if err != nil {
			t.Fatal(err)
		}
		close(wait)
		conn, err := ln.Accept()
		if err != nil {
			t.Fatal(err)
		}
		defer ln.Close()
		defer conn.Close()

		reader := bufio.NewReader(conn)
		tp := textproto.NewReader(reader)

		for {
			message, err := tp.ReadLine()
			if err != nil {
				t.Fatal(err)
			}
			message = strings.Replace(message, "\r\n", "", 1)
			if strings.HasPrefix(message, "NICK") {
				fmt.Fprintf(conn, ":tmi.twitch.tv 001 justinfan123123 :Welcome, GLHF!\r\n")
				fmt.Fprintf(conn, "PING hello\r\n")
			}
			if strings.HasPrefix(message, "PONG") {
				receivedMsg = message
				close(waitEnd)
			}
		}
	}()

	// wait for server to start
	select {
	case <-wait:
	case <-time.After(time.Second * 3):
		t.Fatal("testserver didn't start")
	}

	client := NewClient("justinfan123123", "oauth:123123132")
	client.SetIrcAddress(":4327")

	go client.Connect()

	// wait for server to receive message
	select {
	case <-waitEnd:
	case <-time.After(time.Second * 3):
		t.Fatal("no join message received")
	}

	assertStringsEqual(t, "PONG hello", receivedMsg)
}

func TestCanNotDialInvalidAddress(t *testing.T) {
	client := NewClient("justinfan123123", "oauth:123123132")
	client.SetIrcAddress("127.0.0.1:123123123123")

	err := client.Connect()
	if err.Error() != "dial tcp: address 123123123123: invalid port" {
		t.Fatal("invalid dial error message")
	}
}
<commit_msg>use assert function instead<commit_after>package twitch

import (
	"bufio"
	"fmt"
	"net"
	"net/textproto"
	"reflect"
	"strings"
	"testing"
	"time"
)

func TestCanCreateClient(t *testing.T) {
	client := NewClient("justinfan123123", "oauth:1123123")

	if reflect.TypeOf(*client) != reflect.TypeOf(Client{}) {
		t.Error("client is not of type Client")
	}
}

func TestCanConnectAndAuthenticate(t *testing.T) {
	var oauthMsg string
	wait := make(chan struct{})
	waitPass := make(chan struct{})
	go func() {
		ln, err := net.Listen("tcp", ":4321")
		if err != nil {
			t.Fatal(err)
		}
		close(wait)
		conn, err := ln.Accept()
		if err != nil {
			t.Fatal(err)
		}
		defer ln.Close()
		defer conn.Close()

		reader := bufio.NewReader(conn)
		tp := textproto.NewReader(reader)

		for {
			message, err := tp.ReadLine()
			if err != nil {
				t.Fatal(err)
			}
			message = strings.Replace(message, "\r\n", "", 1)
			if strings.HasPrefix(message, "PASS") {
				oauthMsg = message
				close(waitPass)
			}
		}
	}()

	// wait for server to start
	select {
	case <-wait:
	case <-time.After(time.Second * 3):
		t.Fatal("client didn't connect")
	}

	client := NewClient("justinfan123123", "oauth:123123132")
	client.SetIrcAddress(":4321")
	go client.Connect()

	select {
	case <-waitPass:
	case <-time.After(time.Second * 3):
		t.Fatal("no oauth read")
	}

	if oauthMsg != "PASS oauth:123123132" {
		t.Fatalf("invalid authentication data: oauth: %s", oauthMsg)
	}
}

func TestCanReceivePRIVMSGMessage(t *testing.T) {
	testMessage := "@badges=subscriber/6,premium/1;color=#FF0000;display-name=Redflamingo13;emotes=;id=2a31a9df-d6ff-4840-b211-a2547c7e656e;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1490382457309;turbo=0;user-id=78424343;user-type= :redflamingo13!redflamingo13@redflamingo13.tmi.twitch.tv PRIVMSG #pajlada :Thrashh5, FeelsWayTooAmazingMan kinda"
	wait := make(chan struct{})

	go func() {
		ln, err := net.Listen("tcp", ":4322")
		if err != nil {
			t.Fatal(err)
		}
		close(wait)
		conn, err := ln.Accept()
		if err != nil {
			t.Fatal(err)
		}
		defer ln.Close()
		defer conn.Close()

		fmt.Fprintf(conn, "%s\r\n", testMessage)
	}()

	// wait for server to start
	select {
	case <-wait:
	case <-time.After(time.Second * 3):
		t.Fatal("client didn't connect")
	}

	client := NewClient("justinfan123123", "oauth:123123132")
	client.SetIrcAddress(":4322")
	go client.Connect()

	waitMsg := make(chan string)
	var receivedMsg string

	client.OnNewMessage(func(channel string, user User, message Message) {
		receivedMsg = message.Text
		close(waitMsg)
	})

	// wait for server to start
	select {
	case <-waitMsg:
	case <-time.After(time.Second * 3):
		t.Fatal("no message sent")
	}

	if receivedMsg != "Thrashh5, FeelsWayTooAmazingMan kinda" {
		t.Fatal("invalid message text received")
	}
}

func TestCanReceiveCLEARCHATMessage(t *testing.T) {
	testMessage := `@ban-duration=1;ban-reason=testing\sxd;room-id=11148817;target-user-id=40910607 :tmi.twitch.tv CLEARCHAT #pajlada :ampzyh`
	wait := make(chan struct{})

	go func() {
		ln, err := net.Listen("tcp", ":4323")
		if err != nil {
			t.Fatal(err)
		}
		close(wait)
		conn, err := ln.Accept()
		if err != nil {
			t.Fatal(err)
		}
		defer ln.Close()
		defer conn.Close()

		fmt.Fprintf(conn, "%s\r\n", testMessage)
	}()

	// wait for server to start
	select {
	case <-wait:
	case <-time.After(time.Second * 3):
		t.Fatal("client didn't connect")
	}

	client := NewClient("justinfan123123", "oauth:123123132")
	client.SetIrcAddress(":4323")
	go client.Connect()

	waitMsg := make(chan string)
	var receivedMsg string

	client.OnNewClearchatMessage(func(channel string, user User, message Message) {
		receivedMsg = message.Text
		close(waitMsg)
	})

	// wait for server to start
	select {
	case <-waitMsg:
	case <-time.After(time.Second * 3):
		t.Fatal("no message sent")
	}

	assertStringsEqual(t, "ampzyh was timed out for 1s: testing xd", receivedMsg)
}

func TestCanReceiveROOMSTATEMessage(t *testing.T) {
	testMessage := `@slow=10 :tmi.twitch.tv ROOMSTATE #gempir`
	wait := make(chan struct{})

	go func() {
		ln, err := net.Listen("tcp", ":4324")
		if err != nil {
			t.Fatal(err)
		}
		close(wait)
		conn, err := ln.Accept()
		if err != nil {
			t.Fatal(err)
		}
		defer ln.Close()
		defer conn.Close()

		fmt.Fprintf(conn, "%s\r\n", testMessage)
	}()

	// wait for server to start
	select {
	case <-wait:
	case <-time.After(time.Second * 3):
		t.Fatal("client didn't connect")
	}

	client := NewClient("justinfan123123", "oauth:123123132")
	client.SetIrcAddress(":4324")
	go client.Connect()

	waitMsg := make(chan string)
	var receivedTag string

	client.OnNewRoomstateMessage(func(channel string, user User, message Message) {
		receivedTag = message.Tags["slow"]
		close(waitMsg)
	})

	// wait for server to start
	select {
	case <-waitMsg:
	case <-time.After(time.Second * 3):
		t.Fatal("no message sent")
	}

	assertStringsEqual(t, "10", receivedTag)
}

func TestCanSayMessage(t *testing.T) {
	testMessage := "Do not go gentle into that good night."
	wait := make(chan struct{})

	waitEnd := make(chan struct{})
	var receivedMsg string

	go func() {
		ln, err := net.Listen("tcp", ":4325")
		if err != nil {
			t.Fatal(err)
		}
		close(wait)
		conn, err := ln.Accept()
		if err != nil {
			t.Fatal(err)
		}
		defer ln.Close()
		defer conn.Close()

		reader := bufio.NewReader(conn)
		tp := textproto.NewReader(reader)

		for {
			message, err := tp.ReadLine()
			if err != nil {
				t.Fatal(err)
			}
			message = strings.Replace(message, "\r\n", "", 1)
			if strings.HasPrefix(message, "NICK") {
				fmt.Fprintf(conn, ":tmi.twitch.tv 001 justinfan123123 :Welcome, GLHF!\r\n")
			}
			if strings.HasPrefix(message, "PRIVMSG") {
				receivedMsg = message
				close(waitEnd)
			}
		}
	}()

	// wait for server to start
	select {
	case <-wait:
	case <-time.After(time.Second * 3):
		t.Fatal("testserver didn't start")
	}

	client := NewClient("justinfan123123", "oauth:123123132")
	client.SetIrcAddress(":4325")

	go client.Connect()

	client.Say("gempir", testMessage)

	// wait for server to receive message
	select {
	case <-waitEnd:
	case <-time.After(time.Second * 3):
		t.Fatal("no privmsg received")
	}

	assertStringsEqual(t, "PRIVMSG #gempir :"+testMessage, receivedMsg)
}

func TestCanJoinChannel(t *testing.T) {
	wait := make(chan struct{})

	waitEnd := make(chan struct{})
	var receivedMsg string

	go func() {
		ln, err := net.Listen("tcp", ":4326")
		if err != nil {
			t.Fatal(err)
		}
		close(wait)
		conn, err := ln.Accept()
		if err != nil {
			t.Fatal(err)
		}
		defer ln.Close()
		defer conn.Close()

		reader := bufio.NewReader(conn)
		tp := textproto.NewReader(reader)

		for {
			message, err := tp.ReadLine()
			if err != nil {
				t.Fatal(err)
			}
			message = strings.Replace(message, "\r\n", "", 1)
			if strings.HasPrefix(message, "NICK") {
				fmt.Fprintf(conn, ":tmi.twitch.tv 001 justinfan123123 :Welcome, GLHF!\r\n")
			}
			if strings.HasPrefix(message, "JOIN") {
				receivedMsg = message
				close(waitEnd)
			}
		}
	}()

	// wait for server to start
	select {
	case <-wait:
	case <-time.After(time.Second * 3):
		t.Fatal("testserver didn't start")
	}

	client := NewClient("justinfan123123", "oauth:123123132")
	client.SetIrcAddress(":4326")

	go client.Connect()

	client.Join("gempir")

	// wait for server to receive message
	select {
	case <-waitEnd:
	case <-time.After(time.Second * 3):
		t.Fatal("no join message received")
	}

	assertStringsEqual(t, "JOIN #gempir", receivedMsg)
}

func TestCanPong(t *testing.T) {
	wait := make(chan struct{})

	waitEnd := make(chan struct{})
	var receivedMsg string

	go func() {
		ln, err := net.Listen("tcp", ":4327")
		if err != nil {
			t.Fatal(err)
		}
		close(wait)
		conn, err := ln.Accept()
		if err != nil {
			t.Fatal(err)
		}
		defer ln.Close()
		defer conn.Close()

		reader := bufio.NewReader(conn)
		tp := textproto.NewReader(reader)

		for {
			message, err := tp.ReadLine()
			if err != nil {
				t.Fatal(err)
			}
			message = strings.Replace(message, "\r\n", "", 1)
			if strings.HasPrefix(message, "NICK") {
				fmt.Fprintf(conn, ":tmi.twitch.tv 001 justinfan123123 :Welcome, GLHF!\r\n")
				fmt.Fprintf(conn, "PING hello\r\n")
			}
			if strings.HasPrefix(message, "PONG") {
				receivedMsg = message
				close(waitEnd)
			}
		}
	}()

	// wait for server to start
	select {
	case <-wait:
	case <-time.After(time.Second * 3):
		t.Fatal("testserver didn't start")
	}

	client := NewClient("justinfan123123", "oauth:123123132")
	client.SetIrcAddress(":4327")

	go client.Connect()

	// wait for server to receive message
	select {
	case <-waitEnd:
	case <-time.After(time.Second * 3):
		t.Fatal("no join message received")
	}

	assertStringsEqual(t, "PONG hello", receivedMsg)
}

func TestCanNotDialInvalidAddress(t *testing.T) {
	client := NewClient("justinfan123123", "oauth:123123132")
	client.SetIrcAddress("127.0.0.1:123123123123")

	err := client.Connect()
	assertStringsEqual(t, "dial tcp: address 123123123123: invalid port", err.Error())
}
<|endoftext|>