<commit_before>package meta

import (
	"bufio"
	"flag"
	"io"

	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/vault/api"
	"github.com/hashicorp/vault/command/token"
	"github.com/mitchellh/cli"
)

// FlagSetFlags is an enum to define what flags are present in the
// default FlagSet returned by Meta.FlagSet.
type FlagSetFlags uint

type TokenHelperFunc func() (token.TokenHelper, error)

const (
	FlagSetNone    FlagSetFlags = 0
	FlagSetServer  FlagSetFlags = 1 << iota
	FlagSetDefault              = FlagSetServer
)

var (
	additionalOptionsUsage = func() string {
		return `
  -wrap-ttl=""            Indicates that the response should be wrapped in a
                          cubbyhole token with the requested TTL. The response
                          can be fetched by calling the "sys/wrapping/unwrap"
                          endpoint, passing in the wrappping token's ID. This
                          is a numeric string with an optional suffix
                          "s", "m", or "h"; if no suffix is specified it will
                          be parsed as seconds. May also be specified via
                          VAULT_WRAP_TTL.
`
	}
)

// Meta contains the meta-options and functionality that nearly every
// Vault command inherits.
type Meta struct {
	ClientToken string
	Ui          cli.Ui

	// The things below can be set, but aren't common
	ForceAddress string // Address to force for API clients

	// These are set by the command line flags.
	flagAddress    string
	flagCACert     string
	flagCAPath     string
	flagClientCert string
	flagClientKey  string
	flagWrapTTL    string
	flagInsecure   bool

	// Queried if no token can be found
	TokenHelper TokenHelperFunc
}

func (m *Meta) DefaultWrappingLookupFunc(operation, path string) string {
	if m.flagWrapTTL != "" {
		return m.flagWrapTTL
	}

	return api.DefaultWrappingLookupFunc(operation, path)
}

// Client returns the API client to a Vault server given the configured
// flag settings for this command.
func (m *Meta) Client() (*api.Client, error) {
	config := api.DefaultConfig()

	err := config.ReadEnvironment()
	if err != nil {
		return nil, errwrap.Wrapf("error reading environment: {{err}}", err)
	}

	if m.flagAddress != "" {
		config.Address = m.flagAddress
	}
	if m.ForceAddress != "" {
		config.Address = m.ForceAddress
	}
	// If we need custom TLS configuration, then set it
	if m.flagCACert != "" || m.flagCAPath != "" || m.flagClientCert != "" || m.flagClientKey != "" || m.flagInsecure {
		t := &api.TLSConfig{
			CACert:        m.flagCACert,
			CAPath:        m.flagCAPath,
			ClientCert:    m.flagClientCert,
			ClientKey:     m.flagClientKey,
			TLSServerName: "",
			Insecure:      m.flagInsecure,
		}
		config.ConfigureTLS(t)
	}

	// Build the client
	client, err := api.NewClient(config)
	if err != nil {
		return nil, err
	}

	client.SetWrappingLookupFunc(m.DefaultWrappingLookupFunc)

	// If we have a token directly, then set that
	token := m.ClientToken

	// Try to set the token to what is already stored
	if token == "" {
		token = client.Token()
	}

	// If we don't have a token, check the token helper
	if token == "" {
		if m.TokenHelper != nil {
			// If we have a token, then set that
			tokenHelper, err := m.TokenHelper()
			if err != nil {
				return nil, err
			}
			token, err = tokenHelper.Get()
			if err != nil {
				return nil, err
			}
		}
	}

	// Set the token
	if token != "" {
		client.SetToken(token)
	}

	return client, nil
}

// FlagSet returns a FlagSet with the common flags that every
// command implements. The exact behavior of FlagSet can be configured
// using the flags as the second parameter, for example to disable
// server settings on the commands that don't talk to a server.
func (m *Meta) FlagSet(n string, fs FlagSetFlags) *flag.FlagSet {
	f := flag.NewFlagSet(n, flag.ContinueOnError)

	// FlagSetServer tells us to enable the settings for selecting
	// the server information.
	if fs&FlagSetServer != 0 {
		f.StringVar(&m.flagAddress, "address", "", "")
		f.StringVar(&m.flagCACert, "ca-cert", "", "")
		f.StringVar(&m.flagCAPath, "ca-path", "", "")
		f.StringVar(&m.flagClientCert, "client-cert", "", "")
		f.StringVar(&m.flagClientKey, "client-key", "", "")
		f.StringVar(&m.flagWrapTTL, "wrap-ttl", "", "")
		f.BoolVar(&m.flagInsecure, "insecure", false, "")
		f.BoolVar(&m.flagInsecure, "tls-skip-verify", false, "")
	}

	// Create an io.Writer that writes to our Ui properly for errors.
	// This is kind of a hack, but it does the job. Basically: create
	// a pipe, use a scanner to break it into lines, and output each line
	// to the UI. Do this forever.
	errR, errW := io.Pipe()
	errScanner := bufio.NewScanner(errR)
	go func() {
		for errScanner.Scan() {
			m.Ui.Error(errScanner.Text())
		}
	}()
	f.SetOutput(errW)

	return f
}

// GeneralOptionsUsage returns the usage documentation for commonly
// available options
func GeneralOptionsUsage() string {
	general := `
  -address=addr           The address of the Vault server.
                          Overrides the VAULT_ADDR environment variable if set.

  -ca-cert=path           Path to a PEM encoded CA cert file to use to
                          verify the Vault server SSL certificate.
                          Overrides the VAULT_CACERT environment variable if set.

  -ca-path=path           Path to a directory of PEM encoded CA cert files
                          to verify the Vault server SSL certificate. If both
                          -ca-cert and -ca-path are specified, -ca-cert is used.
                          Overrides the VAULT_CAPATH environment variable if set.

  -client-cert=path       Path to a PEM encoded client certificate for TLS
                          authentication to the Vault server. Must also specify
                          -client-key. Overrides the VAULT_CLIENT_CERT
                          environment variable if set.

  -client-key=path        Path to an unencrypted PEM encoded private key
                          matching the client certificate from -client-cert.
                          Overrides the VAULT_CLIENT_KEY environment variable
                          if set.

  -tls-skip-verify        Do not verify TLS certificate. This is highly
                          not recommended. Verification will also be skipped
                          if VAULT_SKIP_VERIFY is set.
`

	general += additionalOptionsUsage()
	return general
}
<commit_msg>Fix command docstring typo: wrappping -> wrapping. (#2696)<commit_after>package meta

import (
	"bufio"
	"flag"
	"io"

	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/vault/api"
	"github.com/hashicorp/vault/command/token"
	"github.com/mitchellh/cli"
)

// FlagSetFlags is an enum to define what flags are present in the
// default FlagSet returned by Meta.FlagSet.
type FlagSetFlags uint

type TokenHelperFunc func() (token.TokenHelper, error)

const (
	FlagSetNone    FlagSetFlags = 0
	FlagSetServer  FlagSetFlags = 1 << iota
	FlagSetDefault              = FlagSetServer
)

var (
	additionalOptionsUsage = func() string {
		return `
  -wrap-ttl=""            Indicates that the response should be wrapped in a
                          cubbyhole token with the requested TTL. The response
                          can be fetched by calling the "sys/wrapping/unwrap"
                          endpoint, passing in the wrapping token's ID. This
                          is a numeric string with an optional suffix
                          "s", "m", or "h"; if no suffix is specified it will
                          be parsed as seconds. May also be specified via
                          VAULT_WRAP_TTL.
`
	}
)

// Meta contains the meta-options and functionality that nearly every
// Vault command inherits.
type Meta struct {
	ClientToken string
	Ui          cli.Ui

	// The things below can be set, but aren't common
	ForceAddress string // Address to force for API clients

	// These are set by the command line flags.
	flagAddress    string
	flagCACert     string
	flagCAPath     string
	flagClientCert string
	flagClientKey  string
	flagWrapTTL    string
	flagInsecure   bool

	// Queried if no token can be found
	TokenHelper TokenHelperFunc
}

func (m *Meta) DefaultWrappingLookupFunc(operation, path string) string {
	if m.flagWrapTTL != "" {
		return m.flagWrapTTL
	}

	return api.DefaultWrappingLookupFunc(operation, path)
}

// Client returns the API client to a Vault server given the configured
// flag settings for this command.
func (m *Meta) Client() (*api.Client, error) {
	config := api.DefaultConfig()

	err := config.ReadEnvironment()
	if err != nil {
		return nil, errwrap.Wrapf("error reading environment: {{err}}", err)
	}

	if m.flagAddress != "" {
		config.Address = m.flagAddress
	}
	if m.ForceAddress != "" {
		config.Address = m.ForceAddress
	}
	// If we need custom TLS configuration, then set it
	if m.flagCACert != "" || m.flagCAPath != "" || m.flagClientCert != "" || m.flagClientKey != "" || m.flagInsecure {
		t := &api.TLSConfig{
			CACert:        m.flagCACert,
			CAPath:        m.flagCAPath,
			ClientCert:    m.flagClientCert,
			ClientKey:     m.flagClientKey,
			TLSServerName: "",
			Insecure:      m.flagInsecure,
		}
		config.ConfigureTLS(t)
	}

	// Build the client
	client, err := api.NewClient(config)
	if err != nil {
		return nil, err
	}

	client.SetWrappingLookupFunc(m.DefaultWrappingLookupFunc)

	// If we have a token directly, then set that
	token := m.ClientToken

	// Try to set the token to what is already stored
	if token == "" {
		token = client.Token()
	}

	// If we don't have a token, check the token helper
	if token == "" {
		if m.TokenHelper != nil {
			// If we have a token, then set that
			tokenHelper, err := m.TokenHelper()
			if err != nil {
				return nil, err
			}
			token, err = tokenHelper.Get()
			if err != nil {
				return nil, err
			}
		}
	}

	// Set the token
	if token != "" {
		client.SetToken(token)
	}

	return client, nil
}

// FlagSet returns a FlagSet with the common flags that every
// command implements. The exact behavior of FlagSet can be configured
// using the flags as the second parameter, for example to disable
// server settings on the commands that don't talk to a server.
func (m *Meta) FlagSet(n string, fs FlagSetFlags) *flag.FlagSet {
	f := flag.NewFlagSet(n, flag.ContinueOnError)

	// FlagSetServer tells us to enable the settings for selecting
	// the server information.
	if fs&FlagSetServer != 0 {
		f.StringVar(&m.flagAddress, "address", "", "")
		f.StringVar(&m.flagCACert, "ca-cert", "", "")
		f.StringVar(&m.flagCAPath, "ca-path", "", "")
		f.StringVar(&m.flagClientCert, "client-cert", "", "")
		f.StringVar(&m.flagClientKey, "client-key", "", "")
		f.StringVar(&m.flagWrapTTL, "wrap-ttl", "", "")
		f.BoolVar(&m.flagInsecure, "insecure", false, "")
		f.BoolVar(&m.flagInsecure, "tls-skip-verify", false, "")
	}

	// Create an io.Writer that writes to our Ui properly for errors.
	// This is kind of a hack, but it does the job. Basically: create
	// a pipe, use a scanner to break it into lines, and output each line
	// to the UI. Do this forever.
	errR, errW := io.Pipe()
	errScanner := bufio.NewScanner(errR)
	go func() {
		for errScanner.Scan() {
			m.Ui.Error(errScanner.Text())
		}
	}()
	f.SetOutput(errW)

	return f
}

// GeneralOptionsUsage returns the usage documentation for commonly
// available options
func GeneralOptionsUsage() string {
	general := `
  -address=addr           The address of the Vault server.
                          Overrides the VAULT_ADDR environment variable if set.

  -ca-cert=path           Path to a PEM encoded CA cert file to use to
                          verify the Vault server SSL certificate.
                          Overrides the VAULT_CACERT environment variable if set.

  -ca-path=path           Path to a directory of PEM encoded CA cert files
                          to verify the Vault server SSL certificate. If both
                          -ca-cert and -ca-path are specified, -ca-cert is used.
                          Overrides the VAULT_CAPATH environment variable if set.

  -client-cert=path       Path to a PEM encoded client certificate for TLS
                          authentication to the Vault server. Must also specify
                          -client-key. Overrides the VAULT_CLIENT_CERT
                          environment variable if set.

  -client-key=path        Path to an unencrypted PEM encoded private key
                          matching the client certificate from -client-cert.
                          Overrides the VAULT_CLIENT_KEY environment variable
                          if set.

  -tls-skip-verify        Do not verify TLS certificate. This is highly
                          not recommended. Verification will also be skipped
                          if VAULT_SKIP_VERIFY is set.
`

	general += additionalOptionsUsage()
	return general
}
<|endoftext|>


<commit_before>//  Copyright (c) 2016 Couchbase, Inc.
//  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 moss

import (
	"bytes"
	"encoding/binary"
	"encoding/json"
	"fmt"

	"github.com/edsrzf/mmap-go"
)

func (s *Store) persistFooter(file File, footer *Footer) error {
	jBuf, err := json.Marshal(footer)
	if err != nil {
		return err
	}

	finfo, err := file.Stat()
	if err != nil {
		return err
	}

	footerPos := pageAlign(finfo.Size())
	footerLen := footerBegLen + len(jBuf) + footerEndLen

	footerBuf := bytes.NewBuffer(make([]byte, 0, footerLen))
	footerBuf.Write(STORE_MAGIC_BEG)
	footerBuf.Write(STORE_MAGIC_BEG)
	binary.Write(footerBuf, STORE_ENDIAN, uint32(STORE_VERSION))
	binary.Write(footerBuf, STORE_ENDIAN, uint32(footerLen))
	footerBuf.Write(jBuf)
	binary.Write(footerBuf, STORE_ENDIAN, footerPos)
	binary.Write(footerBuf, STORE_ENDIAN, uint32(footerLen))
	footerBuf.Write(STORE_MAGIC_END)
	footerBuf.Write(STORE_MAGIC_END)

	footerWritten, err := file.WriteAt(footerBuf.Bytes(), footerPos)
	if err != nil {
		return err
	}
	if footerWritten != len(footerBuf.Bytes()) {
		return fmt.Errorf("store: persistFooter error writing all footerBuf")
	}

	return nil
}

// --------------------------------------------------------

// ReadFooter reads the last valid Footer from a file.
func ReadFooter(options *StoreOptions, file File) (*Footer, error) {
	finfo, err := file.Stat()
	if err != nil {
		return nil, err
	}
	return ScanFooter(options, file, finfo.Size())
}

// --------------------------------------------------------

// ScanFooter scans a file backwards from the given pos for a valid Footer.
func ScanFooter(options *StoreOptions, file File, pos int64) (*Footer, error) {
	footerEnd := make([]byte, footerEndLen)
	for {
		for { // Scan backwards for STORE_MAGIC_END, which might be a potential footer.
			if pos <= int64(footerBegLen+footerEndLen) {
				return nil, fmt.Errorf("store: no valid footer found")
			}
			n, err := file.ReadAt(footerEnd, pos-int64(footerEndLen))
			if err != nil {
				return nil, err
			}
			if n != footerEndLen {
				return nil, fmt.Errorf("store: read footer too small")
			}
			if bytes.Equal(STORE_MAGIC_END, footerEnd[8+4:8+4+lenMagicEnd]) &&
				bytes.Equal(STORE_MAGIC_END, footerEnd[8+4+lenMagicEnd:]) {
				break
			}

			pos-- // TODO: optimizations to scan backwards faster.
		}

		// Read and check the potential footer.
		footerEndBuf := bytes.NewBuffer(footerEnd)

		var offset int64
		if err := binary.Read(footerEndBuf, STORE_ENDIAN, &offset); err != nil {
			return nil, err
		}

		var length uint32
		if err := binary.Read(footerEndBuf, STORE_ENDIAN, &length); err != nil {
			return nil, err
		}

		if offset > 0 &&
			offset < pos-int64(footerBegLen+footerEndLen) &&
			length == uint32(pos-offset) {
			data := make([]byte, pos-offset-int64(footerEndLen))

			n, err := file.ReadAt(data, offset)
			if err != nil {
				return nil, err
			}
			if n != len(data) {
				return nil, fmt.Errorf("store: read footer data too small")
			}

			if bytes.Equal(STORE_MAGIC_BEG, data[:lenMagicBeg]) &&
				bytes.Equal(STORE_MAGIC_BEG, data[lenMagicBeg:2*lenMagicBeg]) {
				b := bytes.NewBuffer(data[2*lenMagicBeg:])

				var version uint32
				if err := binary.Read(b, STORE_ENDIAN, &version); err != nil {
					return nil, err
				}
				if version != STORE_VERSION {
					return nil, fmt.Errorf("store: version mismatch, "+
						"current: %v != found: %v", STORE_VERSION, version)
				}

				var length0 uint32
				if err := binary.Read(b, STORE_ENDIAN, &length0); err != nil {
					return nil, err
				}
				if length0 != length {
					return nil, fmt.Errorf("store: length mismatch, "+
						"wanted: %v != found: %v", length0, length)
				}

				f := &Footer{refs: 1}

				err = json.Unmarshal(data[2*lenMagicBeg+4+4:], f)
				if err != nil {
					return nil, err
				}

				err = f.loadSegments(options, &FileRef{file: file, refs: 1})
				if err != nil {
					return nil, err
				}

				return f, nil
			} // Else, perhaps file was unlucky in having STORE_MAGIC_END's.
		} // Else, perhaps a persist file was stored in a file.

		pos-- // Footer was invalid, so keep scanning.
	}
}

// --------------------------------------------------------

// loadSegments() loads the segments of a footer, if not already
// loaded.  The FileRef will be owned by the footer on success.
func (f *Footer) loadSegments(options *StoreOptions, fref *FileRef) error {
	if f.ss != nil && f.ss.a != nil {
		return nil
	}

	osFile := ToOsFile(fref.file)
	if osFile == nil {
		return fmt.Errorf("store: loadFooterSegments convert to os.File error")
	}

	mm, err := mmap.Map(osFile, mmap.RDONLY, 0)
	if err != nil {
		return fmt.Errorf("store: loadFooterSegments mmap.Map(), err: %v", err)
	}

	mref := &mmapRef{fref: fref, mm: mm, refs: 1}

	err = f.loadSegmentsFromMRef(&options.CollectionOptions, mref)
	if err != nil {
		mm.Unmap()

		return err
	}

	return nil
}

// loadSegmentsFromMRef() loads footer's segments via the provided
// mmapRef.  The mmapRef will be owned by the footer on success.
func (f *Footer) loadSegmentsFromMRef(options *CollectionOptions,
	mref *mmapRef) (err error) {
	a := make([]Segment, len(f.SegmentLocs))

	for i := range f.SegmentLocs {
		sloc := &f.SegmentLocs[i]

		segmentLoader, exists := SegmentLoaders[sloc.Kind]
		if !exists || segmentLoader == nil {
			return fmt.Errorf("store: unknown SegmentLoc kind, sloc: %+v", sloc)
		}

		var kvs []uint64
		var buf []byte

		if sloc.KvsBytes > 0 {
			kvsBytes := mref.mm[sloc.KvsOffset : sloc.KvsOffset+sloc.KvsBytes]
			kvs, err = ByteSliceToUint64Slice(kvsBytes)
			if err != nil {
				return err
			}

			if sloc.BufBytes > 0 {
				buf = mref.mm[sloc.BufOffset : sloc.BufOffset+sloc.BufBytes]
			}
		}

		seg, err := segmentLoader(sloc, kvs, buf)
		if err != nil {
			return err
		}

		a[i] = seg
	}

	f.ss = &segmentStack{options: options, a: a, refs: 1}

	f.mref = mref

	return nil
}

// --------------------------------------------------------

func (f *Footer) Close() error {
	f.DecRef()
	return nil
}

func (f *Footer) AddRef() {
	f.m.Lock()
	f.refs++
	f.m.Unlock()
}

func (f *Footer) DecRef() {
	f.m.Lock()
	f.refs--
	if f.refs <= 0 {
		f.mref.DecRef()
		f.mref = nil
		f.ss = nil
	}
	f.m.Unlock()
}

// --------------------------------------------------------

// mrefSegmentStack() returns the current mmapRef and segmentStack for
// a footer.  The caller must DecRef() the returned mmapRef.
func (f *Footer) mrefSegmentStack() (*mmapRef, *segmentStack) {
	f.m.Lock()

	mref := f.mref.AddRef()
	ss := f.ss

	f.m.Unlock()

	return mref, ss
}

// mrefRefresh provides a newer, latest mmapRef to the chain of
// footers.  This allows older footers (perhaps still being used by
// slow readers) to drop their old mmap's and switch to the latest
// mmap's, which should reduce the number of mmap's in concurrent use.
//
// Also, mrefRefresh will splice out old footers from the chain that
// have no more references.
func (f *Footer) mrefRefresh(mrefNew *mmapRef) *Footer {
	if f == nil {
		return nil
	}

	f.m.Lock()

	refs := f.refs
	if refs > 1 {
		mrefOld := f.mref
		if mrefOld != nil {
			err := f.loadSegmentsFromMRef(f.ss.options, mrefNew)
			if err == nil {
				mrefNew.AddRef()
				mrefOld.DecRef()
			}
		}
	}

	prevFooter := f.prevFooter

	f.m.Unlock()

	prevFooterRefreshed := f.mrefRefreshPrevFooter(prevFooter, mrefNew)
	if refs <= 0 {
		return prevFooterRefreshed
	}

	return f
}

func (f *Footer) mrefRefreshPrevFooter(prevFooter *Footer,
	mrefNew *mmapRef) *Footer {
	prevFooterRefreshed := prevFooter.mrefRefresh(mrefNew)
	if prevFooterRefreshed != prevFooter {
		f.m.Lock()
		f.prevFooter = prevFooterRefreshed // Splice the chain.
		f.m.Unlock()
	}

	return prevFooterRefreshed
}

// --------------------------------------------------------

// Get retrieves a val from the footer, and will return nil val
// if the entry does not exist in the footer.
func (f *Footer) Get(key []byte, readOptions ReadOptions) ([]byte, error) {
	mref, ss := f.mrefSegmentStack()

	rv, err := ss.Get(key, readOptions)
	if err == nil && rv != nil {
		rv = append(make([]byte, 0, len(rv)), rv...) // Copy.
	}

	mref.DecRef()

	return rv, err
}

// StartIterator returns a new Iterator instance on this footer.
//
// On success, the returned Iterator will be positioned so that
// Iterator.Current() will either provide the first entry in the
// range or ErrIteratorDone.
//
// A startKeyIncl of nil means the logical "bottom-most" possible key
// and an endKeyExcl of nil means the logical "top-most" possible key.
func (f *Footer) StartIterator(startKeyIncl, endKeyExcl []byte,
	iteratorOptions IteratorOptions) (Iterator, error) {
	mref, ss := f.mrefSegmentStack()

	iter, err := ss.StartIterator(startKeyIncl, endKeyExcl, iteratorOptions)
	if err != nil {
		mref.DecRef()
		return nil, err
	}

	initCloser, ok := iter.(InitCloser)
	if !ok || initCloser == nil {
		iter.Close()
		mref.DecRef()
		return nil, ErrUnexpected
	}

	err = initCloser.InitCloser(mref)
	if err != nil {
		iter.Close()
		mref.DecRef()
		return nil, err
	}

	return iter, nil
}
<commit_msg>MB-21477 - checks for footer SegmentLocs corruption<commit_after>//  Copyright (c) 2016 Couchbase, Inc.
//  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 moss

import (
	"bytes"
	"encoding/binary"
	"encoding/json"
	"fmt"

	"github.com/edsrzf/mmap-go"
)

func (s *Store) persistFooter(file File, footer *Footer) error {
	jBuf, err := json.Marshal(footer)
	if err != nil {
		return err
	}

	finfo, err := file.Stat()
	if err != nil {
		return err
	}

	footerPos := pageAlign(finfo.Size())
	footerLen := footerBegLen + len(jBuf) + footerEndLen

	footerBuf := bytes.NewBuffer(make([]byte, 0, footerLen))
	footerBuf.Write(STORE_MAGIC_BEG)
	footerBuf.Write(STORE_MAGIC_BEG)
	binary.Write(footerBuf, STORE_ENDIAN, uint32(STORE_VERSION))
	binary.Write(footerBuf, STORE_ENDIAN, uint32(footerLen))
	footerBuf.Write(jBuf)
	binary.Write(footerBuf, STORE_ENDIAN, footerPos)
	binary.Write(footerBuf, STORE_ENDIAN, uint32(footerLen))
	footerBuf.Write(STORE_MAGIC_END)
	footerBuf.Write(STORE_MAGIC_END)

	footerWritten, err := file.WriteAt(footerBuf.Bytes(), footerPos)
	if err != nil {
		return err
	}
	if footerWritten != len(footerBuf.Bytes()) {
		return fmt.Errorf("store: persistFooter error writing all footerBuf")
	}

	return nil
}

// --------------------------------------------------------

// ReadFooter reads the last valid Footer from a file.
func ReadFooter(options *StoreOptions, file File) (*Footer, error) {
	finfo, err := file.Stat()
	if err != nil {
		return nil, err
	}
	return ScanFooter(options, file, finfo.Size())
}

// --------------------------------------------------------

// ScanFooter scans a file backwards from the given pos for a valid Footer.
func ScanFooter(options *StoreOptions, file File, pos int64) (*Footer, error) {
	footerEnd := make([]byte, footerEndLen)
	for {
		for { // Scan backwards for STORE_MAGIC_END, which might be a potential footer.
			if pos <= int64(footerBegLen+footerEndLen) {
				return nil, fmt.Errorf("store: no valid footer found")
			}
			n, err := file.ReadAt(footerEnd, pos-int64(footerEndLen))
			if err != nil {
				return nil, err
			}
			if n != footerEndLen {
				return nil, fmt.Errorf("store: read footer too small")
			}
			if bytes.Equal(STORE_MAGIC_END, footerEnd[8+4:8+4+lenMagicEnd]) &&
				bytes.Equal(STORE_MAGIC_END, footerEnd[8+4+lenMagicEnd:]) {
				break
			}

			pos-- // TODO: optimizations to scan backwards faster.
		}

		// Read and check the potential footer.
		footerEndBuf := bytes.NewBuffer(footerEnd)

		var offset int64
		if err := binary.Read(footerEndBuf, STORE_ENDIAN, &offset); err != nil {
			return nil, err
		}

		var length uint32
		if err := binary.Read(footerEndBuf, STORE_ENDIAN, &length); err != nil {
			return nil, err
		}

		if offset > 0 &&
			offset < pos-int64(footerBegLen+footerEndLen) &&
			length == uint32(pos-offset) {
			data := make([]byte, pos-offset-int64(footerEndLen))

			n, err := file.ReadAt(data, offset)
			if err != nil {
				return nil, err
			}
			if n != len(data) {
				return nil, fmt.Errorf("store: read footer data too small")
			}

			if bytes.Equal(STORE_MAGIC_BEG, data[:lenMagicBeg]) &&
				bytes.Equal(STORE_MAGIC_BEG, data[lenMagicBeg:2*lenMagicBeg]) {
				b := bytes.NewBuffer(data[2*lenMagicBeg:])

				var version uint32
				if err := binary.Read(b, STORE_ENDIAN, &version); err != nil {
					return nil, err
				}
				if version != STORE_VERSION {
					return nil, fmt.Errorf("store: version mismatch, "+
						"current: %v != found: %v", STORE_VERSION, version)
				}

				var length0 uint32
				if err := binary.Read(b, STORE_ENDIAN, &length0); err != nil {
					return nil, err
				}
				if length0 != length {
					return nil, fmt.Errorf("store: length mismatch, "+
						"wanted: %v != found: %v", length0, length)
				}

				f := &Footer{refs: 1}

				err = json.Unmarshal(data[2*lenMagicBeg+4+4:], f)
				if err != nil {
					return nil, err
				}

				err = f.loadSegments(options, &FileRef{file: file, refs: 1})
				if err != nil {
					return nil, err
				}

				return f, nil
			} // Else, perhaps file was unlucky in having STORE_MAGIC_END's.
		} // Else, perhaps a persist file was stored in a file.

		pos-- // Footer was invalid, so keep scanning.
	}
}

// --------------------------------------------------------

// loadSegments() loads the segments of a footer, if not already
// loaded.  The FileRef will be owned by the footer on success.
func (f *Footer) loadSegments(options *StoreOptions, fref *FileRef) error {
	if f.ss != nil && f.ss.a != nil {
		return nil
	}

	osFile := ToOsFile(fref.file)
	if osFile == nil {
		return fmt.Errorf("store: loadFooterSegments convert to os.File error")
	}

	mm, err := mmap.Map(osFile, mmap.RDONLY, 0)
	if err != nil {
		return fmt.Errorf("store: loadFooterSegments mmap.Map(), err: %v", err)
	}

	mref := &mmapRef{fref: fref, mm: mm, refs: 1}

	err = f.loadSegmentsFromMRef(&options.CollectionOptions, mref)
	if err != nil {
		mm.Unmap()

		return err
	}

	return nil
}

// loadSegmentsFromMRef() loads footer's segments via the provided
// mmapRef.  The mmapRef will be owned by the footer on success.
func (f *Footer) loadSegmentsFromMRef(options *CollectionOptions,
	mref *mmapRef) (err error) {
	a := make([]Segment, len(f.SegmentLocs))

	for i := range f.SegmentLocs {
		sloc := &f.SegmentLocs[i]

		segmentLoader, exists := SegmentLoaders[sloc.Kind]
		if !exists || segmentLoader == nil {
			return fmt.Errorf("store: unknown SegmentLoc kind, sloc: %+v", sloc)
		}

		var kvs []uint64
		var buf []byte

		if sloc.KvsBytes > 0 {
			if sloc.KvsOffset > uint64(len(mref.mm)) ||
				sloc.KvsOffset+sloc.KvsBytes > uint64(len(mref.mm)) {
				return fmt.Errorf("store_footer: KvsOffset/KvsBytes too big,"+
					" len(mref.mm): %d, sloc: %+v, footer: %+v,"+
					" f.SegmentLocs: %+v, i: %d, options: %v",
					len(mref.mm), sloc, f, f.SegmentLocs, i, options)
			}

			kvsBytes := mref.mm[sloc.KvsOffset : sloc.KvsOffset+sloc.KvsBytes]
			kvs, err = ByteSliceToUint64Slice(kvsBytes)
			if err != nil {
				return err
			}

			if sloc.BufBytes > 0 {
				if sloc.BufOffset > uint64(len(mref.mm)) ||
					sloc.BufOffset+sloc.BufBytes > uint64(len(mref.mm)) {
					return fmt.Errorf("store_footer: BufOffset/BufBytes too big,"+
						" len(mref.mm): %d, sloc: %+v, footer: %+v,"+
						" f.SegmentLocs: %+v, i: %d, options: %v",
						len(mref.mm), sloc, f, f.SegmentLocs, i, options)
				}

				buf = mref.mm[sloc.BufOffset : sloc.BufOffset+sloc.BufBytes]
			}
		}

		seg, err := segmentLoader(sloc, kvs, buf)
		if err != nil {
			return err
		}

		a[i] = seg
	}

	f.ss = &segmentStack{options: options, a: a, refs: 1}

	f.mref = mref

	return nil
}

// --------------------------------------------------------

func (f *Footer) Close() error {
	f.DecRef()
	return nil
}

func (f *Footer) AddRef() {
	f.m.Lock()
	f.refs++
	f.m.Unlock()
}

func (f *Footer) DecRef() {
	f.m.Lock()
	f.refs--
	if f.refs <= 0 {
		f.mref.DecRef()
		f.mref = nil
		f.ss = nil
	}
	f.m.Unlock()
}

// --------------------------------------------------------

// mrefSegmentStack() returns the current mmapRef and segmentStack for
// a footer.  The caller must DecRef() the returned mmapRef.
func (f *Footer) mrefSegmentStack() (*mmapRef, *segmentStack) {
	f.m.Lock()

	mref := f.mref.AddRef()
	ss := f.ss

	f.m.Unlock()

	return mref, ss
}

// mrefRefresh provides a newer, latest mmapRef to the chain of
// footers.  This allows older footers (perhaps still being used by
// slow readers) to drop their old mmap's and switch to the latest
// mmap's, which should reduce the number of mmap's in concurrent use.
//
// Also, mrefRefresh will splice out old footers from the chain that
// have no more references.
func (f *Footer) mrefRefresh(mrefNew *mmapRef) *Footer {
	if f == nil {
		return nil
	}

	f.m.Lock()

	refs := f.refs
	if refs > 1 {
		mrefOld := f.mref
		if mrefOld != nil {
			err := f.loadSegmentsFromMRef(f.ss.options, mrefNew)
			if err == nil {
				mrefNew.AddRef()
				mrefOld.DecRef()
			}
		}
	}

	prevFooter := f.prevFooter

	f.m.Unlock()

	prevFooterRefreshed := f.mrefRefreshPrevFooter(prevFooter, mrefNew)
	if refs <= 0 {
		return prevFooterRefreshed
	}

	return f
}

func (f *Footer) mrefRefreshPrevFooter(prevFooter *Footer,
	mrefNew *mmapRef) *Footer {
	prevFooterRefreshed := prevFooter.mrefRefresh(mrefNew)
	if prevFooterRefreshed != prevFooter {
		f.m.Lock()
		f.prevFooter = prevFooterRefreshed // Splice the chain.
		f.m.Unlock()
	}

	return prevFooterRefreshed
}

// --------------------------------------------------------

// Get retrieves a val from the footer, and will return nil val
// if the entry does not exist in the footer.
func (f *Footer) Get(key []byte, readOptions ReadOptions) ([]byte, error) {
	mref, ss := f.mrefSegmentStack()

	rv, err := ss.Get(key, readOptions)
	if err == nil && rv != nil {
		rv = append(make([]byte, 0, len(rv)), rv...) // Copy.
	}

	mref.DecRef()

	return rv, err
}

// StartIterator returns a new Iterator instance on this footer.
//
// On success, the returned Iterator will be positioned so that
// Iterator.Current() will either provide the first entry in the
// range or ErrIteratorDone.
//
// A startKeyIncl of nil means the logical "bottom-most" possible key
// and an endKeyExcl of nil means the logical "top-most" possible key.
func (f *Footer) StartIterator(startKeyIncl, endKeyExcl []byte,
	iteratorOptions IteratorOptions) (Iterator, error) {
	mref, ss := f.mrefSegmentStack()

	iter, err := ss.StartIterator(startKeyIncl, endKeyExcl, iteratorOptions)
	if err != nil {
		mref.DecRef()
		return nil, err
	}

	initCloser, ok := iter.(InitCloser)
	if !ok || initCloser == nil {
		iter.Close()
		mref.DecRef()
		return nil, ErrUnexpected
	}

	err = initCloser.InitCloser(mref)
	if err != nil {
		iter.Close()
		mref.DecRef()
		return nil, err
	}

	return iter, nil
}
<|endoftext|>


<commit_before>package config

import (
	"errors"
	"strconv"
	"strings"
	"time"
)

// CurrentVersionNumber is the current application's version literal
const CurrentVersionNumber = "0.3.11"

const ApiVersion = "/go-ipfs/" + CurrentVersionNumber + "/"

// CurrentCommit is the current git commit, this is set as a ldflag in the Makefile
var CurrentCommit string

// Version regulates checking if the most recent version is run
type Version struct {
	// Current is the ipfs version for which config was generated
	Current string

	// Check signals how to react on updates:
	// - "ignore" for not checking
	// - "warn" for issuing a warning and proceeding
	// - "error" for exiting with an error
	Check string

	// CheckDate is a timestamp for the last time API endpoint was checked for updates
	CheckDate time.Time

	// CheckPeriod is the time duration over which the update check will not be performed
	// (Note: cannot use time.Duration because marshalling with json breaks it)
	CheckPeriod string

	// AutoUpdate is optional
	AutoUpdate AutoUpdateSetting
}

// supported Version.Check values
const (
	// CheckError value for Version.Check to raise error and exit if version is obsolete
	CheckError = "error"

	// CheckWarn value for Version.Check to show warning message if version is obsolete
	CheckWarn = "warn"

	// CheckIgnore value for Version.Check to not perform update check
	CheckIgnore = "ignore"
)

// AutoUpdateSetting implements json.Unmarshaler to check values in config
type AutoUpdateSetting int

// AutoUpdateSetting values
const (
	AutoUpdateNever AutoUpdateSetting = iota // do not auto-update
	AutoUpdatePatch                          // only on new patch versions
	AutoUpdateMinor                          // on new minor or patch versions (Default)
	AutoUpdateMajor                          // on all, even Major, version changes
)

// ErrUnknownAutoUpdateSetting is returned when an unknown value is read from the config
var ErrUnknownAutoUpdateSetting = errors.New("unknown value for AutoUpdate")

// defaultCheckPeriod governs h
var defaultCheckPeriod = time.Hour * 48

// UnmarshalJSON checks the input against known strings
func (s *AutoUpdateSetting) UnmarshalJSON(in []byte) error {

	switch strings.ToLower(string(in)) {
	case `"never"`:
		*s = AutoUpdateNever
	case `"major"`:
		*s = AutoUpdateMajor
	case `"minor"`:
		*s = AutoUpdateMinor
	case `"patch"`:
		*s = AutoUpdatePatch
	default:
		*s = AutoUpdateMinor
		return ErrUnknownAutoUpdateSetting
	}
	return nil
}

// MarshalJSON converts the value back to JSON string
func (s AutoUpdateSetting) MarshalJSON() ([]byte, error) {
	return []byte(`"` + s.String() + `"`), nil
}

// String converts valye to human readable string
func (s AutoUpdateSetting) String() string {
	switch s {
	case AutoUpdateNever:
		return "never"
	case AutoUpdateMajor:
		return "major"
	case AutoUpdateMinor:
		return "minor"
	case AutoUpdatePatch:
		return "patch"
	default:
		return ErrUnknownAutoUpdateSetting.Error()
	}
}

func (v *Version) checkPeriodDuration() time.Duration {
	d, err := strconv.Atoi(v.CheckPeriod)
	if err != nil {
		log.Warning("config.Version.CheckPeriod parse error. Using default.")
		return defaultCheckPeriod
	}
	return time.Duration(d)
}

// ShouldCheckForUpdate returns if update check API endpoint is needed for this specific runtime
func (v *Version) ShouldCheckForUpdate() bool {

	period := v.checkPeriodDuration()
	if v.Check == CheckIgnore || v.CheckDate.Add(period).After(time.Now()) {
		return false
	}
	return true
}

// VersionDefaultValue returns the default version config value (for init).
func VersionDefaultValue() Version {
	return Version{
		Current:     CurrentVersionNumber,
		Check:       "error",
		CheckPeriod: strconv.Itoa(int(defaultCheckPeriod)),
		AutoUpdate:  AutoUpdateMinor,
	}
}
<commit_msg>update version<commit_after>package config

import (
	"errors"
	"strconv"
	"strings"
	"time"
)

// CurrentCommit is the current git commit, this is set as a ldflag in the Makefile
var CurrentCommit string

// CurrentVersionNumber is the current application's version literal
const CurrentVersionNumber = "0.4.0-dev"

const ApiVersion = "/go-ipfs/" + CurrentVersionNumber + "/"

// Version regulates checking if the most recent version is run
type Version struct {
	// Current is the ipfs version for which config was generated
	Current string

	// Check signals how to react on updates:
	// - "ignore" for not checking
	// - "warn" for issuing a warning and proceeding
	// - "error" for exiting with an error
	Check string

	// CheckDate is a timestamp for the last time API endpoint was checked for updates
	CheckDate time.Time

	// CheckPeriod is the time duration over which the update check will not be performed
	// (Note: cannot use time.Duration because marshalling with json breaks it)
	CheckPeriod string

	// AutoUpdate is optional
	AutoUpdate AutoUpdateSetting
}

// supported Version.Check values
const (
	// CheckError value for Version.Check to raise error and exit if version is obsolete
	CheckError = "error"

	// CheckWarn value for Version.Check to show warning message if version is obsolete
	CheckWarn = "warn"

	// CheckIgnore value for Version.Check to not perform update check
	CheckIgnore = "ignore"
)

// AutoUpdateSetting implements json.Unmarshaler to check values in config
type AutoUpdateSetting int

// AutoUpdateSetting values
const (
	AutoUpdateNever AutoUpdateSetting = iota // do not auto-update
	AutoUpdatePatch                          // only on new patch versions
	AutoUpdateMinor                          // on new minor or patch versions (Default)
	AutoUpdateMajor                          // on all, even Major, version changes
)

// ErrUnknownAutoUpdateSetting is returned when an unknown value is read from the config
var ErrUnknownAutoUpdateSetting = errors.New("unknown value for AutoUpdate")

// defaultCheckPeriod governs h
var defaultCheckPeriod = time.Hour * 48

// UnmarshalJSON checks the input against known strings
func (s *AutoUpdateSetting) UnmarshalJSON(in []byte) error {

	switch strings.ToLower(string(in)) {
	case `"never"`:
		*s = AutoUpdateNever
	case `"major"`:
		*s = AutoUpdateMajor
	case `"minor"`:
		*s = AutoUpdateMinor
	case `"patch"`:
		*s = AutoUpdatePatch
	default:
		*s = AutoUpdateMinor
		return ErrUnknownAutoUpdateSetting
	}
	return nil
}

// MarshalJSON converts the value back to JSON string
func (s AutoUpdateSetting) MarshalJSON() ([]byte, error) {
	return []byte(`"` + s.String() + `"`), nil
}

// String converts valye to human readable string
func (s AutoUpdateSetting) String() string {
	switch s {
	case AutoUpdateNever:
		return "never"
	case AutoUpdateMajor:
		return "major"
	case AutoUpdateMinor:
		return "minor"
	case AutoUpdatePatch:
		return "patch"
	default:
		return ErrUnknownAutoUpdateSetting.Error()
	}
}

func (v *Version) checkPeriodDuration() time.Duration {
	d, err := strconv.Atoi(v.CheckPeriod)
	if err != nil {
		log.Warning("config.Version.CheckPeriod parse error. Using default.")
		return defaultCheckPeriod
	}
	return time.Duration(d)
}

// ShouldCheckForUpdate returns if update check API endpoint is needed for this specific runtime
func (v *Version) ShouldCheckForUpdate() bool {

	period := v.checkPeriodDuration()
	if v.Check == CheckIgnore || v.CheckDate.Add(period).After(time.Now()) {
		return false
	}
	return true
}

// VersionDefaultValue returns the default version config value (for init).
func VersionDefaultValue() Version {
	return Version{
		Current:     CurrentVersionNumber,
		Check:       "error",
		CheckPeriod: strconv.Itoa(int(defaultCheckPeriod)),
		AutoUpdate:  AutoUpdateMinor,
	}
}
<|endoftext|>


<commit_before>package sequence

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"sync"
)

// just for testing
type Sequencer struct {
	Counter      uint32
	file         string
	sequenceLock sync.Mutex
}

func (s *Sequencer) load() error {
	blob, err := ioutil.ReadFile(s.file)
	if err != nil {
		return err
	}
	return json.Unmarshal(blob, s)
}

func (s *Sequencer) dump() error {
	path := s.file
	blob, err := json.Marshal(s)
	if err != nil {
		return err
	}

	newpath := path + "_new"
	if err = ioutil.WriteFile(newpath, blob, 0644); err != nil {
		return err
	}
	return os.Rename(newpath, path)
}

func NewSequencer(path string) (m *Sequencer) {
	s := &Sequencer{Counter: 0, file: path}
	s.load()
	s.dump()
	return s
}

func (m *Sequencer) String() string {
	m.sequenceLock.Lock()
	defer m.sequenceLock.Unlock()
	return fmt.Sprintf("Squence: <%s> %d", m.file, m.Counter)
}

func (m *Sequencer) NextId(count uint32) (uint32, uint32) {
	m.sequenceLock.Lock()
	defer m.sequenceLock.Unlock()
	ret := m.Counter
	m.Counter += uint32(count)
	m.dump()
	return ret, m.Counter
}

func (m *Sequencer) SetMax(seenValue uint32) {
	m.sequenceLock.Lock()
	defer m.sequenceLock.Unlock()
	if m.Counter <= seenValue {
		m.Counter = seenValue + 1
		m.dump()
	}
}

func (m *Sequencer) Peek() uint32 {
	m.sequenceLock.Lock()
	defer m.sequenceLock.Unlock()
	return m.Counter
}
<commit_msg>sequence remove file operation<commit_after>package sequence

import (
	"fmt"
	"sync"
)

// just for testing
type Sequencer struct {
	Counter      uint32
	sequenceLock sync.Mutex
}

func NewSequencer() (m *Sequencer) {
	s := &Sequencer{Counter: 0}
	return s
}

func (m *Sequencer) String() string {
	m.sequenceLock.Lock()
	defer m.sequenceLock.Unlock()
	return fmt.Sprintf("Squence: %d", m.Counter)
}

func (m *Sequencer) NextId(count uint32) (uint32, uint32) {
	m.sequenceLock.Lock()
	defer m.sequenceLock.Unlock()
	ret := m.Counter
	m.Counter += uint32(count)
	return ret, m.Counter
}

func (m *Sequencer) SetMax(seenValue uint32) uint32 {
	m.sequenceLock.Lock()
	defer m.sequenceLock.Unlock()
	if m.Counter <= seenValue {
		m.Counter = seenValue
	}
	return m.Counter
}

func (m *Sequencer) Peek() uint32 {
	m.sequenceLock.Lock()
	defer m.sequenceLock.Unlock()
	return m.Counter
}
<|endoftext|>


<commit_before>// Copyright 2019 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 internal

import (
	"context"
)

const (
	ExperimentAutocomplete       = "autocomplete"
	ExperimentFrontendFetch      = "frontend-fetch"
	ExperimentMasterVersion      = "master-version"
	ExperimentExecutableExamples = "executable-examples"
	ExperimentSidenav            = "sidenav"
	ExperimentTranslateHTML      = "translate-html"
	ExperimentUseDirectories     = "use-directories"
	ExperimentUsePackageImports  = "use-package-imports"
	ExperimentUsePathInfo        = "use-path-info"
)

// Experiments represents all of the active experiments in the codebase and
// a description of each experiment.
var Experiments = map[string]string{
	ExperimentAutocomplete:       "Enable autocomplete with search.",
	ExperimentFrontendFetch:      "Enable ability to fetch a package that doesn't exist on pkg.go.dev.",
	ExperimentMasterVersion:      "Enable viewing path@master.",
	ExperimentExecutableExamples: "Display executable examples with their import statements, so that they are runnable via the Go playground.",
	ExperimentSidenav:            "Display documentation index on the left sidenav.",
	ExperimentTranslateHTML:      "Parse HTML text in READMEs, to properly display images.",
	ExperimentUseDirectories:     "Read from paths, documentation, readmes, and package_imports tables.",
	ExperimentUsePathInfo:        "Check the paths table if a path exists, as opposed to the packages or modules table.",
	ExperimentUsePackageImports:  "Read imports from the package_imports table.",
}

// Experiment holds data associated with an experimental feature for frontend
// or worker.
type Experiment struct {
	// Name is the name of the feature.
	Name string

	// Rollout is the percentage of requests enrolled in the experiment.
	Rollout uint

	// Description provides a description of the experiment.
	Description string
}

// ExperimentSource is the interface used by the middleware to interact with
// experiments data.
type ExperimentSource interface {
	// GetExperiments fetches active experiments from the
	// ExperimentSource.
	GetExperiments(ctx context.Context) ([]*Experiment, error)
}

// LocalExperimentSource is used when developing locally using the direct proxy
// mode.
type LocalExperimentSource struct {
	experiments []*Experiment
}

// NewLocalExperimentSource returns a LocalExperimentSource with the provided experiments.
func NewLocalExperimentSource(experiments []*Experiment) *LocalExperimentSource {
	return &LocalExperimentSource{experiments: experiments}
}

// GetExperiments returns the experiments for the given LocalExperimentSource.
func (e *LocalExperimentSource) GetExperiments(ctx context.Context) ([]*Experiment, error) {
	return e.experiments, nil
}
<commit_msg>internal/experiment: re-add the new homepage experiment<commit_after>// Copyright 2019 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 internal

import (
	"context"
)

const (
	ExperimentAutocomplete       = "autocomplete"
	ExperimentFrontendFetch      = "frontend-fetch"
	ExperimentMasterVersion      = "master-version"
	ExperimentExecutableExamples = "executable-examples"
	ExperimentNewHomepage        = "new-homepage"
	ExperimentSidenav            = "sidenav"
	ExperimentTranslateHTML      = "translate-html"
	ExperimentUseDirectories     = "use-directories"
	ExperimentUsePackageImports  = "use-package-imports"
	ExperimentUsePathInfo        = "use-path-info"
)

// Experiments represents all of the active experiments in the codebase and
// a description of each experiment.
var Experiments = map[string]string{
	ExperimentAutocomplete:       "Enable autocomplete with search.",
	ExperimentFrontendFetch:      "Enable ability to fetch a package that doesn't exist on pkg.go.dev.",
	ExperimentMasterVersion:      "Enable viewing path@master.",
	ExperimentNewHomepage:        "Enable the new hompage.",
	ExperimentExecutableExamples: "Display executable examples with their import statements, so that they are runnable via the Go playground.",
	ExperimentSidenav:            "Display documentation index on the left sidenav.",
	ExperimentTranslateHTML:      "Parse HTML text in READMEs, to properly display images.",
	ExperimentUseDirectories:     "Read from paths, documentation, readmes, and package_imports tables.",
	ExperimentUsePathInfo:        "Check the paths table if a path exists, as opposed to the packages or modules table.",
	ExperimentUsePackageImports:  "Read imports from the package_imports table.",
}

// Experiment holds data associated with an experimental feature for frontend
// or worker.
type Experiment struct {
	// Name is the name of the feature.
	Name string

	// Rollout is the percentage of requests enrolled in the experiment.
	Rollout uint

	// Description provides a description of the experiment.
	Description string
}

// ExperimentSource is the interface used by the middleware to interact with
// experiments data.
type ExperimentSource interface {
	// GetExperiments fetches active experiments from the
	// ExperimentSource.
	GetExperiments(ctx context.Context) ([]*Experiment, error)
}

// LocalExperimentSource is used when developing locally using the direct proxy
// mode.
type LocalExperimentSource struct {
	experiments []*Experiment
}

// NewLocalExperimentSource returns a LocalExperimentSource with the provided experiments.
func NewLocalExperimentSource(experiments []*Experiment) *LocalExperimentSource {
	return &LocalExperimentSource{experiments: experiments}
}

// GetExperiments returns the experiments for the given LocalExperimentSource.
func (e *LocalExperimentSource) GetExperiments(ctx context.Context) ([]*Experiment, error) {
	return e.experiments, nil
}
<|endoftext|>


<commit_before>// Copyright © 2016 Aaron Longwell
//
// Use of this source code is governed by an MIT licese.
// Details in the LICENSE file.

package trello

import (
	"fmt"
	"time"
)

type Action struct {
	ID              string      `json:"id"`
	IDMemberCreator string      `json:"idMemberCreator"`
	Type            string      `json:"type"`
	Date            time.Time   `json:"date"`
	Data            *ActionData `json:"data,omitempty"`
	MemberCreator   *Member     `json:"memberCreator,omitempty"`
	Member          *Member     `json:"member,omitempty"`
}

type ActionData struct {
	Text           string    `json:"text,omitempty"`
	List           *List     `json:"list,omitempty"`
	Card           *Card     `json:"card,omitempty"`
	CardSource     *Card     `json:"cardSource,omitempty"`
	Board          *Board    `json:"board,omitempty"`
	Old            *Card     `json:"old,omitempty"`
	ListBefore     *List     `json:"listBefore,omitempty"`
	ListAfter      *List     `json:"listAfter,omitempty"`
	DateLastEdited time.Time `json:"dateLastEdited"`

	CheckItem *CheckItem `json:"checkItem"`
	Checklist *Checklist `json:"checklist"`
}

func (b *Board) GetActions(args Arguments) (actions ActionCollection, err error) {
	path := fmt.Sprintf("boards/%s/actions", b.ID)
	err = b.client.Get(path, args, &actions)
	return
}

func (l *List) GetActions(args Arguments) (actions ActionCollection, err error) {
	path := fmt.Sprintf("lists/%s/actions", l.ID)
	err = l.client.Get(path, args, &actions)
	return
}

func (c *Card) GetActions(args Arguments) (actions ActionCollection, err error) {
	path := fmt.Sprintf("cards/%s/actions", c.ID)
	err = c.client.Get(path, args, &actions)
	return
}

// GetListChangeActions retrieves a slice of Actions which resulted in changes
// to the card's active List. This includes the createCard and copyCard action (which
// place the card in its first list, and the updateCard:closed action, which remove it
// from its last list.
//
// This function is just an alias for:
//   card.GetActions(Arguments{"filter": "createCard,copyCard,updateCard:idList,updateCard:closed", "limit": "1000"})
//
func (c *Card) GetListChangeActions() (actions ActionCollection, err error) {
	return c.GetActions(Arguments{"filter": "createCard,copyCard,updateCard:idList,updateCard:closed"})
}

func (c *Card) GetMembershipChangeActions() (actions ActionCollection, err error) {
	// We include updateCard:closed as if the member is implicitly removed from the card when it's closed.
	// This allows us to "close out" the duration length.
	return c.GetActions(Arguments{"filter": "addMemberToCard,removeMemberFromCard,updateCard:closed"})
}

// DidCreateCard() returns true if this action created a card, false otherwise.
func (a *Action) DidCreateCard() bool {
	switch a.Type {
	case "createCard", "emailCard", "copyCard", "convertToCardFromCheckItem":
		return true
	case "moveCardToBoard":
		return true // Unsure about this one
	default:
		return false
	}
}

func (a *Action) DidArchiveCard() bool {
	return a.Type == "updateCard" && a.Data.Card != nil && a.Data.Card.Closed
}

func (a *Action) DidUnarchiveCard() bool {
	return a.Type == "updateCard" && a.Data.Old != nil && a.Data.Old.Closed
}

// Returns true if this action created the card (in which case it caused it to enter its
// first list), archived the card (in which case it caused it to leave its last List),
// or was an updateCard action involving a change to the list. This is supporting
// functionality for ListDuration.
//
func (a *Action) DidChangeListForCard() bool {
	if a.DidCreateCard() {
		return true
	}
	if a.DidArchiveCard() {
		return true
	}
	if a.DidUnarchiveCard() {
		return true
	}
	if a.Type == "updateCard" {
		if a.Data.ListAfter != nil {
			return true
		}
	}
	return false
}

func (a *Action) DidChangeCardMembership() bool {
	switch a.Type {
	case "addMemberToCard":
		return true
	case "removeMemberFromCard":
		return true
	default:
		return false
	}
}

// ListAfterAction calculates which List the card ended up in after this action
// completed. Returns nil when the action resulted in the card being archived (in
// which case we consider it to not be in a list anymore), or when the action isn't
// related to a list at all (in which case this is a nonsensical question to ask).
//
func ListAfterAction(a *Action) *List {
	switch a.Type {
	case "createCard", "copyCard", "emailCard", "convertToCardFromCheckItem":
		return a.Data.List
	case "updateCard":
		if a.DidArchiveCard() {
			return nil
		} else if a.DidUnarchiveCard() {
			return a.Data.List
		}
		if a.Data.ListAfter != nil {
			return a.Data.ListAfter
		}
	}
	return nil
}
<commit_msg>Adds nil-check protection in DidArchiveCard() DidUnarchiveCard() and DidChangeListForCard().<commit_after>// Copyright © 2016 Aaron Longwell
//
// Use of this source code is governed by an MIT licese.
// Details in the LICENSE file.

package trello

import (
	"fmt"
	"time"
)

type Action struct {
	ID              string      `json:"id"`
	IDMemberCreator string      `json:"idMemberCreator"`
	Type            string      `json:"type"`
	Date            time.Time   `json:"date"`
	Data            *ActionData `json:"data,omitempty"`
	MemberCreator   *Member     `json:"memberCreator,omitempty"`
	Member          *Member     `json:"member,omitempty"`
}

type ActionData struct {
	Text           string    `json:"text,omitempty"`
	List           *List     `json:"list,omitempty"`
	Card           *Card     `json:"card,omitempty"`
	CardSource     *Card     `json:"cardSource,omitempty"`
	Board          *Board    `json:"board,omitempty"`
	Old            *Card     `json:"old,omitempty"`
	ListBefore     *List     `json:"listBefore,omitempty"`
	ListAfter      *List     `json:"listAfter,omitempty"`
	DateLastEdited time.Time `json:"dateLastEdited"`

	CheckItem *CheckItem `json:"checkItem"`
	Checklist *Checklist `json:"checklist"`
}

func (b *Board) GetActions(args Arguments) (actions ActionCollection, err error) {
	path := fmt.Sprintf("boards/%s/actions", b.ID)
	err = b.client.Get(path, args, &actions)
	return
}

func (l *List) GetActions(args Arguments) (actions ActionCollection, err error) {
	path := fmt.Sprintf("lists/%s/actions", l.ID)
	err = l.client.Get(path, args, &actions)
	return
}

func (c *Card) GetActions(args Arguments) (actions ActionCollection, err error) {
	path := fmt.Sprintf("cards/%s/actions", c.ID)
	err = c.client.Get(path, args, &actions)
	return
}

// GetListChangeActions retrieves a slice of Actions which resulted in changes
// to the card's active List. This includes the createCard and copyCard action (which
// place the card in its first list, and the updateCard:closed action, which remove it
// from its last list.
//
// This function is just an alias for:
//   card.GetActions(Arguments{"filter": "createCard,copyCard,updateCard:idList,updateCard:closed", "limit": "1000"})
//
func (c *Card) GetListChangeActions() (actions ActionCollection, err error) {
	return c.GetActions(Arguments{"filter": "createCard,copyCard,updateCard:idList,updateCard:closed"})
}

func (c *Card) GetMembershipChangeActions() (actions ActionCollection, err error) {
	// We include updateCard:closed as if the member is implicitly removed from the card when it's closed.
	// This allows us to "close out" the duration length.
	return c.GetActions(Arguments{"filter": "addMemberToCard,removeMemberFromCard,updateCard:closed"})
}

// DidCreateCard() returns true if this action created a card, false otherwise.
func (a *Action) DidCreateCard() bool {
	switch a.Type {
	case "createCard", "emailCard", "copyCard", "convertToCardFromCheckItem":
		return true
	case "moveCardToBoard":
		return true // Unsure about this one
	default:
		return false
	}
}

func (a *Action) DidArchiveCard() bool {
	return (a.Type == "updateCard") && a.Data != nil && a.Data.Card != nil && a.Data.Card.Closed
}

func (a *Action) DidUnarchiveCard() bool {
	return (a.Type == "updateCard") && a.Data != nil && a.Data.Old != nil && a.Data.Old.Closed
}

// Returns true if this action created the card (in which case it caused it to enter its
// first list), archived the card (in which case it caused it to leave its last List),
// or was an updateCard action involving a change to the list. This is supporting
// functionality for ListDuration.
//
func (a *Action) DidChangeListForCard() bool {
	if a.DidCreateCard() {
		return true
	}
	if a.DidArchiveCard() {
		return true
	}
	if a.DidUnarchiveCard() {
		return true
	}
	if a.Type == "updateCard" {
		if a.Data != nil && a.Data.ListAfter != nil {
			return true
		}
	}
	return false
}

func (a *Action) DidChangeCardMembership() bool {
	switch a.Type {
	case "addMemberToCard":
		return true
	case "removeMemberFromCard":
		return true
	default:
		return false
	}
}

// ListAfterAction calculates which List the card ended up in after this action
// completed. Returns nil when the action resulted in the card being archived (in
// which case we consider it to not be in a list anymore), or when the action isn't
// related to a list at all (in which case this is a nonsensical question to ask).
//
func ListAfterAction(a *Action) *List {
	switch a.Type {
	case "createCard", "copyCard", "emailCard", "convertToCardFromCheckItem":
		return a.Data.List
	case "updateCard":
		if a.DidArchiveCard() {
			return nil
		} else if a.DidUnarchiveCard() {
			return a.Data.List
		}
		if a.Data.ListAfter != nil {
			return a.Data.ListAfter
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package isolated

import (
	"fmt"
	"io/ioutil"
	"os"

	"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("space command", func() {
	var (
		orgName   string
		spaceName string
	)

	BeforeEach(func() {
		orgName = helpers.NewOrgName()
		spaceName = helpers.NewSpaceName()
	})

	Describe("help", func() {
		Context("when --help flag is set", func() {
			It("Displays command usage to output", func() {
				session := helpers.CF("space", "--help")
				Eventually(session).Should(Say("NAME:"))
				Eventually(session).Should(Say("space - Show space info"))
				Eventually(session).Should(Say("USAGE:"))
				Eventually(session).Should(Say("cf space SPACE \\[--guid\\] \\[--security-group-rules\\]"))
				Eventually(session).Should(Say("OPTIONS:"))
				Eventually(session).Should(Say("--guid\\s+Retrieve and display the given space's guid\\.  All other output for the space is suppressed\\."))
				Eventually(session).Should(Say("--security-group-rules\\s+Retrieve the rules for all the security groups associated with the space\\."))
				Eventually(session).Should(Say("SEE ALSO:"))
				Eventually(session).Should(Say("set-space-isolation-segment, space-quota, space-users"))
			})
		})
	})

	Context("when the environment is not setup correctly", func() {
		Context("when no API endpoint is set", func() {
			BeforeEach(func() {
				helpers.UnsetAPI()
			})

			It("fails with no API endpoint set message", func() {
				session := helpers.CF("space", "some-space")
				Eventually(session.Out).Should(Say("FAILED"))
				Eventually(session.Err).Should(Say("No API endpoint set. Use 'cf login' or 'cf api' to target an endpoint."))
				Eventually(session).Should(Exit(1))
			})
		})

		Context("when not logged in", func() {
			BeforeEach(func() {
				helpers.LogoutCF()
			})

			It("fails with not logged in message", func() {
				session := helpers.CF("space", "some-space")
				Eventually(session.Out).Should(Say("FAILED"))
				Eventually(session.Err).Should(Say("Not logged in. Use 'cf login' to log in."))
				Eventually(session).Should(Exit(1))
			})
		})

		Context("when no organization is targeted", func() {
			BeforeEach(func() {
				helpers.LoginCF()
			})

			AfterEach(func() {
				helpers.LogoutCF()
			})

			It("fails with no organization targeted message and exits 1", func() {
				session := helpers.CF("space", spaceName)
				_, _ = helpers.GetCredentials()
				Eventually(session.Out).Should(Say("FAILED"))
				Eventually(session.Err).Should(Say("No org targeted, use 'cf target -o ORG' to target an org."))
				Eventually(session).Should(Exit(1))
			})
		})
	})

	Context("when the environment is set up correctly", func() {
		BeforeEach(func() {
			helpers.LoginCF()
			setupCF(orgName, spaceName)
		})

		AfterEach(func() {
			helpers.ClearTarget()
		})

		Context("when the space does not exist", func() {
			It("displays not found and exits 1", func() {
				badSpaceName := fmt.Sprintf("%s-1", spaceName)
				session := helpers.CF("space", badSpaceName)
				userName, _ := helpers.GetCredentials()
				Eventually(session.Out).Should(Say("Getting info for space %s in org %s as %s...", badSpaceName, orgName, userName))
				Eventually(session.Out).Should(Say("FAILED"))
				Eventually(session.Err).Should(Say("Space '%s' not found.", badSpaceName))
				Eventually(session).Should(Exit(1))
			})
		})

		Context("when the space exists", func() {
			Context("when the --guid flag is used", func() {
				It("displays the space guid", func() {
					session := helpers.CF("space", "--guid", spaceName)
					Eventually(session.Out).Should(Say("[\\da-f]{8}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{12}"))
					Eventually(session).Should(Exit(0))
				})
			})

			Context("when the --guid flag is not used", func() {
				var (
					securityGroup0      helpers.SecurityGroup
					securityGroup1      helpers.SecurityGroup
					securityGroupName2  string
					securityGroupRules2 *os.File
					err                 error
				)

				BeforeEach(func() {
					securityGroup0 = helpers.NewSecurityGroup(helpers.NewSecurityGroupName("0"), "tcp", "4.3.2.1/24", "80,443", "foo security group")
					securityGroup0.Create()
					Eventually(helpers.CF("bind-security-group", securityGroup0.Name, orgName, spaceName, "--lifecycle", "staging")).Should(Exit(0))

					securityGroup1 = helpers.NewSecurityGroup(helpers.NewSecurityGroupName("1"), "tcp", "1.2.3.4/24", "80,443", "some security group")
					securityGroup1.Create()
					Eventually(helpers.CF("bind-security-group", securityGroup1.Name, orgName, spaceName)).Should(Exit(0))

					securityGroupName2 = helpers.NewSecurityGroupName("2")
					securityGroupRules2, err = ioutil.TempFile("", "security-group-rules")
					Expect(err).ToNot(HaveOccurred())

					securityGroupRules2.Write([]byte(`
						[
							{
								"protocol": "udp",
								"destination": "92.0.0.1/24",
								"ports": "80,443",
								"description": "some other other security group"
							},
							{
								"protocol": "tcp",
								"destination": "5.7.9.11/24",
								"ports": "80,443",
								"description": "some other security group"
							}
						]
					`))

					Eventually(helpers.CF("create-security-group", securityGroupName2, securityGroupRules2.Name())).Should(Exit(0))
					os.Remove(securityGroupRules2.Name())

					Eventually(helpers.CF("bind-security-group", securityGroupName2, orgName, spaceName)).Should(Exit(0))
					Eventually(helpers.CF("bind-security-group", securityGroupName2, orgName, spaceName, "--lifecycle", "staging")).Should(Exit(0))
				})

				Context("when no flags are used", func() {
					var (
						appName              string
						spaceQuotaName       string
						serviceInstance      string
						isolationSegmentName string
					)

					BeforeEach(func() {
						appName = helpers.PrefixedRandomName("app")
						helpers.WithHelloWorldApp(func(appDir string) {
							Eventually(helpers.CF("push", appName, "-p", appDir, "-b", "staticfile_buildpack")).Should(Exit(0))
						})
						serviceInstance = helpers.PrefixedRandomName("si")
						Eventually(helpers.CF("create-user-provided-service", serviceInstance, "-p", "{}")).Should(Exit(0))
						Eventually(helpers.CF("bind-service", appName, serviceInstance)).Should(Exit(0))
						spaceQuotaName = helpers.PrefixedRandomName("space-quota")
						Eventually(helpers.CF("create-space-quota", spaceQuotaName)).Should(Exit(0))
						Eventually(helpers.CF("set-space-quota", spaceName, spaceQuotaName)).Should(Exit(0))
						isolationSegmentName = helpers.NewIsolationSegmentName()
						Eventually(helpers.CF("create-isolation-segment", isolationSegmentName)).Should(Exit(0))
						Eventually(helpers.CF("enable-org-isolation", orgName, isolationSegmentName)).Should(Exit(0))
						Eventually(helpers.CF("set-space-isolation-segment", spaceName, isolationSegmentName)).Should(Exit(0))

						Eventually(helpers.CF("bind-security-group", securityGroup1.Name, orgName, spaceName)).Should(Exit(0))
					})

					It("displays a table with space name, org, apps, services, isolation segment, space quota and security groups", func() {
						session := helpers.CF("space", spaceName)
						userName, _ := helpers.GetCredentials()
						Eventually(session.Out).Should(Say("Getting info for space %s in org %s as %s...", spaceName, orgName, userName))

						Eventually(session.Out).Should(Say("name:\\s+%s", spaceName))
						Eventually(session.Out).Should(Say("org:\\s+%s", orgName))
						Eventually(session.Out).Should(Say("apps:\\s+%s", appName))
						Eventually(session.Out).Should(Say("services:\\s+%s", serviceInstance))
						Eventually(session.Out).Should(Say("isolation segment:\\s+%s", isolationSegmentName))
						Eventually(session.Out).Should(Say("space quota:\\s+%s", spaceQuotaName))
						Eventually(session.Out).Should(Say("running security groups:\\s+.*%s,.* %s", securityGroup1.Name, securityGroupName2))
						Eventually(session.Out).Should(Say("staging security groups:\\s+.*%s,.* %s", securityGroup0.Name, securityGroupName2))
					})
				})

				Context("when the space does not have an isolation segment and its org has a default isolation segment", func() {
					var orgIsolationSegmentName string

					BeforeEach(func() {
						orgIsolationSegmentName = helpers.NewIsolationSegmentName()
						Eventually(helpers.CF("create-isolation-segment", orgIsolationSegmentName)).Should(Exit(0))
						Eventually(helpers.CF("enable-org-isolation", orgName, orgIsolationSegmentName)).Should(Exit(0))
						orgIsolationSegmentGUID := helpers.GetIsolationSegmentGUID(orgIsolationSegmentName)
						orgGUID := helpers.GetOrgGUID(orgName)

						Eventually(helpers.CF("curl", "-X", "PATCH",
							fmt.Sprintf("/v3/organizations/%s/relationships/default_isolation_segment", orgGUID),
							"-d", fmt.Sprintf(`{"data":{"guid":"%s"}`, orgIsolationSegmentGUID))).Should(Exit(0))
					})

					It("shows the org default isolation segment", func() {
						session := helpers.CF("space", spaceName)
						Eventually(session.Out).Should(Say("isolation segment:\\s+%s", orgIsolationSegmentName))
						Eventually(session).Should(Exit(0))
					})
				})

				Context("when the security group rules flag is used", func() {
					It("displays the space information as well as all security group rules", func() {
						session := helpers.CF("space", "--security-group-rules", spaceName)
						userName, _ := helpers.GetCredentials()
						Eventually(session.Out).Should(Say("Getting info for space %s in org %s as %s...", spaceName, orgName, userName))

						Eventually(session.Out).Should(Say("name:"))
						Eventually(session.Out).Should(Say("org:"))
						Eventually(session.Out).Should(Say("apps:"))
						Eventually(session.Out).Should(Say("services:"))
						Eventually(session.Out).Should(Say("isolation segment:"))
						Eventually(session.Out).Should(Say("space quota:"))
						Eventually(session.Out).Should(Say("running security groups:"))
						Eventually(session.Out).Should(Say("staging security groups:"))
						Eventually(session.Out).Should(Say("\n\n"))

						Eventually(session.Out).Should(Say("security group\\s+destination\\s+ports\\s+protocol\\s+lifecycle\\s+description"))
						Eventually(session.Out).Should(Say("#\\d+\\s+%s\\s+4.3.2.1/24\\s+80,443\\s+tcp\\s+staging\\s+foo security group", securityGroup0.Name))
						Eventually(session.Out).Should(Say("#\\d+\\s+%s\\s+1.2.3.4/24\\s+80,443\\s+tcp\\s+running\\s+some security group", securityGroup1.Name))
						Eventually(session.Out).Should(Say("#\\d+\\s+%s\\s+5.7.9.11/24\\s+80,443\\s+tcp\\s+running\\s+some other security group", securityGroupName2))
						Eventually(session.Out).Should(Say("\\s+%s\\s+5.7.9.11/24\\s+80,443\\s+tcp\\s+staging\\s+some other security group", securityGroupName2))
						Eventually(session.Out).Should(Say("\\s+%s\\s+92.0.0.1/24\\s+80,443\\s+udp\\s+running\\s+some other other security group", securityGroupName2))
						Eventually(session.Out).Should(Say("\\s+%s\\s+92.0.0.1/24\\s+80,443\\s+udp\\s+staging\\s+some other other security group", securityGroupName2))
					})
				})
			})
		})
	})
})
<commit_msg>wait for session to finish before exiting tests<commit_after>package isolated

import (
	"fmt"
	"io/ioutil"
	"os"

	"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("space command", func() {
	var (
		orgName   string
		spaceName string
	)

	BeforeEach(func() {
		orgName = helpers.NewOrgName()
		spaceName = helpers.NewSpaceName()
	})

	Describe("help", func() {
		Context("when --help flag is set", func() {
			It("Displays command usage to output", func() {
				session := helpers.CF("space", "--help")
				Eventually(session).Should(Say("NAME:"))
				Eventually(session).Should(Say("space - Show space info"))
				Eventually(session).Should(Say("USAGE:"))
				Eventually(session).Should(Say("cf space SPACE \\[--guid\\] \\[--security-group-rules\\]"))
				Eventually(session).Should(Say("OPTIONS:"))
				Eventually(session).Should(Say("--guid\\s+Retrieve and display the given space's guid\\.  All other output for the space is suppressed\\."))
				Eventually(session).Should(Say("--security-group-rules\\s+Retrieve the rules for all the security groups associated with the space\\."))
				Eventually(session).Should(Say("SEE ALSO:"))
				Eventually(session).Should(Say("set-space-isolation-segment, space-quota, space-users"))
				Eventually(session).Should(Exit(0))
			})
		})
	})

	Context("when the environment is not setup correctly", func() {
		Context("when no API endpoint is set", func() {
			BeforeEach(func() {
				helpers.UnsetAPI()
			})

			It("fails with no API endpoint set message", func() {
				session := helpers.CF("space", "some-space")
				Eventually(session).Should(Say("FAILED"))
				Eventually(session.Err).Should(Say("No API endpoint set. Use 'cf login' or 'cf api' to target an endpoint."))
				Eventually(session).Should(Exit(1))
			})
		})

		Context("when not logged in", func() {
			BeforeEach(func() {
				helpers.LogoutCF()
			})

			It("fails with not logged in message", func() {
				session := helpers.CF("space", "some-space")
				Eventually(session).Should(Say("FAILED"))
				Eventually(session.Err).Should(Say("Not logged in. Use 'cf login' to log in."))
				Eventually(session).Should(Exit(1))
			})
		})

		Context("when no organization is targeted", func() {
			BeforeEach(func() {
				helpers.LoginCF()
			})

			AfterEach(func() {
				helpers.LogoutCF()
			})

			It("fails with no organization targeted message and exits 1", func() {
				session := helpers.CF("space", spaceName)
				_, _ = helpers.GetCredentials()
				Eventually(session).Should(Say("FAILED"))
				Eventually(session.Err).Should(Say("No org targeted, use 'cf target -o ORG' to target an org."))
				Eventually(session).Should(Exit(1))
			})
		})
	})

	Context("when the environment is set up correctly", func() {
		BeforeEach(func() {
			helpers.LoginCF()
			setupCF(orgName, spaceName)
		})

		AfterEach(func() {
			helpers.ClearTarget()
		})

		Context("when the space does not exist", func() {
			It("displays not found and exits 1", func() {
				badSpaceName := fmt.Sprintf("%s-1", spaceName)
				session := helpers.CF("space", badSpaceName)
				userName, _ := helpers.GetCredentials()
				Eventually(session).Should(Say("Getting info for space %s in org %s as %s\\.\\.\\.", badSpaceName, orgName, userName))
				Eventually(session).Should(Say("FAILED"))
				Eventually(session.Err).Should(Say("Space '%s' not found.", badSpaceName))
				Eventually(session).Should(Exit(1))
			})
		})

		Context("when the space exists", func() {
			Context("when the --guid flag is used", func() {
				It("displays the space guid", func() {
					session := helpers.CF("space", "--guid", spaceName)
					Eventually(session).Should(Say("[\\da-f]{8}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{4}-[\\da-f]{12}"))
					Eventually(session).Should(Exit(0))
				})
			})

			Context("when the --guid flag is not used", func() {
				var (
					securityGroup0      helpers.SecurityGroup
					securityGroup1      helpers.SecurityGroup
					securityGroupName2  string
					securityGroupRules2 *os.File
					err                 error
				)

				BeforeEach(func() {
					securityGroup0 = helpers.NewSecurityGroup(helpers.NewSecurityGroupName("0"), "tcp", "4.3.2.1/24", "80,443", "foo security group")
					securityGroup0.Create()
					Eventually(helpers.CF("bind-security-group", securityGroup0.Name, orgName, spaceName, "--lifecycle", "staging")).Should(Exit(0))

					securityGroup1 = helpers.NewSecurityGroup(helpers.NewSecurityGroupName("1"), "tcp", "1.2.3.4/24", "80,443", "some security group")
					securityGroup1.Create()
					Eventually(helpers.CF("bind-security-group", securityGroup1.Name, orgName, spaceName)).Should(Exit(0))

					securityGroupName2 = helpers.NewSecurityGroupName("2")
					securityGroupRules2, err = ioutil.TempFile("", "security-group-rules")
					Expect(err).ToNot(HaveOccurred())

					securityGroupRules2.Write([]byte(`
						[
							{
								"protocol": "udp",
								"destination": "92.0.0.1/24",
								"ports": "80,443",
								"description": "some other other security group"
							},
							{
								"protocol": "tcp",
								"destination": "5.7.9.11/24",
								"ports": "80,443",
								"description": "some other security group"
							}
						]
					`))

					Eventually(helpers.CF("create-security-group", securityGroupName2, securityGroupRules2.Name())).Should(Exit(0))
					os.Remove(securityGroupRules2.Name())

					Eventually(helpers.CF("bind-security-group", securityGroupName2, orgName, spaceName)).Should(Exit(0))
					Eventually(helpers.CF("bind-security-group", securityGroupName2, orgName, spaceName, "--lifecycle", "staging")).Should(Exit(0))
				})

				Context("when no flags are used", func() {
					var (
						appName              string
						spaceQuotaName       string
						serviceInstance      string
						isolationSegmentName string
					)

					BeforeEach(func() {
						appName = helpers.PrefixedRandomName("app")
						helpers.WithHelloWorldApp(func(appDir string) {
							Eventually(helpers.CF("push", appName, "-p", appDir, "-b", "staticfile_buildpack")).Should(Exit(0))
						})
						serviceInstance = helpers.PrefixedRandomName("si")
						Eventually(helpers.CF("create-user-provided-service", serviceInstance, "-p", "{}")).Should(Exit(0))
						Eventually(helpers.CF("bind-service", appName, serviceInstance)).Should(Exit(0))
						spaceQuotaName = helpers.PrefixedRandomName("space-quota")
						Eventually(helpers.CF("create-space-quota", spaceQuotaName)).Should(Exit(0))
						Eventually(helpers.CF("set-space-quota", spaceName, spaceQuotaName)).Should(Exit(0))
						isolationSegmentName = helpers.NewIsolationSegmentName()
						Eventually(helpers.CF("create-isolation-segment", isolationSegmentName)).Should(Exit(0))
						Eventually(helpers.CF("enable-org-isolation", orgName, isolationSegmentName)).Should(Exit(0))
						Eventually(helpers.CF("set-space-isolation-segment", spaceName, isolationSegmentName)).Should(Exit(0))

						Eventually(helpers.CF("bind-security-group", securityGroup1.Name, orgName, spaceName)).Should(Exit(0))
					})

					It("displays a table with space name, org, apps, services, isolation segment, space quota and security groups", func() {
						session := helpers.CF("space", spaceName)
						userName, _ := helpers.GetCredentials()
						Eventually(session).Should(Say("Getting info for space %s in org %s as %s\\.\\.\\.", spaceName, orgName, userName))

						Eventually(session).Should(Say("name:\\s+%s", spaceName))
						Eventually(session).Should(Say("org:\\s+%s", orgName))
						Eventually(session).Should(Say("apps:\\s+%s", appName))
						Eventually(session).Should(Say("services:\\s+%s", serviceInstance))
						Eventually(session).Should(Say("isolation segment:\\s+%s", isolationSegmentName))
						Eventually(session).Should(Say("space quota:\\s+%s", spaceQuotaName))
						Eventually(session).Should(Say("running security groups:\\s+.*%s,.* %s", securityGroup1.Name, securityGroupName2))
						Eventually(session).Should(Say("staging security groups:\\s+.*%s,.* %s", securityGroup0.Name, securityGroupName2))
						Eventually(session).Should(Exit(0))
					})
				})

				Context("when the space does not have an isolation segment and its org has a default isolation segment", func() {
					var orgIsolationSegmentName string

					BeforeEach(func() {
						orgIsolationSegmentName = helpers.NewIsolationSegmentName()
						Eventually(helpers.CF("create-isolation-segment", orgIsolationSegmentName)).Should(Exit(0))
						Eventually(helpers.CF("enable-org-isolation", orgName, orgIsolationSegmentName)).Should(Exit(0))
						Eventually(helpers.CF("set-org-default-isolation-segment", orgName, orgIsolationSegmentName)).Should(Exit(0))
					})

					It("shows the org default isolation segment", func() {
						session := helpers.CF("space", spaceName)
						Eventually(session).Should(Say("isolation segment:\\s+%s", orgIsolationSegmentName))
						Eventually(session).Should(Exit(0))
					})
				})

				Context("when the security group rules flag is used", func() {
					It("displays the space information as well as all security group rules", func() {
						session := helpers.CF("space", "--security-group-rules", spaceName)
						userName, _ := helpers.GetCredentials()
						Eventually(session).Should(Say("Getting info for space %s in org %s as %s\\.\\.\\.", spaceName, orgName, userName))

						Eventually(session).Should(Say("name:"))
						Eventually(session).Should(Say("org:"))
						Eventually(session).Should(Say("apps:"))
						Eventually(session).Should(Say("services:"))
						Eventually(session).Should(Say("isolation segment:"))
						Eventually(session).Should(Say("space quota:"))
						Eventually(session).Should(Say("running security groups:"))
						Eventually(session).Should(Say("staging security groups:"))
						Eventually(session).Should(Say("\n\n"))

						Eventually(session).Should(Say("security group\\s+destination\\s+ports\\s+protocol\\s+lifecycle\\s+description"))
						Eventually(session).Should(Say("#\\d+\\s+%s\\s+4.3.2.1/24\\s+80,443\\s+tcp\\s+staging\\s+foo security group", securityGroup0.Name))
						Eventually(session).Should(Say("#\\d+\\s+%s\\s+1.2.3.4/24\\s+80,443\\s+tcp\\s+running\\s+some security group", securityGroup1.Name))
						Eventually(session).Should(Say("#\\d+\\s+%s\\s+5.7.9.11/24\\s+80,443\\s+tcp\\s+running\\s+some other security group", securityGroupName2))
						Eventually(session).Should(Say("\\s+%s\\s+5.7.9.11/24\\s+80,443\\s+tcp\\s+staging\\s+some other security group", securityGroupName2))
						Eventually(session).Should(Say("\\s+%s\\s+92.0.0.1/24\\s+80,443\\s+udp\\s+running\\s+some other other security group", securityGroupName2))
						Eventually(session).Should(Say("\\s+%s\\s+92.0.0.1/24\\s+80,443\\s+udp\\s+staging\\s+some other other security group", securityGroupName2))
						Eventually(session).Should(Exit(0))
					})
				})
			})
		})
	})
})
<|endoftext|>


<commit_before>// Copyright 2016 The Serviced 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 cache

import (
	lru "github.com/hashicorp/golang-lru"
	"time"
)

type SimpleLRUCache struct {
	maxItems        int
	expiration      time.Duration
	cleanupInterval time.Duration

	// Keep the cache implementation private
	lruCache *lru.Cache
}

// assert the LRUCache interface is implemented by SimpleLRUCache
var _ LRUCache = &SimpleLRUCache{}

// An internal struct to track the expiration time for each item in the cache
type cacheItem struct {
	key     string
	value   interface{}
	expires time.Time
}

//
// Creates a new instance of SimpleLRUCache that supports both a maximum cache size and a maximum time-to-live for each
// item in the cache.
// If on Set(), the number of active items exceeds maxItems, the oldest item will be removed to make room for the
// the new item.  Additionally, the cache tracks the age of item and automatically removes items that have aged past
// their expiration time.
//
// Parameters:
// maxItems        - the maximum number of items in the cache
// expiration      - the expiration time for each item added to the cache. Items older than this value will automatically
//                   be removed.
// cleanupInterval - the interval at which the cleaner runs to remove expired items from the cache.
//                   If 0, then the cleaner is not started.  The minimum non-zero value is one second.
// shutdown        - a channel that can be used to shutdown the cleanup timer
//
func NewSimpleLRUCache(maxItems int, expiration time.Duration, cleanupInterval time.Duration, shutdown chan struct{}) (*SimpleLRUCache, error) {

	//
	if cleanupInterval > 0 && cleanupInterval < time.Second {
		cleanupInterval = time.Second
	}

	simpleLRUCache := SimpleLRUCache{
		maxItems:        maxItems,
		expiration:      expiration,
		cleanupInterval: cleanupInterval,
	}

	var err error
	simpleLRUCache.lruCache, err = lru.New(maxItems)
	if err != nil {
		return nil, err
	}

	if cleanupInterval > 0 {
		simpleLRUCache.startCleaner(shutdown)
	}
	return &simpleLRUCache, nil
}

// Gets the value for key; returns the value and true if found, nil and false otherwise.
func (c *SimpleLRUCache) Get(key string) (interface{}, bool) {
	data, ok := c.lruCache.Get(key)
	if data != nil && ok {
		var item cacheItem
		item, _ = data.(cacheItem)
		item.expires = time.Now().Add(c.expiration) // update the expiration
		c.lruCache.Add(key, item)
		return item.value, true
	}
	return nil, false
}

// Add a value to the cache for the specified key.
// If the key already exists in the cache, it's value will be replaced.
func (c *SimpleLRUCache) Set(key string, value interface{}) {
	item := cacheItem{
		key:     key,
		value:   value,
		expires: time.Now().Add(c.expiration),
	}

	c.lruCache.Add(key, item)
}

// Returns the maximum size of the cache.
func (c *SimpleLRUCache) GetMaxSize() int {
	return c.maxItems
}

// Returns the current number of items in the cache.
func (c *SimpleLRUCache) GetCurrentSize() int {
	return c.lruCache.Len()
}

func (c *SimpleLRUCache) GetExpiration() time.Duration {
	return c.expiration
}

func (c *SimpleLRUCache) GetCleanupInterval() time.Duration {
	return c.cleanupInterval
}

func (c *SimpleLRUCache) startCleaner(shutdown chan struct{}) {
	timer := time.Tick(c.cleanupInterval)
	go (func() {
		for {
			select {
			case <-timer:
				c.cleanup()
			case <-shutdown:
				return
			}
		}
	})()
}

func (c *SimpleLRUCache) cleanup() {
	now := time.Now()
	for _, key := range c.lruCache.Keys() {
		data, ok := c.lruCache.Get(key)
		if data != nil && ok {
			var item cacheItem
			item, _ = data.(cacheItem)
			if item.expires.Before(now) {
				c.lruCache.Remove(key)
			}
		}
	}
}
<commit_msg>Add ability to override current time function<commit_after>// Copyright 2016 The Serviced 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 cache

import (
	lru "github.com/hashicorp/golang-lru"
	"time"
)

type SimpleLRUCache struct {
	maxItems        int
	expiration      time.Duration
	cleanupInterval time.Duration

	// Keep the cache implementation private
	lruCache *lru.Cache
}

// assert the LRUCache interface is implemented by SimpleLRUCache
var _ LRUCache = &SimpleLRUCache{}

var timeFunc func() time.Time = time.Now

// At sets a fake time, executes the provided
// function, then restores the default time getter,
// making it possible to test time-sensitive stuff
func At(t time.Time, f func()) {
	defer func() {
		timeFunc = time.Now
	}()

	timeFunc = func() time.Time {
		return t
	}

	f()
}

// An internal struct to track the expiration time for each item in the cache
type cacheItem struct {
	key     string
	value   interface{}
	expires time.Time
}

//
// Creates a new instance of SimpleLRUCache that supports both a maximum cache size and a maximum time-to-live for each
// item in the cache.
// If on Set(), the number of active items exceeds maxItems, the oldest item will be removed to make room for the
// the new item.  Additionally, the cache tracks the age of item and automatically removes items that have aged past
// their expiration time.
//
// Parameters:
// maxItems        - the maximum number of items in the cache
// expiration      - the expiration time for each item added to the cache. Items older than this value will automatically
//                   be removed.
// cleanupInterval - the interval at which the cleaner runs to remove expired items from the cache.
//                   If 0, then the cleaner is not started.  The minimum non-zero value is one second.
// shutdown        - a channel that can be used to shutdown the cleanup timer
//
func NewSimpleLRUCache(maxItems int, expiration time.Duration, cleanupInterval time.Duration, shutdown chan struct{}) (*SimpleLRUCache, error) {

	//
	if cleanupInterval > 0 && cleanupInterval < time.Second {
		cleanupInterval = time.Second
	}

	simpleLRUCache := SimpleLRUCache{
		maxItems:        maxItems,
		expiration:      expiration,
		cleanupInterval: cleanupInterval,
	}

	var err error
	simpleLRUCache.lruCache, err = lru.New(maxItems)
	if err != nil {
		return nil, err
	}

	if cleanupInterval > 0 {
		simpleLRUCache.startCleaner(shutdown)
	}
	return &simpleLRUCache, nil
}

// Gets the value for key; returns the value and true if found, nil and false otherwise.
func (c *SimpleLRUCache) Get(key string) (interface{}, bool) {
	data, ok := c.lruCache.Get(key)
	if data != nil && ok {
		var item cacheItem
		item, _ = data.(cacheItem)
		item.expires = timeFunc().Add(c.expiration) // update the expiration
		c.lruCache.Add(key, item)
		return item.value, true
	}
	return nil, false
}

// Add a value to the cache for the specified key.
// If the key already exists in the cache, it's value will be replaced.
func (c *SimpleLRUCache) Set(key string, value interface{}) {
	item := cacheItem{
		key:     key,
		value:   value,
		expires: timeFunc().Add(c.expiration),
	}

	c.lruCache.Add(key, item)
}

// Returns the maximum size of the cache.
func (c *SimpleLRUCache) GetMaxSize() int {
	return c.maxItems
}

// Returns the current number of items in the cache.
func (c *SimpleLRUCache) GetCurrentSize() int {
	return c.lruCache.Len()
}

func (c *SimpleLRUCache) GetExpiration() time.Duration {
	return c.expiration
}

func (c *SimpleLRUCache) GetCleanupInterval() time.Duration {
	return c.cleanupInterval
}

func (c *SimpleLRUCache) startCleaner(shutdown chan struct{}) {
	timer := time.NewTicker(c.cleanupInterval)
	go (func() {
		for {
			select {
			case <-timer.C:
				c.cleanup()
			case <-shutdown:
				timer.Stop()
				return
			}
		}
	})()
}

func (c *SimpleLRUCache) cleanup() {
	now := timeFunc()
	for _, key := range c.lruCache.Keys() {
		data, ok := c.lruCache.Get(key)
		if data != nil && ok {
			var item cacheItem
			item, _ = data.(cacheItem)
			if item.expires.Before(now) {
				c.lruCache.Remove(key)
			}
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 the gousb Authors.  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 usb

import (
	"errors"
	"reflect"
	"testing"
	"time"
)

type fakeTransfer struct {
	buf []byte
	ret int
	err error
}

func (t *fakeTransfer) submit() error      { return nil }
func (t *fakeTransfer) wait() (int, error) { return t.ret, t.err }
func (t *fakeTransfer) free() error        { return nil }

func TestEndpoint(t *testing.T) {
	for _, epCfg := range []struct {
		method string
		InterfaceSetup
		EndpointInfo
	}{
		{"Read", testBulkInSetup, testBulkInEP},
		{"Write", testIsoOutSetup, testIsoOutEP},
	} {
		t.Run(epCfg.method, func(t *testing.T) {
			for _, tc := range []struct {
				buf     []byte
				ret     int
				err     error
				want    int
				wantErr bool
			}{
				{buf: nil, ret: 10, want: 0},
				{buf: make([]byte, 128), ret: 60, want: 60},
				{buf: make([]byte, 128), ret: 10, err: errors.New("some error"), want: 10, wantErr: true},
			} {
				ep := &endpoint{
					InterfaceSetup: epCfg.InterfaceSetup,
					EndpointInfo:   epCfg.EndpointInfo,
					newUSBTransfer: func(buf []byte, timeout time.Duration) (transferIntf, error) {
						return &fakeTransfer{buf: buf, ret: tc.ret, err: tc.err}, nil
					},
				}
				op, ok := reflect.TypeOf(ep).MethodByName(epCfg.method)
				if !ok {
					t.Fatalf("method %s not found in endpoint struct", epCfg.method)
				}
				opv := op.Func.Interface().(func(*endpoint, []byte) (int, error))
				got, err := opv(ep, tc.buf)
				if (err != nil) != tc.wantErr {
					t.Errorf("bulkInEP.Read(): got err: %v, err != nil is %v, want %v", err, err != nil, tc.wantErr)
					continue
				}
				if got != tc.want {
					t.Errorf("bulkInEP.Read(): got %d bytes, want %d", got, tc.want)
				}
			}
		})
	}
}

func TestEndpointWrongDirection(t *testing.T) {
	ep := &endpoint{
		InterfaceSetup: testBulkInSetup,
		EndpointInfo:   testBulkInEP,
	}
	_, err := ep.Write([]byte{1, 2, 3})
	if err == nil {
		t.Error("bulkInEP.Write(): got nil error, want non-nil")
	}
	ep = &endpoint{
		InterfaceSetup: testIsoOutSetup,
		EndpointInfo:   testIsoOutEP,
	}
	_, err = ep.Read(make([]byte, 64))
	if err == nil {
		t.Error("isoOutEP.Read(): got nil error, want non-nil")
	}
}
<commit_msg>use a shared fakelibusb for mocking<commit_after>// Copyright 2017 the gousb Authors.  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 usb

import (
	"reflect"
	"testing"
)

func TestEndpoint(t *testing.T) {
	defer func(i libusbIntf) { libusb = i }(libusb)
	for _, epCfg := range []struct {
		method string
		InterfaceSetup
		EndpointInfo
	}{
		{"Read", testBulkInSetup, testBulkInEP},
		{"Write", testIsoOutSetup, testIsoOutEP},
	} {
		t.Run(epCfg.method, func(t *testing.T) {
			for _, tc := range []struct {
				desc    string
				buf     []byte
				ret     int
				status  TransferStatus
				want    int
				wantErr bool
			}{
				{
					desc: "empty buffer",
					buf:  nil,
					ret:  10,
					want: 0,
				},
				{
					desc: "128B buffer, 60 transferred",
					buf:  make([]byte, 128),
					ret:  60,
					want: 60,
				},
				{
					desc:    "128B buffer, 10 transferred and then error",
					buf:     make([]byte, 128),
					ret:     10,
					status:  LIBUSB_TRANSFER_ERROR,
					want:    10,
					wantErr: true,
				},
			} {
				lib := newFakeLibusb()
				libusb = lib
				ep := &endpoint{
					InterfaceSetup: epCfg.InterfaceSetup,
					EndpointInfo:   epCfg.EndpointInfo,
				}
				op, ok := reflect.TypeOf(ep).MethodByName(epCfg.method)
				if !ok {
					t.Fatalf("method %s not found in endpoint struct", epCfg.method)
				}
				go func() {
					fakeT := lib.waitForSubmitted()
					fakeT.length = tc.ret
					fakeT.status = tc.status
					close(fakeT.done)
				}()
				opv := op.Func.Interface().(func(*endpoint, []byte) (int, error))
				got, err := opv(ep, tc.buf)
				if (err != nil) != tc.wantErr {
					t.Errorf("%s: bulkInEP.Read(): got err: %v, err != nil is %v, want %v", tc.desc, err, err != nil, tc.wantErr)
					continue
				}
				if got != tc.want {
					t.Errorf("%s: bulkInEP.Read(): got %d bytes, want %d", tc.desc, got, tc.want)
				}
			}
		})
	}
}

func TestEndpointWrongDirection(t *testing.T) {
	ep := &endpoint{
		InterfaceSetup: testBulkInSetup,
		EndpointInfo:   testBulkInEP,
	}
	_, err := ep.Write([]byte{1, 2, 3})
	if err == nil {
		t.Error("bulkInEP.Write(): got nil error, want non-nil")
	}
	ep = &endpoint{
		InterfaceSetup: testIsoOutSetup,
		EndpointInfo:   testIsoOutEP,
	}
	_, err = ep.Read(make([]byte, 64))
	if err == nil {
		t.Error("isoOutEP.Read(): got nil error, want non-nil")
	}
}
<|endoftext|>