<commit_before>// local build script file, similar to a makefile or collection of bash scripts in other projects

package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"math"
	"os"
	"os/exec"
	"strings"

	"github.com/urfave/cli/v2"
)

var packages = []string{"cli", "altsrc"}

func main() {
	app := cli.NewApp()

	app.Name = "builder"
	app.Usage = "Generates a new urfave/cli build!"

	app.Commands = cli.Commands{
		{
			Name:   "vet",
			Action: VetActionFunc,
		},
		{
			Name:   "test",
			Action: TestActionFunc,
		},
		{
			Name:   "gfmrun",
			Action: GfmrunActionFunc,
		},
		{
			Name:   "toc",
			Action: TocActionFunc,
		},
		{
			Name:   "check-binary-size",
			Action: checkBinarySizeActionFunc,
		},
	}

	err := app.Run(os.Args)
	if err != nil {
		log.Fatal(err)
	}
}

func runCmd(arg string, args ...string) error {
	cmd := exec.Command(arg, args...)

	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	return cmd.Run()
}

func VetActionFunc(_ *cli.Context) error {
	return runCmd("go", "vet")
}

func TestActionFunc(c *cli.Context) error {
	for _, pkg := range packages {
		var packageName string

		if pkg == "cli" {
			packageName = "github.com/urfave/cli/v2"
		} else {
			packageName = fmt.Sprintf("github.com/urfave/cli/v2/%s", pkg)
		}

		coverProfile := fmt.Sprintf("--coverprofile=%s.coverprofile", pkg)

		err := runCmd("go", "test", "-v", coverProfile, packageName)
		if err != nil {
			return err
		}
	}

	return testCleanup()
}

func testCleanup() error {
	var out bytes.Buffer

	for _, pkg := range packages {
		file, err := os.Open(fmt.Sprintf("%s.coverprofile", pkg))
		if err != nil {
			return err
		}

		b, err := ioutil.ReadAll(file)
		if err != nil {
			return err
		}

		out.Write(b)
		err = file.Close()
		if err != nil {
			return err
		}

		err = os.Remove(fmt.Sprintf("%s.coverprofile", pkg))
		if err != nil {
			return err
		}
	}

	outFile, err := os.Create("coverage.txt")
	if err != nil {
		return err
	}

	_, err = out.WriteTo(outFile)
	if err != nil {
		return err
	}

	err = outFile.Close()
	if err != nil {
		return err
	}

	return nil
}

func GfmrunActionFunc(c *cli.Context) error {
	filename := c.Args().Get(0)
	if filename == "" {
		filename = "README.md"
	}

	file, err := os.Open(filename)
	if err != nil {
		return err
	}
	defer file.Close()

	var counter int
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		if strings.Contains(scanner.Text(), "package main") {
			counter++
		}
	}

	err = file.Close()
	if err != nil {
		return err
	}

	err = scanner.Err()
	if err != nil {
		return err
	}

	return runCmd("gfmrun", "-c", fmt.Sprint(counter), "-s", filename)
}

func TocActionFunc(c *cli.Context) error {
	filename := c.Args().Get(0)
	if filename == "" {
		filename = "README.md"
	}

	err := runCmd("markdown-toc", "-i", filename)
	if err != nil {
		return err
	}

	err = runCmd("git", "diff", "--exit-code")
	if err != nil {
		return err
	}

	return nil
}

// checkBinarySizeActionFunc checks the size of an example binary to ensure that we are keeping size down
// this was originally inspired by https://github.com/urfave/cli/issues/1055, and followed up on as a part
// of https://github.com/urfave/cli/issues/1057
func checkBinarySizeActionFunc(c *cli.Context) (err error) {
	const (
		sourceFilePath       = "./internal/example/example.go"
		builtFilePath        = "./internal/example/built-example"
		desiredMinBinarySize = 3.2
		desiredMaxBinarySize = 3.8
		badNewsEmoji         = "🚨"
		goodNewsEmoji        = "✨"
		checksPassedEmoji    = "✅"
		mbStringFormatter    = "%.1fMB"
	)

	// build example binary
	err = runCmd("go", "build", "-o", builtFilePath, "-ldflags", "-s -w", sourceFilePath)
	if err != nil {
		return err
	}

	// get file info
	fileInfo, err := os.Stat(builtFilePath)
	if err != nil {
		return err
	}

	// get human readable size, in MB with one decimal place.
	// example output is: 35.2MB. (note: this simply an example)
	// that output is much easier to reason about than the `35223432`
	// that you would see output without the rounding
	fileSize := fileInfo.Size()
	fileSizeInMB := float64(fileSize) / float64(1000000)
	roundedFileSize := math.Round(fileSizeInMB*10) / 10
	roundedFileSizeString := fmt.Sprintf(mbStringFormatter, roundedFileSize)

	// check against bounds
	isLessThanDesiredMin := roundedFileSize < desiredMinBinarySize
	isMoreThanDesiredMax := roundedFileSize > desiredMaxBinarySize
	desiredMinSizeString := fmt.Sprintf(mbStringFormatter, desiredMinBinarySize)
	desiredMaxSizeString := fmt.Sprintf(mbStringFormatter, desiredMaxBinarySize)

	// show guidance
	fmt.Println(fmt.Sprintf("\n%s is the current binary size", roundedFileSizeString))
	// show guidance for min size
	if isLessThanDesiredMin {
		fmt.Println(fmt.Sprintf("  %s %s is the target min size", goodNewsEmoji, desiredMinSizeString))
		fmt.Println("") // visual spacing
		fmt.Println("     The binary is smaller than the target min size, which is great news!")
		fmt.Println("     That means that your changes are shrinking the binary size.")
		fmt.Println("     You'll want to go into ./internal/build/build.go and decrease")
		fmt.Println("     the desiredMinBinarySize, and also probably decrease the ")
		fmt.Println("     desiredMaxBinarySize by the same amount. That will ensure that")
		fmt.Println("     future PRs will enforce the newly shrunk binary sizes.")
		fmt.Println("") // visual spacing
		os.Exit(1)
	} else {
		fmt.Println(fmt.Sprintf("  %s %s is the target min size", checksPassedEmoji, desiredMinSizeString))
	}
	// show guidance for max size
	if isMoreThanDesiredMax {
		fmt.Println(fmt.Sprintf("  %s %s is the target max size", badNewsEmoji, desiredMaxSizeString))
		fmt.Println("") // visual spacing
		fmt.Println("     The binary is larger than the target max size.")
		fmt.Println("     That means that your changes are increasing the binary size.")
		fmt.Println("     The first thing you'll want to do is ask your yourself")
		fmt.Println("     Is this change worth increasing the binary size?")
		fmt.Println("     Larger binary sizes for this package can dissuade its use.")
		fmt.Println("     If this change is worth the increase, then we can up the")
		fmt.Println("     desired max binary size. To do that you'll want to go into")
		fmt.Println("     ./internal/build/build.go and increase the desiredMaxBinarySize,")
		fmt.Println("     and increase the desiredMinBinarySize by the same amount.")
		fmt.Println("") // visual spacing
		os.Exit(1)
	} else {
		fmt.Println(fmt.Sprintf("  %s %s is the target max size", checksPassedEmoji, desiredMaxSizeString))
	}

	return nil
}
<commit_msg>move bounds<commit_after>// local build script file, similar to a makefile or collection of bash scripts in other projects

package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"math"
	"os"
	"os/exec"
	"strings"

	"github.com/urfave/cli/v2"
)

var packages = []string{"cli", "altsrc"}

func main() {
	app := cli.NewApp()

	app.Name = "builder"
	app.Usage = "Generates a new urfave/cli build!"

	app.Commands = cli.Commands{
		{
			Name:   "vet",
			Action: VetActionFunc,
		},
		{
			Name:   "test",
			Action: TestActionFunc,
		},
		{
			Name:   "gfmrun",
			Action: GfmrunActionFunc,
		},
		{
			Name:   "toc",
			Action: TocActionFunc,
		},
		{
			Name:   "check-binary-size",
			Action: checkBinarySizeActionFunc,
		},
	}

	err := app.Run(os.Args)
	if err != nil {
		log.Fatal(err)
	}
}

func runCmd(arg string, args ...string) error {
	cmd := exec.Command(arg, args...)

	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	return cmd.Run()
}

func VetActionFunc(_ *cli.Context) error {
	return runCmd("go", "vet")
}

func TestActionFunc(c *cli.Context) error {
	for _, pkg := range packages {
		var packageName string

		if pkg == "cli" {
			packageName = "github.com/urfave/cli/v2"
		} else {
			packageName = fmt.Sprintf("github.com/urfave/cli/v2/%s", pkg)
		}

		coverProfile := fmt.Sprintf("--coverprofile=%s.coverprofile", pkg)

		err := runCmd("go", "test", "-v", coverProfile, packageName)
		if err != nil {
			return err
		}
	}

	return testCleanup()
}

func testCleanup() error {
	var out bytes.Buffer

	for _, pkg := range packages {
		file, err := os.Open(fmt.Sprintf("%s.coverprofile", pkg))
		if err != nil {
			return err
		}

		b, err := ioutil.ReadAll(file)
		if err != nil {
			return err
		}

		out.Write(b)
		err = file.Close()
		if err != nil {
			return err
		}

		err = os.Remove(fmt.Sprintf("%s.coverprofile", pkg))
		if err != nil {
			return err
		}
	}

	outFile, err := os.Create("coverage.txt")
	if err != nil {
		return err
	}

	_, err = out.WriteTo(outFile)
	if err != nil {
		return err
	}

	err = outFile.Close()
	if err != nil {
		return err
	}

	return nil
}

func GfmrunActionFunc(c *cli.Context) error {
	filename := c.Args().Get(0)
	if filename == "" {
		filename = "README.md"
	}

	file, err := os.Open(filename)
	if err != nil {
		return err
	}
	defer file.Close()

	var counter int
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		if strings.Contains(scanner.Text(), "package main") {
			counter++
		}
	}

	err = file.Close()
	if err != nil {
		return err
	}

	err = scanner.Err()
	if err != nil {
		return err
	}

	return runCmd("gfmrun", "-c", fmt.Sprint(counter), "-s", filename)
}

func TocActionFunc(c *cli.Context) error {
	filename := c.Args().Get(0)
	if filename == "" {
		filename = "README.md"
	}

	err := runCmd("markdown-toc", "-i", filename)
	if err != nil {
		return err
	}

	err = runCmd("git", "diff", "--exit-code")
	if err != nil {
		return err
	}

	return nil
}

// checkBinarySizeActionFunc checks the size of an example binary to ensure that we are keeping size down
// this was originally inspired by https://github.com/urfave/cli/issues/1055, and followed up on as a part
// of https://github.com/urfave/cli/issues/1057
func checkBinarySizeActionFunc(c *cli.Context) (err error) {
	const (
		sourceFilePath       = "./internal/example/example.go"
		builtFilePath        = "./internal/example/built-example"
		desiredMinBinarySize = 3.3
		desiredMaxBinarySize = 3.8
		badNewsEmoji         = "🚨"
		goodNewsEmoji        = "✨"
		checksPassedEmoji    = "✅"
		mbStringFormatter    = "%.1fMB"
	)

	// build example binary
	err = runCmd("go", "build", "-o", builtFilePath, "-ldflags", "-s -w", sourceFilePath)
	if err != nil {
		return err
	}

	// get file info
	fileInfo, err := os.Stat(builtFilePath)
	if err != nil {
		return err
	}

	// get human readable size, in MB with one decimal place.
	// example output is: 35.2MB. (note: this simply an example)
	// that output is much easier to reason about than the `35223432`
	// that you would see output without the rounding
	fileSize := fileInfo.Size()
	fileSizeInMB := float64(fileSize) / float64(1000000)
	roundedFileSize := math.Round(fileSizeInMB*10) / 10
	roundedFileSizeString := fmt.Sprintf(mbStringFormatter, roundedFileSize)

	// check against bounds
	isLessThanDesiredMin := roundedFileSize < desiredMinBinarySize
	isMoreThanDesiredMax := roundedFileSize > desiredMaxBinarySize
	desiredMinSizeString := fmt.Sprintf(mbStringFormatter, desiredMinBinarySize)
	desiredMaxSizeString := fmt.Sprintf(mbStringFormatter, desiredMaxBinarySize)

	// show guidance
	fmt.Println(fmt.Sprintf("\n%s is the current binary size", roundedFileSizeString))
	// show guidance for min size
	if isLessThanDesiredMin {
		fmt.Println(fmt.Sprintf("  %s %s is the target min size", goodNewsEmoji, desiredMinSizeString))
		fmt.Println("") // visual spacing
		fmt.Println("     The binary is smaller than the target min size, which is great news!")
		fmt.Println("     That means that your changes are shrinking the binary size.")
		fmt.Println("     You'll want to go into ./internal/build/build.go and decrease")
		fmt.Println("     the desiredMinBinarySize, and also probably decrease the ")
		fmt.Println("     desiredMaxBinarySize by the same amount. That will ensure that")
		fmt.Println("     future PRs will enforce the newly shrunk binary sizes.")
		fmt.Println("") // visual spacing
		os.Exit(1)
	} else {
		fmt.Println(fmt.Sprintf("  %s %s is the target min size", checksPassedEmoji, desiredMinSizeString))
	}
	// show guidance for max size
	if isMoreThanDesiredMax {
		fmt.Println(fmt.Sprintf("  %s %s is the target max size", badNewsEmoji, desiredMaxSizeString))
		fmt.Println("") // visual spacing
		fmt.Println("     The binary is larger than the target max size.")
		fmt.Println("     That means that your changes are increasing the binary size.")
		fmt.Println("     The first thing you'll want to do is ask your yourself")
		fmt.Println("     Is this change worth increasing the binary size?")
		fmt.Println("     Larger binary sizes for this package can dissuade its use.")
		fmt.Println("     If this change is worth the increase, then we can up the")
		fmt.Println("     desired max binary size. To do that you'll want to go into")
		fmt.Println("     ./internal/build/build.go and increase the desiredMaxBinarySize,")
		fmt.Println("     and increase the desiredMinBinarySize by the same amount.")
		fmt.Println("") // visual spacing
		os.Exit(1)
	} else {
		fmt.Println(fmt.Sprintf("  %s %s is the target max size", checksPassedEmoji, desiredMaxSizeString))
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2017 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 file.

// Package https contains helpers for starting an HTTPS server.
package https // import "golang.org/x/build/internal/https"

import (
	"context"
	"crypto/tls"
	"fmt"
	"net"
	"net/http"
	"strings"
	"time"

	"cloud.google.com/go/storage"
	"golang.org/x/build/autocertcache"
	"golang.org/x/crypto/acme/autocert"
	"golang.org/x/net/http2"
)

// Options are the configuration parameters for the HTTP(S) server.
type Options struct {
	// Addr specifies the host and port the server should listen on.
	Addr string

	// AutocertCacheBucket specifies the name of the GCS bucket for
	// Let’s Encrypt to use. If this is not specified, then HTTP traffic is
	// served on Addr.
	AutocertCacheBucket string
}

var defaultOptions = &Options{
	Addr: "localhost:6343",
}

// ListenAndServe serves the given handler by HTTPS (and HTTP, redirecting to
// HTTPS) using the provided options.
//
// ListenAndServe always returns a non-nil error.
func ListenAndServe(handler http.Handler, opt *Options) error {
	if opt == nil {
		opt = defaultOptions
	}
	ln, err := net.Listen("tcp", opt.Addr)
	if err != nil {
		return fmt.Errorf(`net.Listen("tcp", %q): %v`, opt.Addr, err)
	}
	defer ln.Close()

	if opt.AutocertCacheBucket == "" {
		err := http.Serve(ln, handler)
		ln.Close()
		return fmt.Errorf("http.Serve = %v", err)
	}

	// handler is served primarily via HTTPS, so just redirect HTTP to HTTPS.
	redirect := &http.Server{
		Addr:    ln.Addr().String(),
		Handler: http.HandlerFunc(redirectToHTTPS),
	}
	errc := make(chan error)
	go func() {
		err := redirect.Serve(ln)
		errc <- fmt.Errorf("http.Serve = %v", err)
	}()

	ctx, cancel := context.WithCancel(context.TODO())
	go func() {
		errc <- serveAutocertTLS(ctx, handler, opt.AutocertCacheBucket)
	}()

	// Wait for the first error.
	err = <-errc

	// Stop the other handler (whichever it may be) and wait for it to return.
	redirect.Close()
	cancel()
	<-errc

	return err
}

// redirectToHTTPS will redirect to the https version of the URL requested. If
// r.TLS is set or r.Host is empty, a 404 not found response is sent.
func redirectToHTTPS(w http.ResponseWriter, r *http.Request) {
	if r.TLS != nil || r.Host == "" {
		http.NotFound(w, r)
		return
	}

	http.Redirect(w, r, "https://"+r.Host+r.RequestURI, http.StatusFound)
}

// serveAutocertTLS serves the handler h on port 443 using the given GCS bucket
// for its autocert cache. It will only serve on domains of the form *.golang.org.
//
// serveAutocertTLS always returns a non-nil error.
func serveAutocertTLS(ctx context.Context, h http.Handler, bucket string) error {
	ln, err := net.Listen("tcp", ":443")
	if err != nil {
		return err
	}
	defer ln.Close()

	ctx, cancel := context.WithCancel(ctx)
	server := &http.Server{
		Addr:    ln.Addr().String(),
		Handler: h,
	}
	done := make(chan struct{})
	go func() {
		<-ctx.Done()
		server.Close()
		close(done)
	}()
	defer func() {
		cancel()
		<-done
	}()

	sc, err := storage.NewClient(ctx)
	if err != nil {
		return fmt.Errorf("storage.NewClient: %v", err)
	}
	const hostSuffix = ".golang.org"
	m := autocert.Manager{
		Prompt: autocert.AcceptTOS,
		HostPolicy: func(ctx context.Context, host string) error {
			if !strings.HasSuffix(host, hostSuffix) {
				return fmt.Errorf("refusing to serve autocert on provided domain (%q), must have the suffix %q",
					host, hostSuffix)
			}
			return nil
		},
		Cache: autocertcache.NewGoogleCloudStorageCache(sc, bucket),
	}
	config := &tls.Config{
		GetCertificate: m.GetCertificate,
		NextProtos:     []string{"h2", "http/1.1"},
	}
	tlsLn := tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, config)
	if err := http2.ConfigureServer(server, nil); err != nil {
		return fmt.Errorf("http2.ConfigureServer: %v", err)
	}
	return server.Serve(tlsLn)
}

type tcpKeepAliveListener struct {
	*net.TCPListener
}

func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
	tc, err := ln.AcceptTCP()
	if err != nil {
		return
	}
	tc.SetKeepAlive(true)
	tc.SetKeepAlivePeriod(3 * time.Minute)
	return tc, nil
}
<commit_msg>internal/https: remove redundant Close in ListenAndServe<commit_after>// Copyright 2017 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 file.

// Package https contains helpers for starting an HTTPS server.
package https // import "golang.org/x/build/internal/https"

import (
	"context"
	"crypto/tls"
	"fmt"
	"net"
	"net/http"
	"strings"
	"time"

	"cloud.google.com/go/storage"
	"golang.org/x/build/autocertcache"
	"golang.org/x/crypto/acme/autocert"
	"golang.org/x/net/http2"
)

// Options are the configuration parameters for the HTTP(S) server.
type Options struct {
	// Addr specifies the host and port the server should listen on.
	Addr string

	// AutocertCacheBucket specifies the name of the GCS bucket for
	// Let’s Encrypt to use. If this is not specified, then HTTP traffic is
	// served on Addr.
	AutocertCacheBucket string
}

var defaultOptions = &Options{
	Addr: "localhost:6343",
}

// ListenAndServe serves the given handler by HTTPS (and HTTP, redirecting to
// HTTPS) using the provided options.
//
// ListenAndServe always returns a non-nil error.
func ListenAndServe(handler http.Handler, opt *Options) error {
	if opt == nil {
		opt = defaultOptions
	}
	ln, err := net.Listen("tcp", opt.Addr)
	if err != nil {
		return fmt.Errorf(`net.Listen("tcp", %q): %v`, opt.Addr, err)
	}
	defer ln.Close()

	if opt.AutocertCacheBucket == "" {
		err := http.Serve(ln, handler)
		return fmt.Errorf("http.Serve = %v", err)
	}

	// handler is served primarily via HTTPS, so just redirect HTTP to HTTPS.
	redirect := &http.Server{
		Handler: http.HandlerFunc(redirectToHTTPS),
	}
	errc := make(chan error)
	go func() {
		err := redirect.Serve(ln)
		errc <- fmt.Errorf("http.Serve = %v", err)
	}()

	ctx, cancel := context.WithCancel(context.TODO())
	go func() {
		errc <- serveAutocertTLS(ctx, handler, opt.AutocertCacheBucket)
	}()

	// Wait for the first error.
	err = <-errc

	// Stop the other handler (whichever it may be) and wait for it to return.
	redirect.Close()
	cancel()
	<-errc

	return err
}

// redirectToHTTPS will redirect to the https version of the URL requested. If
// r.TLS is set or r.Host is empty, a 404 not found response is sent.
func redirectToHTTPS(w http.ResponseWriter, r *http.Request) {
	if r.TLS != nil || r.Host == "" {
		http.NotFound(w, r)
		return
	}

	http.Redirect(w, r, "https://"+r.Host+r.RequestURI, http.StatusFound)
}

// serveAutocertTLS serves the handler h on port 443 using the given GCS bucket
// for its autocert cache. It will only serve on domains of the form *.golang.org.
//
// serveAutocertTLS always returns a non-nil error.
func serveAutocertTLS(ctx context.Context, h http.Handler, bucket string) error {
	ln, err := net.Listen("tcp", ":443")
	if err != nil {
		return err
	}
	defer ln.Close()

	ctx, cancel := context.WithCancel(ctx)
	server := &http.Server{Handler: h}
	done := make(chan struct{})
	go func() {
		<-ctx.Done()
		server.Close()
		close(done)
	}()
	defer func() {
		cancel()
		<-done
	}()

	sc, err := storage.NewClient(ctx)
	if err != nil {
		return fmt.Errorf("storage.NewClient: %v", err)
	}
	const hostSuffix = ".golang.org"
	m := autocert.Manager{
		Prompt: autocert.AcceptTOS,
		HostPolicy: func(ctx context.Context, host string) error {
			if !strings.HasSuffix(host, hostSuffix) {
				return fmt.Errorf("refusing to serve autocert on provided domain (%q), must have the suffix %q",
					host, hostSuffix)
			}
			return nil
		},
		Cache: autocertcache.NewGoogleCloudStorageCache(sc, bucket),
	}
	config := &tls.Config{
		GetCertificate: m.GetCertificate,
		NextProtos:     []string{"h2", "http/1.1"},
	}
	tlsLn := tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, config)
	if err := http2.ConfigureServer(server, nil); err != nil {
		return fmt.Errorf("http2.ConfigureServer: %v", err)
	}
	return server.Serve(tlsLn)
}

type tcpKeepAliveListener struct {
	*net.TCPListener
}

func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
	tc, err := ln.AcceptTCP()
	if err != nil {
		return
	}
	tc.SetKeepAlive(true)
	tc.SetKeepAlivePeriod(3 * time.Minute)
	return tc, nil
}
<|endoftext|>


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

// Package runutil provides functions for running commands and
// functions and logging their outcome.
package runutil

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"os/signal"
	"strconv"
	"strings"
	"syscall"
	"time"

	"v.io/x/devtools/internal/envutil"
)

const (
	prefix = ">>"
)

var (
	CommandTimedOutErr = fmt.Errorf("command timed out")
)

type Run struct {
	indent int
	opts   Opts
}

type Opts struct {
	Color   bool
	DryRun  bool
	Env     map[string]string
	Stdin   io.Reader
	Stdout  io.Writer
	Stderr  io.Writer
	Verbose bool
}

// New is the Run factory.
func New(env map[string]string, stdin io.Reader, stdout, stderr io.Writer, color, dryRun, verbose bool) *Run {
	if color {
		term := os.Getenv("TERM")
		switch term {
		case "dumb", "":
			color = false
		}
	}
	return &Run{
		indent: 0,
		opts: Opts{
			Color:   color,
			DryRun:  dryRun,
			Env:     env,
			Stdin:   stdin,
			Stdout:  stdout,
			Stderr:  stderr,
			Verbose: verbose,
		},
	}
}

// Command runs the given command and logs its outcome using the
// default options.
func (r *Run) Command(path string, args ...string) error {
	return r.CommandWithOpts(r.opts, path, args...)
}

// CommandWithOpts runs the given command and logs its outcome using
// the given options.
func (r *Run) CommandWithOpts(opts Opts, path string, args ...string) error {
	return r.command(0, opts, path, args...)
}

// TimedCommand runs the given command with a timeout and logs its
// outcome using the default options.
func (r *Run) TimedCommand(timeout time.Duration, path string, args ...string) error {
	return r.TimedCommandWithOpts(timeout, r.opts, path, args...)
}

// TimedCommandWithOpts runs the given command with a timeout and logs
// its outcome using the given options.
func (r *Run) TimedCommandWithOpts(timeout time.Duration, opts Opts, path string, args ...string) error {
	return r.command(timeout, opts, path, args...)
}

func (r *Run) command(timeout time.Duration, opts Opts, path string, args ...string) error {
	r.increaseIndent()
	defer r.decreaseIndent()
	// Check if <path> identifies a binary in the PATH environment
	// variable of the opts.Env.
	binary, err := envutil.NewSnapshot(opts.Env).LookPath(path)
	if err == nil {
		// If so, make sure to execute this binary. This step
		// enables us to "shadow" binaries included in the
		// PATH environment variable of the host OS (which
		// would be otherwise used to lookup <path>).
		//
		// This mechanism is used instead of modifying the
		// PATH environment variable of the host OS as the
		// latter is not thread-safe.
		path = binary
	}
	command := exec.Command(path, args...)
	command.Stdin = opts.Stdin
	command.Stdout = opts.Stdout
	command.Stderr = opts.Stderr
	if len(opts.Env) != 0 {
		snapshot := envutil.NewSnapshotFromOS()
		for key, value := range opts.Env {
			snapshot.Set(key, value)
		}
		command.Env = snapshot.Slice()
	}
	if opts.Verbose || opts.DryRun {
		args := []string{}
		for _, arg := range command.Args {
			// Quote any arguments that contain '"', ''', '|', or ' '.
			if strings.IndexAny(arg, "\"' |") != -1 {
				args = append(args, strconv.Quote(arg))
			} else {
				args = append(args, arg)
			}
		}
		r.printf(r.opts.Stdout, strings.Join(args, " "))
	}
	if opts.DryRun {
		r.printf(r.opts.Stdout, "OK")
		return nil
	}
	if timeout == 0 {
		// No timeout.
		var err error
		if err = command.Run(); err != nil {
			if opts.Verbose {
				r.printf(r.opts.Stdout, "FAILED")
			}
		} else {
			if opts.Verbose {
				r.printf(r.opts.Stdout, "OK")
			}
		}
		return err
	}
	return r.timedCommand(timeout, opts, command)
}

// timedCommand executes the given command, terminating it forcefully
// if it is still running after the given timeout elapses.
func (r *Run) timedCommand(timeout time.Duration, opts Opts, command *exec.Cmd) error {
	// Make the process of this command a new process group leader
	// to facilitate clean up of processes that time out.
	command.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
	// Kill this process group explicitly when receiving SIGTERM
	// or SIGINT signals.
	sigchan := make(chan os.Signal, 1)
	signal.Notify(sigchan, syscall.SIGTERM, syscall.SIGINT)
	go func() {
		<-sigchan
		r.terminateProcessGroup(command)
	}()
	if err := command.Start(); err != nil {
		if opts.Verbose {
			r.printf(r.opts.Stdout, "FAILED")
		}
		return err
	}
	done := make(chan error, 1)
	go func() {
		done <- command.Wait()
	}()
	select {
	case <-time.After(timeout):
		// The command has timed out.
		r.terminateProcessGroup(command)
		// Allow goroutine to exit.
		<-done
		if opts.Verbose {
			r.printf(r.opts.Stdout, "TIMED OUT")
		}
		return CommandTimedOutErr
	case err := <-done:
		if err != nil {
			if opts.Verbose {
				r.printf(r.opts.Stdout, "FAILED")
			}
		} else {
			if opts.Verbose {
				r.printf(r.opts.Stdout, "OK")
			}
		}
		return err
	}
	return nil
}

// terminateProcessGroup sends SIGTERM followed by SIGKILL to the
// process group (the negative value of the process's pid).
func (r *Run) terminateProcessGroup(command *exec.Cmd) {
	pid := -command.Process.Pid
	if err := syscall.Kill(pid, syscall.SIGTERM); err != nil {
		fmt.Fprintf(r.opts.Stderr, "Kill(%v, %v) failed: %v\n", pid, syscall.SIGTERM, err)
	}
	fmt.Fprintf(r.opts.Stderr, "Waiting for command to exit: %q\n", command.Path)
	// Give the process some time to shut down cleanly.
	for i := 0; i < 10; i++ {
		select {
		case <-time.After(time.Second):
			if err := syscall.Kill(pid, 0); err != nil {
				return
			}
		}
	}
	// If it still exists, send SIGKILL to it.
	if err := syscall.Kill(pid, 0); err == nil {
		if err := syscall.Kill(-command.Process.Pid, syscall.SIGKILL); err != nil {
			fmt.Fprintf(r.opts.Stderr, "Kill(%v, %v) failed: %v\n", pid, syscall.SIGKILL, err)
		}
	}
}

// Function runs the given function and logs its outcome using the
// default verbosity.
func (r *Run) Function(fn func() error, format string, args ...interface{}) error {
	return r.FunctionWithOpts(r.opts, fn, format, args...)
}

// FunctionWithOpts runs the given function and logs its outcome using
// the given options.
func (r *Run) FunctionWithOpts(opts Opts, fn func() error, format string, args ...interface{}) error {
	r.increaseIndent()
	defer r.decreaseIndent()
	if opts.Verbose {
		r.printf(r.opts.Stdout, format, args...)
	}
	err := fn()
	if err != nil {
		if opts.Verbose {
			r.printf(r.opts.Stdout, "FAILED: %v", err)
		}
	} else {
		if opts.Verbose {
			r.printf(r.opts.Stdout, "OK")
		}
	}
	return err
}

// Output logs the given list of lines using the default verbosity.
func (r *Run) Output(output []string) {
	r.OutputWithOpts(r.opts, output)
}

// OutputWithOpts logs the given list of lines using the given
// options.
func (r *Run) OutputWithOpts(opts Opts, output []string) {
	if opts.Verbose {
		for _, line := range output {
			r.logLine(line)
		}
	}
}

// Opts returns the options of the run instance.
func (r *Run) Opts() Opts {
	return r.opts
}

func (r *Run) decreaseIndent() {
	r.indent--
}

func (r *Run) increaseIndent() {
	r.indent++
}

func (r *Run) logLine(line string) {
	if !strings.HasPrefix(line, prefix) {
		r.increaseIndent()
		defer r.decreaseIndent()
	}
	r.printf(r.opts.Stdout, "%v", line)
}

func (r *Run) printf(stdout io.Writer, format string, args ...interface{}) {
	timestamp := time.Now().Format("15:04:05.99")
	args = append([]interface{}{timestamp, strings.Repeat(prefix, r.indent)}, args...)
	fmt.Fprintf(stdout, "[%s] %v "+format+"\n", args...)
}
<commit_msg>internal/runutil: emit leading zeros in centiseconds field<commit_after>// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package runutil provides functions for running commands and
// functions and logging their outcome.
package runutil

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"os/signal"
	"strconv"
	"strings"
	"syscall"
	"time"

	"v.io/x/devtools/internal/envutil"
)

const (
	prefix = ">>"
)

var (
	CommandTimedOutErr = fmt.Errorf("command timed out")
)

type Run struct {
	indent int
	opts   Opts
}

type Opts struct {
	Color   bool
	DryRun  bool
	Env     map[string]string
	Stdin   io.Reader
	Stdout  io.Writer
	Stderr  io.Writer
	Verbose bool
}

// New is the Run factory.
func New(env map[string]string, stdin io.Reader, stdout, stderr io.Writer, color, dryRun, verbose bool) *Run {
	if color {
		term := os.Getenv("TERM")
		switch term {
		case "dumb", "":
			color = false
		}
	}
	return &Run{
		indent: 0,
		opts: Opts{
			Color:   color,
			DryRun:  dryRun,
			Env:     env,
			Stdin:   stdin,
			Stdout:  stdout,
			Stderr:  stderr,
			Verbose: verbose,
		},
	}
}

// Command runs the given command and logs its outcome using the
// default options.
func (r *Run) Command(path string, args ...string) error {
	return r.CommandWithOpts(r.opts, path, args...)
}

// CommandWithOpts runs the given command and logs its outcome using
// the given options.
func (r *Run) CommandWithOpts(opts Opts, path string, args ...string) error {
	return r.command(0, opts, path, args...)
}

// TimedCommand runs the given command with a timeout and logs its
// outcome using the default options.
func (r *Run) TimedCommand(timeout time.Duration, path string, args ...string) error {
	return r.TimedCommandWithOpts(timeout, r.opts, path, args...)
}

// TimedCommandWithOpts runs the given command with a timeout and logs
// its outcome using the given options.
func (r *Run) TimedCommandWithOpts(timeout time.Duration, opts Opts, path string, args ...string) error {
	return r.command(timeout, opts, path, args...)
}

func (r *Run) command(timeout time.Duration, opts Opts, path string, args ...string) error {
	r.increaseIndent()
	defer r.decreaseIndent()
	// Check if <path> identifies a binary in the PATH environment
	// variable of the opts.Env.
	binary, err := envutil.NewSnapshot(opts.Env).LookPath(path)
	if err == nil {
		// If so, make sure to execute this binary. This step
		// enables us to "shadow" binaries included in the
		// PATH environment variable of the host OS (which
		// would be otherwise used to lookup <path>).
		//
		// This mechanism is used instead of modifying the
		// PATH environment variable of the host OS as the
		// latter is not thread-safe.
		path = binary
	}
	command := exec.Command(path, args...)
	command.Stdin = opts.Stdin
	command.Stdout = opts.Stdout
	command.Stderr = opts.Stderr
	if len(opts.Env) != 0 {
		snapshot := envutil.NewSnapshotFromOS()
		for key, value := range opts.Env {
			snapshot.Set(key, value)
		}
		command.Env = snapshot.Slice()
	}
	if opts.Verbose || opts.DryRun {
		args := []string{}
		for _, arg := range command.Args {
			// Quote any arguments that contain '"', ''', '|', or ' '.
			if strings.IndexAny(arg, "\"' |") != -1 {
				args = append(args, strconv.Quote(arg))
			} else {
				args = append(args, arg)
			}
		}
		r.printf(r.opts.Stdout, strings.Join(args, " "))
	}
	if opts.DryRun {
		r.printf(r.opts.Stdout, "OK")
		return nil
	}
	if timeout == 0 {
		// No timeout.
		var err error
		if err = command.Run(); err != nil {
			if opts.Verbose {
				r.printf(r.opts.Stdout, "FAILED")
			}
		} else {
			if opts.Verbose {
				r.printf(r.opts.Stdout, "OK")
			}
		}
		return err
	}
	return r.timedCommand(timeout, opts, command)
}

// timedCommand executes the given command, terminating it forcefully
// if it is still running after the given timeout elapses.
func (r *Run) timedCommand(timeout time.Duration, opts Opts, command *exec.Cmd) error {
	// Make the process of this command a new process group leader
	// to facilitate clean up of processes that time out.
	command.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
	// Kill this process group explicitly when receiving SIGTERM
	// or SIGINT signals.
	sigchan := make(chan os.Signal, 1)
	signal.Notify(sigchan, syscall.SIGTERM, syscall.SIGINT)
	go func() {
		<-sigchan
		r.terminateProcessGroup(command)
	}()
	if err := command.Start(); err != nil {
		if opts.Verbose {
			r.printf(r.opts.Stdout, "FAILED")
		}
		return err
	}
	done := make(chan error, 1)
	go func() {
		done <- command.Wait()
	}()
	select {
	case <-time.After(timeout):
		// The command has timed out.
		r.terminateProcessGroup(command)
		// Allow goroutine to exit.
		<-done
		if opts.Verbose {
			r.printf(r.opts.Stdout, "TIMED OUT")
		}
		return CommandTimedOutErr
	case err := <-done:
		if err != nil {
			if opts.Verbose {
				r.printf(r.opts.Stdout, "FAILED")
			}
		} else {
			if opts.Verbose {
				r.printf(r.opts.Stdout, "OK")
			}
		}
		return err
	}
	return nil
}

// terminateProcessGroup sends SIGTERM followed by SIGKILL to the
// process group (the negative value of the process's pid).
func (r *Run) terminateProcessGroup(command *exec.Cmd) {
	pid := -command.Process.Pid
	if err := syscall.Kill(pid, syscall.SIGTERM); err != nil {
		fmt.Fprintf(r.opts.Stderr, "Kill(%v, %v) failed: %v\n", pid, syscall.SIGTERM, err)
	}
	fmt.Fprintf(r.opts.Stderr, "Waiting for command to exit: %q\n", command.Path)
	// Give the process some time to shut down cleanly.
	for i := 0; i < 10; i++ {
		select {
		case <-time.After(time.Second):
			if err := syscall.Kill(pid, 0); err != nil {
				return
			}
		}
	}
	// If it still exists, send SIGKILL to it.
	if err := syscall.Kill(pid, 0); err == nil {
		if err := syscall.Kill(-command.Process.Pid, syscall.SIGKILL); err != nil {
			fmt.Fprintf(r.opts.Stderr, "Kill(%v, %v) failed: %v\n", pid, syscall.SIGKILL, err)
		}
	}
}

// Function runs the given function and logs its outcome using the
// default verbosity.
func (r *Run) Function(fn func() error, format string, args ...interface{}) error {
	return r.FunctionWithOpts(r.opts, fn, format, args...)
}

// FunctionWithOpts runs the given function and logs its outcome using
// the given options.
func (r *Run) FunctionWithOpts(opts Opts, fn func() error, format string, args ...interface{}) error {
	r.increaseIndent()
	defer r.decreaseIndent()
	if opts.Verbose {
		r.printf(r.opts.Stdout, format, args...)
	}
	err := fn()
	if err != nil {
		if opts.Verbose {
			r.printf(r.opts.Stdout, "FAILED: %v", err)
		}
	} else {
		if opts.Verbose {
			r.printf(r.opts.Stdout, "OK")
		}
	}
	return err
}

// Output logs the given list of lines using the default verbosity.
func (r *Run) Output(output []string) {
	r.OutputWithOpts(r.opts, output)
}

// OutputWithOpts logs the given list of lines using the given
// options.
func (r *Run) OutputWithOpts(opts Opts, output []string) {
	if opts.Verbose {
		for _, line := range output {
			r.logLine(line)
		}
	}
}

// Opts returns the options of the run instance.
func (r *Run) Opts() Opts {
	return r.opts
}

func (r *Run) decreaseIndent() {
	r.indent--
}

func (r *Run) increaseIndent() {
	r.indent++
}

func (r *Run) logLine(line string) {
	if !strings.HasPrefix(line, prefix) {
		r.increaseIndent()
		defer r.decreaseIndent()
	}
	r.printf(r.opts.Stdout, "%v", line)
}

func (r *Run) printf(stdout io.Writer, format string, args ...interface{}) {
	timestamp := time.Now().Format("15:04:05.00")
	args = append([]interface{}{timestamp, strings.Repeat(prefix, r.indent)}, args...)
	fmt.Fprintf(stdout, "[%s] %v "+format+"\n", args...)
}
<|endoftext|>


<commit_before>package option

import (
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/s3"
)

// The PutObjectInput type is an adapter to change a parameter in
// s3.PutObjectInput.
type PutObjectInput func(req *s3.PutObjectInput)

// SSEKMSKeyID returns a PutObjectInput that changes a SSE-KMS Key ID.
func SSEKMSKeyID(keyID string) PutObjectInput {
	return func(req *s3.PutObjectInput) {
		req.SSEKMSKeyId = aws.String(keyID)
	}
}

// ACLPrivate returns a PutObjectInput that set ACL private.
func ACLPrivate() PutObjectInput {
	return func(req *s3.PutObjectInput) {
		req.ACL = aws.String(s3.ObjectCannedACLPrivate)
	}
}

// ACLPublicRead returns a PutObjectInput that set ACL public-read.
func ACLPublicRead() PutObjectInput {
	return func(req *s3.PutObjectInput) {
		req.ACL = aws.String(s3.ObjectCannedACLPublicRead)
	}
}

// ContentType returns a PutObjectInput that set Content-Type.
func ContentType(ct string) PutObjectInput {
	return func(req *s3.PutObjectInput) {
		req.ContentType = aws.String(ct)
	}
}

// ContentLength returns a PutObjectInput that set Content-Length.
func ContentLength(length int64) PutObjectInput {
	return func(req *s3.PutObjectInput) {
		req.ContentLength = aws.Int64(length)
	}
}
<commit_msg>bucket: Set ServerSideEncryption = "aws:kms"<commit_after>package option

import (
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/s3"
)

// The PutObjectInput type is an adapter to change a parameter in
// s3.PutObjectInput.
type PutObjectInput func(req *s3.PutObjectInput)

// SSEKMSKeyID returns a PutObjectInput that changes a SSE-KMS Key ID.
func SSEKMSKeyID(keyID string) PutObjectInput {
	return func(req *s3.PutObjectInput) {
		req.SSEKMSKeyId = aws.String(keyID)
		req.ServerSideEncryption = aws.String("aws:kms")
	}
}

// ACLPrivate returns a PutObjectInput that set ACL private.
func ACLPrivate() PutObjectInput {
	return func(req *s3.PutObjectInput) {
		req.ACL = aws.String(s3.ObjectCannedACLPrivate)
	}
}

// ACLPublicRead returns a PutObjectInput that set ACL public-read.
func ACLPublicRead() PutObjectInput {
	return func(req *s3.PutObjectInput) {
		req.ACL = aws.String(s3.ObjectCannedACLPublicRead)
	}
}

// ContentType returns a PutObjectInput that set Content-Type.
func ContentType(ct string) PutObjectInput {
	return func(req *s3.PutObjectInput) {
		req.ContentType = aws.String(ct)
	}
}

// ContentLength returns a PutObjectInput that set Content-Length.
func ContentLength(length int64) PutObjectInput {
	return func(req *s3.PutObjectInput) {
		req.ContentLength = aws.Int64(length)
	}
}
<|endoftext|>


<commit_before>// SPDX-License-Identifier: MIT

package token

import (
	"reflect"
	"sort"

	"golang.org/x/text/language"

	"github.com/caixw/apidoc/v7/internal/locale"
)

// Types 所有类型信息的集合
type Types struct {
	XMLName struct{} `xml:"types"`
	Types   []*Type  `xml:"type"`
}

// Type 用于生成文档中的类型信息
type Type struct {
	XMLName struct{} `xml:"type"`

	Name  string   `xml:"name,attr"`
	Usage InnerXML `xml:"usage"`
	Items []*Item  `xml:"item,omitempty"`
}

// InnerXML 可以用于在字符串嵌套 HTML
type InnerXML struct {
	Text string `xml:",innerxml"`
}

// Item 用于描述文档类型中的单条记录内容
type Item struct {
	Name     string `xml:"name,attr"` // 变量名
	Type     string `xml:"type,attr"` // 变量的类型
	Array    bool   `xml:"array,attr"`
	Required bool   `xml:"required,attr"`
	Usage    string `xml:",innerxml"`
}

// NewTypes 分析 v，返回 Type 类型的数据
func NewTypes(v interface{}, tag language.Tag) (*Types, error) {
	locale.SetTag(tag)

	n := newNode("", reflect.ValueOf(v))
	types := &Types{}
	if err := types.dumpToTypes(n); err != nil {
		return nil, err
	}

	types.sanitize()

	return types, nil
}

// 清除一些无用的数据
func (types *Types) sanitize() {
	for _, t := range types.Types {
		if len(t.Items) == 1 && t.Items[0].Name == "." {
			t.Items = nil
		}
	}

	sort.SliceStable(types.Types, func(i, j int) bool {
		if types.Types[i].Items == nil {
			return false
		}
		return types.Types[j].Items == nil
	})
}

func (types *Types) dumpToTypes(n *node) error {
	t := &Type{
		Name:  n.typeName,
		Usage: InnerXML{Text: locale.Sprintf(n.value.usage)},
		Items: make([]*Item, 0, len(n.attrs)+len(n.elems)),
	}
	types.Types = append(types.Types, t) // 保证子元素在后显示

	for _, attr := range n.attrs {
		t.appendItem("@"+attr.name, attr.Value, attr.usage, !attr.omitempty)

		if nn := newNode(attr.name, attr.Value); nn.typeName != "" && !types.typeExists(nn.typeName) {
			if err := types.dumpToTypes(nn); err != nil {
				return err
			}
		}
	}

	for _, elem := range n.elems {
		t.appendItem(elem.name, elem.Value, elem.usage, !elem.omitempty)

		typ := getRealType(elem.Type())
		v := getRealValue(elem.Value)

		if typ.Kind() == reflect.Slice || typ.Kind() == reflect.Array {
			typ = getRealType(typ.Elem())
			v = reflect.New(typ).Elem()
		}

		if nn := newNode(elem.name, v); nn.typeName != "" && !types.typeExists(nn.typeName) {
			if err := types.dumpToTypes(nn); err != nil {
				return err
			}
		}
	}

	if n.cdata != nil {
		t.appendItem(".", n.cdata.Value, n.cdata.usage, !n.cdata.omitempty)
	}

	if n.content != nil {
		t.appendItem(".", n.content.Value, n.content.usage, !n.content.omitempty)
	}

	return nil
}

func (t *Type) appendItem(name string, v reflect.Value, usageKey string, req bool) {
	var isSlice bool
	typ := getRealValue(v).Type()
	for typ.Kind() == reflect.Slice || typ.Kind() == reflect.Array {
		isSlice = true
		typ = typ.Elem()
	}

	tt := typ.Name()
	if vv := parseValue(reflect.New(typ).Elem()); vv != nil {
		tt = vv.name
	}
	t.Items = append(t.Items, &Item{
		Name:     name,
		Type:     tt,
		Required: req,
		Array:    isSlice,
		Usage:    locale.Sprintf(usageKey),
	})
}

func (types *Types) typeExists(typeName string) bool {
	for _, t := range types.Types {
		if t.Name == typeName {
			return true
		}
	}
	return false
}
<commit_msg>fix(internal/token): 修正排序错误<commit_after>// SPDX-License-Identifier: MIT

package token

import (
	"reflect"
	"sort"

	"golang.org/x/text/language"

	"github.com/caixw/apidoc/v7/internal/locale"
)

// Types 所有类型信息的集合
type Types struct {
	XMLName struct{} `xml:"types"`
	Types   []*Type  `xml:"type"`
}

// Type 用于生成文档中的类型信息
type Type struct {
	XMLName struct{} `xml:"type"`

	Name  string   `xml:"name,attr"`
	Usage InnerXML `xml:"usage"`
	Items []*Item  `xml:"item,omitempty"`
}

// InnerXML 可以用于在字符串嵌套 HTML
type InnerXML struct {
	Text string `xml:",innerxml"`
}

// Item 用于描述文档类型中的单条记录内容
type Item struct {
	Name     string `xml:"name,attr"` // 变量名
	Type     string `xml:"type,attr"` // 变量的类型
	Array    bool   `xml:"array,attr"`
	Required bool   `xml:"required,attr"`
	Usage    string `xml:",innerxml"`
}

// NewTypes 分析 v，返回 Type 类型的数据
func NewTypes(v interface{}, tag language.Tag) (*Types, error) {
	locale.SetTag(tag)

	n := newNode("", reflect.ValueOf(v))
	types := &Types{}
	if err := types.dumpToTypes(n); err != nil {
		return nil, err
	}

	types.sanitize()

	return types, nil
}

// 清除一些无用的数据
func (types *Types) sanitize() {
	for _, t := range types.Types {
		if len(t.Items) == 1 && t.Items[0].Name == "." {
			t.Items = nil
		}
	}

	sort.SliceStable(types.Types, func(i, j int) bool {
		if len(types.Types[i].Items) == 0 {
			return false
		}
		return len(types.Types[j].Items) == 0
	})
}

func (types *Types) dumpToTypes(n *node) error {
	t := &Type{
		Name:  n.typeName,
		Usage: InnerXML{Text: locale.Sprintf(n.value.usage)},
		Items: make([]*Item, 0, len(n.attrs)+len(n.elems)),
	}
	types.Types = append(types.Types, t) // 保证子元素在后显示

	for _, attr := range n.attrs {
		t.appendItem("@"+attr.name, attr.Value, attr.usage, !attr.omitempty)

		if nn := newNode(attr.name, attr.Value); nn.typeName != "" && !types.typeExists(nn.typeName) {
			if err := types.dumpToTypes(nn); err != nil {
				return err
			}
		}
	}

	for _, elem := range n.elems {
		t.appendItem(elem.name, elem.Value, elem.usage, !elem.omitempty)

		typ := getRealType(elem.Type())
		v := getRealValue(elem.Value)

		if typ.Kind() == reflect.Slice || typ.Kind() == reflect.Array {
			typ = getRealType(typ.Elem())
			v = reflect.New(typ).Elem()
		}

		if nn := newNode(elem.name, v); nn.typeName != "" && !types.typeExists(nn.typeName) {
			if err := types.dumpToTypes(nn); err != nil {
				return err
			}
		}
	}

	if n.cdata != nil {
		t.appendItem(".", n.cdata.Value, n.cdata.usage, !n.cdata.omitempty)
	}

	if n.content != nil {
		t.appendItem(".", n.content.Value, n.content.usage, !n.content.omitempty)
	}

	return nil
}

func (t *Type) appendItem(name string, v reflect.Value, usageKey string, req bool) {
	var isSlice bool
	typ := getRealValue(v).Type()
	for typ.Kind() == reflect.Slice || typ.Kind() == reflect.Array {
		isSlice = true
		typ = typ.Elem()
	}

	tt := typ.Name()
	if vv := parseValue(reflect.New(typ).Elem()); vv != nil {
		tt = vv.name
	}
	t.Items = append(t.Items, &Item{
		Name:     name,
		Type:     tt,
		Required: req,
		Array:    isSlice,
		Usage:    locale.Sprintf(usageKey),
	})
}

func (types *Types) typeExists(typeName string) bool {
	for _, t := range types.Types {
		if t.Name == typeName {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>//
// Copyright 2015 Rakuten Marketing LLC
//
// 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 syslog_test

import (
	"fmt"
	"log/syslog"
	"net"
	"strings"
	"testing"

	"github.com/mediaFORGE/gol"
	"github.com/mediaFORGE/gol/fields/severity"
	"github.com/mediaFORGE/gol/formatters"
	golmock "github.com/mediaFORGE/gol/internal/mock"
	golsys "github.com/mediaFORGE/gol/loggers/syslog"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	"github.com/stretchr/testify/suite"
)

// MockUDPServer
type MockUDPServer struct {
	mock.Mock
	t *testing.T
}

func (m *MockUDPServer) ReadMessage(conn *net.UDPConn, buf []byte) {
	n, addr, err := conn.ReadFromUDP(buf)
	assert.Nil(m.t, err)
	assert.NotNil(m.t, "abc", addr)
	assert.True(m.t, n > 0)

	msg := string(buf[0:n])
	m.receivedMessage(msg)

	if err != nil {
		fmt.Println("Error: ", err)
	}
}

func (m *MockUDPServer) receivedMessage(msg string) {
	m.Mock.Called(msg)
}

func (m *MockUDPServer) Run(ch chan bool, readch chan bool) {
	if addr, err := net.ResolveUDPAddr("udp", ":10001"); err != nil {
		assert.Fail(m.t, "could not resolve UDP server address")
	} else {
		/* Now listen at selected port */
		if conn, err := net.ListenUDP("udp", addr); err != nil {
			assert.Fail(m.t, "could not listen to UDP server port")
		} else {
			defer conn.Close()

			buf := make([]byte, 1024)

			ch <- true

			for {
				if !<-readch {
					break // end loop execution and end go routine
				}
				m.ReadMessage(conn, buf)
			}
		}
	}
}

// SyslogTestSuite test suite for the github.com/mediaFORGE/gol/loggers/syslog package.
type SyslogTestSuite struct {
	suite.Suite
	logger gol.Logger
}

func (s *SyslogTestSuite) SetupTest() {
	s.logger = golsys.New("udp", ":10001", syslog.LOG_INFO, "test", &formatters.Text{})
}

func (s *SyslogTestSuite) TestSend() {
	udpserver := MockUDPServer{t: s.T()}
	udpserver.Mock.On("receivedMessage", mock.Anything).Return(nil)

	syncch := make(chan bool, 1)
	readch := make(chan bool, 1)
	go udpserver.Run(syncch, readch)
	<-syncch

	var lvl severity.Type
	for lvl = severity.Emergency; lvl <= severity.Debug; lvl++ {
		s.logger.Send(gol.NewMessage(lvl, "message", "unknown"))
		readch <- true // read message sent
	}

	// invalid severity will be sent with the default logger severity level
	s.logger.Send(gol.NewMessage(severity.Debug+1, "invalid", "severity"))
	readch <- true  // read message sent
	readch <- false // end concurrent go routine

	udpserver.AssertExpectations(s.T())

	found := 0
	for lvl = severity.Emergency; lvl <= severity.Debug; lvl++ {
		for _, call := range udpserver.Calls {
			if call.Method == "receivedMessage" {
				for _, arg := range call.Arguments {
					if strings.Contains(arg.(string), lvl.String()) {
						found++
					}
				}
			}
		}
	}
	assert.Equal(s.T(), 8, found)
}

func (s *SyslogTestSuite) TestSendMessageWithoutSeverity() {
	assert.Error(s.T(), s.logger.Send(&gol.LogMessage{"message": "unknown"}))
}

func (s *SyslogTestSuite) TestSendNil() {
	assert.Nil(s.T(), s.logger.Send(nil))
}

func (s *SyslogTestSuite) TestSendNilFormatter() {
	// reset logger built by SetupTest
	s.logger = golsys.New("udp", ":10001", syslog.LOG_INFO, "test", nil)
	assert.Error(s.T(), s.logger.Send(gol.NewEmergency("message", "unknown")))
}

func (s *SyslogTestSuite) TestSendFormatterError() {
	msg := gol.NewEmergency("message", "unknown")
	m := &golmock.LogFormatter{}
	m.On("Format", msg).Return("", fmt.Errorf("internal error"))

	s.logger.SetFormatter(m)
	assert.Error(s.T(), s.logger.Send(msg))

	m.AssertExpectations(s.T())
}
<commit_msg>fixed racing condition<commit_after>//
// Copyright 2015 Rakuten Marketing LLC
//
// 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 syslog_test

import (
	"fmt"
	"log/syslog"
	"net"
	"strings"
	"testing"

	"github.com/mediaFORGE/gol"
	"github.com/mediaFORGE/gol/fields/severity"
	"github.com/mediaFORGE/gol/formatters"
	golmock "github.com/mediaFORGE/gol/internal/mock"
	golsys "github.com/mediaFORGE/gol/loggers/syslog"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	"github.com/stretchr/testify/suite"
)

// MockUDPServer
type MockUDPServer struct {
	mock.Mock
	t *testing.T
}

func (m *MockUDPServer) ReadMessage(conn *net.UDPConn, buf []byte) string {
	n, addr, err := conn.ReadFromUDP(buf)
	assert.Nil(m.t, err)
	assert.NotNil(m.t, "abc", addr)
	assert.True(m.t, n > 0)

	return string(buf[0:n])
}

func (m *MockUDPServer) Run(ch chan bool, readch chan bool, msgchan chan string) {
	if addr, err := net.ResolveUDPAddr("udp", ":10001"); err != nil {
		assert.Fail(m.t, "could not resolve UDP server address")
	} else {
		/* Now listen at selected port */
		if conn, err := net.ListenUDP("udp", addr); err != nil {
			assert.Fail(m.t, "could not listen to UDP server port")
		} else {
			defer conn.Close()

			buf := make([]byte, 1024)

			ch <- true

			for {
				if !<-readch {
					break // end loop execution and end go routine
				}
				msgchan <- m.ReadMessage(conn, buf)
			}
		}
	}
}

// SyslogTestSuite test suite for the github.com/mediaFORGE/gol/loggers/syslog package.
type SyslogTestSuite struct {
	suite.Suite
	logger gol.Logger
}

func (s *SyslogTestSuite) SetupTest() {
	s.logger = golsys.New("udp", ":10001", syslog.LOG_INFO, "test", &formatters.Text{})
}

func (s *SyslogTestSuite) TestSend() {
	udpserver := MockUDPServer{t: s.T()}
	udpserver.Mock.On("receivedMessage", mock.Anything).Return(nil)

	syncch := make(chan bool, 1)
	readch := make(chan bool, 1)
	msgch := make(chan string, 1)
	go udpserver.Run(syncch, readch, msgch)
	<-syncch

	var lvl severity.Type
	for lvl = severity.Emergency; lvl <= severity.Debug; lvl++ {
		s.logger.Send(gol.NewMessage(lvl, "message", "unknown"))
		readch <- true // read message sent
		assert.True(s.T(), strings.Contains(<-msgch, lvl.String()))
	}

	// invalid severity will be sent with the default logger severity level
	s.logger.Send(gol.NewMessage(severity.Debug+1, "invalid", "severity"))
	readch <- true // read message sent
	assert.True(s.T(), strings.Contains(<-msgch, "severity=UNKNOWN"))
	readch <- false // end concurrent go routine
}

func (s *SyslogTestSuite) TestSendMessageWithoutSeverity() {
	assert.Error(s.T(), s.logger.Send(&gol.LogMessage{"message": "unknown"}))
}

func (s *SyslogTestSuite) TestSendNil() {
	assert.Nil(s.T(), s.logger.Send(nil))
}

func (s *SyslogTestSuite) TestSendNilFormatter() {
	// reset logger built by SetupTest
	s.logger = golsys.New("udp", ":10001", syslog.LOG_INFO, "test", nil)
	assert.Error(s.T(), s.logger.Send(gol.NewEmergency("message", "unknown")))
}

func (s *SyslogTestSuite) TestSendFormatterError() {
	msg := gol.NewEmergency("message", "unknown")
	m := &golmock.LogFormatter{}
	m.On("Format", msg).Return("", fmt.Errorf("internal error"))

	s.logger.SetFormatter(m)
	assert.Error(s.T(), s.logger.Send(msg))

	m.AssertExpectations(s.T())
}
<|endoftext|>


<commit_before>package backup

import (
	"os"
	"strings"
	"time"

	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
)

// Instance represents the backup relevant subset of a LXD instance.
// This is used rather than instance.Instance to avoid import loops.
type Instance interface {
	Name() string
	Project() string
}

// InstanceBackup represents an instance backup.
type InstanceBackup struct {
	CommonBackup

	instance     Instance
	instanceOnly bool
}

// NewInstanceBackup instantiates a new InstanceBackup struct.
func NewInstanceBackup(state *state.State, inst Instance, ID int, name string, creationDate time.Time, expiryDate time.Time, instanceOnly bool, optimizedStorage bool) *InstanceBackup {
	return &InstanceBackup{
		CommonBackup: CommonBackup{
			state:            state,
			id:               ID,
			name:             name,
			creationDate:     creationDate,
			expiryDate:       expiryDate,
			optimizedStorage: optimizedStorage,
		},
		instance:     inst,
		instanceOnly: instanceOnly,
	}
}

// InstanceOnly returns whether only the instance itself is to be backed up.
func (b *InstanceBackup) InstanceOnly() bool {
	return b.instanceOnly
}

// Rename renames an instance backup.
func (b *InstanceBackup) Rename(newName string) error {
	oldBackupPath := shared.VarPath("backups", "instances", project.Instance(b.instance.Project(), b.name))
	newBackupPath := shared.VarPath("backups", "instances", project.Instance(b.instance.Project(), newName))

	// Create the new backup path.
	backupsPath := shared.VarPath("backups", "instances", project.Instance(b.instance.Project(), b.instance.Name()))
	if !shared.PathExists(backupsPath) {
		err := os.MkdirAll(backupsPath, 0700)
		if err != nil {
			return err
		}
	}

	// Rename the backup directory.
	err := os.Rename(oldBackupPath, newBackupPath)
	if err != nil {
		return err
	}

	// Check if we can remove the instance directory.
	empty, _ := shared.PathIsEmpty(backupsPath)
	if empty {
		err := os.Remove(backupsPath)
		if err != nil {
			return err
		}
	}

	// Rename the database record.
	err = b.state.Cluster.RenameInstanceBackup(b.name, newName)
	if err != nil {
		return err
	}

	return nil
}

// Delete removes an instance backup.
func (b *InstanceBackup) Delete() error {
	backupPath := shared.VarPath("backups", "instances", project.Instance(b.instance.Project(), b.name))

	// Delete the on-disk data.
	if shared.PathExists(backupPath) {
		err := os.RemoveAll(backupPath)
		if err != nil {
			return err
		}
	}

	// Check if we can remove the instance directory.
	backupsPath := shared.VarPath("backups", "instances", project.Instance(b.instance.Project(), b.instance.Name()))
	empty, _ := shared.PathIsEmpty(backupsPath)
	if empty {
		err := os.Remove(backupsPath)
		if err != nil {
			return err
		}
	}

	// Remove the database record.
	err := b.state.Cluster.DeleteInstanceBackup(b.name)
	if err != nil {
		return err
	}

	return nil
}

// Render returns an InstanceBackup struct of the backup.
func (b *InstanceBackup) Render() *api.InstanceBackup {
	return &api.InstanceBackup{
		Name:             strings.SplitN(b.name, "/", 2)[1],
		CreatedAt:        b.creationDate,
		ExpiresAt:        b.expiryDate,
		InstanceOnly:     b.instanceOnly,
		ContainerOnly:    b.instanceOnly,
		OptimizedStorage: b.optimizedStorage,
	}
}
<commit_msg>lxd/backup/backup/instance: Fix old parent directory removal in InstanceBackup.Rename()<commit_after>package backup

import (
	"os"
	"strings"
	"time"

	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
)

// Instance represents the backup relevant subset of a LXD instance.
// This is used rather than instance.Instance to avoid import loops.
type Instance interface {
	Name() string
	Project() string
}

// InstanceBackup represents an instance backup.
type InstanceBackup struct {
	CommonBackup

	instance     Instance
	instanceOnly bool
}

// NewInstanceBackup instantiates a new InstanceBackup struct.
func NewInstanceBackup(state *state.State, inst Instance, ID int, name string, creationDate time.Time, expiryDate time.Time, instanceOnly bool, optimizedStorage bool) *InstanceBackup {
	return &InstanceBackup{
		CommonBackup: CommonBackup{
			state:            state,
			id:               ID,
			name:             name,
			creationDate:     creationDate,
			expiryDate:       expiryDate,
			optimizedStorage: optimizedStorage,
		},
		instance:     inst,
		instanceOnly: instanceOnly,
	}
}

// InstanceOnly returns whether only the instance itself is to be backed up.
func (b *InstanceBackup) InstanceOnly() bool {
	return b.instanceOnly
}

// Rename renames an instance backup.
func (b *InstanceBackup) Rename(newName string) error {
	oldBackupPath := shared.VarPath("backups", "instances", project.Instance(b.instance.Project(), b.name))
	newBackupPath := shared.VarPath("backups", "instances", project.Instance(b.instance.Project(), newName))

	// Extract the old and new parent backup paths from the old and new backup names rather than use
	// instance.Name() as this may be in flux if the instance itself is being renamed, whereas the relevant
	// instance name is encoded into the backup names.
	oldParentName, _, _ := shared.InstanceGetParentAndSnapshotName(b.name)
	oldParentBackupsPath := shared.VarPath("backups", "instances", project.Instance(b.instance.Project(), oldParentName))
	newParentName, _, _ := shared.InstanceGetParentAndSnapshotName(newName)
	newParentBackupsPath := shared.VarPath("backups", "instances", project.Instance(b.instance.Project(), newParentName))

	// Create the new backup path if doesn't exist.
	if !shared.PathExists(newParentBackupsPath) {
		err := os.MkdirAll(newParentBackupsPath, 0700)
		if err != nil {
			return err
		}
	}

	// Rename the backup directory.
	err := os.Rename(oldBackupPath, newBackupPath)
	if err != nil {
		return err
	}

	// Check if we can remove the old parent directory.
	empty, _ := shared.PathIsEmpty(oldParentBackupsPath)
	if empty {
		err := os.Remove(oldParentBackupsPath)
		if err != nil {
			return err
		}
	}

	// Rename the database record.
	err = b.state.Cluster.RenameInstanceBackup(b.name, newName)
	if err != nil {
		return err
	}

	return nil
}

// Delete removes an instance backup.
func (b *InstanceBackup) Delete() error {
	backupPath := shared.VarPath("backups", "instances", project.Instance(b.instance.Project(), b.name))

	// Delete the on-disk data.
	if shared.PathExists(backupPath) {
		err := os.RemoveAll(backupPath)
		if err != nil {
			return err
		}
	}

	// Check if we can remove the instance directory.
	backupsPath := shared.VarPath("backups", "instances", project.Instance(b.instance.Project(), b.instance.Name()))
	empty, _ := shared.PathIsEmpty(backupsPath)
	if empty {
		err := os.Remove(backupsPath)
		if err != nil {
			return err
		}
	}

	// Remove the database record.
	err := b.state.Cluster.DeleteInstanceBackup(b.name)
	if err != nil {
		return err
	}

	return nil
}

// Render returns an InstanceBackup struct of the backup.
func (b *InstanceBackup) Render() *api.InstanceBackup {
	return &api.InstanceBackup{
		Name:             strings.SplitN(b.name, "/", 2)[1],
		CreatedAt:        b.creationDate,
		ExpiresAt:        b.expiryDate,
		InstanceOnly:     b.instanceOnly,
		ContainerOnly:    b.instanceOnly,
		OptimizedStorage: b.optimizedStorage,
	}
}
<|endoftext|>


<commit_before>package intersect

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

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

func intersect(a, b []uint64) []uint64 {
	m := make(map[uint64]struct{})
	for _, i := range a {
		m[i] = struct{}{}
	}
	out := make([]uint64, 0, 100)
	for _, j := range b {
		if _, ok := m[j]; ok {
			out = append(out, j)
		}
	}
	return out
}

func createArray(sz int, limit int64) []uint64 {
	a := make([]uint64, sz)
	ma := make(map[uint64]struct{})
	for i := 0; i < sz; i++ {
		for {
			ei := uint64(rand.Int63n(limit))
			if _, ok := ma[ei]; !ok {
				a[i] = ei
				ma[ei] = struct{}{}
				break
			}
		}
	}
	sort.Slice(a, func(i, j int) bool {
		return a[i] < a[j]
	})
	return a
}

func TestSize(t *testing.T) {
	rand.Seed(time.Now().UnixNano())
	sz := 1
	for i := 0; i < 5; i++ {
		sz *= 10
		a := createArray(sz, math.MaxInt32)
		dl := encodeDelta(a, 32)
		dd, err := dl.Marshal()
		require.Nil(t, err)

		fl := encodeFixed(a)
		fd, err := fl.Marshal()
		require.Nil(t, err)
		fmt.Printf("Size=%d Size of delta: %v fixed: %v\n", sz, len(dd), len(fd))

		mi := func(d []uint64) uint64 {
			var dm uint64
			for _, e := range d {
				if dm < e {
					dm = e
				}
			}
			return dm
		}
		dm := mi(dl.Uids)
		fm := mi(fl.Uids)
		fmt.Printf("Max delta: %v. Max fixed: %v. Bits delta: %v. Bits fixed: %v\n",
			dm, fm, math.Log2(float64(dm)), math.Log2(float64(fm)))
		fmt.Println()
	}
}

func TestTwoLevelIntersect(t *testing.T) {
	rand.Seed(time.Now().UnixNano())
	a := createArray(100, 1000)
	da := encodeDelta(a, 3)
	b := createArray(200, 1000)
	db := encodeDelta(b, 3)
	fmt.Println("a=", da.Buckets, da.Uids)
	fmt.Println("b=", db.Buckets, db.Uids)

	fmt.Printf("a=%v\nb=%v\n", a, b)
	final := make([]uint64, 0, 100)
	mergeDeltaIntersect(da, db, &final)

	exp := make([]uint64, 0, 100)
	mergeIntersect(a, b, &exp)
	fmt.Printf("exp=%v\n", exp)
	require.Equal(t, exp, final)

	final = final[:0]
	BinDelta(da, db, &final)
	require.Equal(t, exp, final)
}

func TestIntersect(t *testing.T) {
	rand.Seed(time.Now().UnixNano())
	a := createArray(100, 100)
	b := createArray(100, 100)

	res1 := make([]uint64, 0, 100)
	mergeIntersect(a, b, &res1)

	res2 := make([]uint64, 0, 100)
	binIntersect(a, b, &res2)

	//res3 := make([]uint64, 0, 100)
	//binIterative(a, b, &res3)

	exp := intersect(a, b)

	require.Equal(t, exp, res1, "merge not working")
	require.Equal(t, exp, res2, "binIntersect not working")
	//require.Equal(t, exp, res3, "binIterative not working")
}

func BenchmarkMarshal(b *testing.B) {
	rand.Seed(time.Now().UnixNano())
	rs := []int{1, 10, 50, 100, 500, 1000, 10000, 100000, 1000000}
	for _, r := range rs {
		u := createArray(r, math.MaxInt32)

		var dsize, fsize int
		b.Run(fmt.Sprintf("delta-%d", r),
			func(b *testing.B) {
				var total time.Duration
				for i := 0; i < b.N; i++ {
					d := encodeDelta(u, 32)
					data, err := d.Marshal()
					if err != nil {
						b.Fatalf("Error: %v", err)
					}
					// Assuming 128 MB per sec. => 128 bytes per micro.
					dur := time.Duration(len(data) / 128)
					total += dur
					time.Sleep(dur * time.Microsecond)
					if i == 0 {
						dsize = len(data)
					}

					var out DeltaList
					if err := out.Unmarshal(data); err != nil {
						b.Fatalf("Error: %v", err)
					}
				}
				// fmt.Printf("Total sleep: %v\n", total)
			})

		b.Run(fmt.Sprintf("fixed-%d", r),
			func(b *testing.B) {
				var total time.Duration
				for i := 0; i < b.N; i++ {
					d := encodeFixed(u)
					data, err := d.Marshal()
					if err != nil {
						b.Fatalf("Error: %v", err)
					}
					dur := time.Duration(len(data) / 128)
					total += dur
					// Assuming 128 MB per sec. => 128 bytes per micro.
					time.Sleep(dur * time.Microsecond)

					if i == 0 {
						fsize = len(data)
					}
					var out FixedList
					if err := out.Unmarshal(data); err != nil {
						b.Fatalf("Error: %v", err)
					}
				}
				// fmt.Printf("Total fixed sleep: %v\n", total)
			})
		fmt.Printf("SIZE Delta: %v Fixed: %v\n", dsize, fsize)
		fmt.Println()
	}
}

func BenchmarkListIntersect(b *testing.B) {
	randomTests := func(sz int, overlap float64) {
		rs := []int{1, 10, 50, 100, 500, 1000, 10000, 100000, 1000000}

		for _, r := range rs {
			sz2 := sz * r
			if sz2 > 1000000 {
				break
			}
			limit := int64(float64(sz2) / overlap)

			u1 := createArray(sz, limit)
			d1 := encodeDelta(u1, 32)
			u2 := createArray(sz2, limit)
			d2 := encodeDelta(u2, 32)
			result := make([]uint64, 0, sz)

			b.Run(fmt.Sprintf(":Bin:size=%d:overlap=%.2f:ratio=%d:", sz, overlap, r),
				func(b *testing.B) {
					for k := 0; k < b.N; k++ {
						BinIntersect(u2, u1, &result)
						result = result[:0]
					}
				})
			b.Run(fmt.Sprintf(":Mer:size=%d:overlap=%.2f:ratio=%d", sz, overlap, r),
				func(b *testing.B) {
					for k := 0; k < b.N; k++ {
						mergeIntersect(u1, u2, &result)
						result = result[:0]
					}
				})

			b.Run(fmt.Sprintf(":Two:size=%d:overlap=%.2f:ratio=%d", sz, overlap, r),
				func(b *testing.B) {
					var f func(a, b *DeltaList, final *[]uint64)
					if r < 500 {
						f = mergeDeltaIntersect
					} else {
						f = BinDelta
					}
					for k := 0; k < b.N; k++ {
						f(d1, d2, &result)
						result = result[:0]
					}
				})
			fmt.Println()
		}
	}
	//randomTests(10, 0.01)
	//randomTests(100, 0.01)
	//randomTests(1000, 0.01)
	//randomTests(10000, 0.01)

	// Overlap has no effect on Bin numbers.
	overlaps := []float64{0.00001, 0.8}
	for _, overlap := range overlaps {
		randomTests(10, overlap)
		randomTests(100, overlap)
		randomTests(1000, overlap)
		randomTests(10000, overlap)
		fmt.Println()
	}

	//randomTests(10, 0.4)
	//randomTests(100, 0.4)
	//randomTests(1000, 0.4)
	//randomTests(10000, 0.4)
	//fmt.Println()

	//randomTests(10, 0.8)
	//randomTests(100, 0.8)
	//randomTests(1000, 0.8)
	//randomTests(10000, 0.8)
	//fmt.Println()
}
<commit_msg>Add benchmark for existing methods.<commit_after>package intersect

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

	"github.com/dgraph-io/dgraph/task"
	"github.com/stretchr/testify/require"
)

func intersect(a, b []uint64) []uint64 {
	m := make(map[uint64]struct{})
	for _, i := range a {
		m[i] = struct{}{}
	}
	out := make([]uint64, 0, 100)
	for _, j := range b {
		if _, ok := m[j]; ok {
			out = append(out, j)
		}
	}
	return out
}

func createArray(sz int, limit int64) []uint64 {
	a := make([]uint64, sz)
	ma := make(map[uint64]struct{})
	for i := 0; i < sz; i++ {
		for {
			ei := uint64(rand.Int63n(limit))
			if _, ok := ma[ei]; !ok {
				a[i] = ei
				ma[ei] = struct{}{}
				break
			}
		}
	}
	sort.Slice(a, func(i, j int) bool {
		return a[i] < a[j]
	})
	return a
}

func TestSize(t *testing.T) {
	rand.Seed(time.Now().UnixNano())
	sz := 1
	for i := 0; i < 5; i++ {
		sz *= 10
		a := createArray(sz, math.MaxInt32)
		dl := encodeDelta(a, 32)
		dd, err := dl.Marshal()
		require.Nil(t, err)

		fl := encodeFixed(a)
		fd, err := fl.Marshal()
		require.Nil(t, err)
		fmt.Printf("Size=%d Size of delta: %v fixed: %v\n", sz, len(dd), len(fd))

		mi := func(d []uint64) uint64 {
			var dm uint64
			for _, e := range d {
				if dm < e {
					dm = e
				}
			}
			return dm
		}
		dm := mi(dl.Uids)
		fm := mi(fl.Uids)
		fmt.Printf("Max delta: %v. Max fixed: %v. Bits delta: %v. Bits fixed: %v\n",
			dm, fm, math.Log2(float64(dm)), math.Log2(float64(fm)))
		fmt.Println()
	}
}

func TestTwoLevelIntersect(t *testing.T) {
	rand.Seed(time.Now().UnixNano())
	a := createArray(100, 1000)
	da := encodeDelta(a, 3)
	b := createArray(200, 1000)
	db := encodeDelta(b, 3)
	fmt.Println("a=", da.Buckets, da.Uids)
	fmt.Println("b=", db.Buckets, db.Uids)

	fmt.Printf("a=%v\nb=%v\n", a, b)
	final := make([]uint64, 0, 100)
	mergeDeltaIntersect(da, db, &final)

	exp := make([]uint64, 0, 100)
	mergeIntersect(a, b, &exp)
	fmt.Printf("exp=%v\n", exp)
	require.Equal(t, exp, final)

	final = final[:0]
	BinDelta(da, db, &final)
	require.Equal(t, exp, final)
}

func TestIntersect(t *testing.T) {
	rand.Seed(time.Now().UnixNano())
	a := createArray(100, 100)
	b := createArray(100, 100)

	res1 := make([]uint64, 0, 100)
	mergeIntersect(a, b, &res1)

	res2 := make([]uint64, 0, 100)
	binIntersect(a, b, &res2)

	//res3 := make([]uint64, 0, 100)
	//binIterative(a, b, &res3)

	exp := intersect(a, b)

	require.Equal(t, exp, res1, "merge not working")
	require.Equal(t, exp, res2, "binIntersect not working")
	//require.Equal(t, exp, res3, "binIterative not working")
}

func BenchmarkMarshal(b *testing.B) {
	rand.Seed(time.Now().UnixNano())
	rs := []int{1, 10, 50, 100, 500, 1000, 10000, 100000, 1000000}
	for _, r := range rs {
		u := createArray(r, math.MaxInt32)

		var dsize, fsize int
		b.Run(fmt.Sprintf("delta-%d", r),
			func(b *testing.B) {
				var total time.Duration
				for i := 0; i < b.N; i++ {
					d := encodeDelta(u, 32)
					data, err := d.Marshal()
					if err != nil {
						b.Fatalf("Error: %v", err)
					}
					// Assuming 128 MB per sec. => 128 bytes per micro.
					dur := time.Duration(len(data) / 128)
					total += dur
					time.Sleep(dur * time.Microsecond)
					if i == 0 {
						dsize = len(data)
					}

					var out DeltaList
					if err := out.Unmarshal(data); err != nil {
						b.Fatalf("Error: %v", err)
					}
				}
				// fmt.Printf("Total sleep: %v\n", total)
			})

		b.Run(fmt.Sprintf("fixed-%d", r),
			func(b *testing.B) {
				var total time.Duration
				for i := 0; i < b.N; i++ {
					d := encodeFixed(u)
					data, err := d.Marshal()
					if err != nil {
						b.Fatalf("Error: %v", err)
					}
					dur := time.Duration(len(data) / 128)
					total += dur
					// Assuming 128 MB per sec. => 128 bytes per micro.
					time.Sleep(dur * time.Microsecond)

					if i == 0 {
						fsize = len(data)
					}
					var out FixedList
					if err := out.Unmarshal(data); err != nil {
						b.Fatalf("Error: %v", err)
					}
				}
				// fmt.Printf("Total fixed sleep: %v\n", total)
			})
		fmt.Printf("SIZE Delta: %v Fixed: %v\n", dsize, fsize)
		fmt.Println()
	}
}

func BenchmarkListIntersect(b *testing.B) {
	randomTests := func(sz int, overlap float64) {
		rs := []int{1, 10, 50, 100, 500, 1000, 10000, 100000, 1000000}

		for _, r := range rs {
			sz2 := sz * r
			if sz2 > 1000000 {
				break
			}
			limit := int64(float64(sz2) / overlap)

			u1 := createArray(sz, limit)
			d1 := encodeDelta(u1, 32)
			u2 := createArray(sz2, limit)
			d2 := encodeDelta(u2, 32)
			result := make([]uint64, 0, sz)

			u := &task.List{u1}
			v := &task.List{u2}
			ucopy := make([]uint64, len(u1), len(u1))
			copy(ucopy, u1)

			b.Run(fmt.Sprintf(":Cur:size=%d:overlap=%.2f:ratio=%d:", sz, overlap, r),
				func(b *testing.B) {
					for k := 0; k < b.N; k++ {
						//u.Uids = u.Uids[:sz]
						//copy(u.Uids, ucopy)
						IntersectWith(u, v)
					}
				})

			b.Run(fmt.Sprintf(":Bin:size=%d:overlap=%.2f:ratio=%d:", sz, overlap, r),
				func(b *testing.B) {
					for k := 0; k < b.N; k++ {
						BinIntersect(u2, u1, &result)
						result = result[:0]
					}
				})
			b.Run(fmt.Sprintf(":Mer:size=%d:overlap=%.2f:ratio=%d", sz, overlap, r),
				func(b *testing.B) {
					for k := 0; k < b.N; k++ {
						mergeIntersect(u1, u2, &result)
						result = result[:0]
					}
				})

			b.Run(fmt.Sprintf(":Two:size=%d:overlap=%.2f:ratio=%d", sz, overlap, r),
				func(b *testing.B) {
					var f func(a, b *DeltaList, final *[]uint64)
					if r < 500 {
						f = mergeDeltaIntersect
					} else {
						f = BinDelta
					}
					for k := 0; k < b.N; k++ {
						f(d1, d2, &result)
						result = result[:0]
					}
				})
			fmt.Println()
		}
	}
	//randomTests(10, 0.01)
	//randomTests(100, 0.01)
	//randomTests(1000, 0.01)
	//randomTests(10000, 0.01)

	// Overlap has no effect on Bin numbers.
	overlaps := []float64{0.00001, 0.8}
	for _, overlap := range overlaps {
		randomTests(10, overlap)
		randomTests(100, overlap)
		randomTests(1000, overlap)
		randomTests(10000, overlap)
		fmt.Println()
	}

	//randomTests(10, 0.4)
	//randomTests(100, 0.4)
	//randomTests(1000, 0.4)
	//randomTests(10000, 0.4)
	//fmt.Println()

	//randomTests(10, 0.8)
	//randomTests(100, 0.8)
	//randomTests(1000, 0.8)
	//randomTests(10000, 0.8)
	//fmt.Println()
}
<|endoftext|>


<commit_before>package push

import (
	"fmt"
	"strings"

	"code.cloudfoundry.org/cli/integration/helpers"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
)

var _ = Describe("push with hostname", func() {
	var (
		appName string
		route   string
	)

	BeforeEach(func() {
		appName = helpers.NewAppName()
	})

	Context("when the default domain is a HTTP domain", func() {
		Context("when no host is provided / host defaults to app name", func() {
			BeforeEach(func() {
				route = fmt.Sprintf("%s.%s", strings.ToLower(appName), helpers.DefaultSharedDomain())
			})

			Context("when the default route does not exist", func() {
				It("creates and maps the route", func() {
					helpers.WithHelloWorldApp(func(dir string) {
						session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, appName, "--no-start")
						Eventually(session).Should(Say("routes:"))
						Eventually(session).Should(Say("(?i)\\+\\s+%s", route))
						Eventually(session).Should(Exit(0))
					})

					session := helpers.CF("app", appName)
					Eventually(session).Should(Say("name:\\s+%s", appName))
					Eventually(session).Should(Say("routes:\\s+%s", route))
					Eventually(session).Should(Exit(0))
				})
			})

			Context("the default route exists and is unmapped", func() {
				BeforeEach(func() {
					Eventually(helpers.CF("create-route", space, helpers.DefaultSharedDomain(), "-n", strings.ToLower(appName))).Should(Exit(0))
				})

				It("maps the route", func() {
					helpers.WithHelloWorldApp(func(dir string) {
						session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, appName, "--no-start")
						Eventually(session).Should(Say("routes:"))
						Eventually(session).Should(Say("(?i)\\+\\s+%s", route))
						Eventually(session).Should(Exit(0))
					})

					session := helpers.CF("app", appName)
					Eventually(session).Should(Say("name:\\s+%s", appName))
					Eventually(session).Should(Say("routes:\\s+%s", route))
					Eventually(session).Should(Exit(0))
				})
			})

			Context("when the default route is mapped to the application", func() {
				BeforeEach(func() {
					helpers.WithHelloWorldApp(func(dir string) {
						Eventually(helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, appName, "--no-start")).Should(Exit(0))
					})
				})

				It("does nothing", func() {
					helpers.WithHelloWorldApp(func(dir string) {
						session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, appName, "--no-start")
						Eventually(session).Should(Say("routes:"))
						Eventually(session).Should(Say("(?i)\\s+%s", route))
						Eventually(session).Should(Exit(0))
					})

					session := helpers.CF("app", appName)
					Eventually(session).Should(Say("name:\\s+%s", appName))
					Eventually(session).Should(Say("routes:\\s+%s", route))
					Eventually(session).Should(Exit(0))
				})
			})
		})

		Context("when the host is provided", func() {
			var hostname string

			BeforeEach(func() {
				hostname = strings.ToLower(helpers.NewAppName())
				route = fmt.Sprintf("%s.%s", hostname, helpers.DefaultSharedDomain())
			})

			Context("when the default route does not exist", func() {
				It("creates and maps the route", func() {
					helpers.WithHelloWorldApp(func(dir string) {
						session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, appName, "--hostname", hostname, "--no-start")
						Eventually(session).Should(Say("routes:"))
						Eventually(session).Should(Say("(?i)\\+\\s+%s", route))
						Eventually(session).Should(Exit(0))
					})

					session := helpers.CF("app", appName)
					Eventually(session).Should(Say("name:\\s+%s", appName))
					Eventually(session).Should(Say("routes:\\s+%s", route))
					Eventually(session).Should(Exit(0))

					By("does not remap default route after")
					helpers.WithHelloWorldApp(func(dir string) {
						session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, appName, "--no-start")
						Eventually(session).Should(Say("routes:"))
						Consistently(session).ShouldNot(Say("(?i)\\+\\s+.*%s.*", helpers.DefaultSharedDomain()))
						Eventually(session).Should(Exit(0))
					})
				})
			})

			Context("the default route exists and is unmapped", func() {
				BeforeEach(func() {
					Eventually(helpers.CF("create-route", space, helpers.DefaultSharedDomain(), "-n", strings.ToLower(appName))).Should(Exit(0))
				})

				It("creates and maps the route with the provided hostname", func() {
					helpers.WithHelloWorldApp(func(dir string) {
						session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, appName, "--hostname", hostname, "--no-start")
						Eventually(session).Should(Say("routes:"))
						Eventually(session).Should(Say("(?i)\\+\\s+%s", route))
						Eventually(session).Should(Exit(0))
					})

					session := helpers.CF("app", appName)
					Eventually(session).Should(Say("name:\\s+%s", appName))
					Eventually(session).Should(Say("routes:\\s+%s", route))
					Eventually(session).Should(Exit(0))
				})
			})

			Context("when the default route is mapped to the application", func() {
				BeforeEach(func() {
					helpers.WithHelloWorldApp(func(dir string) {
						Eventually(helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, appName, "--hostname", hostname, "--no-start")).Should(Exit(0))
					})
				})

				It("does nothing", func() {
					helpers.WithHelloWorldApp(func(dir string) {
						session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, appName, "--hostname", hostname, "--no-start")
						Eventually(session).Should(Say("routes:"))
						Eventually(session).Should(Say("(?i)\\s+%s", route))
						Eventually(session).Should(Exit(0))
					})

					session := helpers.CF("app", appName)
					Eventually(session).Should(Say("name:\\s+%s", appName))
					Eventually(session).Should(Say("routes:\\s+%s", route))
					Eventually(session).Should(Exit(0))
				})
			})
		})
	})

	Context("when using a tcp domain", func() {
		var (
			domain     helpers.Domain
			domainName string
		)

		BeforeEach(func() {
			domainName = helpers.DomainName()
			domain = helpers.NewDomain(organization, domainName)
			domain.CreateWithRouterGroup(helpers.FindOrCreateTCPRouterGroup(GinkgoParallelNode()))
		})

		AfterEach(func() {
			domain.DeleteShared()
		})

		It("returns an error", func() {
			session := helpers.CF(PushCommandName, appName, "--hostname", "I-dont-care", "-d", domainName, "--no-start")
			Eventually(session.Err).Should(Say("The route is invalid: a hostname cannot be used with a TCP domain."))
			Eventually(session).Should(Exit(1))
		})
	})
})
<commit_msg>enhance these test to be 100% correct<commit_after>package push

import (
	"fmt"
	"strings"

	"code.cloudfoundry.org/cli/integration/helpers"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
)

var _ = Describe("push with hostname", func() {
	var (
		appName string
		route   string
	)

	BeforeEach(func() {
		appName = helpers.NewAppName()
	})

	Context("when the default domain is a HTTP domain", func() {
		Context("when no host is provided / host defaults to app name", func() {
			BeforeEach(func() {
				route = fmt.Sprintf("%s.%s", strings.ToLower(appName), helpers.DefaultSharedDomain())
			})

			Context("when the default route does not exist", func() {
				It("creates and maps the route", func() {
					helpers.WithHelloWorldApp(func(dir string) {
						session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, appName, "--no-start")
						Eventually(session).Should(Say("routes:"))
						Eventually(session).Should(Say("(?i)\\+\\s+%s", route))
						Eventually(session).Should(Exit(0))
					})

					session := helpers.CF("app", appName)
					Eventually(session).Should(Say("name:\\s+%s", appName))
					Eventually(session).Should(Say("routes:\\s+%s", route))
					Eventually(session).Should(Exit(0))
				})
			})

			Context("the default route exists and is unmapped", func() {
				BeforeEach(func() {
					Eventually(helpers.CF("create-route", space, helpers.DefaultSharedDomain(), "-n", strings.ToLower(appName))).Should(Exit(0))
				})

				It("maps the route", func() {
					helpers.WithHelloWorldApp(func(dir string) {
						session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, appName, "--no-start")
						Eventually(session).Should(Say("routes:"))
						Eventually(session).Should(Say("(?i)\\+\\s+%s", route))
						Eventually(session).Should(Exit(0))
					})

					session := helpers.CF("app", appName)
					Eventually(session).Should(Say("name:\\s+%s", appName))
					Eventually(session).Should(Say("routes:\\s+%s", route))
					Eventually(session).Should(Exit(0))
				})
			})

			Context("when the default route is mapped to the application", func() {
				BeforeEach(func() {
					helpers.WithHelloWorldApp(func(dir string) {
						Eventually(helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, appName, "--no-start")).Should(Exit(0))
					})
				})

				It("does nothing", func() {
					helpers.WithHelloWorldApp(func(dir string) {
						session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, appName, "--no-start")
						Eventually(session).Should(Say("routes:"))
						Eventually(session).Should(Say("(?i)\\s+%s", route))
						Eventually(session).Should(Exit(0))
					})

					appGUID := helpers.AppGUID(appName)
					session := helpers.CF("curl", fmt.Sprintf("/v2/apps/%s/routes", appGUID))
					Eventually(session).Should(Say(`"total_results":\s+1,`))
					Eventually(session).Should(Exit(0))
				})
			})
		})

		Context("when the host is provided", func() {
			var hostname string

			BeforeEach(func() {
				hostname = strings.ToLower(helpers.NewAppName())
				route = fmt.Sprintf("%s.%s", hostname, helpers.DefaultSharedDomain())
			})

			Context("when the default route does not exist", func() {
				It("creates and maps the route", func() {
					helpers.WithHelloWorldApp(func(dir string) {
						session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, appName, "--hostname", hostname, "--no-start")
						Eventually(session).Should(Say("routes:"))
						Eventually(session).Should(Say("(?i)\\+\\s+%s", route))
						Eventually(session).Should(Exit(0))
					})

					session := helpers.CF("app", appName)
					Eventually(session).Should(Say("name:\\s+%s", appName))
					Eventually(session).Should(Say("routes:\\s+%s", route))
					Eventually(session).Should(Exit(0))

					By("does not remap default route after")
					helpers.WithHelloWorldApp(func(dir string) {
						session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, appName, "--no-start")
						Eventually(session).Should(Say("routes:"))
						Consistently(session).ShouldNot(Say("(?i)\\+\\s+.*%s.*", helpers.DefaultSharedDomain()))
						Eventually(session).Should(Exit(0))
					})
				})
			})

			Context("the default route exists and is unmapped", func() {
				BeforeEach(func() {
					Eventually(helpers.CF("create-route", space, helpers.DefaultSharedDomain(), "-n", strings.ToLower(appName))).Should(Exit(0))
				})

				It("creates and maps the route with the provided hostname", func() {
					helpers.WithHelloWorldApp(func(dir string) {
						session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, appName, "--hostname", hostname, "--no-start")
						Eventually(session).Should(Say("routes:"))
						Eventually(session).Should(Say("(?i)\\+\\s+%s", route))
						Eventually(session).Should(Exit(0))
					})

					session := helpers.CF("app", appName)
					Eventually(session).Should(Say("name:\\s+%s", appName))
					Eventually(session).Should(Say("routes:\\s+%s", route))
					Eventually(session).Should(Exit(0))
				})
			})

			Context("when the default route is mapped to the application", func() {
				BeforeEach(func() {
					helpers.WithHelloWorldApp(func(dir string) {
						Eventually(helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, appName, "--hostname", hostname, "--no-start")).Should(Exit(0))
					})
				})

				It("does nothing", func() {
					helpers.WithHelloWorldApp(func(dir string) {
						session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, appName, "--hostname", hostname, "--no-start")
						Eventually(session).Should(Say("routes:"))
						Eventually(session).Should(Say("(?i)\\s+%s", route))
						Eventually(session).Should(Exit(0))
					})

					appGUID := helpers.AppGUID(appName)
					session := helpers.CF("curl", fmt.Sprintf("/v2/apps/%s/routes", appGUID))
					Eventually(session).Should(Say(`"total_results":\s+1,`))
					Eventually(session).Should(Exit(0))
				})
			})
		})
	})

	Context("when using a tcp domain", func() {
		var (
			domain     helpers.Domain
			domainName string
		)

		BeforeEach(func() {
			domainName = helpers.DomainName()
			domain = helpers.NewDomain(organization, domainName)
			domain.CreateWithRouterGroup(helpers.FindOrCreateTCPRouterGroup(GinkgoParallelNode()))
		})

		AfterEach(func() {
			domain.DeleteShared()
		})

		It("returns an error", func() {
			session := helpers.CF(PushCommandName, appName, "--hostname", "I-dont-care", "-d", domainName, "--no-start")
			Eventually(session.Err).Should(Say("The route is invalid: a hostname cannot be used with a TCP domain."))
			Eventually(session).Should(Exit(1))
		})
	})
})
<|endoftext|>