<commit_before>package main

import (
	"bytes"
	"context"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"regexp"
	"strings"

	"go.skia.org/infra/go/auth"
	"go.skia.org/infra/go/common"
	"go.skia.org/infra/go/cq"
	"go.skia.org/infra/go/gerrit"
	"go.skia.org/infra/go/gitiles"
	"go.skia.org/infra/go/httputils"
	"go.skia.org/infra/go/sklog"
	"go.skia.org/infra/go/supported_branches"
	"go.skia.org/infra/go/util"
)

var (
	// Flags.
	branch         = flag.String("branch", "", "Name of the new branch, without refs/heads prefix.")
	deleteBranch   = flag.String("delete", "", "Name of an existing branch to delete, without refs/heads prefix.")
	excludeTrybots = common.NewMultiStringFlag("exclude-trybots", nil, "Regular expressions for trybot names to exclude.")
	owner          = flag.String("owner", "", "Owner of the new branch.")
	repoUrl        = flag.String("repo", common.REPO_SKIA, "URL of the git repository.")
	submit         = flag.Bool("submit", false, "If set, automatically submit the CL to update the CQ and supported branches.")
)

func main() {
	common.Init()

	if *branch == "" {
		sklog.Fatal("--branch is required.")
	}
	newRef := fmt.Sprintf("refs/heads/%s", *branch)
	if *owner == "" {
		sklog.Fatal("--owner is required.")
	}
	excludeTrybotRegexp := make([]*regexp.Regexp, 0, len(*excludeTrybots))
	for _, excludeTrybot := range *excludeTrybots {
		re, err := regexp.Compile(excludeTrybot)
		if err != nil {
			sklog.Fatalf("Failed to compile regular expression from %q; %s", excludeTrybot, err)
		}
		excludeTrybotRegexp = append(excludeTrybotRegexp, re)
	}

	// Setup.
	wd, err := ioutil.TempDir("", "new-branch")
	if err != nil {
		sklog.Fatal(err)
	}
	defer util.RemoveAll(wd)

	ts, err := auth.NewDefaultTokenSource(true, auth.SCOPE_GERRIT)
	if err != nil {
		sklog.Fatal(err)
	}
	client := httputils.DefaultClientConfig().WithTokenSource(ts).Client()
	gUrl := strings.Split(*repoUrl, ".googlesource.com")[0] + "-review.googlesource.com"
	gitcookiesPath := gerrit.DefaultGitCookiesPath()
	g, err := gerrit.NewGerrit(gUrl, gitcookiesPath, client)
	if err != nil {
		sklog.Fatal(err)
	}
	repo := gitiles.NewRepo(*repoUrl, client)
	ctx := context.Background()
	baseCommitInfo, err := repo.Details(ctx, cq.CQ_CFG_REF)
	if err != nil {
		sklog.Fatal(err)
	}
	baseCommit := baseCommitInfo.Hash

	// Download the CQ config file and modify it.
	var buf bytes.Buffer
	if err := repo.ReadFileAtRef(ctx, cq.CQ_CFG_FILE, baseCommit, &buf); err != nil {
		sklog.Fatal(err)
	}
	newCfgBytes, err := cq.WithUpdateCQConfig(buf.Bytes(), func(cfg *cq.Config) error {
		cg, _, _, err := cq.MatchConfigGroup(cfg, newRef)
		if err != nil {
			return err
		}
		if cg != nil {
			_, _ = fmt.Fprintf(os.Stderr, "Already have %s in %s; not adding a duplicate.\n", newRef, cq.CQ_CFG_FILE)
		} else {
			if err := cq.CloneBranch(cfg, "master", *branch, false, false, excludeTrybotRegexp); err != nil {
				return err
			}
		}
		if *deleteBranch != "" {
			if err := cq.DeleteBranch(cfg, *deleteBranch); err != nil {
				return err
			}
		}
		return nil
	})

	// Download and modify the supported-branches.json file.
	buf = bytes.Buffer{}
	if err := repo.ReadFileAtRef(ctx, supported_branches.SUPPORTED_BRANCHES_FILE, baseCommit, &buf); err != nil {
		sklog.Fatal(err)
	}
	sbc, err := supported_branches.DecodeConfig(&buf)
	if err != nil {
		sklog.Fatal(err)
	}
	deleteRef := ""
	if *deleteBranch != "" {
		deleteRef = fmt.Sprintf("refs/heads/%s", *deleteBranch)
	}
	foundNewRef := false
	newBranches := make([]*supported_branches.SupportedBranch, 0, len(sbc.Branches)+1)
	for _, sb := range sbc.Branches {
		if deleteRef == "" || deleteRef != sb.Ref {
			newBranches = append(newBranches, sb)
		}
		if sb.Ref == newRef {
			foundNewRef = true
		}
	}
	if foundNewRef {
		_, _ = fmt.Fprintf(os.Stderr, "Already have %s in %s; not adding a duplicate.\n", newRef, supported_branches.SUPPORTED_BRANCHES_FILE)
	} else {
		newBranches = append(newBranches, &supported_branches.SupportedBranch{
			Ref:   newRef,
			Owner: *owner,
		})
	}
	sbc.Branches = newBranches
	buf = bytes.Buffer{}
	if err := supported_branches.EncodeConfig(&buf, sbc); err != nil {
		sklog.Fatal(err)
	}

	// Create the Gerrit CL.
	commitMsg := fmt.Sprintf("Add supported branch %s", *branch)
	if *deleteBranch != "" {
		commitMsg += fmt.Sprintf(", remove %s", *deleteBranch)
	}
	repoSplit := strings.Split(*repoUrl, "/")
	project := strings.TrimSuffix(repoSplit[len(repoSplit)-1], ".git")
	ci, err := gerrit.CreateAndEditChange(context.TODO(), g, project, cq.CQ_CFG_REF, commitMsg, baseCommit, func(ctx context.Context, g gerrit.GerritInterface, ci *gerrit.ChangeInfo) error {
		if err := g.EditFile(ctx, ci, cq.CQ_CFG_FILE, string(newCfgBytes)); err != nil {
			return err
		}
		if err := g.EditFile(ctx, ci, supported_branches.SUPPORTED_BRANCHES_FILE, string(buf.Bytes())); err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		sklog.Fatal(err)
	}
	fmt.Println(fmt.Sprintf("Uploaded change https://skia-review.googlesource.com/%d", ci.Issue))
	if *submit {
		if err := g.Submit(ctx, ci); err != nil {
			sklog.Fatalf("Failed to submit CL: %s", err)
		}
	}
}
<commit_msg>[supported branches] Need to Code-Review+1 before submitting<commit_after>package main

import (
	"bytes"
	"context"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"regexp"
	"strings"

	"go.skia.org/infra/go/auth"
	"go.skia.org/infra/go/common"
	"go.skia.org/infra/go/cq"
	"go.skia.org/infra/go/gerrit"
	"go.skia.org/infra/go/gitiles"
	"go.skia.org/infra/go/httputils"
	"go.skia.org/infra/go/sklog"
	"go.skia.org/infra/go/supported_branches"
	"go.skia.org/infra/go/util"
)

var (
	// Flags.
	branch         = flag.String("branch", "", "Name of the new branch, without refs/heads prefix.")
	deleteBranch   = flag.String("delete", "", "Name of an existing branch to delete, without refs/heads prefix.")
	excludeTrybots = common.NewMultiStringFlag("exclude-trybots", nil, "Regular expressions for trybot names to exclude.")
	owner          = flag.String("owner", "", "Owner of the new branch.")
	repoUrl        = flag.String("repo", common.REPO_SKIA, "URL of the git repository.")
	submit         = flag.Bool("submit", false, "If set, automatically submit the CL to update the CQ and supported branches.")
)

func main() {
	common.Init()

	if *branch == "" {
		sklog.Fatal("--branch is required.")
	}
	newRef := fmt.Sprintf("refs/heads/%s", *branch)
	if *owner == "" {
		sklog.Fatal("--owner is required.")
	}
	excludeTrybotRegexp := make([]*regexp.Regexp, 0, len(*excludeTrybots))
	for _, excludeTrybot := range *excludeTrybots {
		re, err := regexp.Compile(excludeTrybot)
		if err != nil {
			sklog.Fatalf("Failed to compile regular expression from %q; %s", excludeTrybot, err)
		}
		excludeTrybotRegexp = append(excludeTrybotRegexp, re)
	}

	// Setup.
	wd, err := ioutil.TempDir("", "new-branch")
	if err != nil {
		sklog.Fatal(err)
	}
	defer util.RemoveAll(wd)

	ts, err := auth.NewDefaultTokenSource(true, auth.SCOPE_GERRIT)
	if err != nil {
		sklog.Fatal(err)
	}
	client := httputils.DefaultClientConfig().WithTokenSource(ts).Client()
	gUrl := strings.Split(*repoUrl, ".googlesource.com")[0] + "-review.googlesource.com"
	gitcookiesPath := gerrit.DefaultGitCookiesPath()
	g, err := gerrit.NewGerrit(gUrl, gitcookiesPath, client)
	if err != nil {
		sklog.Fatal(err)
	}
	repo := gitiles.NewRepo(*repoUrl, client)
	ctx := context.Background()
	baseCommitInfo, err := repo.Details(ctx, cq.CQ_CFG_REF)
	if err != nil {
		sklog.Fatal(err)
	}
	baseCommit := baseCommitInfo.Hash

	// Download the CQ config file and modify it.
	var buf bytes.Buffer
	if err := repo.ReadFileAtRef(ctx, cq.CQ_CFG_FILE, baseCommit, &buf); err != nil {
		sklog.Fatal(err)
	}
	newCfgBytes, err := cq.WithUpdateCQConfig(buf.Bytes(), func(cfg *cq.Config) error {
		cg, _, _, err := cq.MatchConfigGroup(cfg, newRef)
		if err != nil {
			return err
		}
		if cg != nil {
			_, _ = fmt.Fprintf(os.Stderr, "Already have %s in %s; not adding a duplicate.\n", newRef, cq.CQ_CFG_FILE)
		} else {
			if err := cq.CloneBranch(cfg, "master", *branch, false, false, excludeTrybotRegexp); err != nil {
				return err
			}
		}
		if *deleteBranch != "" {
			if err := cq.DeleteBranch(cfg, *deleteBranch); err != nil {
				return err
			}
		}
		return nil
	})

	// Download and modify the supported-branches.json file.
	buf = bytes.Buffer{}
	if err := repo.ReadFileAtRef(ctx, supported_branches.SUPPORTED_BRANCHES_FILE, baseCommit, &buf); err != nil {
		sklog.Fatal(err)
	}
	sbc, err := supported_branches.DecodeConfig(&buf)
	if err != nil {
		sklog.Fatal(err)
	}
	deleteRef := ""
	if *deleteBranch != "" {
		deleteRef = fmt.Sprintf("refs/heads/%s", *deleteBranch)
	}
	foundNewRef := false
	newBranches := make([]*supported_branches.SupportedBranch, 0, len(sbc.Branches)+1)
	for _, sb := range sbc.Branches {
		if deleteRef == "" || deleteRef != sb.Ref {
			newBranches = append(newBranches, sb)
		}
		if sb.Ref == newRef {
			foundNewRef = true
		}
	}
	if foundNewRef {
		_, _ = fmt.Fprintf(os.Stderr, "Already have %s in %s; not adding a duplicate.\n", newRef, supported_branches.SUPPORTED_BRANCHES_FILE)
	} else {
		newBranches = append(newBranches, &supported_branches.SupportedBranch{
			Ref:   newRef,
			Owner: *owner,
		})
	}
	sbc.Branches = newBranches
	buf = bytes.Buffer{}
	if err := supported_branches.EncodeConfig(&buf, sbc); err != nil {
		sklog.Fatal(err)
	}

	// Create the Gerrit CL.
	commitMsg := fmt.Sprintf("Add supported branch %s", *branch)
	if *deleteBranch != "" {
		commitMsg += fmt.Sprintf(", remove %s", *deleteBranch)
	}
	repoSplit := strings.Split(*repoUrl, "/")
	project := strings.TrimSuffix(repoSplit[len(repoSplit)-1], ".git")
	ci, err := gerrit.CreateAndEditChange(ctx, g, project, cq.CQ_CFG_REF, commitMsg, baseCommit, func(ctx context.Context, g gerrit.GerritInterface, ci *gerrit.ChangeInfo) error {
		if err := g.EditFile(ctx, ci, cq.CQ_CFG_FILE, string(newCfgBytes)); err != nil {
			return err
		}
		if err := g.EditFile(ctx, ci, supported_branches.SUPPORTED_BRANCHES_FILE, string(buf.Bytes())); err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		sklog.Fatal(err)
	}
	fmt.Println(fmt.Sprintf("Uploaded change https://skia-review.googlesource.com/%d", ci.Issue))
	if *submit {
		if err := g.SetReadyForReview(ctx, ci); err != nil {
			sklog.Fatalf("Failed to set ready for review: %s", err)
		}
		if err := g.SetReview(ctx, ci, "", gerrit.CONFIG_CHROMIUM.SelfApproveLabels, nil); err != nil {
			sklog.Fatalf("Failed to set Code-Review+1: %s", err)
		}
		if err := g.Submit(ctx, ci); err != nil {
			sklog.Fatalf("Failed to submit CL: %s", err)
		}
	}
}
<|endoftext|>


<commit_before>package box

import (
	"fmt"
)

func (f *Run) Stretch2(nb int, bx []Box) (pb int) {
	panic("unimplemented")
}

// Elastic tabstop experiment section. Not ready for general use by any means
// the text/tabwriter package implements elastic tabstops, but that package
// assumes that all chars are the same width and that text needs to be rescanned.
//
// The frame already distinguishes between tabs, newlines, and plain text characters
// by encapsulating them in measured boxes. A direct copy of the tabwriter code would
// ignore the datastructures in the frame and their sublinear runtime cost.
//
func (f *Run) Stretch(nb int) (pb int) {
	if nb <= 0 {
		return 0
	}
	//	fmt.Println()
	//	fmt.Printf("\n\ntrace bn=%d\n", nb)
	nc := 0
	nl := 0
	dx := 0

	cmax := make(map[int]int)
	cbox := make(map[int][]int)

	nb = f.StartCell(nb)
	fmt.Println("\n\ncell start at box", nb)
	pb = nb - 1
Loop:
	for ; nb < f.Nbox; nb++ {
		b := &f.Box[nb]
		fmt.Printf("switch box: %#v\n", b)
		switch b.Break() {
		case '\t':
			dx += b.Width
			cbox[nc] = append(cbox[nc], nb)
			max := cmax[nc]
			if dx > max {
				cmax[nc] = dx
			}
			nc++
			fmt.Printf("	tab: dx=%d ncol=%d\n", dx, nc)
			dx = 0
		case '\n':
			nl++
			dx = 0
			if nc == 0 {
				// A line with no tabs; end of cell
				fmt.Printf("	nl (no cols): dx=%d nl=%d\n", dx, nl-1)
				break Loop
			}
			fmt.Printf("	nl : dx=%d nl=%d nc=%d\n", dx, nl-1, nc)
			nc = 0
		default:
			dx += b.Width
			fmt.Printf("	plain : dx=%d wid=%d nc=%d\n", dx, b.Width, nc)
		}
	}
	for c, bns := range cbox {
		max := cmax[c]
		for _, bn := range bns {
			b := &f.Box[bn]
			b.Width = max
			if bn == 0 {
				continue
			}
			pb := f.Box[bn-1]
			if pb.Break() != '\n' {
				b.Width -= f.Box[bn-1].Width
			}
			if b.Width < b.Minwidth {
				b.Width = b.Minwidth
			}
		}
	}
	return pb
}

func (f *Run) Findcol(bn int, coln int) (cbn int, xmax int) {
	c := 0
	for ; bn < f.Nbox; bn++ {
		b := &f.Box[bn]
		if b.Break() == '\t' {
			c++
		}
		if b.Break() != '\n' {
			xmax += b.Width
		}
		if c == coln {
			break
		}
		bn++
	}
	if c != coln {
		return -1, 0
	}
	return bn, xmax

}

func (f *Run) Colof(bn int) (coln, xmax int) {
	if bn == 0 {
		return 0, 0
	}
	bs := f.StartLine(bn)
	for {
		b := &f.Box[bs]
		if b.Break() == '\t' {
			coln++
		}
		if b.Break() != '\n' {
			xmax += b.Width
		}
		if bn == bs {
			break
		}
		bs++
	}
	if xmax != 0 {
		coln++
	}
	return coln, xmax
}

// EndCell returns the first box beyond the end of the
// current cell under bn
func (f *Run) EndCell(bn int) int {
	oldbn := bn
	bn = f.StartLine(bn)
	ltb := 0
	ncol := 0
Loop:
	for ; bn != f.Nbox; bn++ {
		b := &f.Box[bn]
		switch b.Break() {
		case '\n':
			if ncol == 0 {
				bn = ltb
				break Loop
			}
			ncol = 0
		case '\t':
			ncol++
			ltb = bn
		}
	}
	if bn <= oldbn {
		return oldbn
	}
	return bn + 1
}

// StartCell returns the first box in the cell
func (f *Run) StartCell(bn int) int {
	if bn == 0 {
		return 0
	}
	ncols := 0
	nrows := 0
	oldbn := bn
	bn = f.EndLine(bn)
	b := &f.Box[bn]
	for bn-1 != 0 {
		b = &f.Box[bn-1]
		switch b.Break() {
		case '\n':

			if ncols == 0 {
				if nrows == 0 {
					return oldbn
				}
				return bn + 1
			}
			nrows++
			ncols = 0
		case '\t':
			ncols++
		default:
		}
		bn--
	}
	//	println("bn-1", bn-1)
	//	f.DumpBoxes()
	if bn-1 == 0 && f.Box[bn-1].Break() != '\n' {
		return 0
	}
	//	println("return", bn)
	return bn
}

// NextCell is like EndCell, except it doesn't assume bn
// is part of a cell. It skips past the current cell under
// bn and any non-cellular boxes afterward, returning the
// starting box of the next cell or f.Nbox
func (f *Run) NextCell(bn int) int {
	bn = f.EndCell(bn)
	oldbn := bn
	for ; bn != f.Nbox; bn++ {
		b := &f.Box[bn]
		if b.Break() == '\t' {
			bn = f.StartCell(bn)
			break
		}
	}
	if bn <= oldbn {
		return oldbn
	}
	return bn
}

func (f *Run) StartLine(bn int) int {
	for ; bn-1 >= 0; bn-- {
		b := &f.Box[bn-1]
		if b.Break() == '\n' {
			break
		}
	}
	return bn
}

func (f *Run) EndLine(bn int) int {
	for bn < f.Nbox {
		b := &f.Box[bn]
		if b.Break() == '\n' {
			break
		}
		bn++
	}
	return bn
}

func (f *Run) NextLine(bn int) int {
	bn = f.EndLine(bn)
	if bn < f.Nbox {
		return bn + 1
	}
	return bn
}

func (f *Run) PrevLine(bn int) int {
	for ; bn >= 0; bn-- {
		b := &f.Box[bn]
		if b.Break() == '\n' {
			break
		}
	}
	if bn == -1 && f.Box[0].Break() == '\n' {
		return 0
	}
	for bn-1 >= 0 {
		b := &f.Box[bn-1]
		if b.Break() == '\n' {
			break
		}
		bn--
	}
	return bn
}
<commit_msg>elastic<commit_after>package box

import (
	"fmt"
)

func (f *Run) Stretch2(nb int, bx []Box) (pb int) {
	panic("unimplemented")
}

// Elastic tabstop experiment section. Not ready for general use by any means
// the text/tabwriter package implements elastic tabstops, but that package
// assumes that all chars are the same width and that text needs to be rescanned.
//
// The frame already distinguishes between tabs, newlines, and plain text characters
// by encapsulating them in measured boxes. A direct copy of the tabwriter code would
// ignore the datastructures in the frame and their sublinear runtime cost.
//
func (f *Run) Stretch(nb int) (pb int) {
	if nb <= 0 {
		return 0
	}
	//	fmt.Println()
	//	fmt.Printf("\n\ntrace bn=%d\n", nb)
	nc := 0
	nl := 0
	dx := 0

	cmax := make(map[int]int)
	cbox := make(map[int][]int)

	nb = f.StartCell(nb)
	fmt.Println("\n\ncell start at box", nb)
	pb = nb - 1
Loop:
	for ; nb < f.Nbox; nb++ {
		b := &f.Box[nb]
		fmt.Printf("switch box: %#v\n", b)
		switch b.Break() {
		case '\t':
			dx += b.Width
			cbox[nc] = append(cbox[nc], nb)
			max := cmax[nc]
			if dx > max {
				cmax[nc] = dx
			}
			nc++
			fmt.Printf("	tab: dx=%d ncol=%d\n", dx, nc)
			dx = 0
		case '\n':
			nl++
			dx = 0
			if nc == 0 {
				// A line with no tabs; end of cell
				fmt.Printf("	nl (no cols): dx=%d nl=%d\n", dx, nl-1)
				break Loop
			}
			fmt.Printf("	nl : dx=%d nl=%d nc=%d\n", dx, nl-1, nc)
			nc = 0
		default:
			dx += b.Width
			fmt.Printf("	plain : dx=%d wid=%d nc=%d\n", dx, b.Width, nc)
		}
	}
	for c, bns := range cbox {
		max := cmax[c]
		for _, bn := range bns {
			b := &f.Box[bn]
			b.Width = max
			if bn == 0 {
				continue
			}
			pb := f.Box[bn-1]
			if pb.Break() != '\n' {
				b.Width -= f.Box[bn-1].Width
			}
			if b.Width < b.Minwidth {
				b.Width = b.Minwidth
			}
		}
	}
	return pb
}

func (f *Run) Findcol(bn int, coln int) (cbn int, xmax int) {
	c := 0
	for ; bn < f.Nbox; bn++ {
		b := &f.Box[bn]
		if b.Break() == '\t' {
			c++
		}
		if b.Break() != '\n' {
			xmax += b.Width
		}
		if c == coln {
			break
		}
		bn++
	}
	if c != coln {
		return -1, 0
	}
	return bn, xmax

}

func (f *Run) Colof(bn int) (coln, xmax int) {
	if bn == 0 {
		return 0, 0
	}
	bs := f.StartLine(bn)
	for {
		b := &f.Box[bs]
		if b.Break() == '\t' {
			coln++
		}
		if b.Break() != '\n' {
			xmax += b.Width
		}
		if bn == bs {
			break
		}
		bs++
	}
	if xmax != 0 {
		coln++
	}
	return coln, xmax
}

// EndCell returns the first box beyond the end of the
// current cell under bn
func (f *Run) EndCell(bn int) int {
	oldbn := bn
	bn = f.StartLine(bn)
	ltb := 0
	ncol := 0
Loop:
	for ; bn != f.Nbox; bn++ {
		b := &f.Box[bn]
		switch b.Break() {
		case '\n':
			if ncol == 0 {
				bn = ltb
				break Loop
			}
			ncol = 0
		case '\t':
			ncol++
			ltb = bn
		}
	}
	if bn <= oldbn {
		return oldbn
	}
	if bn == f.Nbox{
		return bn
	}
	return bn + 1
}

// StartCell returns the first box in the cell
func (f *Run) StartCell(bn int) int {
	if bn == 0{
		return 0
	}
	ncols := 0
	nrows := 0
	oldbn := bn
	bn = f.EndLine(bn)
	var b *Box
	for bn-1 != 0 {
		b = &f.Box[bn-1]
		switch b.Break() {
		case '\n':

			if ncols == 0 {
				if nrows == 0 {
					return oldbn
				}
				return bn + 1
			}
			nrows++
			ncols = 0
		case '\t':
			ncols++
		default:
		}
		bn--
	}
	//	println("bn-1", bn-1)
	//	f.DumpBoxes()
	if bn-1 == 0 && f.Box[bn-1].Break() != '\n' {
		return 0
	}
	//	println("return", bn)
	return bn
}

// NextCell is like EndCell, except it doesn't assume bn
// is part of a cell. It skips past the current cell under
// bn and any non-cellular boxes afterward, returning the
// starting box of the next cell or f.Nbox
func (f *Run) NextCell(bn int) int {
	bn = f.EndCell(bn)
	oldbn := bn
	for ; bn != f.Nbox; bn++ {
		b := &f.Box[bn]
		if b.Break() == '\t' {
			bn = f.StartCell(bn)
			break
		}
	}
	if bn <= oldbn {
		return oldbn
	}
	return bn
}

func (f *Run) StartLine(bn int) int {
	for ; bn-1 >= 0; bn-- {
		b := &f.Box[bn-1]
		if b.Break() == '\n' {
			break
		}
	}
	return bn
}

func (f *Run) EndLine(bn int) int {
	for bn < f.Nbox {
		b := &f.Box[bn]
		if b.Break() == '\n' {
			break
		}
		bn++
	}
	return bn
}

func (f *Run) NextLine(bn int) int {
	bn = f.EndLine(bn)
	if bn < f.Nbox {
		return bn + 1
	}
	return bn
}

func (f *Run) PrevLine(bn int) int {
	for ; bn >= 0; bn-- {
		b := &f.Box[bn]
		if b.Break() == '\n' {
			break
		}
	}
	if bn == -1 && f.Box[0].Break() == '\n' {
		return 0
	}
	for bn-1 >= 0 {
		b := &f.Box[bn-1]
		if b.Break() == '\n' {
			break
		}
		bn--
	}
	return bn
}
<|endoftext|>


<commit_before>package nanboxing

import (
	"math"
	"testing"
)

func TestBoolBox(t *testing.T) {
	trueBox := NewBool(true)
	falseBox := NewBool(false)
	if false == math.IsNaN(float64(trueBox)) {
		t.Error("The box should be NaN")
	}
	if false == math.IsNaN(float64(falseBox)) {
		t.Error("The box should be NaN")
	}
	if true != trueBox.ToBool() {
		t.Error("trueBox value should be true")
	}
	if false != falseBox.ToBool() {
		t.Error("falseBox value should be false")
	}
}

func TestNumberBox(t *testing.T) {
	{
		zeroBox := NewNumber(0)
		if false == math.IsNaN(float64(zeroBox)) {
			t.Error("The box should be NaN")
		}
		if 0 != zeroBox.ToNumber() {
			t.Error("zeroBox value should be 0")
		}
	}
	{
		negativeBox := NewNumber(-1)
		if false == math.IsNaN(float64(negativeBox)) {
			t.Error("The box should be NaN")
		}
		if -1 != negativeBox.ToNumber() {
			t.Error("negativeBox value should be -1")
		}
	}
	{
		positiveBox := NewNumber(1)
		if false == math.IsNaN(float64(positiveBox)) {
			t.Error("The box should be NaN")
		}
		if 1 != positiveBox.ToNumber() {
			t.Error("positiveBox value should be 1")
		}
	}
	{
		maxNegativeBox := NewNumber(math.MinInt32)
		if false == math.IsNaN(float64(maxNegativeBox)) {
			t.Error("The box should be NaN")
		}
		if math.MinInt32 != maxNegativeBox.ToNumber() {
			t.Errorf("maxNegativeBox value should be %d", math.MinInt32)
		}
	}
	{
		maxPositiveBox := NewNumber(math.MaxInt32)
		if false == math.IsNaN(float64(maxPositiveBox)) {
			t.Error("The box should be NaN")
		}
		if math.MaxInt32 != maxPositiveBox.ToNumber() {
			t.Errorf("maxPositiveBox value should be %d", math.MaxInt32)
		}
	}
}

func TestObject(t *testing.T) {
	objectSource := make(Object)
	b1 := NewNumber(1)
	b2 := NewNumber(2)
	objectSource["key1"] = b1
	objectSource["key2"] = b2

	objectBox := NewObject(objectSource)
	if false == math.IsNaN(float64(objectBox)) {
		t.Error("The box should be NaN")
	}
	objectTarget := objectBox.ToObject()
	if len(objectSource) != len(objectTarget) {
		t.Error("objectTarget and objectSource should have the same length")
	}

	// To address elements in the map we have to go through a temporary variable
	// This is why the test set back to original variables the entries from the
	// objectTarget
	// objectTarget["key1"].ToNumber() or objectTarget["key2"].ToNumber() are forbidden
	b1 = objectTarget["key1"]
	b2 = objectTarget["key2"]
	if 1 != b1.ToNumber() {
		t.Error("objectTarget[\"key1\"] should be equal to objectSource[\"key1\"")
	}
	if 2 != b2.ToNumber() {
		t.Error("objectTarget[\"key2\"] should be equal to objectSource[\"key2\"")
	}
}

func TestArray(t *testing.T) {
	arraySource := Array{NewNumber(1), NewNumber(2)}
	arrayBox := NewArray(arraySource)
	if false == math.IsNaN(float64(arrayBox)) {
		t.Error("The box should be NaN")
	}
	arrayTarget := arrayBox.ToArray()
	numberBox1 := arrayTarget[0]
	numberBox2 := arrayTarget[1]
	if 1 != numberBox1.ToNumber() {
		t.Error("arrayTarget[0] should be equal to arraySource[0]")
	}
	if 2 != numberBox2.ToNumber() {
		t.Error("arrayTarget[1] should be equal to arraySource[1]")
	}
}
func TestTag(t *testing.T) {
	box := NewBool(true)
	tag := box.Tag()
	if TagBool != tag {
		t.Errorf("The tag should be Bool, box value %x, NaN value %x, test %x", box, math.NaN(), uint64(0x7FF8000000000001))
	}
	maxPositiveBox := NewNumber(math.MaxInt32)
	tag = maxPositiveBox.Tag()
	if TagNumber != tag {
		t.Errorf("The tag should be Number, box value %x.")
	}
	(&maxPositiveBox).SetTag(tagString)
	tag = maxPositiveBox.Tag()
	if tagString != tag {
		t.Errorf("The tag should be String, box value %x.")
	}
	(&maxPositiveBox).SetTag(TagNumber)
	tag = maxPositiveBox.Tag()
	if math.MaxInt32 != maxPositiveBox.ToNumber() {
		t.Errorf("maxPositiveBox value should be %d", math.MaxInt32)
	}
}

func TestPayload(t *testing.T) {
	maxPositiveBox := NewNumber(math.MinInt32)
	maxPositiveBox = maxPositiveBox.SetPayload(uint64(math.MaxInt32))

}

func TestIs(t *testing.T) {
	{
		trueBox := NewBool(true)
		if true == trueBox.IsFloat64() {
			t.Error("trueBox should not be a valid float64")
		}
		if false == trueBox.IsBool() {
			t.Error("trueBox should be a valid bool box")
		}
	}
	{
		maxNegativeBox := NewNumber(math.MinInt32)
		if false == maxNegativeBox.IsNumber() {
			t.Error("maxNegativeBox box type should be number")
		}
		randomPositiveBox := NewNumber(12) // Or almost random
		if false == randomPositiveBox.IsNumber() {
			t.Error("randomPositiveBox box type should be number")
		}
	}
	{
		laLaLaLaObject := make(Object)
		laLaLaLaObject["three"] = NewString("Once")
		laLaLaLaObject["little"] = NewString("upon")
		laLaLaLaObject["pigs"] = NewString("a time...")
		laLaLaLaBox := NewObject(laLaLaLaObject)
		if false == laLaLaLaBox.IsObject() {
			t.Error("laLaLaLaObject box type should be object")
		}
	}
	{
		leetArray := Array{NewNumber(1), NewNumber(3), NewNumber(3), NewNumber(7)}
		leetBox := NewArray(leetArray)
		if false == leetBox.IsArray() {
			t.Error("leetArray box type should be array")
		}
	}
}
<commit_msg>Improve SetPayload test<commit_after>package nanboxing

import (
	"math"
	"testing"
)

func TestBoolBox(t *testing.T) {
	trueBox := NewBool(true)
	falseBox := NewBool(false)
	if false == math.IsNaN(float64(trueBox)) {
		t.Error("The box should be NaN")
	}
	if false == math.IsNaN(float64(falseBox)) {
		t.Error("The box should be NaN")
	}
	if true != trueBox.ToBool() {
		t.Error("trueBox value should be true")
	}
	if false != falseBox.ToBool() {
		t.Error("falseBox value should be false")
	}
}

func TestNumberBox(t *testing.T) {
	{
		zeroBox := NewNumber(0)
		if false == math.IsNaN(float64(zeroBox)) {
			t.Error("The box should be NaN")
		}
		if 0 != zeroBox.ToNumber() {
			t.Error("zeroBox value should be 0")
		}
	}
	{
		negativeBox := NewNumber(-1)
		if false == math.IsNaN(float64(negativeBox)) {
			t.Error("The box should be NaN")
		}
		if -1 != negativeBox.ToNumber() {
			t.Error("negativeBox value should be -1")
		}
	}
	{
		positiveBox := NewNumber(1)
		if false == math.IsNaN(float64(positiveBox)) {
			t.Error("The box should be NaN")
		}
		if 1 != positiveBox.ToNumber() {
			t.Error("positiveBox value should be 1")
		}
	}
	{
		maxNegativeBox := NewNumber(math.MinInt32)
		if false == math.IsNaN(float64(maxNegativeBox)) {
			t.Error("The box should be NaN")
		}
		if math.MinInt32 != maxNegativeBox.ToNumber() {
			t.Errorf("maxNegativeBox value should be %d", math.MinInt32)
		}
	}
	{
		maxPositiveBox := NewNumber(math.MaxInt32)
		if false == math.IsNaN(float64(maxPositiveBox)) {
			t.Error("The box should be NaN")
		}
		if math.MaxInt32 != maxPositiveBox.ToNumber() {
			t.Errorf("maxPositiveBox value should be %d", math.MaxInt32)
		}
	}
}

func TestObject(t *testing.T) {
	objectSource := make(Object)
	b1 := NewNumber(1)
	b2 := NewNumber(2)
	objectSource["key1"] = b1
	objectSource["key2"] = b2

	objectBox := NewObject(objectSource)
	if false == math.IsNaN(float64(objectBox)) {
		t.Error("The box should be NaN")
	}
	objectTarget := objectBox.ToObject()
	if len(objectSource) != len(objectTarget) {
		t.Error("objectTarget and objectSource should have the same length")
	}

	// To address elements in the map we have to go through a temporary variable
	// This is why the test set back to original variables the entries from the
	// objectTarget
	// objectTarget["key1"].ToNumber() or objectTarget["key2"].ToNumber() are forbidden
	b1 = objectTarget["key1"]
	b2 = objectTarget["key2"]
	if 1 != b1.ToNumber() {
		t.Error("objectTarget[\"key1\"] should be equal to objectSource[\"key1\"")
	}
	if 2 != b2.ToNumber() {
		t.Error("objectTarget[\"key2\"] should be equal to objectSource[\"key2\"")
	}
}

func TestArray(t *testing.T) {
	arraySource := Array{NewNumber(1), NewNumber(2)}
	arrayBox := NewArray(arraySource)
	if false == math.IsNaN(float64(arrayBox)) {
		t.Error("The box should be NaN")
	}
	arrayTarget := arrayBox.ToArray()
	numberBox1 := arrayTarget[0]
	numberBox2 := arrayTarget[1]
	if 1 != numberBox1.ToNumber() {
		t.Error("arrayTarget[0] should be equal to arraySource[0]")
	}
	if 2 != numberBox2.ToNumber() {
		t.Error("arrayTarget[1] should be equal to arraySource[1]")
	}
}
func TestTag(t *testing.T) {
	box := NewBool(true)
	tag := box.Tag()
	if TagBool != tag {
		t.Errorf("The tag should be Bool, box value %x, NaN value %x, test %x", box, math.NaN(), uint64(0x7FF8000000000001))
	}
	maxPositiveBox := NewNumber(math.MaxInt32)
	tag = maxPositiveBox.Tag()
	if TagNumber != tag {
		t.Errorf("The tag should be Number, box value %x.")
	}
	(&maxPositiveBox).SetTag(tagString)
	tag = maxPositiveBox.Tag()
	if tagString != tag {
		t.Errorf("The tag should be String, box value %x.")
	}
	(&maxPositiveBox).SetTag(TagNumber)
	tag = maxPositiveBox.Tag()
	if math.MaxInt32 != maxPositiveBox.ToNumber() {
		t.Errorf("maxPositiveBox value should be %d", math.MaxInt32)
	}
}

func TestPayload(t *testing.T) {
	maxPositiveBox := NewNumber(math.MinInt32)
	if math.MinInt32 != maxPositiveBox.ToNumber() {
		t.Errorf("maxPositiveBox value should be %d", math.MinInt32)
	}
	maxPositiveBox.SetPayload(uint64(math.MaxInt32))
	if TagNumber != maxPositiveBox.Tag() {
		t.Errorf("The tag should be Number, box value %x.")
	}
	if math.MaxInt32 != maxPositiveBox.ToNumber() {
		t.Errorf("maxPositiveBox value should be %d", math.MaxInt32)
	}
}

func TestIs(t *testing.T) {
	{
		trueBox := NewBool(true)
		if true == trueBox.IsFloat64() {
			t.Error("trueBox should not be a valid float64")
		}
		if false == trueBox.IsBool() {
			t.Error("trueBox should be a valid bool box")
		}
	}
	{
		maxNegativeBox := NewNumber(math.MinInt32)
		if false == maxNegativeBox.IsNumber() {
			t.Error("maxNegativeBox box type should be number")
		}
		randomPositiveBox := NewNumber(12) // Or almost random
		if false == randomPositiveBox.IsNumber() {
			t.Error("randomPositiveBox box type should be number")
		}
	}
	{
		laLaLaLaObject := make(Object)
		laLaLaLaObject["three"] = NewString("Once")
		laLaLaLaObject["little"] = NewString("upon")
		laLaLaLaObject["pigs"] = NewString("a time...")
		laLaLaLaBox := NewObject(laLaLaLaObject)
		if false == laLaLaLaBox.IsObject() {
			t.Error("laLaLaLaObject box type should be object")
		}
	}
	{
		leetArray := Array{NewNumber(1), NewNumber(3), NewNumber(3), NewNumber(7)}
		leetBox := NewArray(leetArray)
		if false == leetBox.IsArray() {
			t.Error("leetArray box type should be array")
		}
	}
}
<|endoftext|>


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

package pubsub

import (
	"fmt"
	"math"
	"strings"

	pb "google.golang.org/genproto/googleapis/pubsub/v1"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

// maxPayload is the maximum number of bytes to devote to actual ids in
// acknowledgement or modifyAckDeadline requests. A serialized
// AcknowledgeRequest proto has a small constant overhead, plus the size of the
// subscription name, plus 3 bytes per ID (a tag byte and two size bytes). A
// ModifyAckDeadlineRequest has an additional few bytes for the deadline. We
// don't know the subscription name here, so we just assume the size exclusive
// of ids is 100 bytes.
//
// With gRPC there is no way for the client to know the server's max message size (it is
// configurable on the server). We know from experience that it
// it 512K.
const (
	maxPayload       = 512 * 1024
	reqFixedOverhead = 100
	overheadPerID    = 3
	maxSendRecvBytes = 20 * 1024 * 1024 // 20M
)

func convertMessages(rms []*pb.ReceivedMessage) ([]*Message, error) {
	msgs := make([]*Message, 0, len(rms))
	for i, m := range rms {
		msg, err := toMessage(m)
		if err != nil {
			return nil, fmt.Errorf("pubsub: cannot decode the retrieved message at index: %d, message: %+v", i, m)
		}
		msgs = append(msgs, msg)
	}
	return msgs, nil
}

func trunc32(i int64) int32 {
	if i > math.MaxInt32 {
		i = math.MaxInt32
	}
	return int32(i)
}

// func newStreamingPuller(ctx context.Context, subc *vkit.SubscriberClient, subName string, ackDeadlineSecs int32) *streamingPuller {
// 	p := &streamingPuller{
// 		ctx:             ctx,
// 		subName:         subName,
// 		ackDeadlineSecs: ackDeadlineSecs,
// 		subc:            subc,
// 	}
// 	p.c = sync.NewCond(&p.mu)
// 	return p
// }

// type streamingPuller struct {
// 	ctx             context.Context
// 	subName         string
// 	ackDeadlineSecs int32
// 	subc            *vkit.SubscriberClient

// 	mu       sync.Mutex
// 	c        *sync.Cond
// 	inFlight bool
// 	closed   bool // set after CloseSend called
// 	spc      pb.Subscriber_StreamingPullClient
// 	err      error
// }

// // open establishes (or re-establishes) a stream for pulling messages.
// // It takes care that only one RPC is in flight at a time.
// func (p *streamingPuller) open() error {
// 	p.c.L.Lock()
// 	defer p.c.L.Unlock()
// 	p.openLocked()
// 	return p.err
// }

// func (p *streamingPuller) openLocked() {
// 	if p.inFlight {
// 		// Another goroutine is opening; wait for it.
// 		for p.inFlight {
// 			p.c.Wait()
// 		}
// 		return
// 	}
// 	// No opens in flight; start one.
// 	// Keep the lock held, to avoid a race where we
// 	// close the old stream while opening a new one.
// 	p.inFlight = true
// 	spc, err := p.subc.StreamingPull(p.ctx, gax.WithGRPCOptions(grpc.MaxCallRecvMsgSize(maxSendRecvBytes)))
// 	if err == nil {
// 		err = spc.Send(&pb.StreamingPullRequest{
// 			Subscription:             p.subName,
// 			StreamAckDeadlineSeconds: p.ackDeadlineSecs,
// 		})
// 	}
// 	p.spc = spc
// 	p.err = err
// 	p.inFlight = false
// 	p.c.Broadcast()
// }

// func (p *streamingPuller) call(f func(pb.Subscriber_StreamingPullClient) error) error {
// 	p.c.L.Lock()
// 	defer p.c.L.Unlock()
// 	// Wait for an open in flight.
// 	for p.inFlight {
// 		p.c.Wait()
// 	}
// 	var err error
// 	var bo gax.Backoff
// 	for {
// 		select {
// 		case <-p.ctx.Done():
// 			p.err = p.ctx.Err()
// 		default:
// 		}
// 		if p.err != nil {
// 			return p.err
// 		}
// 		spc := p.spc
// 		// Do not call f with the lock held. Only one goroutine calls Send
// 		// (streamingMessageIterator.sender) and only one calls Recv
// 		// (streamingMessageIterator.receiver). If we locked, then a
// 		// blocked Recv would prevent a Send from happening.
// 		p.c.L.Unlock()
// 		err = f(spc)
// 		p.c.L.Lock()
// 		if !p.closed && err != nil && isRetryable(err) {
// 			// Sleep with exponential backoff. Normally we wouldn't hold the lock while sleeping,
// 			// but here it can't do any harm, since the stream is broken anyway.
// 			gax.Sleep(p.ctx, bo.Pause())
// 			p.openLocked()
// 			continue
// 		}
// 		// Not an error, or not a retryable error; stop retrying.
// 		p.err = err
// 		return err
// 	}
// }

// Logic from https://github.com/GoogleCloudPlatform/google-cloud-java/blob/master/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/v1/StatusUtil.java.
func isRetryable(err error) bool {
	s, ok := status.FromError(err)
	if !ok { // includes io.EOF, normal stream close, which causes us to reopen
		return true
	}
	switch s.Code() {
	case codes.DeadlineExceeded, codes.Internal, codes.Canceled, codes.ResourceExhausted:
		return true
	case codes.Unavailable:
		return !strings.Contains(s.Message(), "Server shutdownNow invoked")
	default:
		return false
	}
}

// func (p *streamingPuller) fetchMessages() ([]*Message, error) {
// 	var res *pb.StreamingPullResponse
// 	err := p.call(func(spc pb.Subscriber_StreamingPullClient) error {
// 		var err error
// 		res, err = spc.Recv()
// 		return err
// 	})
// 	if err != nil {
// 		return nil, err
// 	}
// 	return convertMessages(res.ReceivedMessages)
// }

// func (p *streamingPuller) send(req *pb.StreamingPullRequest) error {
// 	// Note: len(modAckIDs) == len(modSecs)
// 	var rest *pb.StreamingPullRequest
// 	for len(req.AckIds) > 0 || len(req.ModifyDeadlineAckIds) > 0 {
// 		req, rest = splitRequest(req, maxPayload)
// 		err := p.call(func(spc pb.Subscriber_StreamingPullClient) error {
// 			x := spc.Send(req)
// 			return x
// 		})
// 		if err != nil {
// 			return err
// 		}
// 		req = rest
// 	}
// 	return nil
// }

// func (p *streamingPuller) closeSend() {
// 	p.mu.Lock()
// 	p.closed = true
// 	p.spc.CloseSend()
// 	p.mu.Unlock()
// }

// Split req into a prefix that is smaller than maxSize, and a remainder.
func splitRequest(req *pb.StreamingPullRequest, maxSize int) (prefix, remainder *pb.StreamingPullRequest) {
	const int32Bytes = 4

	// Copy all fields before splitting the variable-sized ones.
	remainder = &pb.StreamingPullRequest{}
	*remainder = *req
	// Split message so it isn't too big.
	size := reqFixedOverhead
	i := 0
	for size < maxSize && (i < len(req.AckIds) || i < len(req.ModifyDeadlineAckIds)) {
		if i < len(req.AckIds) {
			size += overheadPerID + len(req.AckIds[i])
		}
		if i < len(req.ModifyDeadlineAckIds) {
			size += overheadPerID + len(req.ModifyDeadlineAckIds[i]) + int32Bytes
		}
		i++
	}

	min := func(a, b int) int {
		if a < b {
			return a
		}
		return b
	}

	j := i
	if size > maxSize {
		j--
	}
	k := min(j, len(req.AckIds))
	remainder.AckIds = req.AckIds[k:]
	req.AckIds = req.AckIds[:k]
	k = min(j, len(req.ModifyDeadlineAckIds))
	remainder.ModifyDeadlineAckIds = req.ModifyDeadlineAckIds[k:]
	remainder.ModifyDeadlineSeconds = req.ModifyDeadlineSeconds[k:]
	req.ModifyDeadlineAckIds = req.ModifyDeadlineAckIds[:k]
	req.ModifyDeadlineSeconds = req.ModifyDeadlineSeconds[:k]
	return req, remainder
}
<commit_msg>pubsub: remove commented-out code<commit_after>// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package pubsub

import (
	"fmt"
	"math"
	"strings"

	pb "google.golang.org/genproto/googleapis/pubsub/v1"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

// maxPayload is the maximum number of bytes to devote to actual ids in
// acknowledgement or modifyAckDeadline requests. A serialized
// AcknowledgeRequest proto has a small constant overhead, plus the size of the
// subscription name, plus 3 bytes per ID (a tag byte and two size bytes). A
// ModifyAckDeadlineRequest has an additional few bytes for the deadline. We
// don't know the subscription name here, so we just assume the size exclusive
// of ids is 100 bytes.
//
// With gRPC there is no way for the client to know the server's max message size (it is
// configurable on the server). We know from experience that it
// it 512K.
const (
	maxPayload       = 512 * 1024
	reqFixedOverhead = 100
	overheadPerID    = 3
	maxSendRecvBytes = 20 * 1024 * 1024 // 20M
)

func convertMessages(rms []*pb.ReceivedMessage) ([]*Message, error) {
	msgs := make([]*Message, 0, len(rms))
	for i, m := range rms {
		msg, err := toMessage(m)
		if err != nil {
			return nil, fmt.Errorf("pubsub: cannot decode the retrieved message at index: %d, message: %+v", i, m)
		}
		msgs = append(msgs, msg)
	}
	return msgs, nil
}

func trunc32(i int64) int32 {
	if i > math.MaxInt32 {
		i = math.MaxInt32
	}
	return int32(i)
}

// Logic from https://github.com/GoogleCloudPlatform/google-cloud-java/blob/master/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/v1/StatusUtil.java.
func isRetryable(err error) bool {
	s, ok := status.FromError(err)
	if !ok { // includes io.EOF, normal stream close, which causes us to reopen
		return true
	}
	switch s.Code() {
	case codes.DeadlineExceeded, codes.Internal, codes.Canceled, codes.ResourceExhausted:
		return true
	case codes.Unavailable:
		return !strings.Contains(s.Message(), "Server shutdownNow invoked")
	default:
		return false
	}
}

// Split req into a prefix that is smaller than maxSize, and a remainder.
func splitRequest(req *pb.StreamingPullRequest, maxSize int) (prefix, remainder *pb.StreamingPullRequest) {
	const int32Bytes = 4

	// Copy all fields before splitting the variable-sized ones.
	remainder = &pb.StreamingPullRequest{}
	*remainder = *req
	// Split message so it isn't too big.
	size := reqFixedOverhead
	i := 0
	for size < maxSize && (i < len(req.AckIds) || i < len(req.ModifyDeadlineAckIds)) {
		if i < len(req.AckIds) {
			size += overheadPerID + len(req.AckIds[i])
		}
		if i < len(req.ModifyDeadlineAckIds) {
			size += overheadPerID + len(req.ModifyDeadlineAckIds[i]) + int32Bytes
		}
		i++
	}

	min := func(a, b int) int {
		if a < b {
			return a
		}
		return b
	}

	j := i
	if size > maxSize {
		j--
	}
	k := min(j, len(req.AckIds))
	remainder.AckIds = req.AckIds[k:]
	req.AckIds = req.AckIds[:k]
	k = min(j, len(req.ModifyDeadlineAckIds))
	remainder.ModifyDeadlineAckIds = req.ModifyDeadlineAckIds[k:]
	remainder.ModifyDeadlineSeconds = req.ModifyDeadlineSeconds[k:]
	req.ModifyDeadlineAckIds = req.ModifyDeadlineAckIds[:k]
	req.ModifyDeadlineSeconds = req.ModifyDeadlineSeconds[:k]
	return req, remainder
}
<|endoftext|>


<commit_before>package buffstreams

import ()

import (
	"encoding/binary"
	"errors"
	"github.com/Sirupsen/logrus"
	"net"
	"sync"
)

type BuffManager struct {
	dialedConnections map[string]net.Conn
	listeningSockets  map[string]net.Listener
	//TODO I could control access to the maps better if I centralized how they got accessed - less locking code littered around
	sync.RWMutex
}

func New() *BuffManager {
	bm := &BuffManager{
		dialedConnections: make(map[string]net.Conn),
		listeningSockets:  make(map[string]net.Listener),
	}
	return bm
}

type ListenCallback func([]byte) error

func formatAddress(address string, port string) string {
	return address + ":" + port
}

func (bm *BuffManager) StartListening(port string, cb ListenCallback) error {
	address := formatAddress("", port)
	receiveSocket, err := net.Listen("tcp", address)
	if err != nil {
		return err
	}
	bm.startListening(address, receiveSocket, cb)
	return nil
}

func (bm *BuffManager) startListening(address string, socket net.Listener, cb ListenCallback) {
	bm.Lock()
	bm.listeningSockets[address] = socket
	bm.Unlock()

	go func(address string, listener net.Listener) {
		for {
			conn, err := listener.Accept()
			if err != nil {
				// alert error
				// Hmmm but how - I'm in goroutine land. Need a class level logger? Don't want to
				// tie people to the bleh default logger, also personally would prefer logrus for levels
			} else {
				// Hand this off and immediately listen for more
				go handleListenedConn(address, conn, cb)
			}
		}
	}(address, socket)
}

func handleListenedConn(address string, conn net.Conn, cb ListenCallback) {
	for {
		// Handle getting the data header
		logrus.Info("Reading")
		headerByteSize := MessageSizeToBitLength(4098) // MAKE THIS CONFIGURABLE once you have any idea how you're going to do that
		headerBuffer := make([]byte, headerByteSize)

		// First, read the number of bytes required to determine the message length
		_, err := readFromConnection(conn, headerBuffer)
		if err != nil && err.Error() == "EOF" {
			// Log the error we got from the call to read
			logrus.Error("Error reading from the connection. Likely it closed on the clients end")
			logrus.Error(err)
			conn.Close()
			return
		}

		// Now turn that buffer of bytes into an integer - represnts size of message body
		msgLength, bytesParsed := binary.Uvarint(headerBuffer)
		// Not sure what the correct way to handle these errors are. For now, bomb out
		if bytesParsed == 0 {
			// "Buffer too small"
			logrus.Error("0 Bytes parsed from header")
			logrus.Error(err)
			conn.Close()
			return
		} else if bytesParsed < 0 {
			// "Buffer overflow"
			logrus.Error("Less than zero bytes parsed from header?")
			logrus.Error(err)
			conn.Close()
			return
		}
		dataBuffer := make([]byte, msgLength)
		bytesLen, err := readFromConnection(conn, dataBuffer)
		if err != nil && err.Error() == "EOF" {
			// log the error from the call to read
			logrus.Error("Failure to read from connection")
			logrus.Error(err)
			conn.Close()
			return
		}

		// If we read bytes, there wasn't an error, or if there was it was only EOF
		// And readbytes + EOF is normal, just as readbytes + no err, next read 0 bytes EOF
		// So... we take action on the actual message data
		if bytesLen > 0 && (err == nil || (err != nil && err.Error() == "EOF")) {
			// I ultimately have some design choices here
			// Currently, I am invoking a delegate thats been passed down the stack
			// I could...
			// Just push it onto a queue (not a slow ass channel, but a queue)
			// which has a reference passed down to it, and the main process
			// spawns a goroutine to reap off the queue and handle those in parallel

			// Callback, atm
			logrus.Info("Callback")
			err = cb(dataBuffer)
			if err != nil {
				logrus.Error("Error in Callback")
				logrus.Error(err)
			}
		}
	}
}

func readFromConnection(reader net.Conn, buffer []byte) (int, error) {
	// This fills the buffer
	bytesLen, err := reader.Read(buffer)
	// Output the content of the bytes to the queue
	if bytesLen == 0 {
		if err != nil && err.Error() == "EOF" {
			// "End of individual transmission"
			// We're just done reading from that conn
			return bytesLen, err
		}
	}

	if err != nil {
		//"Underlying network failure?"
		// Not sure what this error would be, but it could exist and i've seen it handled
		// as a general case in other networking code. Following in the footsteps of (greatness|madness)
	}
	// Read some bytes, return the length
	return bytesLen, nil
}

// If you want to dial out but not immediately write, use this method
func (bm *BuffManager) DialOut(ip string, port string) error {
	address := formatAddress(ip, port)
	bm.RLock()
	if _, ok := bm.dialedConnections[address]; ok == true {
		bm.RUnlock()
		// Need to clean it out on any error...
		return errors.New("You have a connection to this ip and port open already")
	}
	bm.RUnlock()
	tcpAddr, err := net.ResolveTCPAddr("tcp", address)
	if err != nil {
		return err
	}
	conn, err := net.DialTCP("tcp", nil, tcpAddr)
	if err != nil {
		return err
	} else {
		// Store the connection, it's valid
		bm.Lock()
		bm.dialedConnections[address] = conn
		bm.Unlock()
	}
	return nil
}

func (bm *BuffManager) CloseDialer(ip string, port string) error {
	address := formatAddress(ip, port)
	bm.Lock()
	if _, ok := bm.dialedConnections[address]; ok != true {
		err := bm.dialedConnections[address].Close()
		delete(bm.dialedConnections, address)
		bm.Unlock()
		return err
	}
	bm.Unlock()
	return nil
}

// Write data and dial out if the conn isn't open
func (bm *BuffManager) WriteTo(ip string, port string, data []byte, persist bool) (int, error) {
	address := formatAddress(ip, port)
	// Get the connection if it's cached, or open a new one
	bm.RLock()
	if _, ok := bm.dialedConnections[address]; ok != true {
		bm.RUnlock()
		err := bm.DialOut(ip, port)
		if err != nil {
			// Error dialing out, cannot write
			// bail
			return 0, err
		}
	} else {
		bm.RUnlock()
	}
	// Calculate how big the message is, using a consistent header size. MAKE THIS CONFIGURABLE in some sane way
	toWriteLen := UInt16ToByteArray(uint16(len(data)), MessageSizeToBitLength(4096))
	// Append the size to the message, so now it has a header
	toWrite := append(toWriteLen, data...)
	written, err := bm.dialedConnections[address].Write(toWrite)
	if err != nil || persist == true {
		err := bm.CloseDialer(ip, port)
		if err != nil {
			// TODO ponder the following:
			// Error closing the dialer, should we still return 0 written?
			// What if some bytes written, then failure, then also the close throws an error
			// []error is a better return type, but not sure if thats a thing you're supposed to do...
			// Possibilities for error not as complicated as i'm thinking?
			return 0, err
		}
	}
	// Return the bytes written, any error
	return written, err
}
<commit_msg>Locking around write, comments<commit_after>package buffstreams

import ()

import (
	"encoding/binary"
	"errors"
	"github.com/Sirupsen/logrus"
	"net"
	"sync"
)

type BuffManager struct {
	dialedConnections map[string]net.Conn
	listeningSockets  map[string]net.Listener
	//TODO I could control access to the maps better if I centralized how they got accessed - less locking code littered around
	sync.RWMutex
}

func New() *BuffManager {
	bm := &BuffManager{
		dialedConnections: make(map[string]net.Conn),
		listeningSockets:  make(map[string]net.Listener),
	}
	return bm
}

type ListenCallback func([]byte) error

func formatAddress(address string, port string) string {
	return address + ":" + port
}

func (bm *BuffManager) StartListening(port string, cb ListenCallback) error {
	address := formatAddress("", port)
	receiveSocket, err := net.Listen("tcp", address)
	if err != nil {
		return err
	}
	bm.startListening(address, receiveSocket, cb)
	return nil
}

func (bm *BuffManager) startListening(address string, socket net.Listener, cb ListenCallback) {
	bm.Lock()
	bm.listeningSockets[address] = socket
	bm.Unlock()

	go func(address string, listener net.Listener) {
		for {
			// Wait for someone to connect
			conn, err := listener.Accept()
			if err != nil {
				// alert error
				// Hmmm but how - I'm in goroutine land. Need a class level logger? Don't want to
				// tie people to the bleh default logger, also personally would prefer logrus for levels
			} else {
				// Hand this off and immediately listen for more
				go handleListenedConn(address, conn, cb)
			}
		}
	}(address, socket)
}

func handleListenedConn(address string, conn net.Conn, cb ListenCallback) {
	for {
		// Handle getting the data header
		logrus.Info("Reading")
		headerByteSize := MessageSizeToBitLength(4098) // MAKE THIS CONFIGURABLE once you have any idea how you're going to do that
		headerBuffer := make([]byte, headerByteSize)
		// First, read the number of bytes required to determine the message length
		_, err := readFromConnection(conn, headerBuffer)
		if err != nil && err.Error() == "EOF" {
			// Log the error we got from the call to read
			logrus.Error("Error reading from the connection. Likely it closed on the clients end")
			logrus.Error(err)
			conn.Close()
			return
		}

		// Now turn that buffer of bytes into an integer - represnts size of message body
		msgLength, bytesParsed := binary.Uvarint(headerBuffer)
		// Not sure what the correct way to handle these errors are. For now, bomb out
		if bytesParsed == 0 {
			// "Buffer too small"
			logrus.Error("0 Bytes parsed from header")
			logrus.Error(err)
			conn.Close()
			return
		} else if bytesParsed < 0 {
			// "Buffer overflow"
			logrus.Error("Less than zero bytes parsed from header?")
			logrus.Error(err)
			conn.Close()
			return
		}
		dataBuffer := make([]byte, msgLength)
		bytesLen, err := readFromConnection(conn, dataBuffer)
		if err != nil && err.Error() == "EOF" {
			// log the error from the call to read
			logrus.Error("Failure to read from connection")
			logrus.Error(err)
			conn.Close()
			return
		}

		// If we read bytes, there wasn't an error, or if there was it was only EOF
		// And readbytes + EOF is normal, just as readbytes + no err, next read 0 bytes EOF
		// So... we take action on the actual message data
		if bytesLen > 0 && (err == nil || (err != nil && err.Error() == "EOF")) {
			// I ultimately have some design choices here
			// Currently, I am invoking a delegate thats been passed down the stack
			// I could...
			// Just push it onto a queue (not a slow ass channel, but a queue)
			// which has a reference passed down to it, and the main process
			// spawns a goroutine to reap off the queue and handle those in parallel

			// Callback, atm
			logrus.Info("Callback")
			err = cb(dataBuffer)
			if err != nil {
				logrus.Error("Error in Callback")
				logrus.Error(err)
			}
		}
	}
}

func readFromConnection(reader net.Conn, buffer []byte) (int, error) {
	// This fills the buffer
	bytesLen, err := reader.Read(buffer)
	// Output the content of the bytes to the queue
	if bytesLen == 0 {
		if err != nil && err.Error() == "EOF" {
			// "End of individual transmission"
			// We're just done reading from that conn
			return bytesLen, err
		}
	}

	if err != nil {
		//"Underlying network failure?"
		// Not sure what this error would be, but it could exist and i've seen it handled
		// as a general case in other networking code. Following in the footsteps of (greatness|madness)
	}
	// Read some bytes, return the length
	return bytesLen, nil
}

// If you want to dial out but not immediately write, use this method
func (bm *BuffManager) DialOut(ip string, port string) error {
	address := formatAddress(ip, port)
	bm.RLock()
	if _, ok := bm.dialedConnections[address]; ok == true {
		bm.RUnlock()
		// Need to clean it out on any error...
		return errors.New("You have a connection to this ip and port open already")
	}
	bm.RUnlock()
	tcpAddr, err := net.ResolveTCPAddr("tcp", address)
	if err != nil {
		return err
	}
	conn, err := net.DialTCP("tcp", nil, tcpAddr)
	if err != nil {
		return err
	} else {
		// Store the connection, it's valid
		bm.Lock()
		bm.dialedConnections[address] = conn
		bm.Unlock()
	}
	return nil
}

func (bm *BuffManager) CloseDialer(ip string, port string) error {
	address := formatAddress(ip, port)
	bm.Lock()
	if _, ok := bm.dialedConnections[address]; ok != true {
		err := bm.dialedConnections[address].Close()
		delete(bm.dialedConnections, address)
		bm.Unlock()
		return err
	}
	bm.Unlock()
	return nil
}

// Write data and dial out if the conn isn't open
func (bm *BuffManager) WriteTo(ip string, port string, data []byte, persist bool) (int, error) {
	address := formatAddress(ip, port)
	// Get the connection if it's cached, or open a new one
	bm.RLock()
	if _, ok := bm.dialedConnections[address]; ok != true {
		bm.RUnlock()
		err := bm.DialOut(ip, port)
		if err != nil {
			// Error dialing out, cannot write
			// bail
			return 0, err
		}
	} else {
		bm.RUnlock()
	}
	// Calculate how big the message is, using a consistent header size. MAKE THIS CONFIGURABLE in some sane way
	toWriteLen := UInt16ToByteArray(uint16(len(data)), MessageSizeToBitLength(4096))
	// Append the size to the message, so now it has a header
	toWrite := append(toWriteLen, data...)
	bm.Lock()
	written, err := bm.dialedConnections[address].Write(toWrite)
	bm.Unlock()
	if err != nil || persist == true {
		err := bm.CloseDialer(ip, port)
		if err != nil {
			// TODO ponder the following:
			// Error closing the dialer, should we still return 0 written?
			// What if some bytes written, then failure, then also the close throws an error
			// []error is a better return type, but not sure if thats a thing you're supposed to do...
			// Possibilities for error not as complicated as i'm thinking?
			return 0, err
		}
	}
	// Return the bytes written, any error
	return written, err
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Tekton Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha2

import (
	"path/filepath"

	"github.com/tektoncd/pipeline/pkg/apis/pipeline"
	corev1 "k8s.io/api/core/v1"
)

// WorkspaceDeclaration is a declaration of a volume that a Task requires.
type WorkspaceDeclaration struct {
	// Name is the name by which you can bind the volume at runtime.
	Name string `json:"name"`
	// Description is an optional human readable description of this volume.
	// +optional
	Description string `json:"description,omitempty"`
	// MountPath overrides the directory that the volume will be made available at.
	// +optional
	MountPath string `json:"mountPath,omitempty"`
	// ReadOnly dictates whether a mounted volume is writable. By default this
	// field is false and so mounted volumes are writable.
	ReadOnly bool `json:"readOnly,omitempty"`
}

// GetMountPath returns the mountPath for w which is the MountPath if provided or the
// default if not.
func (w *WorkspaceDeclaration) GetMountPath() string {
	if w.MountPath != "" {
		return w.MountPath
	}
	return filepath.Join(pipeline.WorkspaceDir, w.Name)
}

// WorkspaceBinding maps a Task's declared workspace to a Volume.
type WorkspaceBinding struct {
	// Name is the name of the workspace populated by the volume.
	Name string `json:"name"`
	// SubPath is optionally a directory on the volume which should be used
	// for this binding (i.e. the volume will be mounted at this sub directory).
	// +optional
	SubPath string `json:"subPath,omitempty"`
	// PersistentVolumeClaimVolumeSource represents a reference to a
	// PersistentVolumeClaim in the same namespace. Either this OR EmptyDir can be used.
	// +optional
	PersistentVolumeClaim *corev1.PersistentVolumeClaimVolumeSource `json:"persistentVolumeClaim,omitempty"`
	// EmptyDir represents a temporary directory that shares a Task's lifetime.
	// More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir
	// Either this OR PersistentVolumeClaim can be used.
	// +optional
	EmptyDir *corev1.EmptyDirVolumeSource `json:"emptyDir,omitempty"`
	// ConfigMap represents a configMap that should populate this workspace.
	// +optional
	ConfigMap *corev1.ConfigMapVolumeSource `json:"configMap,omitempty"`
	// Secret represents a secret that should populate this workspace.
	// +optional
	Secret *corev1.SecretVolumeSource `json:"secret,omitempty"`
}

// WorkspacePipelineDeclaration creates a named slot in a Pipeline that a PipelineRun
// is expected to populate with a workspace binding.
type WorkspacePipelineDeclaration struct {
	// Name is the name of a workspace to be provided by a PipelineRun.
	Name string `json:"name"`
}

// WorkspacePipelineTaskBinding describes how a workspace passed into the pipeline should be
// mapped to a task's declared workspace.
type WorkspacePipelineTaskBinding struct {
	// Name is the name of the workspace as declared by the task
	Name string `json:"name"`
	// Workspace is the name of the workspace declared by the pipeline
	Workspace string `json:"workspace"`
}
<commit_msg>Add a description field to pipeline workspace declarations<commit_after>/*
Copyright 2019 The Tekton Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha2

import (
	"path/filepath"

	"github.com/tektoncd/pipeline/pkg/apis/pipeline"
	corev1 "k8s.io/api/core/v1"
)

// WorkspaceDeclaration is a declaration of a volume that a Task requires.
type WorkspaceDeclaration struct {
	// Name is the name by which you can bind the volume at runtime.
	Name string `json:"name"`
	// Description is an optional human readable description of this volume.
	// +optional
	Description string `json:"description,omitempty"`
	// MountPath overrides the directory that the volume will be made available at.
	// +optional
	MountPath string `json:"mountPath,omitempty"`
	// ReadOnly dictates whether a mounted volume is writable. By default this
	// field is false and so mounted volumes are writable.
	ReadOnly bool `json:"readOnly,omitempty"`
}

// GetMountPath returns the mountPath for w which is the MountPath if provided or the
// default if not.
func (w *WorkspaceDeclaration) GetMountPath() string {
	if w.MountPath != "" {
		return w.MountPath
	}
	return filepath.Join(pipeline.WorkspaceDir, w.Name)
}

// WorkspaceBinding maps a Task's declared workspace to a Volume.
type WorkspaceBinding struct {
	// Name is the name of the workspace populated by the volume.
	Name string `json:"name"`
	// SubPath is optionally a directory on the volume which should be used
	// for this binding (i.e. the volume will be mounted at this sub directory).
	// +optional
	SubPath string `json:"subPath,omitempty"`
	// PersistentVolumeClaimVolumeSource represents a reference to a
	// PersistentVolumeClaim in the same namespace. Either this OR EmptyDir can be used.
	// +optional
	PersistentVolumeClaim *corev1.PersistentVolumeClaimVolumeSource `json:"persistentVolumeClaim,omitempty"`
	// EmptyDir represents a temporary directory that shares a Task's lifetime.
	// More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir
	// Either this OR PersistentVolumeClaim can be used.
	// +optional
	EmptyDir *corev1.EmptyDirVolumeSource `json:"emptyDir,omitempty"`
	// ConfigMap represents a configMap that should populate this workspace.
	// +optional
	ConfigMap *corev1.ConfigMapVolumeSource `json:"configMap,omitempty"`
	// Secret represents a secret that should populate this workspace.
	// +optional
	Secret *corev1.SecretVolumeSource `json:"secret,omitempty"`
}

// WorkspacePipelineDeclaration creates a named slot in a Pipeline that a PipelineRun
// is expected to populate with a workspace binding.
type WorkspacePipelineDeclaration struct {
	// Name is the name of a workspace to be provided by a PipelineRun.
	Name string `json:"name"`
	// Description is a human readable string describing how the workspace will be
	// used in the Pipeline. It can be useful to include a bit of detail about which
	// tasks are intended to have access to the data on the workspace.
	// +optional
	Description string `json:"description"`
}

// WorkspacePipelineTaskBinding describes how a workspace passed into the pipeline should be
// mapped to a task's declared workspace.
type WorkspacePipelineTaskBinding struct {
	// Name is the name of the workspace as declared by the task
	Name string `json:"name"`
	// Workspace is the name of the workspace declared by the pipeline
	Workspace string `json:"workspace"`
}
<|endoftext|>


<commit_before>package ari

import (
	"fmt"
	"testing"
	"time"

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

type BridgeTests struct {
	suite.Suite
	c chan *Event
}

func (s *BridgeTests) SetupSuite() {
	DefaultClient.Go()
	time.Sleep(1 * time.Second)
}

func (s *BridgeTests) TearDownSuite() {
	DefaultClient.Close()
}

func (s *BridgeTests) TestBridgeCreate() {
	//Creation of empty bridge.
	bridge, err := DefaultClient.CreateBridge(CreateBridgeRequest{
		Id:   "testBridge",
		Type: "mixing",
	})
	s.Nil(err, "Bridge creation failed.")

	//the channel created for User1's phone
	u1Chan := OriginateRequest{
		Endpoint:  "PJSIP/102",
		App:       "default",
		AppArgs:   "",
		ChannelId: "Chan1",
	}

	_, err = DefaultClient.CreateChannel(u1Chan)
	s.Nil(err, "Channel for User1 not created")

	e := <-DefaultClient.Events
	for e.Type != "StasisStart" {
		e = <-DefaultClient.Events
	}

	err = DefaultClient.AnswerChannel("Chan1")
	s.Nil(err)
	//User1 has picked up his phone, Asterisk has finished the channel creation

	//the channel created for User2's phone
	u2Chan := OriginateRequest{
		Endpoint:  "PJSIP/101",
		App:       "default",
		AppArgs:   "",
		ChannelId: "Chan2",
	}

	_, err = DefaultClient.CreateChannel(u2Chan)
	s.Nil(err, "Channel for User2 not created")

	e = <-DefaultClient.Events
	for e.Type != "StasisStart" {
		e = <-DefaultClient.Events
	}

	err = DefaultClient.AnswerChannel("Chan2")
	s.Nil(err)
	//User2 has picked up his phone, Asterisk has finished the channel creation

	req := AddChannelRequest{
		ChannelId: "Chan1,Chan2",
	}

	//Incorporated 'Get Bridge' test:
	getBridge, err := DefaultClient.GetBridge(bridge.Id)
	s.Nil(err)
	s.NotNil(getBridge)
	fmt.Println("Bridge retrieved: ", getBridge)

	err = DefaultClient.AddChannel(bridge.Id, req)

	s.Nil(err, "Channels not added to bridge.")

}

func (s *BridgeTests) TestPlayMoh() {
	err := DefaultClient.PlayMusicOnHold("testBridge", "default")
	s.Nil(err, "Couldn't play MOH to bridge")
	fmt.Println("Sleeping 1 second to play MOH to bridge.")
	time.Sleep(1 * time.Second)
	err = DefaultClient.BridgeStopMoh("testBridge")
	s.Nil(err, "Couldn't stop MOH on bridge.")

}

func (s *BridgeTests) TestZDeleteBridge() {
	err := DefaultClient.BridgeDelete("testBridge")
	s.Nil(err, "Couldn't delete bridge")
}

func (s *BridgeTests) TestBridgeList() {
	list, err := DefaultClient.ListBridges()
	s.Nil(err, "Could not attain list of bridges.")
	fmt.Println("List of bridges: ")
	for _, element := range list {
		fmt.Println(element)
	}
	fmt.Println("End list of bridges.")
}

func (s *BridgeTests) TestRemoveChannel() {
	err := DefaultClient.RemoveChannel("testBridge", "Chan1")
	s.Nil(err, "Failure removing Chan1 from bridge.")
	fmt.Println("Chan1 should be removed from bridge. Sleeping for 3 seconds.")
	time.Sleep(3 * time.Second)
}

func (s *BridgeTests) TestBridgeRecordAndPlay() {
	req := RecordRequest{
		Name:               "name",
		Format:             "wav",
		Beep:               true,
		IfExists:           "overwrite",
		MaxDurationSeconds: 4,
	}
	rec, err := DefaultClient.RecordBridge("testBridge", req)
	fmt.Println("Allowing 5 seconds for liveRecording")
	time.Sleep(5 * time.Second)
	s.Nil(err, "Could not start live recording 'name'")
	s.Equal(rec.Name, "name", "Name retrieved does not match for live recording")
	s.Equal(rec.Format, "wav", "Format retrieved does not match for live recording")

	//Playback the recording.
	req2 := PlayMediaRequest{
		Media: "recording:name",
	}
	_, err = DefaultClient.PlayToBridge("testBridge", req2)
	fmt.Println("Waiting 5 seconds to play back recording")
	time.Sleep(5 * time.Second)
	s.Nil(err, "Couldn't play recording back to 'testBridge'")
}

type BridgeTestsSplit struct {
	suite.Suite
	c chan *Event
}

func (s *BridgeTestsSplit) SetupSuite() {
	DefaultClient.Go()
	time.Sleep(1 * time.Second)
}

func (s *BridgeTestsSplit) TearDownSuite() {
	DefaultClient.Close()
}

func (s *BridgeTestsSplit) TestBridgeOtherApp() {
	// Create a channel with the default client
	ch1, err := DefaultClient.NewChannel("PJSIP/101", nil)
	s.Nil(err, "Failed to create first channel")

	// Wait for answer
	e := <-DefaultClient.Events
	for e.Type != "StasisStart" {
		e = <-DefaultClient.Events
	}

	// Create a bridge with the default client
	br, err := DefaultClient.NewBridge()
	s.Nil(err, "Failed to create bridge")

	// Create a new client
	nc, err := NewClient("second", DefaultBaseUri, DefaultWsUri, DefaultUsername, DefaultSecret)
	s.Nil(err, "Failed to create second client")
	nc.Go()

	// Create channel on second client
	ch2, err := nc.NewChannel("PJSIP/102", nil)
	s.Nil(err, "Failed to create second channel")

	// Wait for answer
	e = <-nc.Events
	for e.Type != "StasisStart" {
		e = <-nc.Events
	}

	// Add ch1 to bridge
	err = br.Add(ch1.Id)
	s.Nil(err, "Failed to add ch1 to bridge")

	// Add ch2 to the bridge
	err = nc.AddChannel(br.Id, AddChannelRequest{ChannelId: ch2.Id})
	s.Nil(err, "Failed to add ch2 to bridge")

	fmt.Println("Waiting 1 second for achievement of serenity")
	time.Sleep(1 * time.Second)

	// Tear down bridge
	err = br.Delete()
	s.Nil(err, "Failed to delete bridge")

	// Hang up ch1
	err = ch1.Hangup()
	s.Nil(err, "Failed to hang up ch1")

	// Hang up ch2
	err = ch2.Hangup()
	s.Nil(err, "Failed to hang up ch2")

	// Shut down extra application
	nc.Close()
}

func TestBridgeSuite(t *testing.T) {
	//suite.Run(t, new(BridgeTests))

	suite.Run(t, new(BridgeTestsSplit))
}
<commit_msg>Reenable all bridge tests<commit_after>package ari

import (
	"fmt"
	"testing"
	"time"

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

type BridgeTests struct {
	suite.Suite
	c chan *Event
}

func (s *BridgeTests) SetupSuite() {
	DefaultClient.Go()
	time.Sleep(1 * time.Second)
}

func (s *BridgeTests) TearDownSuite() {
	DefaultClient.Close()
}

func (s *BridgeTests) TestBridgeCreate() {
	//Creation of empty bridge.
	bridge, err := DefaultClient.CreateBridge(CreateBridgeRequest{
		Id:   "testBridge",
		Type: "mixing",
	})
	s.Nil(err, "Bridge creation failed.")

	//the channel created for User1's phone
	u1Chan := OriginateRequest{
		Endpoint:  "PJSIP/102",
		App:       "default",
		AppArgs:   "",
		ChannelId: "Chan1",
	}

	_, err = DefaultClient.CreateChannel(u1Chan)
	s.Nil(err, "Channel for User1 not created")

	e := <-DefaultClient.Events
	for e.Type != "StasisStart" {
		e = <-DefaultClient.Events
	}

	err = DefaultClient.AnswerChannel("Chan1")
	s.Nil(err)
	//User1 has picked up his phone, Asterisk has finished the channel creation

	//the channel created for User2's phone
	u2Chan := OriginateRequest{
		Endpoint:  "PJSIP/101",
		App:       "default",
		AppArgs:   "",
		ChannelId: "Chan2",
	}

	_, err = DefaultClient.CreateChannel(u2Chan)
	s.Nil(err, "Channel for User2 not created")

	e = <-DefaultClient.Events
	for e.Type != "StasisStart" {
		e = <-DefaultClient.Events
	}

	err = DefaultClient.AnswerChannel("Chan2")
	s.Nil(err)
	//User2 has picked up his phone, Asterisk has finished the channel creation

	req := AddChannelRequest{
		ChannelId: "Chan1,Chan2",
	}

	//Incorporated 'Get Bridge' test:
	getBridge, err := DefaultClient.GetBridge(bridge.Id)
	s.Nil(err)
	s.NotNil(getBridge)
	fmt.Println("Bridge retrieved: ", getBridge)

	err = DefaultClient.AddChannel(bridge.Id, req)

	s.Nil(err, "Channels not added to bridge.")

}

func (s *BridgeTests) TestPlayMoh() {
	err := DefaultClient.PlayMusicOnHold("testBridge", "default")
	s.Nil(err, "Couldn't play MOH to bridge")
	fmt.Println("Sleeping 1 second to play MOH to bridge.")
	time.Sleep(1 * time.Second)
	err = DefaultClient.BridgeStopMoh("testBridge")
	s.Nil(err, "Couldn't stop MOH on bridge.")

}

func (s *BridgeTests) TestZDeleteBridge() {
	err := DefaultClient.BridgeDelete("testBridge")
	s.Nil(err, "Couldn't delete bridge")
}

func (s *BridgeTests) TestBridgeList() {
	list, err := DefaultClient.ListBridges()
	s.Nil(err, "Could not attain list of bridges.")
	fmt.Println("List of bridges: ")
	for _, element := range list {
		fmt.Println(element)
	}
	fmt.Println("End list of bridges.")
}

func (s *BridgeTests) TestRemoveChannel() {
	err := DefaultClient.RemoveChannel("testBridge", "Chan1")
	s.Nil(err, "Failure removing Chan1 from bridge.")
	fmt.Println("Chan1 should be removed from bridge. Sleeping for 3 seconds.")
	time.Sleep(3 * time.Second)
}

func (s *BridgeTests) TestBridgeRecordAndPlay() {
	req := RecordRequest{
		Name:               "name",
		Format:             "wav",
		Beep:               true,
		IfExists:           "overwrite",
		MaxDurationSeconds: 4,
	}
	rec, err := DefaultClient.RecordBridge("testBridge", req)
	fmt.Println("Allowing 5 seconds for liveRecording")
	time.Sleep(5 * time.Second)
	s.Nil(err, "Could not start live recording 'name'")
	s.Equal(rec.Name, "name", "Name retrieved does not match for live recording")
	s.Equal(rec.Format, "wav", "Format retrieved does not match for live recording")

	//Playback the recording.
	req2 := PlayMediaRequest{
		Media: "recording:name",
	}
	_, err = DefaultClient.PlayToBridge("testBridge", req2)
	fmt.Println("Waiting 5 seconds to play back recording")
	time.Sleep(5 * time.Second)
	s.Nil(err, "Couldn't play recording back to 'testBridge'")
}

type BridgeTestsSplit struct {
	suite.Suite
	c chan *Event
}

func (s *BridgeTestsSplit) SetupSuite() {
	DefaultClient.Go()
	time.Sleep(1 * time.Second)
}

func (s *BridgeTestsSplit) TearDownSuite() {
	DefaultClient.Close()
}

func (s *BridgeTestsSplit) TestBridgeOtherApp() {
	// Create a channel with the default client
	ch1, err := DefaultClient.NewChannel("PJSIP/101", nil)
	s.Nil(err, "Failed to create first channel")

	// Wait for answer
	e := <-DefaultClient.Events
	for e.Type != "StasisStart" {
		e = <-DefaultClient.Events
	}

	// Create a bridge with the default client
	br, err := DefaultClient.NewBridge()
	s.Nil(err, "Failed to create bridge")

	// Create a new client
	nc, err := NewClient("second", DefaultBaseUri, DefaultWsUri, DefaultUsername, DefaultSecret)
	s.Nil(err, "Failed to create second client")
	nc.Go()

	// Create channel on second client
	ch2, err := nc.NewChannel("PJSIP/102", nil)
	s.Nil(err, "Failed to create second channel")

	// Wait for answer
	e = <-nc.Events
	for e.Type != "StasisStart" {
		e = <-nc.Events
	}

	// Add ch1 to bridge
	err = br.Add(ch1.Id)
	s.Nil(err, "Failed to add ch1 to bridge")

	// Add ch2 to the bridge
	err = nc.AddChannel(br.Id, AddChannelRequest{ChannelId: ch2.Id})
	s.Nil(err, "Failed to add ch2 to bridge")

	fmt.Println("Waiting 1 second for achievement of serenity")
	time.Sleep(1 * time.Second)

	// Tear down bridge
	err = br.Delete()
	s.Nil(err, "Failed to delete bridge")

	// Hang up ch1
	err = ch1.Hangup()
	s.Nil(err, "Failed to hang up ch1")

	// Hang up ch2
	err = ch2.Hangup()
	s.Nil(err, "Failed to hang up ch2")

	// Shut down extra application
	nc.Close()
}

func TestBridgeSuite(t *testing.T) {
	suite.Run(t, new(BridgeTests))

	suite.Run(t, new(BridgeTestsSplit))
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"fmt"
	"net/http"
	"net/url"
	"os"

	_ "github.com/go-sql-driver/mysql"
	_ "github.com/lib/pq"
)

func dbHandler(w http.ResponseWriter, r *http.Request) {
	ssl := r.FormValue("ssl") != "false"
	service := r.FormValue("service")

	err := testDBConnection(ssl, service)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	writeJson(w, map[string]interface{}{
		"success": true,
	})
}

func testDBConnection(ssl bool, service string) error {
	var err error
	var db *sql.DB

	if service == "" {
		service = "postgres"
	}

	switch service {
	case "mysql":
		dbu := os.Getenv("DATABASE_URL")
		db, err = mysqlOpen(dbu, ssl)
		if err != nil {
			return err
		}
		return testSQLConnection(db)
	case "postgres":
		dbu := os.Getenv("DATABASE_URL")
		db, err = postgresOpen(dbu, ssl)
		if err != nil {
			return err
		}
		return testSQLConnection(db)
	default:
		return fmt.Errorf("unknown service: " + service)
	}
}

func testSQLConnection(db *sql.DB) error {
	defer db.Close()

	_, err := db.Exec("CREATE TABLE foo(id integer)")
	if err != nil {
		return err
	}
	defer func() {
		db.Exec("DROP TABLE foo")
	}()

	_, err = db.Exec("INSERT INTO foo VALUES(42)")
	if err != nil {
		return err
	}

	var id int
	err = db.QueryRow("SELECT * FROM foo LIMIT 1").Scan(&id)
	if err != nil {
		return err
	}
	if id != 42 {
		return fmt.Errorf("Expected 42, got %d", id)
	}

	return nil
}

func postgresOpen(dbu string, ssl bool) (*sql.DB, error) {
	u, err := url.Parse(dbu)
	if err != nil {
		return nil, err
	}

	if ssl {
		u.RawQuery = "sslmode=verify-full"
	} else {
		u.RawQuery = "sslmode=disable"
	}

	return sql.Open("postgres", u.String())
}

func mysqlOpen(dbu string, ssl bool) (*sql.DB, error) {
	u, err := url.Parse(dbu)
	if err != nil {
		return nil, err
	}

	if ssl {
		u.RawQuery = "tls=true"
	} else {
		u.RawQuery = "tls=false"
	}

	connString := fmt.Sprintf("%s@tcp(%s:%s)%s?%s", u.User.String(), u.Hostname(), u.Port(), u.EscapedPath(), u.RawQuery)

	return sql.Open("mysql", connString)
}
<commit_msg>Ensure ServerName is set in mysql TLS config in healthcheck app<commit_after>package main

import (
	"crypto/tls"
	"database/sql"
	"fmt"
	"net/http"
	"net/url"
	"os"

	mysql "github.com/go-sql-driver/mysql"
	_ "github.com/lib/pq"
)

func dbHandler(w http.ResponseWriter, r *http.Request) {
	ssl := r.FormValue("ssl") != "false"
	service := r.FormValue("service")

	err := testDBConnection(ssl, service)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	writeJson(w, map[string]interface{}{
		"success": true,
	})
}

func testDBConnection(ssl bool, service string) error {
	var err error
	var db *sql.DB

	if service == "" {
		service = "postgres"
	}

	switch service {
	case "mysql":
		dbu := os.Getenv("DATABASE_URL")
		db, err = mysqlOpen(dbu, ssl)
		if err != nil {
			return err
		}
		return testSQLConnection(db)
	case "postgres":
		dbu := os.Getenv("DATABASE_URL")
		db, err = postgresOpen(dbu, ssl)
		if err != nil {
			return err
		}
		return testSQLConnection(db)
	default:
		return fmt.Errorf("unknown service: " + service)
	}
}

func testSQLConnection(db *sql.DB) error {
	defer db.Close()

	_, err := db.Exec("CREATE TABLE foo(id integer)")
	if err != nil {
		return err
	}
	defer func() {
		db.Exec("DROP TABLE foo")
	}()

	_, err = db.Exec("INSERT INTO foo VALUES(42)")
	if err != nil {
		return err
	}

	var id int
	err = db.QueryRow("SELECT * FROM foo LIMIT 1").Scan(&id)
	if err != nil {
		return err
	}
	if id != 42 {
		return fmt.Errorf("Expected 42, got %d", id)
	}

	return nil
}

func postgresOpen(dbu string, ssl bool) (*sql.DB, error) {
	u, err := url.Parse(dbu)
	if err != nil {
		return nil, err
	}

	if ssl {
		u.RawQuery = "sslmode=verify-full"
	} else {
		u.RawQuery = "sslmode=disable"
	}

	return sql.Open("postgres", u.String())
}

func mysqlOpen(dbu string, ssl bool) (*sql.DB, error) {
	u, err := url.Parse(dbu)
	if err != nil {
		return nil, err
	}

	if ssl {
		u.RawQuery = "tls=custom"
		if err := mysql.RegisterTLSConfig("custom", &tls.Config{ServerName: u.Hostname()}); err != nil {
			return nil, err
		}

	} else {
		u.RawQuery = "tls=false"
	}

	connString := fmt.Sprintf("%s@tcp(%s:%s)%s?%s", u.User.String(), u.Hostname(), u.Port(), u.EscapedPath(), u.RawQuery)

	return sql.Open("mysql", connString)
}
<|endoftext|>


<commit_before>package util

import (
	"io"
	"fmt"
	"mime"
	"strconv"
	"net/http"
	"io/ioutil"
	"encoding/json"
	"crypto/rand"
	"encoding/hex"

	"github.com/jcloudpub/speedy/chunkmaster/util/log"
)

func EncodeJson(data interface {}) ([]byte, error) {
	body, err := json.Marshal(data)
	if err != nil {
		return nil, err
	}
	return body, nil
}

func DecodeJson(data []byte) (map[string]interface {}, error) {
	var m map[string]interface {}
	err := json.Unmarshal(data, &m)
	if err != nil {
		return nil, err
	}
	return m, nil
}

func Call(method, baseUrl, path string, body io.Reader, headers map[string][]string)([]byte, int, error) {
	client := &http.Client{}
	req, err := http.NewRequest(method, baseUrl + path, body)
	if err != nil {
		return nil, 408, err
	}

	req.Header.Set("User-Agent", "XENIUMD-AGENT")
	if method == "POST" {
		req.Header.Set("Content-Type", "application/json")
	}

	if headers != nil {
		for k, v := range headers {
			req.Header[k] = v
		}
	}
	resp, err := client.Do(req)
	if err != nil {
		return nil, resp.StatusCode, err
	}

	dataBody, err:= ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()
	if err != nil {
		return nil, resp.StatusCode, err
	}
	return dataBody, resp.StatusCode, nil
}

func Response(data []byte, statusCode int, resp http.ResponseWriter) {
	resp.WriteHeader(statusCode)
	fmt.Fprintf(resp, string(data))
}

func NotFoundHandle(resp http.ResponseWriter, req *http.Request) {
	resp.WriteHeader(http.StatusNotFound)
	fmt.Fprintf(resp, "Not Found this page.")
}

func HandleError(resp http.ResponseWriter, respData string, err error, respCode int) {
	if err != nil {
		log.Errorf("resp err. %v", err)
	}

	log.Errorf("respCode %d.", respCode)
	resp.WriteHeader(respCode)

	log.Errorf("respData %s.", respData)
	fmt.Fprintf(resp, respData)
}

func ContentTypeCheck(r *http.Request) error {
	ct := r.Header.Get("Content-Type")

	// No Content-Type header is ok as long as there's no Body
	if ct == "" {
		if r.Body == nil || r.ContentLength == 0 {
			return nil
		}
	}

	// Otherwise it better be json
	if MatchsContentType(ct, "application/json") {
		return nil
	}

	return fmt.Errorf("Content-Type spcified (%s) must be 'application/json'", ct)
}

func MatchsContentType(contentType, expectedType string) bool {
	mimetype, _, err := mime.ParseMediaType(contentType)

	if err != nil {
		fmt.Errorf("Error parsing media type: %s error: %s", contentType, err.Error())
	}

	return err == nil && mimetype == expectedType
}

func truncateID(id string) string {
	shortLen := 12
	if len(id) < shortLen {
		shortLen = len(id)
	}
	return id[:shortLen]
}

// GenerateRandomID returns an unique id
func GenerateRandomID() string {
	for {
		id := make([]byte, 16)
		if _, err := io.ReadFull(rand.Reader, id); err != nil {
			panic(err) // This shouldn't happen
		}
		value := hex.EncodeToString(id)
		// if we try to parse the truncated for as an int and we don't have
		// an error then the value is all numberic and causes issues when
		// used as a hostname. ref #3869
		if _, err := strconv.ParseInt(truncateID(value), 10, 32); err == nil {
			continue
		}
		return value
	}
}

func CheckMapString(m map[string]interface{}, key string) (string, error) {
	if _, ok := m[key]; !ok {
		return "", fmt.Errorf(key + " is nil")
	}

	if v, ok := m[key].(string); ok {
		return v, nil
	}

	return "", fmt.Errorf(key + " is not string type")
}

func CheckMapInt64(m map[string]interface{}, key string) (int64, error) {
	if _, ok := m[key]; !ok {
		return 0, fmt.Errorf(key + " is nil")
	}

	if v, ok := m[key].(float64); ok {
		return int64(v), nil
	}

	return 0, fmt.Errorf(key + " is not int64 type")
}

func CheckMapInt(m map[string]interface{}, key string) (int, error) {
	if _, ok := m[key]; !ok {
		return 0, fmt.Errorf(key + " is nil")
	}

	if v, ok := m[key].(float64); ok {
		return int(v), nil
	}

	return 0, fmt.Errorf(key + " is not int type")
}

func CheckMapUInt16(m map[string]interface{}, key string) (uint16, error) {
	if _, ok := m[key]; !ok {
		return 0, fmt.Errorf(key + " is nil")
	}

	if v, ok := m[key].(float64); ok {
		return uint16(v), nil
	}

	return 0, fmt.Errorf(key + " is not uint16 type")
}

func CheckMapUInt32(m map[string]interface{}, key string) (uint32, error) {
	if _, ok := m[key]; !ok {
		return 0, fmt.Errorf(key + " is nil")
	}

	if v, ok := m[key].(float64); ok {
		return uint32(v), nil
	}

	return 0, fmt.Errorf(key + " is not uint32 type")
}
<commit_msg>set http header, delete<commit_after>package util

import (
	"io"
	"fmt"
	"mime"
	"strconv"
	"net/http"
	"io/ioutil"
	"encoding/json"
	"crypto/rand"
	"encoding/hex"

	"github.com/jcloudpub/speedy/chunkmaster/util/log"
)

func EncodeJson(data interface {}) ([]byte, error) {
	body, err := json.Marshal(data)
	if err != nil {
		return nil, err
	}
	return body, nil
}

func DecodeJson(data []byte) (map[string]interface {}, error) {
	var m map[string]interface {}
	err := json.Unmarshal(data, &m)
	if err != nil {
		return nil, err
	}
	return m, nil
}

func Call(method, baseUrl, path string, body io.Reader, headers map[string][]string)([]byte, int, error) {
	client := &http.Client{}
	req, err := http.NewRequest(method, baseUrl + path, body)
	if err != nil {
		return nil, 408, err
	}

	if method == "POST" {
		req.Header.Set("Content-Type", "application/json")
	}

	if headers != nil {
		for k, v := range headers {
			req.Header[k] = v
		}
	}
	resp, err := client.Do(req)
	if err != nil {
		return nil, resp.StatusCode, err
	}

	dataBody, err:= ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()
	if err != nil {
		return nil, resp.StatusCode, err
	}
	return dataBody, resp.StatusCode, nil
}

func Response(data []byte, statusCode int, resp http.ResponseWriter) {
	resp.WriteHeader(statusCode)
	fmt.Fprintf(resp, string(data))
}

func NotFoundHandle(resp http.ResponseWriter, req *http.Request) {
	resp.WriteHeader(http.StatusNotFound)
	fmt.Fprintf(resp, "Not Found this page.")
}

func HandleError(resp http.ResponseWriter, respData string, err error, respCode int) {
	if err != nil {
		log.Errorf("resp err. %v", err)
	}

	log.Errorf("respCode %d.", respCode)
	resp.WriteHeader(respCode)

	log.Errorf("respData %s.", respData)
	fmt.Fprintf(resp, respData)
}

func ContentTypeCheck(r *http.Request) error {
	ct := r.Header.Get("Content-Type")

	// No Content-Type header is ok as long as there's no Body
	if ct == "" {
		if r.Body == nil || r.ContentLength == 0 {
			return nil
		}
	}

	// Otherwise it better be json
	if MatchsContentType(ct, "application/json") {
		return nil
	}

	return fmt.Errorf("Content-Type spcified (%s) must be 'application/json'", ct)
}

func MatchsContentType(contentType, expectedType string) bool {
	mimetype, _, err := mime.ParseMediaType(contentType)

	if err != nil {
		fmt.Errorf("Error parsing media type: %s error: %s", contentType, err.Error())
	}

	return err == nil && mimetype == expectedType
}

func truncateID(id string) string {
	shortLen := 12
	if len(id) < shortLen {
		shortLen = len(id)
	}
	return id[:shortLen]
}

// GenerateRandomID returns an unique id
func GenerateRandomID() string {
	for {
		id := make([]byte, 16)
		if _, err := io.ReadFull(rand.Reader, id); err != nil {
			panic(err) // This shouldn't happen
		}
		value := hex.EncodeToString(id)
		// if we try to parse the truncated for as an int and we don't have
		// an error then the value is all numberic and causes issues when
		// used as a hostname. ref #3869
		if _, err := strconv.ParseInt(truncateID(value), 10, 32); err == nil {
			continue
		}
		return value
	}
}

func CheckMapString(m map[string]interface{}, key string) (string, error) {
	if _, ok := m[key]; !ok {
		return "", fmt.Errorf(key + " is nil")
	}

	if v, ok := m[key].(string); ok {
		return v, nil
	}

	return "", fmt.Errorf(key + " is not string type")
}

func CheckMapInt64(m map[string]interface{}, key string) (int64, error) {
	if _, ok := m[key]; !ok {
		return 0, fmt.Errorf(key + " is nil")
	}

	if v, ok := m[key].(float64); ok {
		return int64(v), nil
	}

	return 0, fmt.Errorf(key + " is not int64 type")
}

func CheckMapInt(m map[string]interface{}, key string) (int, error) {
	if _, ok := m[key]; !ok {
		return 0, fmt.Errorf(key + " is nil")
	}

	if v, ok := m[key].(float64); ok {
		return int(v), nil
	}

	return 0, fmt.Errorf(key + " is not int type")
}

func CheckMapUInt16(m map[string]interface{}, key string) (uint16, error) {
	if _, ok := m[key]; !ok {
		return 0, fmt.Errorf(key + " is nil")
	}

	if v, ok := m[key].(float64); ok {
		return uint16(v), nil
	}

	return 0, fmt.Errorf(key + " is not uint16 type")
}

func CheckMapUInt32(m map[string]interface{}, key string) (uint32, error) {
	if _, ok := m[key]; !ok {
		return 0, fmt.Errorf(key + " is nil")
	}

	if v, ok := m[key].(float64); ok {
		return uint32(v), nil
	}

	return 0, fmt.Errorf(key + " is not uint32 type")
}
<|endoftext|>


<commit_before>package vfs

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"

	"github.com/cozy/cozy-stack/pkg/config/config"
	"github.com/cozy/cozy-stack/pkg/logger"
	"github.com/cozy/cozy-stack/pkg/previewfs"
)

// ServePDFPreview will send the preview image for a PDF.
func ServePDFPreview(w http.ResponseWriter, req *http.Request, fs VFS, doc *FileDoc) error {
	name := fmt.Sprintf("%s-preview.jpg", doc.ID())
	modtime := doc.UpdatedAt
	if doc.CozyMetadata != nil && doc.CozyMetadata.UploadedAt != nil {
		modtime = *doc.CozyMetadata.UploadedAt
	}
	buf, err := preview(fs, doc)
	if err != nil {
		return err
	}
	http.ServeContent(w, req, name, modtime, bytes.NewReader(buf.Bytes()))
	return nil
}

func preview(fs VFS, doc *FileDoc) (*bytes.Buffer, error) {
	cache := previewfs.SystemCache()
	if buf, err := cache.Get(doc.MD5Sum); err == nil {
		return buf, nil
	}

	buf, err := generatePreview(fs, doc)
	_ = cache.Set(doc.MD5Sum, buf)
	return buf, err
}

func generatePreview(fs VFS, doc *FileDoc) (*bytes.Buffer, error) {
	f, err := fs.OpenFile(doc)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	tempDir, err := ioutil.TempDir("", "magick")
	if err != nil {
		return nil, err
	}
	defer os.RemoveAll(tempDir)
	envTempDir := fmt.Sprintf("MAGICK_TEMPORARY_PATH=%s", tempDir)
	env := []string{envTempDir}

	convertCmd := config.GetConfig().Jobs.ImageMagickConvertCmd
	if convertCmd == "" {
		convertCmd = "convert"
	}
	args := []string{
		"-limit", "Memory", "2GB",
		"-limit", "Map", "3GB",
		"-[0]",           // Takes the input from stdin
		"-quality", "82", // A good compromise between file size and quality
		"-interlace", "none", // Don't use progressive JPEGs, they are heavier
		"-thumbnail", "720x1280>", // Makes a thumbnail that fits inside the given format
		"-colorspace", "sRGB", // Use the colorspace recommended for web, sRGB
		"jpg:-", // Send the output on stdout, in JPEG format
	}

	var stdout, stderr bytes.Buffer
	cmd := exec.Command(convertCmd, args...)
	cmd.Env = env
	cmd.Stdin = f
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	if err := cmd.Run(); err != nil {
		// Truncate very long messages
		msg := stderr.String()
		if len(msg) > 4000 {
			msg = msg[:4000]
		}
		logger.WithNamespace("pdf_preview").
			WithField("stderr", msg).
			WithField("file_id", doc.ID()).
			Errorf("imagemagick failed: %s", err)
		return nil, err
	}
	return &stdout, nil
}
<commit_msg>Increase the size of the preview for PDFs<commit_after>package vfs

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"

	"github.com/cozy/cozy-stack/pkg/config/config"
	"github.com/cozy/cozy-stack/pkg/logger"
	"github.com/cozy/cozy-stack/pkg/previewfs"
)

// ServePDFPreview will send the preview image for a PDF.
func ServePDFPreview(w http.ResponseWriter, req *http.Request, fs VFS, doc *FileDoc) error {
	name := fmt.Sprintf("%s-preview.jpg", doc.ID())
	modtime := doc.UpdatedAt
	if doc.CozyMetadata != nil && doc.CozyMetadata.UploadedAt != nil {
		modtime = *doc.CozyMetadata.UploadedAt
	}
	buf, err := preview(fs, doc)
	if err != nil {
		return err
	}
	http.ServeContent(w, req, name, modtime, bytes.NewReader(buf.Bytes()))
	return nil
}

func preview(fs VFS, doc *FileDoc) (*bytes.Buffer, error) {
	cache := previewfs.SystemCache()
	if buf, err := cache.Get(doc.MD5Sum); err == nil {
		return buf, nil
	}

	buf, err := generatePreview(fs, doc)
	_ = cache.Set(doc.MD5Sum, buf)
	return buf, err
}

func generatePreview(fs VFS, doc *FileDoc) (*bytes.Buffer, error) {
	f, err := fs.OpenFile(doc)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	tempDir, err := ioutil.TempDir("", "magick")
	if err != nil {
		return nil, err
	}
	defer os.RemoveAll(tempDir)
	envTempDir := fmt.Sprintf("MAGICK_TEMPORARY_PATH=%s", tempDir)
	env := []string{envTempDir}

	convertCmd := config.GetConfig().Jobs.ImageMagickConvertCmd
	if convertCmd == "" {
		convertCmd = "convert"
	}
	args := []string{
		"-limit", "Memory", "2GB",
		"-limit", "Map", "3GB",
		"-[0]",           // Takes the input from stdin
		"-quality", "82", // A good compromise between file size and quality
		"-interlace", "none", // Don't use progressive JPEGs, they are heavier
		"-thumbnail", "1080x1920>", // Makes a thumbnail that fits inside the given format
		"-colorspace", "sRGB", // Use the colorspace recommended for web, sRGB
		"jpg:-", // Send the output on stdout, in JPEG format
	}

	var stdout, stderr bytes.Buffer
	cmd := exec.Command(convertCmd, args...)
	cmd.Env = env
	cmd.Stdin = f
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	if err := cmd.Run(); err != nil {
		// Truncate very long messages
		msg := stderr.String()
		if len(msg) > 4000 {
			msg = msg[:4000]
		}
		logger.WithNamespace("pdf_preview").
			WithField("stderr", msg).
			WithField("file_id", doc.ID()).
			Errorf("imagemagick failed: %s", err)
		return nil, err
	}
	return &stdout, nil
}
<|endoftext|>