<commit_before>package airbrake

import (
	"bytes"
	"errors"
	"log"
	"net/http"
	"os"
	"runtime"
  "reflect"
	"sync"
	"text/template"
)

var (
	ApiKey   = ""
	Endpoint = "https://airbrake.io/notifier_api/v2/notices.xml"
	Verbose  = false

	badResponse   = errors.New("Bad response")
	apiKeyMissing = errors.New("Please set the airbrake.ApiKey before doing calls")
	dunno         = []byte("???")
	centerDot     = []byte("·")
	dot           = []byte(".")
	tmpl          = template.Must(template.New("error").Parse(source))
)

type Line struct {
	Function string
	File     string
	Line     int
}

// stack implements Stack, skipping N frames
func stacktrace(skip int) (lines []Line) {
	for i := skip; ; i++ {
		pc, file, line, ok := runtime.Caller(i)
		if !ok {
			break
		}

		item := Line{string(function(pc)), string(file), line}

		// ignore panic method
		if item.Function != "panic" {
			lines = append(lines, item)
		}
	}
	return
}

var channel chan map[string]interface{}
var once sync.Once

// function returns, if possible, the name of the function containing the PC.
func function(pc uintptr) []byte {
	fn := runtime.FuncForPC(pc)
	if fn == nil {
		return dunno
	}
	name := []byte(fn.Name())
	// The name includes the path name to the package, which is unnecessary
	// since the file name is already included.  Plus, it has center dots.
	// That is, we see
	//  runtime/debug.*T·ptrmethod
	// and want
	//  *T.ptrmethod
	if period := bytes.Index(name, dot); period >= 0 {
		name = name[period+1:]
	}
	name = bytes.Replace(name, centerDot, dot, -1)
	return name
}

func initChannel() {
	channel = make(chan map[string]interface{}, 100)

	go func() {
		for params := range channel {
			buffer := bytes.NewBufferString("")

			if err := tmpl.Execute(buffer, params); err != nil {
				log.Printf("Airbreak error: %s", err)
				continue
			}

			if Verbose {
				log.Printf("Airbreak payload for endpoint %s: %s", Endpoint, buffer)
			}

			response, err := http.Post(Endpoint, "text/xml", buffer)
			response.Body.Close()

			if err != nil {
				log.Printf("Airbreak error: %s", err)
				continue
			}

			if Verbose {
				log.Printf("Airbreak post: %s status code: %d", params["Error"], response.StatusCode)
			}
		}
	}()
}

func Error(e error, request *http.Request) error {
	once.Do(initChannel)

	if ApiKey == "" {
		return apiKeyMissing
	}

	params := map[string]interface{}{
    "Class":     reflect.TypeOf(e).String(),
		"Error":     e,
		"ApiKey":    ApiKey,
		"ErrorName": e.Error(),
		"Request":   request,
	}

  if params["Class"] == "" {
    params["Class"] = "Panic"
  }

	pwd, err := os.Getwd()
	if err == nil {
		params["Pwd"] = pwd
	}

	params["Backtrace"] = stacktrace(3)

	channel <- params
	return nil
}

func CapturePanic(r *http.Request) {
	if rec := recover(); rec != nil {

		if err, ok := rec.(error); ok {
			log.Printf("Recording err %s", err)
			Error(err, r)
		} else if err, ok := rec.(string); ok {
			log.Printf("Recording string %s", err)
			Error(errors.New(err), r)
		}

		panic(rec)
	}
}

const source = `<?xml version="1.0" encoding="UTF-8"?>
<notice version="2.0">
  <api-key>{{ .ApiKey }}</api-key>
  <notifier>
    <name>Airbrake Golang</name>
    <version>0.0.1</version>
    <url>http://airbrake.io</url>
  </notifier>
  <error>
    <class>{{ html .Class }}</class>
    <message>{{ with .ErrorName }}{{html .}}{{ end }}</message>
    <backtrace>
      {{ range .Backtrace }}
      <line method="{{.Function}}" file="{{.File}}" number="{{.Line}}"/>
      {{ end }}
    </backtrace>
  </error>
  {{ with .Request }}
  <request>
    <url>{{ .URL }}</url>
    <component/>
    <action/>
  </request>
  {{ end }}
  <server-environment>
    <environment-name>production</environment-name>
    <project-root>{{ .Pwd }}</project-root>
  </server-environment>
</notice>`
<commit_msg>formatting etc<commit_after>package airbrake

import (
	"bytes"
	"errors"
	"log"
	"net/http"
	"os"
	"reflect"
	"runtime"
	"sync"
	"text/template"
)

var (
	ApiKey   = ""
	Endpoint = "https://airbrake.io/notifier_api/v2/notices.xml"
	Verbose  = false

	badResponse   = errors.New("Bad response")
	apiKeyMissing = errors.New("Please set the airbrake.ApiKey before doing calls")
	dunno         = []byte("???")
	centerDot     = []byte("·")
	dot           = []byte(".")
	tmpl          = template.Must(template.New("error").Parse(source))
)

type Line struct {
	Function string
	File     string
	Line     int
}

// stack implements Stack, skipping N frames
func stacktrace(skip int) (lines []Line) {
	for i := skip; ; i++ {
		pc, file, line, ok := runtime.Caller(i)
		if !ok {
			break
		}

		item := Line{string(function(pc)), string(file), line}

		// ignore panic method
		if item.Function != "panic" {
			lines = append(lines, item)
		}
	}
	return
}

var channel chan map[string]interface{}
var once sync.Once

// function returns, if possible, the name of the function containing the PC.
func function(pc uintptr) []byte {
	fn := runtime.FuncForPC(pc)
	if fn == nil {
		return dunno
	}
	name := []byte(fn.Name())
	// The name includes the path name to the package, which is unnecessary
	// since the file name is already included.  Plus, it has center dots.
	// That is, we see
	//  runtime/debug.*T·ptrmethod
	// and want
	//  *T.ptrmethod
	if period := bytes.Index(name, dot); period >= 0 {
		name = name[period+1:]
	}
	name = bytes.Replace(name, centerDot, dot, -1)
	return name
}

func initChannel() {
	channel = make(chan map[string]interface{}, 100)

	go func() {
		for params := range channel {
			buffer := bytes.NewBufferString("")

			if err := tmpl.Execute(buffer, params); err != nil {
				log.Printf("Airbreak error: %s", err)
				continue
			}

			if Verbose {
				log.Printf("Airbreak payload for endpoint %s: %s", Endpoint, buffer)
			}

			response, err := http.Post(Endpoint, "text/xml", buffer)
			response.Body.Close()

			if err != nil {
				log.Printf("Airbreak error: %s", err)
				continue
			}

			if Verbose {
				log.Printf("Airbreak post: %s status code: %d", params["Error"], response.StatusCode)
			}
		}
	}()
}

func Error(e error, request *http.Request) error {
	once.Do(initChannel)

	if ApiKey == "" {
		return apiKeyMissing
	}

	params := map[string]interface{}{
		"Class":     reflect.TypeOf(e).String(),
		"Error":     e,
		"ApiKey":    ApiKey,
		"ErrorName": e.Error(),
		"Request":   request,
	}

	if params["Class"] == "" {
		params["Class"] = "Panic"
	}

	pwd, err := os.Getwd()
	if err == nil {
		params["Pwd"] = pwd
	}

	params["Backtrace"] = stacktrace(3)

	channel <- params
	return nil
}

func CapturePanic(r *http.Request) {
	if rec := recover(); rec != nil {

		if err, ok := rec.(error); ok {
			log.Printf("Recording err %s", err)
			Error(err, r)
		} else if err, ok := rec.(string); ok {
			log.Printf("Recording string %s", err)
			Error(errors.New(err), r)
		}

		panic(rec)
	}
}

const source = `<?xml version="1.0" encoding="UTF-8"?>
<notice version="2.0">
  <api-key>{{ .ApiKey }}</api-key>
  <notifier>
    <name>Airbrake Golang</name>
    <version>0.0.1</version>
    <url>http://airbrake.io</url>
  </notifier>
  <error>
    <class>{{ html .Class }}</class>
    <message>{{ with .ErrorName }}{{html .}}{{ end }}</message>
    <backtrace>
      {{ range .Backtrace }}
      <line method="{{.Function}}" file="{{.File}}" number="{{.Line}}"/>
      {{ end }}
    </backtrace>
  </error>
  {{ with .Request }}
  <request>
    <url>{{ .URL }}</url>
    <component/>
    <action/>
  </request>
  {{ end }}
  <server-environment>
    <environment-name>production</environment-name>
    <project-root>{{ .Pwd }}</project-root>
  </server-environment>
</notice>`
<|endoftext|>


<commit_before>package topgun_test

import (
	"encoding/json"
	"net/http"
	"time"

	"github.com/concourse/atc"
	_ "github.com/lib/pq"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
)

var _ = Describe("Multiple ATCs Login Session Test", func() {
	Context("with two atcs available", func() {
		var atcs []boshInstance
		var atc0URL string
		var atc1URL string
		var client *http.Client
		var manifestFile string

		JustBeforeEach(func() {
			By("Configuring two ATCs")
			Deploy(manifestFile)
			waitForRunningWorker()

			atcs = JobInstances("atc")
			atc0URL = "http://" + atcs[0].IP + ":8080"
			atc1URL = "http://" + atcs[1].IP + ":8080"
		})

		AfterEach(func() {
			restartSession := spawnBosh("start", atcs[0].Name)
			<-restartSession.Exited
			Eventually(restartSession).Should(gexec.Exit(0))
		})

		Context("Using database storage for dex", func() {
			BeforeEach(func() {
				manifestFile = "deployments/concourse-two-atcs-slow-tracking.yml"
			})

			It("uses the same client for multiple ATCs", func() {
				var numClient int
				err := psql.Select("COUNT(*)").From("client").RunWith(dbConn).QueryRow().Scan(&numClient)
				Expect(err).ToNot(HaveOccurred())
				Expect(numClient).To(Equal(1))
			})
		})

		Context("make api request to a different atc by a token from a stopped atc", func() {
			BeforeEach(func() {
				manifestFile = "deployments/concourse-two-atcs-slow-tracking.yml"
			})

			It("request successfully", func() {
				var (
					err       error
					request   *http.Request
					response  *http.Response
					reqHeader http.Header
				)

				client = &http.Client{}
				token, err := fetchToken(atc0URL, "some-user", "password")
				Expect(err).ToNot(HaveOccurred())

				By("stopping the first atc")
				stopSession := spawnBosh("stop", atcs[0].Name)
				Eventually(stopSession).Should(gexec.Exit(0))

				reqHeader = http.Header{}
				reqHeader.Set("Authorization", "Bearer "+token.AccessToken)

				By("make request with the token to second atc")
				request, err = http.NewRequest("GET", atc1URL+"/api/v1/workers", nil)
				request.Header = reqHeader
				Expect(err).NotTo(HaveOccurred())

				response, err = client.Do(request)
				Expect(err).NotTo(HaveOccurred())

				var workers []atc.Worker
				err = json.NewDecoder(response.Body).Decode(&workers)
				Expect(err).NotTo(HaveOccurred())
			})
		})

		Context("when two atcs have the same external url (dex redirect uri is the same)", func() {
			BeforeEach(func() {
				manifestFile = "deployments/concourse-two-atcs-with-same-redirect-uri.yml"
			})

			It("is able to login to both atcs", func() {
				Eventually(func() *gexec.Session {
					return flyLogin("-c", atc0URL).Wait()
				}, 2*time.Minute).Should(gexec.Exit(0))

				Eventually(func() *gexec.Session {
					return flyLogin("-c", atc1URL).Wait()
				}, 2*time.Minute).Should(gexec.Exit(0))
			})
		})
	})
})
<commit_msg>Update multiple atc login test<commit_after>package topgun_test

import (
	"encoding/json"
	"net/http"
	"time"

	"github.com/concourse/atc"
	_ "github.com/lib/pq"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
)

var _ = Describe("Multiple ATCs Login Session Test", func() {
	Context("with two atcs available", func() {
		var atcs []boshInstance
		var atc0URL string
		var atc1URL string
		var client *http.Client
		var manifestFile string

		JustBeforeEach(func() {
			By("Configuring two ATCs")
			Deploy(manifestFile)
			waitForRunningWorker()

			atcs = JobInstances("atc")
			atc0URL = "http://" + atcs[0].IP + ":8080"
			atc1URL = "http://" + atcs[1].IP + ":8080"
		})

		AfterEach(func() {
			restartSession := spawnBosh("start", atcs[0].Name)
			<-restartSession.Exited
			Eventually(restartSession).Should(gexec.Exit(0))
		})

		Context("Using database storage for dex", func() {
			BeforeEach(func() {
				manifestFile = "deployments/concourse-two-atcs-slow-tracking.yml"
			})

			It("uses the same client for multiple ATCs", func() {
				var numClient int
				err := psql.Select("COUNT(*)").From("client").RunWith(dbConn).QueryRow().Scan(&numClient)
				Expect(err).ToNot(HaveOccurred())
				Expect(numClient).To(Equal(1))
			})
		})

		Context("make api request to a different atc by a token from a stopped atc", func() {
			BeforeEach(func() {
				manifestFile = "deployments/concourse-two-atcs-slow-tracking.yml"
			})

			It("request successfully", func() {
				var (
					err       error
					request   *http.Request
					response  *http.Response
					reqHeader http.Header
				)

				client = &http.Client{}
				token, err := fetchToken(atc0URL, "some-user", "password")
				Expect(err).ToNot(HaveOccurred())

				By("stopping the first atc")
				stopSession := spawnBosh("stop", atcs[0].Name)
				Eventually(stopSession).Should(gexec.Exit(0))

				reqHeader = http.Header{}
				reqHeader.Set("Authorization", "Bearer "+token.AccessToken)

				By("make request with the token to second atc")
				request, err = http.NewRequest("GET", atc1URL+"/api/v1/workers", nil)
				request.Header = reqHeader
				Expect(err).NotTo(HaveOccurred())

				response, err = client.Do(request)
				Expect(err).NotTo(HaveOccurred())

				var workers []atc.Worker
				err = json.NewDecoder(response.Body).Decode(&workers)
				Expect(err).NotTo(HaveOccurred())
			})
		})

		Context("when two atcs have the same external url (dex redirect uri is the same)", func() {
			BeforeEach(func() {
				manifestFile = "deployments/concourse-two-atcs-with-same-redirect-uri.yml"
			})

			It("should be able to login to both ATCs", func() {
				Eventually(func() *gexec.Session {
					return flyLogin("-c", atc0URL).Wait()
				}, 2*time.Minute).Should(gexec.Exit(0))

				Eventually(func() *gexec.Session {
					return flyLogin("-c", atc1URL).Wait()
				}, 2*time.Minute).Should(gexec.Exit(0))

				By("Deploying a second time (with a different token signing key")
				Deploy(manifestFile)

				Eventually(func() *gexec.Session {
					return flyLogin("-c", atc0URL).Wait()
				}, 2*time.Minute).Should(gexec.Exit(0))

				Eventually(func() *gexec.Session {
					return flyLogin("-c", atc1URL).Wait()
				}, 2*time.Minute).Should(gexec.Exit(0))
			})
		})
	})
})
<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-1.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import "fmt"

func main() {
    fmt.Printf("IBM header.\n")
}
<commit_msg>Delete bad-apache-license.go<commit_after><|endoftext|>


<commit_before>package githop

import (
	"fmt"
	"sort"
	"strings"
	"time"

	"github.com/google/go-github/github"
)

const (
	DisplayDateFormat        = "3:04pm"
	DisplayDateTooltipFormat = "Monday January 2 3:04pm"
)

type DigestCommit struct {
	DisplaySHA       string
	URL              string
	Title            string
	Message          string
	PushDate         time.Time
	CommitDate       time.Time
	RepositoryCommit *github.RepositoryCommit
}

func newDigestCommit(commit *github.RepositoryCommit, repo *github.Repository, location *time.Location) DigestCommit {
	messagePieces := strings.SplitN(*commit.Commit.Message, "\n", 2)
	title := messagePieces[0]
	message := ""
	if len(messagePieces) == 2 {
		message = messagePieces[1]
	}
	return DigestCommit{
		DisplaySHA:       (*commit.SHA)[:7],
		URL:              fmt.Sprintf("https://github.com/%s/commit/%s", *repo.FullName, *commit.SHA),
		Title:            title,
		Message:          message,
		PushDate:         commit.Commit.Committer.Date.In(location),
		CommitDate:       commit.Commit.Author.Date.In(location),
		RepositoryCommit: commit,
	}
}

func (commit DigestCommit) DisplayDate() string {
	// Prefer the date the comit was pushed, since that's what GitHub filters
	// and sorts by.
	return commit.PushDate.Format(DisplayDateFormat)
}

func (commit DigestCommit) DisplayDateTooltip() string {
	// But show the full details in a tooltip
	return fmt.Sprintf(
		"Pushed at %s\nCommited at %s",
		commit.PushDate.Format(DisplayDateTooltipFormat),
		commit.CommitDate.Format(DisplayDateTooltipFormat))
}

type RepoDigest struct {
	Repo    *github.Repository
	Commits []DigestCommit
}

// sort.Interface implementation for sorting RepoDigests.
type ByRepoFullName []*RepoDigest

func (a ByRepoFullName) Len() int           { return len(a) }
func (a ByRepoFullName) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByRepoFullName) Less(i, j int) bool { return *a[i].Repo.FullName < *a[j].Repo.FullName }

type Digest struct {
	User             *github.User
	StartTime        time.Time
	EndTime          time.Time
	TimezoneLocation *time.Location
	RepoDigests      []*RepoDigest
}

func newDigest(githubClient *github.Client, account *Account) (*Digest, error) {
	user, _, err := githubClient.Users.Get("")
	if err != nil {
		return nil, err
	}

	// The username parameter must be left blank so that we can get all of the
	// repositories the user has access to, not just ones that they own.
	repos, _, err := githubClient.Repositories.List("", nil)
	if err != nil {
		return nil, err
	}

	orgs, _, err := githubClient.Organizations.List("", nil)
	if err != nil {
		return nil, err
	}
	for _, org := range orgs {
		orgRepos, _, err := githubClient.Repositories.ListByOrg(*org.Login, nil)
		if err != nil {
			return nil, err
		}
		newRepos := make([]github.Repository, len(repos)+len(orgRepos))
		copy(newRepos, repos)
		copy(newRepos[len(repos):], orgRepos)
		repos = newRepos
	}

	now := time.Now().In(account.TimezoneLocation)
	digestStartTime := time.Date(now.Year()-1, now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
	digestEndTime := digestStartTime.AddDate(0, 0, 1)

	// Only look at repos that may have activity in the digest interval.
	var digestRepos []github.Repository
	for _, repo := range repos {
		if repo.CreatedAt.Before(digestEndTime) && repo.PushedAt.After(digestStartTime) {
			digestRepos = append(digestRepos, repo)
		}
	}
	repos = digestRepos
	digest := &Digest{
		User:             user,
		RepoDigests:      make([]*RepoDigest, 0, len(repos)),
		StartTime:        digestStartTime,
		EndTime:          digestEndTime,
		TimezoneLocation: account.TimezoneLocation,
	}
	err = digest.fetch(repos, githubClient)
	return digest, err
}

func (digest *Digest) fetch(repos []github.Repository, githubClient *github.Client) error {
	type RepoDigestResponse struct {
		repoDigest *RepoDigest
		err        error
	}
	ch := make(chan *RepoDigestResponse)
	for _, repo := range repos {
		go func(repo github.Repository) {
			commits, _, err := githubClient.Repositories.ListCommits(
				*repo.Owner.Login,
				*repo.Name,
				&github.CommitsListOptions{
					Author: *digest.User.Login,
					Since:  digest.StartTime.UTC(),
					Until:  digest.EndTime.UTC(),
				})
			if err != nil {
				ch <- &RepoDigestResponse{nil, err}
			} else {
				digestCommits := make([]DigestCommit, 0, len(commits))
				for i, _ := range commits {
					digestCommits = append(digestCommits, newDigestCommit(&commits[i], &repo, digest.TimezoneLocation))
				}
				ch <- &RepoDigestResponse{&RepoDigest{&repo, digestCommits}, nil}
			}
		}(repo)
	}
	for i := 0; i < len(repos); i++ {
		select {
		case r := <-ch:
			if r.err != nil {
				return r.err
			}
			if len(r.repoDigest.Commits) > 0 {
				digest.RepoDigests = append(digest.RepoDigests, r.repoDigest)
			}
		}
	}
	sort.Sort(ByRepoFullName(digest.RepoDigests))
	return nil
}

func (digest *Digest) DisplayDate() string {
	return digest.StartTime.Format("January 2, 2006 was a Monday")
}
<commit_msg>Skip over repositories that have never been pushed to.<commit_after>package githop

import (
	"fmt"
	"sort"
	"strings"
	"time"

	"github.com/google/go-github/github"
)

const (
	DisplayDateFormat        = "3:04pm"
	DisplayDateTooltipFormat = "Monday January 2 3:04pm"
)

type DigestCommit struct {
	DisplaySHA       string
	URL              string
	Title            string
	Message          string
	PushDate         time.Time
	CommitDate       time.Time
	RepositoryCommit *github.RepositoryCommit
}

func newDigestCommit(commit *github.RepositoryCommit, repo *github.Repository, location *time.Location) DigestCommit {
	messagePieces := strings.SplitN(*commit.Commit.Message, "\n", 2)
	title := messagePieces[0]
	message := ""
	if len(messagePieces) == 2 {
		message = messagePieces[1]
	}
	return DigestCommit{
		DisplaySHA:       (*commit.SHA)[:7],
		URL:              fmt.Sprintf("https://github.com/%s/commit/%s", *repo.FullName, *commit.SHA),
		Title:            title,
		Message:          message,
		PushDate:         commit.Commit.Committer.Date.In(location),
		CommitDate:       commit.Commit.Author.Date.In(location),
		RepositoryCommit: commit,
	}
}

func (commit DigestCommit) DisplayDate() string {
	// Prefer the date the comit was pushed, since that's what GitHub filters
	// and sorts by.
	return commit.PushDate.Format(DisplayDateFormat)
}

func (commit DigestCommit) DisplayDateTooltip() string {
	// But show the full details in a tooltip
	return fmt.Sprintf(
		"Pushed at %s\nCommited at %s",
		commit.PushDate.Format(DisplayDateTooltipFormat),
		commit.CommitDate.Format(DisplayDateTooltipFormat))
}

type RepoDigest struct {
	Repo    *github.Repository
	Commits []DigestCommit
}

// sort.Interface implementation for sorting RepoDigests.
type ByRepoFullName []*RepoDigest

func (a ByRepoFullName) Len() int           { return len(a) }
func (a ByRepoFullName) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByRepoFullName) Less(i, j int) bool { return *a[i].Repo.FullName < *a[j].Repo.FullName }

type Digest struct {
	User             *github.User
	StartTime        time.Time
	EndTime          time.Time
	TimezoneLocation *time.Location
	RepoDigests      []*RepoDigest
}

func newDigest(githubClient *github.Client, account *Account) (*Digest, error) {
	user, _, err := githubClient.Users.Get("")
	if err != nil {
		return nil, err
	}

	// The username parameter must be left blank so that we can get all of the
	// repositories the user has access to, not just ones that they own.
	repos, _, err := githubClient.Repositories.List("", nil)
	if err != nil {
		return nil, err
	}

	orgs, _, err := githubClient.Organizations.List("", nil)
	if err != nil {
		return nil, err
	}
	for _, org := range orgs {
		orgRepos, _, err := githubClient.Repositories.ListByOrg(*org.Login, nil)
		if err != nil {
			return nil, err
		}
		newRepos := make([]github.Repository, len(repos)+len(orgRepos))
		copy(newRepos, repos)
		copy(newRepos[len(repos):], orgRepos)
		repos = newRepos
	}

	now := time.Now().In(account.TimezoneLocation)
	digestStartTime := time.Date(now.Year()-1, now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
	digestEndTime := digestStartTime.AddDate(0, 0, 1)

	// Only look at repos that may have activity in the digest interval.
	var digestRepos []github.Repository
	for _, repo := range repos {
		if repo.CreatedAt.Before(digestEndTime) && repo.PushedAt != nil &&
			repo.PushedAt.After(digestStartTime) {
			digestRepos = append(digestRepos, repo)
		}
	}
	repos = digestRepos
	digest := &Digest{
		User:             user,
		RepoDigests:      make([]*RepoDigest, 0, len(repos)),
		StartTime:        digestStartTime,
		EndTime:          digestEndTime,
		TimezoneLocation: account.TimezoneLocation,
	}
	err = digest.fetch(repos, githubClient)
	return digest, err
}

func (digest *Digest) fetch(repos []github.Repository, githubClient *github.Client) error {
	type RepoDigestResponse struct {
		repoDigest *RepoDigest
		err        error
	}
	ch := make(chan *RepoDigestResponse)
	for _, repo := range repos {
		go func(repo github.Repository) {
			commits, _, err := githubClient.Repositories.ListCommits(
				*repo.Owner.Login,
				*repo.Name,
				&github.CommitsListOptions{
					Author: *digest.User.Login,
					Since:  digest.StartTime.UTC(),
					Until:  digest.EndTime.UTC(),
				})
			if err != nil {
				ch <- &RepoDigestResponse{nil, err}
			} else {
				digestCommits := make([]DigestCommit, 0, len(commits))
				for i, _ := range commits {
					digestCommits = append(digestCommits, newDigestCommit(&commits[i], &repo, digest.TimezoneLocation))
				}
				ch <- &RepoDigestResponse{&RepoDigest{&repo, digestCommits}, nil}
			}
		}(repo)
	}
	for i := 0; i < len(repos); i++ {
		select {
		case r := <-ch:
			if r.err != nil {
				return r.err
			}
			if len(r.repoDigest.Commits) > 0 {
				digest.RepoDigests = append(digest.RepoDigests, r.repoDigest)
			}
		}
	}
	sort.Sort(ByRepoFullName(digest.RepoDigests))
	return nil
}

func (digest *Digest) DisplayDate() string {
	return digest.StartTime.Format("January 2, 2006 was a Monday")
}
<|endoftext|>


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

package types2_test

import (
	"cmd/compile/internal/syntax"
	"testing"
)

// TestErrorCalls makes sure that check.errorf calls have at
// least 3 arguments (otherwise we should be using check.error).
func TestErrorCalls(t *testing.T) {
	files, err := pkgFiles(".")
	if err != nil {
		t.Fatal(err)
	}

	for _, file := range files {
		syntax.Crawl(file, func(n syntax.Node) bool {
			call, _ := n.(*syntax.CallExpr)
			if call == nil {
				return false
			}
			selx, _ := call.Fun.(*syntax.SelectorExpr)
			if selx == nil {
				return false
			}
			if !(isName(selx.X, "check") && isName(selx.Sel, "errorf")) {
				return false
			}
			// check.errorf calls should have more than 2 arguments:
			// position, format string, and arguments to format
			if n := len(call.ArgList); n <= 2 {
				t.Errorf("%s: got %d arguments, want > 2", call.Pos(), n)
				return true
			}
			return false
		})
	}
}

func isName(n syntax.Node, name string) bool {
	if n, ok := n.(*syntax.Name); ok {
		return n.Value == name
	}
	return false
}
<commit_msg>cmd/compile/internal/types2: adjust errorcalls_test and apply it<commit_after>// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE ast.

package types2_test

import (
	"cmd/compile/internal/syntax"
	"testing"
)

const errorfMinArgCount = 4

// TestErrorCalls makes sure that check.errorf calls have at least
// errorfMinArgCount arguments (otherwise we should use check.error).
func TestErrorCalls(t *testing.T) {
	files, err := pkgFiles(".")
	if err != nil {
		t.Fatal(err)
	}

	for _, file := range files {
		syntax.Crawl(file, func(n syntax.Node) bool {
			call, _ := n.(*syntax.CallExpr)
			if call == nil {
				return false
			}
			selx, _ := call.Fun.(*syntax.SelectorExpr)
			if selx == nil {
				return false
			}
			if !(isName(selx.X, "check") && isName(selx.Sel, "errorf")) {
				return false
			}
			// check.errorf calls should have at least errorfMinArgCount arguments:
			// position, code, format string, and arguments to format
			if n := len(call.ArgList); n < errorfMinArgCount {
				t.Errorf("%s: got %d arguments, want at least %d", call.Pos(), n, errorfMinArgCount)
				return false
			}
			return false
		})
	}
}

func isName(n syntax.Node, name string) bool {
	if n, ok := n.(*syntax.Name); ok {
		return n.Value == name
	}
	return false
}
<|endoftext|>


<commit_before>package revmgo

import (
	"fmt"
	"github.com/revel/revel"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"reflect"
)

var (
	Config    *MongoConfig
	Session   *mgo.Session
	Database  *mgo.Database
	Duplicate func() *mgo.Session
)

type MongoConfig struct {
	Host   string
	Method string
	Db     string
}

func Init() {
	h := revel.Config.StringDefault("revmgo.host", "localhost")
	m := revel.Config.StringDefault("revmgo.method", "clone")
	d := revel.Config.StringDefault("revmgo.database", "test")

	Config = &MongoConfig{h, m, d}

	if err := Dial(); err != nil {
		revel.ERROR.Fatal(err)
	}

	revel.TypeBinders[reflect.TypeOf(bson.NewObjectId())] = revel.Binder{
		Bind: revel.ValueBinder(func(val string, typ reflect.Type) reflect.Value {
			if len(val) == 0 {
				return reflect.Zero(typ)
			}
			if bson.IsObjectIdHex(val) {
				objId := bson.ObjectIdHex(val)
				return reflect.ValueOf(objId)
			} else {
				revel.ERROR.Print("Invalid ObjectId")
				return reflect.Zero(typ)
			}
		}),
		Unbind: func(output map[string]string, name string, val interface{}) {

			hexStr := fmt.Sprintf("%s", val.(bson.ObjectId).Hex())

			if bson.IsObjectIdHex(hexStr) {
				output[name] = hexStr
			} else {
				revel.ERROR.Print("Invalid ObjectId")
				output[name] = ""
			}
		},
	}
}

func Dial() error {

	var (
		m   func() *mgo.Session
		err error
	)

	Session, err = mgo.Dial(Config.Host)

	if err != nil {
		return err
	}

	revel.INFO.Println("Mongo session started")

	switch Config.Method {
	case "clone":
		m = Session.Clone
	case "copy":
		m = Session.Copy
	case "new":
		m = Session.New
	}

	if m == nil {
		revel.WARN.Printf("Method %s is not allowed.", Config.Method)
		Config.Method = "clone"
		m = Session.Clone
	}

	Duplicate = m

	Database = Session.DB(Config.Db)

	return nil
}

type MongoController struct {
	*revel.Controller
	MongoSession *mgo.Session
	Database     *mgo.Database
}

func (m *MongoController) Begin() revel.Result {

	if Session == nil {
		if err := Dial(); err != nil {
			return m.RenderError(err)
		}
	}

	m.MongoSession = Duplicate()
	m.Database = m.MongoSession.DB(Config.Db)

	return nil

}

func (m *MongoController) End() revel.Result {

	if m.MongoSession != nil {
		m.MongoSession.Close()
		m.Database = nil
	}

	return nil
}

func init() {
	revel.OnAppStart(Init)
	revel.InterceptMethod((*MongoController).Begin, revel.BEFORE)
	revel.InterceptMethod((*MongoController).End, revel.FINALLY)
}
<commit_msg>Readding comments<commit_after>package revmgo

import (
	"fmt"
	"github.com/revel/revel"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"reflect"
)

var (
	// Global config
	Config    *MongoConfig
	// Global mgo Session
	Session   *mgo.Session
	// Global mgo Database
	Database  *mgo.Database
	// Optimization: Stores the method to call in mgoSessionDupl so that it only
	// has to be looked up once (or whenever Session changes)
	Duplicate func() *mgo.Session
)

type MongoConfig struct {
	Host   string
	Method string
	Db     string
}

func Init() {
	// Read configuration.
	h := revel.Config.StringDefault("revmgo.host", "localhost")
	m := revel.Config.StringDefault("revmgo.method", "clone")
	d := revel.Config.StringDefault("revmgo.database", "test")

	Config = &MongoConfig{h, m, d}

	// Let's try to connect to Mongo DB right upon starting revel but don't
-	// raise an error. Errors will be handled if there is actually a request
	if err := Dial(); err != nil {
		revel.WARN.Printf("Could not connect to Mongo DB. Error: %s", err)
	}

	// register the custom bson.ObjectId binder
	revel.TypeBinders[reflect.TypeOf(bson.NewObjectId())] = revel.Binder{
		// Make a ObjectId from a request containing it in string format.
		Bind: revel.ValueBinder(func(val string, typ reflect.Type) reflect.Value {
			if len(val) == 0 {
				return reflect.Zero(typ)
			}
			if bson.IsObjectIdHex(val) {
				objId := bson.ObjectIdHex(val)
				return reflect.ValueOf(objId)
			} else {
				revel.ERROR.Print("Invalid ObjectId")
				return reflect.Zero(typ)
			}
		}),
		// Turns ObjectId back to hexString for reverse routing
		Unbind: func(output map[string]string, name string, val interface{}) {

			hexStr := fmt.Sprintf("%s", val.(bson.ObjectId).Hex())

			if bson.IsObjectIdHex(hexStr) {
				output[name] = hexStr
			} else {
				revel.ERROR.Print("Invalid ObjectId")
				output[name] = ""
			}
		},
	}
}

// Main Dial func
func Dial() error {

	var (
		m   func() *mgo.Session
		err error
	)

	Session, err = mgo.Dial(Config.Host)

	if err != nil {
		return err
	}

	revel.INFO.Println("Mongo session started")

	switch Config.Method {
	case "clone":
		m = Session.Clone
	case "copy":
		m = Session.Copy
	case "new":
		m = Session.New
	}

	if m == nil {
		revel.WARN.Printf("Method %s is not allowed.", Config.Method)
		Config.Method = "clone"
		m = Session.Clone
	}

	Duplicate = m

	Database = Session.DB(Config.Db)

	return nil
}

type MongoController struct {
	*revel.Controller
	MongoSession *mgo.Session
	Database     *mgo.Database
}

// Connect to mgo if we haven't already and return a copy/new/clone of the session
func (m *MongoController) Begin() revel.Result {
	// We may not be connected yet if revel was started before Mongo DB or
	// Mongo DB was restarted
	if Session == nil {
		if err := Dial(); err != nil {
			// Extend the error description to include that this is a Mongo Error
			err = fmt.Errorf("Could not connect to Mongo DB. Error: %s", err)
			return m.RenderError(err)
		}
	}

	// Calls Clone(), Copy() or New() depending on the configuration
	m.MongoSession = Duplicate()
	m.Database = m.MongoSession.DB(Config.Db)

	return nil

}

// Close the controller session if we have an active one.
func (m *MongoController) End() revel.Result {
	// This is necessary since End() will be called no matter what
	// (revel.FINALLY) so it may not be connected in which case MongoSession
	// were a nil pointer and panic
	if m.MongoSession != nil {
		m.MongoSession.Close()
		m.Database = nil
	}

	return nil
}

func init() {
	revel.OnAppStart(Init)
	revel.InterceptMethod((*MongoController).Begin, revel.BEFORE)
	revel.InterceptMethod((*MongoController).End, revel.FINALLY)
}
<|endoftext|>


<commit_before>package rtime

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

	"github.com/inconshreveable/log15"
	"github.com/juju/errors"
)

type CodeWriter struct {
	code int
	http.ResponseWriter
}

func (c *CodeWriter) WriteHeader(code int) {
	c.code = code
	c.ResponseWriter.WriteHeader(code)
}

func Midddleware(w http.ResponseWriter, r *http.Request) {
	clientIP := r.RemoteAddr
	if colon := strings.LastIndex(clientIP, ":"); colon != -1 {
		clientIP = clientIP[:colon]
	}

	w2 := &CodeWriter{200, w}

	start := time.Now()
	logger := LOGGER.New(
		"url", r.RequestURI, "method", r.Method, "ip", clientIP,
	)
	logger.Info("http_started")
	logger = logger.New(
		"time", log15.Lazy{func() interface{} { return time.Since(start) }},
		"code", log15.Lazy{func() interface{} { return w2.code }},
	)

	defer func() {
		if err := recover(); err != nil {
			err2, ok := err.(error)
			if ok {
				logger.Error(
					"server_error", "err", errors.ErrorStack(err2),
				)
			} else {
				logger.Error(
					"server_uerror", "err", err, "ip", clientIP,
				)
			}
			http.Error(w, http.StatusText(500), 500)
		}
	}()

	ctx := r.Context()
	r = r.WithContext(ctx)

	w.Header().Set("X-Frame-Options", "DENY")
	w.Header().Set("X-Content-Type-Options", "nosniff")
	w.Header().Set("X-XSS-Protection", "1; mode=block")

	http.DefaultServeMux.ServeHTTP(w2, r.WithContext(ctx))

	logger.Info("http_served")
}
<commit_msg>minor<commit_after>package rtime

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

	"github.com/inconshreveable/log15"
	"github.com/juju/errors"
)

type CodeWriter struct {
	code int
	http.ResponseWriter
}

func (c *CodeWriter) WriteHeader(code int) {
	c.code = code
	c.ResponseWriter.WriteHeader(code)
}

func Midddleware(w http.ResponseWriter, r *http.Request) {
	clientIP := r.RemoteAddr
	if colon := strings.LastIndex(clientIP, ":"); colon != -1 {
		clientIP = clientIP[:colon]
	}

	w2 := &CodeWriter{200, w}

	start := time.Now()
	logger := LOGGER.New(
		"url", r.RequestURI, "method", r.Method, "ip", clientIP,
	)
	logger.Info("http_started")
	logger = logger.New(
		"time", log15.Lazy{func() interface{} { return time.Since(start) }},
		"code", log15.Lazy{func() interface{} { return w2.code }},
	)

	defer func() {
		if err := recover(); err != nil {
			err2, ok := err.(error)
			if ok {
				logger.Error(
					"server_error", "err", errors.ErrorStack(err2),
				)
			} else {
				logger.Error(
					"server_uerror", "err", err, "ip", clientIP,
				)
			}
			http.Error(w, http.StatusText(500), 500)
		}
	}()

	w.Header().Set("X-Frame-Options", "DENY")
	w.Header().Set("X-Content-Type-Options", "nosniff")
	w.Header().Set("X-XSS-Protection", "1; mode=block")

	http.DefaultServeMux.ServeHTTP(w2, r)

	logger.Info("http_served")
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"runtime"
	"time"

	"image"
	"image/png"

	_ "code.google.com/p/go.image/tiff"

	"github.com/shurcooL/trayhost"
)

// TODO: Load from config. Have ability to set config.
var hostFlag = flag.String("host", "", "Target server host.")
var debugFlag = flag.Bool("debug", false, "Adds menu items for debugging purposes.")

func instantShareHandler() {
	fmt.Println("grab content, content-type of clipboard")

	img, err := trayhost.GetClipboardImage()
	if err != nil {
		log.Println(err)
		return
	}

	// Convert image to desired destination format (currently, always PNG).
	var imageData []byte
	switch img.Kind {
	case trayhost.ImageKindPng:
		imageData = img.Bytes
	case trayhost.ImageKindTiff:
		m, _, err := image.Decode(bytes.NewReader(img.Bytes))
		if err != nil {
			log.Panicln("image.Decode:", err)
		}

		var buf bytes.Buffer
		err = png.Encode(&buf, m)
		if err != nil {
			log.Panicln("png.Encode:", err)
		}
		imageData = buf.Bytes()
	default:
		log.Println("unsupported source image kind:", img.Kind)
		return
	}

	fmt.Println("request URL")

	resp, err := (&http.Client{Timeout: 1 * time.Second}).Get(*hostFlag + "/api/getfilename?ext=png")
	if err != nil {
		trayhost.Notification{Title: "Upload Failed", Body: err.Error()}.Display()
		log.Println(err)
		return
	}
	defer resp.Body.Close()
	filename, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		trayhost.Notification{Title: "Upload Failed", Body: err.Error()}.Display()
		log.Println(err)
		return
	}

	fmt.Println("display/put URL in clipboard")

	url := *hostFlag + "/" + string(filename)
	trayhost.SetClipboardString(url)
	trayhost.Notification{Title: "Upload Complete", Body: url, Timeout: 3 * time.Second}.Display()

	fmt.Println("upload image in background of size", len(imageData))

	go func() {
		req, err := http.NewRequest("PUT", url, bytes.NewReader(imageData))
		if err != nil {
			log.Println(err)
			return
		}
		req.Header.Set("Content-Type", "application/octet-stream")
		resp, err := http.DefaultClient.Do(req)
		if err != nil {
			log.Println(err)
			return
		}
		_ = resp.Body.Close()
		fmt.Println("done")
	}()
}

func main() {
	flag.Parse()
	runtime.LockOSThread()

	menuItems := []trayhost.MenuItem{
		trayhost.MenuItem{
			Title:   "Instant Share",
			Handler: instantShareHandler,
		},
		trayhost.SeparatorMenuItem(),
		trayhost.MenuItem{
			Title:   "Quit",
			Handler: trayhost.Exit,
		},
	}
	if *debugFlag {
		menuItems = append(menuItems,
			trayhost.SeparatorMenuItem(),
			trayhost.MenuItem{
				Title: "Debug: Get Clipboard String",
				Handler: func() {
					str, err := trayhost.GetClipboardString()
					fmt.Printf("GetClipboardString(): %q %v\n", str, err)
				},
			},
			trayhost.MenuItem{
				Title: "Debug: Get Clipboard Image",
				Handler: func() {
					img, err := trayhost.GetClipboardImage()
					fmt.Printf("GetClipboardImage(): %v len(%v) %v\n", img.Kind, len(img.Bytes), err)
				},
			},
			trayhost.MenuItem{
				Title: "Debug: Set Clipboard",
				Handler: func() {
					trayhost.SetClipboardString("http://www.example.org/image.png")
				},
			},
			trayhost.MenuItem{
				Title: "Debug: Notification",
				Handler: func() {
					trayhost.Notification{Title: "Upload Complete", Body: "http://virtivia.com:27080/1cerx4db9oeih.png", Timeout: 3 * time.Second}.Display()
					//trayhost.Notification{Title: "Upload Failed", Body: "error description goes here"}.Display()
				},
			},
		)
	}

	// TODO: Create a real icon and bake it into the binary.
	// TODO: Optionally, if non-Retina pixel perfection is required, generate or load 1x image and supply that as a second representation.
	iconData, err := ioutil.ReadFile("./icon@2x.png")
	if err != nil {
		panic(err)
	}

	fmt.Println("Starting.")

	trayhost.Initialize("InstantShare", iconData, menuItems)

	trayhost.EnterLoop()

	fmt.Println("Exiting.")
}
<commit_msg>Disable "Instant Share" menu item when there's nothing usable in clipboard.<commit_after>package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"runtime"
	"time"

	"image"
	"image/png"

	_ "code.google.com/p/go.image/tiff"

	"github.com/shurcooL/trayhost"
)

// TODO: Load from config. Have ability to set config.
var hostFlag = flag.String("host", "", "Target server host.")
var debugFlag = flag.Bool("debug", false, "Adds menu items for debugging purposes.")

func instantShareEnabled() bool {
	fmt.Println("check if clipboard contains something usable")

	_, err := trayhost.GetClipboardImage()
	if err != nil {
		return false
	}

	return true
}

func instantShareHandler() {
	fmt.Println("grab content, content-type of clipboard")

	img, err := trayhost.GetClipboardImage()
	if err != nil {
		log.Println(err)
		return
	}

	// Convert image to desired destination format (currently, always PNG).
	var imageData []byte
	switch img.Kind {
	case trayhost.ImageKindPng:
		imageData = img.Bytes
	case trayhost.ImageKindTiff:
		m, _, err := image.Decode(bytes.NewReader(img.Bytes))
		if err != nil {
			log.Panicln("image.Decode:", err)
		}

		var buf bytes.Buffer
		err = png.Encode(&buf, m)
		if err != nil {
			log.Panicln("png.Encode:", err)
		}
		imageData = buf.Bytes()
	default:
		log.Println("unsupported source image kind:", img.Kind)
		return
	}

	fmt.Println("request URL")

	resp, err := (&http.Client{Timeout: 1 * time.Second}).Get(*hostFlag + "/api/getfilename?ext=png")
	if err != nil {
		trayhost.Notification{Title: "Upload Failed", Body: err.Error()}.Display()
		log.Println(err)
		return
	}
	defer resp.Body.Close()
	filename, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		trayhost.Notification{Title: "Upload Failed", Body: err.Error()}.Display()
		log.Println(err)
		return
	}

	fmt.Println("display/put URL in clipboard")

	url := *hostFlag + "/" + string(filename)
	trayhost.SetClipboardString(url)
	trayhost.Notification{Title: "Upload Complete", Body: url, Timeout: 3 * time.Second}.Display()

	fmt.Println("upload image in background of size", len(imageData))

	go func() {
		req, err := http.NewRequest("PUT", url, bytes.NewReader(imageData))
		if err != nil {
			log.Println(err)
			return
		}
		req.Header.Set("Content-Type", "application/octet-stream")
		resp, err := http.DefaultClient.Do(req)
		if err != nil {
			log.Println(err)
			return
		}
		_ = resp.Body.Close()
		fmt.Println("done")
	}()
}

func main() {
	flag.Parse()
	runtime.LockOSThread()

	menuItems := []trayhost.MenuItem{
		trayhost.MenuItem{
			Title:   "Instant Share",
			Enabled: instantShareEnabled,
			Handler: instantShareHandler,
		},
		trayhost.SeparatorMenuItem(),
		trayhost.MenuItem{
			Title:   "Quit",
			Handler: trayhost.Exit,
		},
	}
	if *debugFlag {
		menuItems = append(menuItems,
			trayhost.SeparatorMenuItem(),
			trayhost.MenuItem{
				Title: "Debug: Get Clipboard String",
				Handler: func() {
					str, err := trayhost.GetClipboardString()
					fmt.Printf("GetClipboardString(): %q %v\n", str, err)
				},
			},
			trayhost.MenuItem{
				Title: "Debug: Get Clipboard Image",
				Handler: func() {
					img, err := trayhost.GetClipboardImage()
					fmt.Printf("GetClipboardImage(): %v len(%v) %v\n", img.Kind, len(img.Bytes), err)
				},
			},
			trayhost.MenuItem{
				Title: "Debug: Set Clipboard",
				Handler: func() {
					trayhost.SetClipboardString("http://www.example.com/image.png")
				},
			},
			trayhost.MenuItem{
				Title: "Debug: Notification",
				Handler: func() {
					trayhost.Notification{Title: "Upload Complete", Body: "http://www.example.com/image.png", Timeout: 3 * time.Second}.Display()
					//trayhost.Notification{Title: "Upload Failed", Body: "error description goes here"}.Display()
				},
			},
		)
	}

	// TODO: Create a real icon and bake it into the binary.
	// TODO: Optionally, if non-Retina pixel perfection is required, generate or load 1x image and supply that as a second representation.
	iconData, err := ioutil.ReadFile("./icon@2x.png")
	if err != nil {
		panic(err)
	}

	fmt.Println("Starting.")

	trayhost.Initialize("Instant Share", iconData, menuItems)

	trayhost.EnterLoop()

	fmt.Println("Exiting.")
}
<|endoftext|>


<commit_before>package client

import (
	"fmt"
	//"github.com/gopherjs/gopherjs/js"
	"github.com/gopherjs/jquery"
	//"honnef.co/go/js/console"
	//"reflect"
	"strings"
)

type ViewImpl struct {
	tag      string
	id       string
	classes  []string
	style    string
	children []*ViewImpl
	text     string
	builders []builder
	event    []*eventHandler
}

type option func(*ViewImpl) *ViewImpl

func Event(name EventName, fn EventFunc) option {
	return func(self *ViewImpl) *ViewImpl {
		self.event = append(self.event, &eventHandler{
			name: name,
			fn:   fn,
		})
		return self
	}
}

func Style(s string) option {
	return func(self *ViewImpl) *ViewImpl {
		self.style = self.style
		return self
	}
}

func Class(cl CssClass) option {
	return func(self *ViewImpl) *ViewImpl {
		self.classes = append(self.classes, cl.ClassName())
		return self
	}
}

func Text(str string) option {
	return func(self *ViewImpl) *ViewImpl {
		self.text = str
		return self
	}
}

func IdConstant(id string) option {
	return func(self *ViewImpl) *ViewImpl {
		if id == "" {
			panic("should not be calling IdConstant() with an empty id")
		}
		self.id = id
		return self
	}
}

func Id(id HtmlId) option {
	return func(self *ViewImpl) *ViewImpl {
		if id == nil {
			panic("should not be calling Id() on a nil HtmlId")
		}
		self.id = id.Id()
		return self
	}
}

func ModelId(m ModelName) option {
	return func(self *ViewImpl) *ViewImpl {
		id := fmt.Sprintf("%s-%s", strings.ToLower(self.tag), m.Id())
		self.id = newHtmlIdNoCheck(self.tag, id).Id()
		return self
	}
}

func addBuilder(b builder) option {
	return func(self *ViewImpl) *ViewImpl {
		self.builders = append(self.builders, b)
		return self
	}
}

func CssExistence(c CssClass, b BooleanAttribute) option {
	return addBuilder(cssExistenceBuilder(c, b))
}

func PropEqual(n propName, b BooleanAttribute) option {
	return addBuilder(propBuilder(n, b, nil))
}

func HtmlAttrEqual(h htmlAttrName, attr Attribute) option {
	return addBuilder(htmlAttrBuilder(h, attr, nil))
}

func HtmlAttrConstant(h htmlAttrName, str string) option {
	attr := NewStringSimple(str)
	return addBuilder(htmlAttrBuilder(h, attr, nil))
}

func TextEqual(attr Attribute) option {
	return addBuilder(textAttrBuilder(attr, nil))
}

//BindEqual constrains the attribute provided to be the same as the value of
//the tag this is located in.  Typically, this is an INPUT tag.  Data flows
//_from_ the input text that the user types to the attribute, not the other
//way around. There is a strange, but useful, edge case
//in the initialization of the INPUT tag: ff the attr provided returns a string
//value, that value is used to initialize INPUT field.
func BindEqual(attr Attribute) option {
	return addBuilder(valueAttrBuilder(attr, nil))
}

//Bind constrains the attribute provided to be a function of the value of
//the tag this call is located in.  Typically, this is an INPUT tag.  Data flows
//_from_ the input text that the user types to the attribute via this constraint
//given, not the other way around.  There is a strange, but useful, edge case
//in the initialization of the INPUT tag: ff the attr provided returns a string
//value, that value is used to initialize INPUT field.
func Bind(attr Attribute, cons Constraint) option {
	return addBuilder(valueAttrBuilder(attr, cons))
}

//HtmlIdFromModel returns an HtmlId object from the given modelname and
//tagname.  Resulting id is unique to the modelname and tag, but not
//between tags with the same name.
func HtmlIdFromModel(tag string, m ModelName) HtmlId {
	id := fmt.Sprintf("%s-%s", strings.ToLower(tag), m.Id())
	return NewHtmlId(tag, id)
}

//ParseHtml returns a NarrowDom that points at the fragment
//of HTML provided in t.  No attempt is made to validate that
//the HTML is sensible, much less syntatically correct.
func ParseHtml(t string) NarrowDom {
	parsed := jquery.ParseHTML(t)
	var nDom NarrowDom
	if TestMode {
		nDom = newTestOps()
	} else {
		nDom = wrap(jquery.NewJQuery(parsed[0]))
	}
	return nDom
}

func (p *ViewImpl) Build() NarrowDom {
	id := ""
	classes := ""
	styles := ""
	if p.id != "" {
		id = fmt.Sprintf(" id='%s'", p.id)
	}

	if p.classes != nil {
		classes = fmt.Sprintf(" class='%s'", strings.Join(p.classes, " "))
	}
	if p.style != "" {
		styles = fmt.Sprintf(" style='%s'", p.style)
	}

	var t string
	if p.text == "" {
		t = fmt.Sprintf("<%s%s%s%s/>", p.tag, id, classes, styles)
	} else {
		t = fmt.Sprintf("<%s%s%s%s>%s</%s>", p.tag, id, classes, styles, p.text, p.tag)
	}
	nDom := ParseHtml(t)
	for _, child := range p.children {
		built := child.Build()
		nDom.Append(built)
	}

	if p.builders != nil {
		for _, b := range p.builders {
			if b == nil {
				panic("found a nil builder in tree construction!")
			}
			b.build(nDom)
		}
	}

	if p.event != nil {
		for _, h := range p.event {
			//we have the object now, assign to j
			h.t = nDom
			h.register()
		}
	}
	return nDom
}

func IMG(obj ...interface{}) *ViewImpl {
	return tag("img", obj...)
}

func FORM(obj ...interface{}) *ViewImpl {
	return tag("form", obj...)
}

func DIV(obj ...interface{}) *ViewImpl {
	return tag("div", obj...)
}

func INPUT(obj ...interface{}) *ViewImpl {
	return tag("input", obj...)
}

func TEXTAREA(obj ...interface{}) *ViewImpl {
	return tag("textarea", obj...)
}

func LABEL(obj ...interface{}) *ViewImpl {
	return tag("label", obj...)
}

func A(obj ...interface{}) *ViewImpl {
	return tag("a", obj...)
}

func SPAN(obj ...interface{}) *ViewImpl {
	return tag("span", obj...)
}

func STRONG(obj ...interface{}) *ViewImpl {
	return tag("strong", obj...)
}
func P(obj ...interface{}) *ViewImpl {
	return tag("p", obj...)
}

func EM(obj ...interface{}) *ViewImpl {
	return tag("em", obj...)
}

func H1(obj ...interface{}) *ViewImpl {
	return tag("h1", obj...)
}

func H2(obj ...interface{}) *ViewImpl {
	return tag("h2", obj...)
}

func H3(obj ...interface{}) *ViewImpl {
	return tag("h3", obj...)
}

func H4(obj ...interface{}) *ViewImpl {
	return tag("h4", obj...)
}

func H5(obj ...interface{}) *ViewImpl {
	return tag("h5", obj...)
}

func H6(obj ...interface{}) *ViewImpl {
	return tag("h6", obj...)
}

func HR(obj ...interface{}) *ViewImpl {
	return tag("hr", obj...)
}

func LI(obj ...interface{}) *ViewImpl {
	return tag("li", obj...)
}

func UL(obj ...interface{}) *ViewImpl {
	return tag("ul", obj...)
}
func OL(obj ...interface{}) *ViewImpl {
	return tag("ol", obj...)
}
func BUTTON(obj ...interface{}) *ViewImpl {
	return tag("button", obj...)
}
func PRE(obj ...interface{}) *ViewImpl {
	return tag("pre", obj...)
}

func tag(tagName string, obj ...interface{}) *ViewImpl {
	p := &ViewImpl{tag: tagName}

	for i := 0; i < len(obj); i++ {
		if obj[i] == nil {
			panic("nil value in view construction")
		}
		opt, ok := obj[i].(option)
		if ok {
			opt(p)
			continue
		}
		v, ok := obj[i].(*ViewImpl)
		if v == nil && ok {
			continue
		}
		if ok {
			p.children = append(p.children, v)
			continue
		}
		varr, isArray := obj[i].([]*ViewImpl)
		if isArray {
			for _, v := range varr {
				if v != nil {
					p.children = append(p.children, v)
				}
			}
			continue
		}
		panic(fmt.Sprintf("unable to understand type of parameter: %v (%T %d) to %s", obj[i], obj[i], i, tagName))
	}
	return p
}
<commit_msg>make Style() work<commit_after>package client

import (
	"fmt"
	//"github.com/gopherjs/gopherjs/js"
	"github.com/gopherjs/jquery"
	//"honnef.co/go/js/console"
	//"reflect"
	"strings"
)

type ViewImpl struct {
	tag      string
	id       string
	classes  []string
	style    string
	children []*ViewImpl
	text     string
	builders []builder
	event    []*eventHandler
}

type option func(*ViewImpl) *ViewImpl

func Event(name EventName, fn EventFunc) option {
	return func(self *ViewImpl) *ViewImpl {
		self.event = append(self.event, &eventHandler{
			name: name,
			fn:   fn,
		})
		return self
	}
}

func Style(s string) option {
	return func(self *ViewImpl) *ViewImpl {
		self.style = self.style + s
		return self
	}
}

func Class(cl CssClass) option {
	return func(self *ViewImpl) *ViewImpl {
		self.classes = append(self.classes, cl.ClassName())
		return self
	}
}

func Text(str string) option {
	return func(self *ViewImpl) *ViewImpl {
		self.text = str
		return self
	}
}

func IdConstant(id string) option {
	return func(self *ViewImpl) *ViewImpl {
		if id == "" {
			panic("should not be calling IdConstant() with an empty id")
		}
		self.id = id
		return self
	}
}

func Id(id HtmlId) option {
	return func(self *ViewImpl) *ViewImpl {
		if id == nil {
			panic("should not be calling Id() on a nil HtmlId")
		}
		self.id = id.Id()
		return self
	}
}

func ModelId(m ModelName) option {
	return func(self *ViewImpl) *ViewImpl {
		id := fmt.Sprintf("%s-%s", strings.ToLower(self.tag), m.Id())
		self.id = newHtmlIdNoCheck(self.tag, id).Id()
		return self
	}
}

func addBuilder(b builder) option {
	return func(self *ViewImpl) *ViewImpl {
		self.builders = append(self.builders, b)
		return self
	}
}

func CssExistence(c CssClass, b BooleanAttribute) option {
	return addBuilder(cssExistenceBuilder(c, b))
}

func PropEqual(n propName, b BooleanAttribute) option {
	return addBuilder(propBuilder(n, b, nil))
}

func HtmlAttrEqual(h htmlAttrName, attr Attribute) option {
	return addBuilder(htmlAttrBuilder(h, attr, nil))
}

func HtmlAttrConstant(h htmlAttrName, str string) option {
	attr := NewStringSimple(str)
	return addBuilder(htmlAttrBuilder(h, attr, nil))
}

func TextEqual(attr Attribute) option {
	return addBuilder(textAttrBuilder(attr, nil))
}

//BindEqual constrains the attribute provided to be the same as the value of
//the tag this is located in.  Typically, this is an INPUT tag.  Data flows
//_from_ the input text that the user types to the attribute, not the other
//way around. There is a strange, but useful, edge case
//in the initialization of the INPUT tag: ff the attr provided returns a string
//value, that value is used to initialize INPUT field.
func BindEqual(attr Attribute) option {
	return addBuilder(valueAttrBuilder(attr, nil))
}

//Bind constrains the attribute provided to be a function of the value of
//the tag this call is located in.  Typically, this is an INPUT tag.  Data flows
//_from_ the input text that the user types to the attribute via this constraint
//given, not the other way around.  There is a strange, but useful, edge case
//in the initialization of the INPUT tag: ff the attr provided returns a string
//value, that value is used to initialize INPUT field.
func Bind(attr Attribute, cons Constraint) option {
	return addBuilder(valueAttrBuilder(attr, cons))
}

//HtmlIdFromModel returns an HtmlId object from the given modelname and
//tagname.  Resulting id is unique to the modelname and tag, but not
//between tags with the same name.
func HtmlIdFromModel(tag string, m ModelName) HtmlId {
	id := fmt.Sprintf("%s-%s", strings.ToLower(tag), m.Id())
	return NewHtmlId(tag, id)
}

//ParseHtml returns a NarrowDom that points at the fragment
//of HTML provided in t.  No attempt is made to validate that
//the HTML is sensible, much less syntatically correct.
func ParseHtml(t string) NarrowDom {
	parsed := jquery.ParseHTML(t)
	var nDom NarrowDom
	if TestMode {
		nDom = newTestOps()
	} else {
		nDom = wrap(jquery.NewJQuery(parsed[0]))
	}
	return nDom
}

func (p *ViewImpl) Build() NarrowDom {
	id := ""
	classes := ""
	styles := ""
	if p.id != "" {
		id = fmt.Sprintf(" id='%s'", p.id)
	}

	if p.classes != nil {
		classes = fmt.Sprintf(" class='%s'", strings.Join(p.classes, " "))
	}
	if p.style != "" {
		styles = fmt.Sprintf(" style='%s'", p.style)
	}

	var t string
	if p.text == "" {
		t = fmt.Sprintf("<%s%s%s%s/>", p.tag, id, classes, styles)
	} else {
		t = fmt.Sprintf("<%s%s%s%s>%s</%s>", p.tag, id, classes, styles, p.text, p.tag)
	}
	nDom := ParseHtml(t)
	for _, child := range p.children {
		built := child.Build()
		nDom.Append(built)
	}

	if p.builders != nil {
		for _, b := range p.builders {
			if b == nil {
				panic("found a nil builder in tree construction!")
			}
			b.build(nDom)
		}
	}

	if p.event != nil {
		for _, h := range p.event {
			//we have the object now, assign to j
			h.t = nDom
			h.register()
		}
	}
	return nDom
}

func IMG(obj ...interface{}) *ViewImpl {
	return tag("img", obj...)
}

func FORM(obj ...interface{}) *ViewImpl {
	return tag("form", obj...)
}

func DIV(obj ...interface{}) *ViewImpl {
	return tag("div", obj...)
}

func INPUT(obj ...interface{}) *ViewImpl {
	return tag("input", obj...)
}

func TEXTAREA(obj ...interface{}) *ViewImpl {
	return tag("textarea", obj...)
}

func LABEL(obj ...interface{}) *ViewImpl {
	return tag("label", obj...)
}

func A(obj ...interface{}) *ViewImpl {
	return tag("a", obj...)
}

func SPAN(obj ...interface{}) *ViewImpl {
	return tag("span", obj...)
}

func STRONG(obj ...interface{}) *ViewImpl {
	return tag("strong", obj...)
}
func P(obj ...interface{}) *ViewImpl {
	return tag("p", obj...)
}

func EM(obj ...interface{}) *ViewImpl {
	return tag("em", obj...)
}

func H1(obj ...interface{}) *ViewImpl {
	return tag("h1", obj...)
}

func H2(obj ...interface{}) *ViewImpl {
	return tag("h2", obj...)
}

func H3(obj ...interface{}) *ViewImpl {
	return tag("h3", obj...)
}

func H4(obj ...interface{}) *ViewImpl {
	return tag("h4", obj...)
}

func H5(obj ...interface{}) *ViewImpl {
	return tag("h5", obj...)
}

func H6(obj ...interface{}) *ViewImpl {
	return tag("h6", obj...)
}

func HR(obj ...interface{}) *ViewImpl {
	return tag("hr", obj...)
}

func LI(obj ...interface{}) *ViewImpl {
	return tag("li", obj...)
}

func UL(obj ...interface{}) *ViewImpl {
	return tag("ul", obj...)
}
func OL(obj ...interface{}) *ViewImpl {
	return tag("ol", obj...)
}
func BUTTON(obj ...interface{}) *ViewImpl {
	return tag("button", obj...)
}
func PRE(obj ...interface{}) *ViewImpl {
	return tag("pre", obj...)
}

func tag(tagName string, obj ...interface{}) *ViewImpl {
	p := &ViewImpl{tag: tagName}

	for i := 0; i < len(obj); i++ {
		if obj[i] == nil {
			panic("nil value in view construction")
		}
		opt, ok := obj[i].(option)
		if ok {
			opt(p)
			continue
		}
		v, ok := obj[i].(*ViewImpl)
		if v == nil && ok {
			continue
		}
		if ok {
			p.children = append(p.children, v)
			continue
		}
		varr, isArray := obj[i].([]*ViewImpl)
		if isArray {
			for _, v := range varr {
				if v != nil {
					p.children = append(p.children, v)
				}
			}
			continue
		}
		panic(fmt.Sprintf("unable to understand type of parameter: %v (%T %d) to %s", obj[i], obj[i], i, tagName))
	}
	return p
}
<|endoftext|>


<commit_before>package xero

import (
	"bytes"
	"encoding/xml"
	"errors"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"

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

type testRoundTrip func(*http.Request) (*http.Response, error)

func (fn testRoundTrip) RoundTrip(r *http.Request) (*http.Response, error) {
	return fn(r)
}

type testGetter func(string, interface{}) error

func (fn testGetter) get(urlStr string, dst interface{}) error {
	return fn(urlStr, dst)
}

type testAuthorizer struct {
	err error
}

func (t *testAuthorizer) AuthorizeRequest(req *http.Request) error {
	return t.err
}

type tHTTPHandler struct {
	t       *testing.T
	handler func(*testing.T, http.ResponseWriter, *http.Request)
}

func (t *tHTTPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	t.handler(t.t, w, r)
}

func TestClient_do(t *testing.T) {
	type testcase struct {
		tname          string
		method         string
		url            string
		authorizer     Authorizer
		client         *http.Client
		ts             func(t *testing.T) *httptest.Server
		expectedStatus int
		expectedError  error
	}
	tt := []testcase{
		testcase{
			tname:      "POST SummarizeErrors = false",
			method:     http.MethodPost,
			authorizer: &testAuthorizer{},
			ts: func(t *testing.T) *httptest.Server {
				handler := &tHTTPHandler{
					t: t,
					handler: func(t *testing.T, w http.ResponseWriter, r *http.Request) {
						assert.Equal(t, http.MethodPost, r.Method)
						assert.Equal(t, "false", r.URL.Query().Get("SummarizeErrors"))
						w.WriteHeader(http.StatusOK)
					},
				}
				return httptest.NewServer(handler)
			},
			expectedStatus: http.StatusOK,
		},
		testcase{
			tname:      "PUT SummarizeErrors = false",
			method:     http.MethodPut,
			authorizer: &testAuthorizer{},
			ts: func(t *testing.T) *httptest.Server {
				handler := &tHTTPHandler{
					t: t,
					handler: func(t *testing.T, w http.ResponseWriter, r *http.Request) {
						assert.Equal(t, http.MethodPut, r.Method)
						assert.Equal(t, "false", r.URL.Query().Get("SummarizeErrors"))
						w.WriteHeader(http.StatusOK)
					},
				}
				return httptest.NewServer(handler)
			},
			expectedStatus: http.StatusOK,
		},
		testcase{
			tname:  "SummarizeErrors bad url",
			method: http.MethodPost,
			url:    "://invalid",
			expectedError: &url.Error{
				Op:  "parse",
				URL: "://invalid",
				Err: errors.New("missing protocol scheme"),
			},
		},
		testcase{
			tname:         "new http request error",
			method:        "bad method",
			expectedError: errors.New("net/http: invalid method \"bad method\""),
		},
		testcase{
			tname:         "authorizer error",
			authorizer:    &testAuthorizer{err: errors.New("authorizer error")},
			expectedError: errors.New("authorizer error"),
		},
		testcase{
			tname:      "ok",
			authorizer: &testAuthorizer{},
			ts: func(*testing.T) *httptest.Server {
				return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
					w.WriteHeader(http.StatusOK)
				}))
			},
			expectedStatus: http.StatusOK,
		},
	}
	for _, tc := range tt {
		t.Run(tc.tname, func(t *testing.T) {
			url := tc.url
			if tc.ts != nil {
				ts := tc.ts(t)
				url = ts.URL
				defer ts.Close()
			}
			client := &Client{
				authorizer: tc.authorizer,
				client:     tc.client,
			}
			rsp, err := client.do(tc.method, url, nil)
			assert.Equal(t, tc.expectedError, err)
			if rsp != nil {
				assert.Equal(t, tc.expectedStatus, rsp.StatusCode)
			}
		})
	}
}

func TestClient_doDecode(t *testing.T) {
	type testcase struct {
		tname         string
		client        *http.Client
		method        string
		urlStr        string
		body          io.Reader
		dst           Response
		expectedError error
		expectedDst   interface{}
	}
	tt := []testcase{
		testcase{
			tname: "request error",
			client: &http.Client{
				Transport: testRoundTrip(func(*http.Request) (*http.Response, error) {
					return nil, errors.New("request error")
				}),
			},
			expectedError: &url.Error{Op: "Get", URL: "", Err: errors.New("request error")},
			expectedDst:   Response{},
		},
		testcase{
			tname: "invalid xml",
			dst:   Response{},
			client: &http.Client{
				Transport: testRoundTrip(func(*http.Request) (*http.Response, error) {
					r := bytes.NewBuffer([]byte("</uwotm8>"))
					return &http.Response{
						Body: ioutil.NopCloser(r),
					}, nil
				}),
			},
			expectedError: &xml.SyntaxError{Msg: "unexpected end element </uwotm8>", Line: 1},
			expectedDst:   Response{},
		},
		testcase{
			tname: "ok",
			dst:   Response{},
			client: &http.Client{
				Transport: testRoundTrip(func(*http.Request) (*http.Response, error) {
					r := bytes.NewBuffer([]byte(`<Response><ProviderName>Foo</ProviderName></Response>`))
					return &http.Response{
						Body: ioutil.NopCloser(r),
					}, nil
				}),
			},
			expectedError: nil,
			expectedDst:   Response{XMLName: xml.Name{Local: "Response"}, ProviderName: "Foo"},
		},
	}
	for _, tc := range tt {
		t.Run(tc.tname, func(t *testing.T) {
			client := &Client{authorizer: new(testAuthorizer), client: tc.client}
			err := client.doDecode(tc.method, tc.urlStr, tc.body, &tc.dst)
			assert.Equal(t, tc.expectedError, err)
			assert.Equal(t, tc.expectedDst, tc.dst)
		})
	}
}

func TestClient_doEncode(t *testing.T) {
	type testcase struct {
		name          string
		enc           func(t *testing.T) Encoder
		client        *http.Client
		rw            io.ReadWriter
		expectedData  []byte
		expectedError error
	}
	tt := []testcase{
		testcase{
			name: "encode error",
			enc: func(t *testing.T) Encoder {
				return &testEncoder{t, func(t *testing.T, w io.Writer) error {
					return errors.New("encoding error")
				}}
			},
			expectedError: errors.New("encoding error"),
		},
		testcase{
			name: "encode error",
			enc: func(t *testing.T) Encoder {
				return &testEncoder{t, func(t *testing.T, w io.Writer) error {
					return nil
				}}
			},
			client: &http.Client{
				Transport: testRoundTrip(func(*http.Request) (*http.Response, error) {
					return nil, errors.New("request error")
				}),
			},
			expectedError: &url.Error{
				Op:  "Post",
				URL: "/?SummarizeErrors=false",
				Err: errors.New("request error"),
			},
		},
	}
	for _, tc := range tt {
		t.Run(tc.name, func(t *testing.T) {
			client := &Client{authorizer: new(testAuthorizer), client: tc.client}
			err := client.doEncode(http.MethodPost, "/", tc.enc(t))
			assert.Equal(t, tc.expectedError, err, "%s", err)
		})
	}
}
<commit_msg>Positive test case for doEncode<commit_after>package xero

import (
	"bytes"
	"encoding/xml"
	"errors"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"

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

type testRoundTrip func(*http.Request) (*http.Response, error)

func (fn testRoundTrip) RoundTrip(r *http.Request) (*http.Response, error) {
	return fn(r)
}

type testGetter func(string, interface{}) error

func (fn testGetter) get(urlStr string, dst interface{}) error {
	return fn(urlStr, dst)
}

type testAuthorizer struct {
	err error
}

func (t *testAuthorizer) AuthorizeRequest(req *http.Request) error {
	return t.err
}

type tHTTPHandler struct {
	t       *testing.T
	handler func(*testing.T, http.ResponseWriter, *http.Request)
}

func (t *tHTTPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	t.handler(t.t, w, r)
}

func TestClient_do(t *testing.T) {
	type testcase struct {
		tname          string
		method         string
		url            string
		authorizer     Authorizer
		client         *http.Client
		ts             func(t *testing.T) *httptest.Server
		expectedStatus int
		expectedError  error
	}
	tt := []testcase{
		testcase{
			tname:      "POST SummarizeErrors = false",
			method:     http.MethodPost,
			authorizer: &testAuthorizer{},
			ts: func(t *testing.T) *httptest.Server {
				handler := &tHTTPHandler{
					t: t,
					handler: func(t *testing.T, w http.ResponseWriter, r *http.Request) {
						assert.Equal(t, http.MethodPost, r.Method)
						assert.Equal(t, "false", r.URL.Query().Get("SummarizeErrors"))
						w.WriteHeader(http.StatusOK)
					},
				}
				return httptest.NewServer(handler)
			},
			expectedStatus: http.StatusOK,
		},
		testcase{
			tname:      "PUT SummarizeErrors = false",
			method:     http.MethodPut,
			authorizer: &testAuthorizer{},
			ts: func(t *testing.T) *httptest.Server {
				handler := &tHTTPHandler{
					t: t,
					handler: func(t *testing.T, w http.ResponseWriter, r *http.Request) {
						assert.Equal(t, http.MethodPut, r.Method)
						assert.Equal(t, "false", r.URL.Query().Get("SummarizeErrors"))
						w.WriteHeader(http.StatusOK)
					},
				}
				return httptest.NewServer(handler)
			},
			expectedStatus: http.StatusOK,
		},
		testcase{
			tname:  "SummarizeErrors bad url",
			method: http.MethodPost,
			url:    "://invalid",
			expectedError: &url.Error{
				Op:  "parse",
				URL: "://invalid",
				Err: errors.New("missing protocol scheme"),
			},
		},
		testcase{
			tname:         "new http request error",
			method:        "bad method",
			expectedError: errors.New("net/http: invalid method \"bad method\""),
		},
		testcase{
			tname:         "authorizer error",
			authorizer:    &testAuthorizer{err: errors.New("authorizer error")},
			expectedError: errors.New("authorizer error"),
		},
		testcase{
			tname:      "ok",
			authorizer: &testAuthorizer{},
			ts: func(*testing.T) *httptest.Server {
				return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
					w.WriteHeader(http.StatusOK)
				}))
			},
			expectedStatus: http.StatusOK,
		},
	}
	for _, tc := range tt {
		t.Run(tc.tname, func(t *testing.T) {
			url := tc.url
			if tc.ts != nil {
				ts := tc.ts(t)
				url = ts.URL
				defer ts.Close()
			}
			client := &Client{
				authorizer: tc.authorizer,
				client:     tc.client,
			}
			rsp, err := client.do(tc.method, url, nil)
			assert.Equal(t, tc.expectedError, err)
			if rsp != nil {
				assert.Equal(t, tc.expectedStatus, rsp.StatusCode)
			}
		})
	}
}

func TestClient_doDecode(t *testing.T) {
	type testcase struct {
		tname         string
		client        *http.Client
		method        string
		urlStr        string
		body          io.Reader
		dst           Response
		expectedError error
		expectedDst   interface{}
	}
	tt := []testcase{
		testcase{
			tname: "request error",
			client: &http.Client{
				Transport: testRoundTrip(func(*http.Request) (*http.Response, error) {
					return nil, errors.New("request error")
				}),
			},
			expectedError: &url.Error{Op: "Get", URL: "", Err: errors.New("request error")},
			expectedDst:   Response{},
		},
		testcase{
			tname: "invalid xml",
			dst:   Response{},
			client: &http.Client{
				Transport: testRoundTrip(func(*http.Request) (*http.Response, error) {
					r := bytes.NewBuffer([]byte("</uwotm8>"))
					return &http.Response{
						Body: ioutil.NopCloser(r),
					}, nil
				}),
			},
			expectedError: &xml.SyntaxError{Msg: "unexpected end element </uwotm8>", Line: 1},
			expectedDst:   Response{},
		},
		testcase{
			tname: "ok",
			dst:   Response{},
			client: &http.Client{
				Transport: testRoundTrip(func(*http.Request) (*http.Response, error) {
					r := bytes.NewBuffer([]byte(`<Response><ProviderName>Foo</ProviderName></Response>`))
					return &http.Response{
						Body: ioutil.NopCloser(r),
					}, nil
				}),
			},
			expectedError: nil,
			expectedDst:   Response{XMLName: xml.Name{Local: "Response"}, ProviderName: "Foo"},
		},
	}
	for _, tc := range tt {
		t.Run(tc.tname, func(t *testing.T) {
			client := &Client{authorizer: new(testAuthorizer), client: tc.client}
			err := client.doDecode(tc.method, tc.urlStr, tc.body, &tc.dst)
			assert.Equal(t, tc.expectedError, err)
			assert.Equal(t, tc.expectedDst, tc.dst)
		})
	}
}

func TestClient_doEncode(t *testing.T) {
	type testcase struct {
		name          string
		enc           func(t *testing.T) Encoder
		client        *http.Client
		expectedError error
	}
	tt := []testcase{
		testcase{
			name: "encode error",
			enc: func(t *testing.T) Encoder {
				return &testEncoder{t, func(t *testing.T, w io.Writer) error {
					return errors.New("encoding error")
				}}
			},
			expectedError: errors.New("encoding error"),
		},
		testcase{
			name: "encode error",
			enc: func(t *testing.T) Encoder {
				return &testEncoder{t, func(t *testing.T, w io.Writer) error {
					return nil
				}}
			},
			client: &http.Client{
				Transport: testRoundTrip(func(*http.Request) (*http.Response, error) {
					return nil, errors.New("request error")
				}),
			},
			expectedError: &url.Error{
				Op:  "Post",
				URL: "/?SummarizeErrors=false",
				Err: errors.New("request error"),
			},
		},
		testcase{
			name: "ok",
			enc: func(t *testing.T) Encoder {
				return &testEncoder{t, func(t *testing.T, w io.Writer) error {
					return nil
				}}
			},
			client: &http.Client{
				Transport: testRoundTrip(func(*http.Request) (*http.Response, error) {
					return &http.Response{Body: ioutil.NopCloser(bytes.NewReader([]byte("")))}, nil
				}),
			},
			expectedError: nil,
		},
	}
	for _, tc := range tt {
		t.Run(tc.name, func(t *testing.T) {
			client := &Client{authorizer: new(testAuthorizer), client: tc.client}
			err := client.doEncode(http.MethodPost, "/", tc.enc(t))
			assert.Equal(t, tc.expectedError, err, "%s", err)
		})
	}
}
<|endoftext|>


<commit_before>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 TestClientTsigAXFR(t *testing.T) {
	m := new(Msg)
	m.SetAxfr("miek.nl.")
	m.SetTsig("axfr.", HmacMD5, 300, time.Now().Unix())

	c := new(Client)
	c.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
	c.Net = "tcp"

	if a, err := c.XfrReceive(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
			}
			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.XfrReceive(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(RR_OPT)
	lease_rr.Hdr.Name = "."
	lease_rr.Hdr.Rrtype = TypeOPT
	e := new(EDNS0_UPDATE_LEASE)
	e.Code = EDNS0UPDATELEASE
	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=="}

	w := new(reply)
	w.client = c
	w.addr = "127.0.0.1:53"
	w.req = m

	if err := w.dial(); err != nil {
		t.Fail()
	}
	if err := w.send(m); err != nil {
		t.Fail()
	}

}
<commit_msg>Update the tests<commit_after>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 TestClientTsigAXFR(t *testing.T) {
	m := new(Msg)
	m.SetAxfr("miek.nl.")
	m.SetTsig("axfr.", HmacMD5, 300, time.Now().Unix())

	c := new(Client)
	c.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
	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
			}
			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(RR_OPT)
	lease_rr.Hdr.Name = "."
	lease_rr.Hdr.Rrtype = TypeOPT
	e := new(EDNS0_UPDATE_LEASE)
	e.Code = EDNS0UPDATELEASE
	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=="}

	w := new(reply)
	w.client = c
	w.addr = "127.0.0.1:53"
	w.req = m

	if err := w.dial(); err != nil {
		t.Fail()
	}
	if err := w.send(m); err != nil {
		t.Fail()
	}

}
<|endoftext|>


<commit_before>package gop2p

import (
	"flag"
	"fmt"
	"testing"
)

var (
	serverAddr *string = flag.String("ServerAddress", "127.0.0.1:9000", "The p2p server address.")
	localAddr  *string = flag.String("LocalAddress", "", "The local address.")
	cid        *string = flag.String("ClientID", "1", "The Client ID.")
)

type TestHandler struct {
}

func (handler *TestHandler) ReceivedCommand(pack *Package) {
}

func (handler *TestHandler) ReceivedData(conn Conn, pack *Package) {
}

////////////////////////////////////////////////////////////
// Test functions
func TestClient(t *testing.T) {
	flag.Parse()

	testHandler := TestHandler{}
	client, err := NewClient(*cid, *serverAddr, *localAddr, &testHandler)
	if err != nil {
		t.Fatal("NewClient err:", err)
	}
	req := Package{
		MsgID: MSGID_KEEPALIVE,
	}
	_, err = client.Send(&req)
	if err != nil {
		t.Error("SendCommand keep-alive failed:", err)
	}
	peers := client.Peers()
	if peers == nil {
		t.Error("Get peers failed")
	}
	var testid string
	for peerid, peer := range peers {
		fmt.Printf("[%s] - %s\r\n", peerid, peer.Addr())
		if peerid != *cid && len(testid) != 0 {
			testid = peerid
		}
	}
	if len(testid) == 0 {
		t.Fatal("No peers")
	}
	conn, err := client.DialP2P(testid)
	if err != nil {
		t.Fatal("DialP2P err:", err)
	}

	req.SeqID = 0
	err = conn.Send(&req)
	if err != nil {
		t.Fatal("conn.Send err:", err)
	}
}
<commit_msg>Remove test file<commit_after><|endoftext|>


<commit_before>package mqtt_test

import (
	"bytes"
	"context"
	"encoding/hex"
	"errors"
	"io"
	"net"
	"strings"
	"testing"
	"time"

	"github.com/pascaldekloe/mqtt"
	"github.com/pascaldekloe/mqtt/mqtttest"
)

// NewClientCONNECTHex is the initial packet send to conns of a newClient.
const newClientCONNECTHex = "100c00044d515454040000000000"

// NewClient returns a new client with a Dialer which returns conns in order of
// appearence.
func newClient(t *testing.T, conns []net.Conn, want ...mqtttest.Transfer) *mqtt.Client {
	timeoutDone := make(chan struct{})
	timeout := time.AfterFunc(time.Second, func() {
		defer close(timeoutDone)
		t.Error("test timeout; closing connections…")
		for _, conn := range conns {
			conn.Close()
		}
	})

	var dialN int
	client := mqtt.NewClient(&mqtt.Config{
		WireTimeout:    time.Second / 4,
		AtLeastOnceMax: 2,
		ExactlyOnceMax: 2,
	}, func(context.Context) (net.Conn, error) {
		dialN++
		t.Log("Dial #", dialN)
		if dialN > len(conns) {
			return nil, errors.New("no more connections for test")
		}
		return conns[dialN-1], nil
	})

	// launch read-routine
	testRoutine(t, func() {
		defer func() {
			if !timeout.Stop() {
				// await all routines
				<-timeoutDone
			}
		}()

		for {
			message, topic, err := client.ReadSlices()
			if big := (*mqtt.BigMessage)(nil); errors.As(err, &big) {
				t.Log("ReadSlices got BigMessage")
				topic = []byte(big.Topic)
				message, err = big.ReadAll()
			}
			switch {
			case err != nil:
				switch {
				case errors.Is(err, mqtt.ErrClosed):
					if len(want) != 0 {
						t.Errorf("client closed, want %d more ReadSlices", len(want))
					}
					return

				case len(want) == 0:
					t.Errorf("ReadSlices got error %q, want close", err)
				case want[0].Err == nil:
					t.Errorf("ReadSlices got error %q, want message %#x @ %q", err, want[0].Message, want[0].Topic)
				case !errors.Is(err, want[0].Err) && err.Error() != want[0].Err.Error():
					t.Errorf("ReadSlices got error %q, want errors.Is %q", err, want[0].Err)
				}
				// small backoff may prevent error flood
				time.Sleep(time.Second / 8)

			case len(want) == 0:
				t.Errorf("ReadSlices got message %q @ %q, want close", message, topic)
			case want[0].Err != nil:
				t.Errorf("ReadSlices got message %#x @ %q, want error %q", message, topic, want[0].Err)
			case !bytes.Equal(message, want[0].Message), string(topic) != want[0].Topic:
				t.Errorf("got message %#x @ %q, want %#x @ %q", message, topic, want[0].Message, want[0].Topic)
			}

			if len(want) != 0 {
				want = want[1:] // move to next in line
			}
		}
	})

	t.Cleanup(func() {
		err := client.Close()
		if err != nil {
			t.Error("client close error:", err)
		}
	})

	return client
}

func TestCONNACK(t *testing.T) {
	golden := []struct {
		send string
		want error
	}{
		{send: "20020001", want: mqtt.ErrProtocolLevel},
		{send: "20020102", want: mqtt.ErrClientID},
		{send: "20020003", want: mqtt.ErrUnavailable},
		{send: "20020104", want: mqtt.ErrAuthBad},
		{send: "20020005", want: mqtt.ErrAuth},
		// The first packet must be a CONNACK.
		{send: "d000", want: errors.New("mqtt: connection reset on protocol violation by the broker: want fixed CONNACK header 0x2002, got 0xd000")},
	}

	for _, gold := range golden {
		t.Run("0x"+gold.send, func(t *testing.T) {
			t.Parallel()

			clientEnd, brokerEnd := net.Pipe()
			blocked, _ := net.Pipe()
			client := newClient(t, []net.Conn{clientEnd, blocked}, mqtttest.Transfer{Err: gold.want})

			wantPacketHex(t, brokerEnd, newClientCONNECTHex)
			sendPacketHex(t, brokerEnd, gold.send)

			err := client.Ping(nil)
			if !errors.Is(err, mqtt.ErrDown) {
				t.Errorf("ping got error %q, want an ErrDown", err)
			}
			err = client.Close()
			if err != nil {
				t.Error("close error:", err)
			}

			// give ReadSlices time to ErrClosed
			time.Sleep(time.Second / 8)
		})
	}
}

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

	client := mqtt.NewClient(&mqtt.Config{WireTimeout: time.Second / 2}, func(context.Context) (net.Conn, error) {
		return nil, errors.New("dialer invoked")
	})
	err := client.VolatileSession("test-client")
	if err != nil {
		t.Fatal("volatile session error:", err)
	}

	// Invoke Close before ReadSlices (connects).
	// Race because we can. ™
	for n := 0; n < 3; n++ {
		go func() {
			err := client.Close()
			if err != nil {
				t.Error("got close error:", err)
			}
		}()
	}
	time.Sleep(time.Second / 4)
	_, _, err = client.ReadSlices()
	if !errors.Is(err, mqtt.ErrClosed) {
		t.Fatalf("ReadSlices got error %q, want ErrClosed", err)
	}
	// Read (routine) stopped now

	// Run twice to ensure the semaphores ain't leaking.
	for n := 0; n < 2; n++ {
		err = client.Subscribe(nil, "x")
		if !errors.Is(err, mqtt.ErrClosed) {
			t.Errorf("Subscribe %d got error %q, want ErrClosed", n, err)
		}
		err = client.Unsubscribe(nil, "x")
		if !errors.Is(err, mqtt.ErrClosed) {
			t.Errorf("Unsubscribe %d got error %q, want ErrClosed", n, err)
		}
		err = client.Publish(nil, nil, "x")
		if !errors.Is(err, mqtt.ErrClosed) {
			t.Errorf("Publish %d got error %q, want ErrClosed", n, err)
		}
		err = client.PublishRetained(nil, nil, "x")
		if !errors.Is(err, mqtt.ErrClosed) {
			t.Errorf("PublishRetained %d got error %q, want ErrClosed", n, err)
		}
		_, err = client.PublishAtLeastOnce(nil, "x")
		if !errors.Is(err, mqtt.ErrClosed) {
			t.Errorf("PublishAtLeastOnce %d got error %q, want ErrClosed", n, err)
		}
		_, err = client.PublishAtLeastOnceRetained(nil, "x")
		if !errors.Is(err, mqtt.ErrClosed) {
			t.Errorf("PublishAtLeastOnceRetained %d got error %q, want ErrClosed", n, err)
		}
		_, err = client.PublishExactlyOnce(nil, "x")
		if !errors.Is(err, mqtt.ErrClosed) {
			t.Errorf("PublishExactlyOnce %d got error %q, want ErrClosed", n, err)
		}
		_, err = client.PublishExactlyOnceRetained(nil, "x")
		if !errors.Is(err, mqtt.ErrClosed) {
			t.Errorf("PublishExactlyOnceRetained %d got error %q, want ErrClosed", n, err)
		}
		err = client.Ping(nil)
		if !errors.Is(err, mqtt.ErrClosed) {
			t.Errorf("Ping %d got error %q, want ErrClosed", n, err)
		}
		err = client.Disconnect(nil)
		if !errors.Is(err, mqtt.ErrClosed) {
			t.Errorf("Disconnect %d got error %q, want ErrClosed", n, err)
		}
	}
	_, _, err = client.ReadSlices()
	if !errors.Is(err, mqtt.ErrClosed) {
		t.Errorf("another ReadSlices got error %q, want ErrClosed", err)
	}
}

func TestReceivePublishAtLeastOnce(t *testing.T) {
	_, conn := newClientPipe(t, mqtttest.Transfer{Message: []byte("hello"), Topic: "greet"})

	sendPacketHex(t, conn, hex.EncodeToString([]byte{
		0x32, 14,
		0, 5, 'g', 'r', 'e', 'e', 't',
		0xab, 0xcd, // packet identifier
		'h', 'e', 'l', 'l', 'o'}))
	wantPacketHex(t, conn, "4002abcd") // PUBACK
}

func TestReceivePublishExactlyOnce(t *testing.T) {
	_, conn := newClientPipe(t, mqtttest.Transfer{Message: []byte("hello"), Topic: "greet"})

	// write PUBLISH
	sendPacketHex(t, conn, hex.EncodeToString([]byte{
		0x34, 14,
		0, 5, 'g', 'r', 'e', 'e', 't',
		0xab, 0xcd, // packet identifier
		'h', 'e', 'l', 'l', 'o'}))
	wantPacketHex(t, conn, "5002abcd") // PUBREC
	sendPacketHex(t, conn, "6002abcd") // PUBREL
	wantPacketHex(t, conn, "7002abcd") // PUBCOMP
}

func TestReceivePublishAtLeastOnceBig(t *testing.T) {
	const bigN = 256 * 1024

	_, conn := newClientPipe(t, mqtttest.Transfer{Message: bytes.Repeat([]byte{'A'}, bigN), Topic: "bam"})

	sendPacketHex(t, conn, hex.EncodeToString([]byte{
		0x32, 0x87, 0x80, 0x10,
		0, 3, 'b', 'a', 'm',
		0xab, 0xcd})+strings.Repeat("41", bigN))
	wantPacketHex(t, conn, "4002abcd") // PUBACK
}

func testRoutine(t *testing.T, f func()) {
	t.Helper()
	done := make(chan struct{})
	t.Cleanup(func() {
		select {
		case <-done:
			break // OK
		case <-time.After(time.Second / 8):
			t.Error("test routine leak")
		}
	})
	go func() {
		defer close(done)
		f()
	}()
}

func sendPacketHex(t *testing.T, conn net.Conn, send string) {
	t.Helper()
	t.Logf("send %s…", typeLabelFromHex(send[0]))
	packet, err := hex.DecodeString(send)
	if err != nil {
		t.Fatalf("test has malformed packet data 0x%s: %s", send, err)
	}
	_, err = conn.Write(packet)
	if err != nil {
		t.Fatalf("broker write 0x%s error: %s", send, err)
	}
}

func wantPacketHex(t *testing.T, conn net.Conn, want string) {
	t.Helper()
	t.Logf("want %s…", typeLabelFromHex(want[0]))
	var buf [128]byte
	_, err := io.ReadFull(conn, buf[:2])
	if err != nil {
		t.Fatalf("broker read error %q, want 0x%s", err, want)
	}
	if buf[1] > 126 {
		t.Fatalf("packet %#x… too big for test, want 0x%s", buf[:2], want)
	}
	n, err := io.ReadFull(conn, buf[2:2+buf[1]])
	if err != nil {
		t.Fatalf("broker read error %q after %#x, want 0x%s", err, buf[:2+n], want)
	}
	got := hex.EncodeToString(buf[:2+n])
	if want != got {
		t.Errorf("broker got packet 0x%s, want 0x%s", got, want)
	}
}

func typeLabelFromHex(char byte) string {
	switch char {
	case '0':
		return "RESERVED0"
	case '1':
		return "CONNECT"
	case '2':
		return "CONNACK"
	case '3':
		return "PUBLISH"
	case '4':
		return "PUBACK"
	case '5':
		return "PUBREC"
	case '6':
		return "PUBREL"
	case '7':
		return "PUBCOMP"
	case '8':
		return "SUBSCRIBE"
	case '9':
		return "SUBACK"
	case 'a', 'A':
		return "UNSUBSCRIBE"
	case 'b', 'B':
		return "UNSUBACK"
	case 'c', 'C':
		return "PINGREQ"
	case 'd', 'D':
		return "PINGRESP"
	case 'e', 'E':
		return "DISCONNECT"
	case 'f', 'F':
		return "RESERVED15"
	default:
		panic("not a hex character")
	}
}
<commit_msg>Run all tests with a client pipe setup in parallel.<commit_after>package mqtt_test

import (
	"bytes"
	"context"
	"encoding/hex"
	"errors"
	"io"
	"net"
	"strings"
	"testing"
	"time"

	"github.com/pascaldekloe/mqtt"
	"github.com/pascaldekloe/mqtt/mqtttest"
)

// NewClientCONNECTHex is the initial packet send to conns of a newClient.
const newClientCONNECTHex = "100c00044d515454040000000000"

// NewClient returns a new client with a Dialer which returns conns in order of
// appearence.
func newClient(t *testing.T, conns []net.Conn, want ...mqtttest.Transfer) *mqtt.Client {
	// This type of test is slow in general.
	t.Parallel()

	timeoutDone := make(chan struct{})
	timeout := time.AfterFunc(time.Second, func() {
		defer close(timeoutDone)
		t.Error("test timeout; closing connections…")
		for _, conn := range conns {
			conn.Close()
		}
	})

	var dialN int
	client := mqtt.NewClient(&mqtt.Config{
		WireTimeout:    time.Second / 4,
		AtLeastOnceMax: 2,
		ExactlyOnceMax: 2,
	}, func(context.Context) (net.Conn, error) {
		dialN++
		t.Log("Dial #", dialN)
		if dialN > len(conns) {
			return nil, errors.New("no more connections for test")
		}
		return conns[dialN-1], nil
	})

	// launch read-routine
	testRoutine(t, func() {
		defer func() {
			if !timeout.Stop() {
				// await all routines
				<-timeoutDone
			}
		}()

		for {
			message, topic, err := client.ReadSlices()
			if big := (*mqtt.BigMessage)(nil); errors.As(err, &big) {
				t.Log("ReadSlices got BigMessage")
				topic = []byte(big.Topic)
				message, err = big.ReadAll()
			}
			switch {
			case err != nil:
				switch {
				case errors.Is(err, mqtt.ErrClosed):
					if len(want) != 0 {
						t.Errorf("client closed, want %d more ReadSlices", len(want))
					}
					return

				case len(want) == 0:
					t.Errorf("ReadSlices got error %q, want close", err)
				case want[0].Err == nil:
					t.Errorf("ReadSlices got error %q, want message %#x @ %q", err, want[0].Message, want[0].Topic)
				case !errors.Is(err, want[0].Err) && err.Error() != want[0].Err.Error():
					t.Errorf("ReadSlices got error %q, want errors.Is %q", err, want[0].Err)
				}
				// small backoff may prevent error flood
				time.Sleep(time.Second / 8)

			case len(want) == 0:
				t.Errorf("ReadSlices got message %q @ %q, want close", message, topic)
			case want[0].Err != nil:
				t.Errorf("ReadSlices got message %#x @ %q, want error %q", message, topic, want[0].Err)
			case !bytes.Equal(message, want[0].Message), string(topic) != want[0].Topic:
				t.Errorf("got message %#x @ %q, want %#x @ %q", message, topic, want[0].Message, want[0].Topic)
			}

			if len(want) != 0 {
				want = want[1:] // move to next in line
			}
		}
	})

	t.Cleanup(func() {
		err := client.Close()
		if err != nil {
			t.Error("client close error:", err)
		}
	})

	return client
}

func TestCONNACK(t *testing.T) {
	golden := []struct {
		send string
		want error
	}{
		{send: "20020001", want: mqtt.ErrProtocolLevel},
		{send: "20020102", want: mqtt.ErrClientID},
		{send: "20020003", want: mqtt.ErrUnavailable},
		{send: "20020104", want: mqtt.ErrAuthBad},
		{send: "20020005", want: mqtt.ErrAuth},
		// The first packet must be a CONNACK.
		{send: "d000", want: errors.New("mqtt: connection reset on protocol violation by the broker: want fixed CONNACK header 0x2002, got 0xd000")},
	}

	for _, gold := range golden {
		t.Run("0x"+gold.send, func(t *testing.T) {
			// local copy before t.Parallel
			gold := gold

			clientEnd, brokerEnd := net.Pipe()
			blocked, _ := net.Pipe()
			client := newClient(t, []net.Conn{clientEnd, blocked}, mqtttest.Transfer{Err: gold.want})

			wantPacketHex(t, brokerEnd, newClientCONNECTHex)
			sendPacketHex(t, brokerEnd, gold.send)

			err := client.Ping(nil)
			if !errors.Is(err, mqtt.ErrDown) {
				t.Errorf("ping got error %q, want an ErrDown", err)
			}
			err = client.Close()
			if err != nil {
				t.Error("close error:", err)
			}

			// give ReadSlices time to ErrClosed
			time.Sleep(time.Second / 8)
		})
	}
}

func TestClose(t *testing.T) {
	client := mqtt.NewClient(&mqtt.Config{WireTimeout: time.Second / 2}, func(context.Context) (net.Conn, error) {
		return nil, errors.New("dialer invoked")
	})
	err := client.VolatileSession("test-client")
	if err != nil {
		t.Fatal("volatile session error:", err)
	}

	// Invoke Close before ReadSlices (connects).
	// Race because we can. ™
	for n := 0; n < 3; n++ {
		go func() {
			err := client.Close()
			if err != nil {
				t.Error("got close error:", err)
			}
		}()
	}
	time.Sleep(time.Second / 4)
	_, _, err = client.ReadSlices()
	if !errors.Is(err, mqtt.ErrClosed) {
		t.Fatalf("ReadSlices got error %q, want ErrClosed", err)
	}
	// Read (routine) stopped now

	// Run twice to ensure the semaphores ain't leaking.
	for n := 0; n < 2; n++ {
		err = client.Subscribe(nil, "x")
		if !errors.Is(err, mqtt.ErrClosed) {
			t.Errorf("Subscribe %d got error %q, want ErrClosed", n, err)
		}
		err = client.Unsubscribe(nil, "x")
		if !errors.Is(err, mqtt.ErrClosed) {
			t.Errorf("Unsubscribe %d got error %q, want ErrClosed", n, err)
		}
		err = client.Publish(nil, nil, "x")
		if !errors.Is(err, mqtt.ErrClosed) {
			t.Errorf("Publish %d got error %q, want ErrClosed", n, err)
		}
		err = client.PublishRetained(nil, nil, "x")
		if !errors.Is(err, mqtt.ErrClosed) {
			t.Errorf("PublishRetained %d got error %q, want ErrClosed", n, err)
		}
		_, err = client.PublishAtLeastOnce(nil, "x")
		if !errors.Is(err, mqtt.ErrClosed) {
			t.Errorf("PublishAtLeastOnce %d got error %q, want ErrClosed", n, err)
		}
		_, err = client.PublishAtLeastOnceRetained(nil, "x")
		if !errors.Is(err, mqtt.ErrClosed) {
			t.Errorf("PublishAtLeastOnceRetained %d got error %q, want ErrClosed", n, err)
		}
		_, err = client.PublishExactlyOnce(nil, "x")
		if !errors.Is(err, mqtt.ErrClosed) {
			t.Errorf("PublishExactlyOnce %d got error %q, want ErrClosed", n, err)
		}
		_, err = client.PublishExactlyOnceRetained(nil, "x")
		if !errors.Is(err, mqtt.ErrClosed) {
			t.Errorf("PublishExactlyOnceRetained %d got error %q, want ErrClosed", n, err)
		}
		err = client.Ping(nil)
		if !errors.Is(err, mqtt.ErrClosed) {
			t.Errorf("Ping %d got error %q, want ErrClosed", n, err)
		}
		err = client.Disconnect(nil)
		if !errors.Is(err, mqtt.ErrClosed) {
			t.Errorf("Disconnect %d got error %q, want ErrClosed", n, err)
		}
	}
	_, _, err = client.ReadSlices()
	if !errors.Is(err, mqtt.ErrClosed) {
		t.Errorf("another ReadSlices got error %q, want ErrClosed", err)
	}
}

func TestReceivePublishAtLeastOnce(t *testing.T) {
	_, conn := newClientPipe(t, mqtttest.Transfer{Message: []byte("hello"), Topic: "greet"})

	sendPacketHex(t, conn, hex.EncodeToString([]byte{
		0x32, 14,
		0, 5, 'g', 'r', 'e', 'e', 't',
		0xab, 0xcd, // packet identifier
		'h', 'e', 'l', 'l', 'o'}))
	wantPacketHex(t, conn, "4002abcd") // PUBACK
}

func TestReceivePublishExactlyOnce(t *testing.T) {
	_, conn := newClientPipe(t, mqtttest.Transfer{Message: []byte("hello"), Topic: "greet"})

	// write PUBLISH
	sendPacketHex(t, conn, hex.EncodeToString([]byte{
		0x34, 14,
		0, 5, 'g', 'r', 'e', 'e', 't',
		0xab, 0xcd, // packet identifier
		'h', 'e', 'l', 'l', 'o'}))
	wantPacketHex(t, conn, "5002abcd") // PUBREC
	sendPacketHex(t, conn, "6002abcd") // PUBREL
	wantPacketHex(t, conn, "7002abcd") // PUBCOMP
}

func TestReceivePublishAtLeastOnceBig(t *testing.T) {
	const bigN = 256 * 1024

	_, conn := newClientPipe(t, mqtttest.Transfer{Message: bytes.Repeat([]byte{'A'}, bigN), Topic: "bam"})

	sendPacketHex(t, conn, hex.EncodeToString([]byte{
		0x32, 0x87, 0x80, 0x10,
		0, 3, 'b', 'a', 'm',
		0xab, 0xcd})+strings.Repeat("41", bigN))
	wantPacketHex(t, conn, "4002abcd") // PUBACK
}

func testRoutine(t *testing.T, f func()) {
	t.Helper()
	done := make(chan struct{})
	t.Cleanup(func() {
		select {
		case <-done:
			break // OK
		case <-time.After(time.Second / 8):
			t.Error("test routine leak")
		}
	})
	go func() {
		defer close(done)
		f()
	}()
}

func sendPacketHex(t *testing.T, conn net.Conn, send string) {
	t.Helper()
	t.Logf("send %s…", typeLabelFromHex(send[0]))
	packet, err := hex.DecodeString(send)
	if err != nil {
		t.Fatalf("test has malformed packet data 0x%s: %s", send, err)
	}
	_, err = conn.Write(packet)
	if err != nil {
		t.Fatalf("broker write 0x%s error: %s", send, err)
	}
}

func wantPacketHex(t *testing.T, conn net.Conn, want string) {
	t.Helper()
	t.Logf("want %s…", typeLabelFromHex(want[0]))
	var buf [128]byte
	_, err := io.ReadFull(conn, buf[:2])
	if err != nil {
		t.Fatalf("broker read error %q, want 0x%s", err, want)
	}
	if buf[1] > 126 {
		t.Fatalf("packet %#x… too big for test, want 0x%s", buf[:2], want)
	}
	n, err := io.ReadFull(conn, buf[2:2+buf[1]])
	if err != nil {
		t.Fatalf("broker read error %q after %#x, want 0x%s", err, buf[:2+n], want)
	}
	got := hex.EncodeToString(buf[:2+n])
	if want != got {
		t.Errorf("broker got packet 0x%s, want 0x%s", got, want)
	}
}

func typeLabelFromHex(char byte) string {
	switch char {
	case '0':
		return "RESERVED0"
	case '1':
		return "CONNECT"
	case '2':
		return "CONNACK"
	case '3':
		return "PUBLISH"
	case '4':
		return "PUBACK"
	case '5':
		return "PUBREC"
	case '6':
		return "PUBREL"
	case '7':
		return "PUBCOMP"
	case '8':
		return "SUBSCRIBE"
	case '9':
		return "SUBACK"
	case 'a', 'A':
		return "UNSUBSCRIBE"
	case 'b', 'B':
		return "UNSUBACK"
	case 'c', 'C':
		return "PINGREQ"
	case 'd', 'D':
		return "PINGRESP"
	case 'e', 'E':
		return "DISCONNECT"
	case 'f', 'F':
		return "RESERVED15"
	default:
		panic("not a hex character")
	}
}
<|endoftext|>