<commit_before>package strategy

type DomainsEndpointStrategy interface {
	OrgDomainURL(orgGuid, name string) string
	DomainURL(name string) string
	OrgDomainsURL(orgGuid string) string
	PrivateDomainsURL() string
	SharedDomainsURL() string
	DeleteDomainURL(guid string) string
	DeleteSharedDomainURL(guid string) string
	PrivateDomainsByOrgURL(guid string) string
}

type domainsEndpointStrategy struct{}

func (s domainsEndpointStrategy) DomainURL(name string) string {
	return buildURL(v2("domains"), params{
		inlineRelationsDepth: 1,
		q:                    map[string]string{"name": name},
	})
}

func (s domainsEndpointStrategy) OrgDomainsURL(orgGuid string) string {
	return v2("organizations", orgGuid, "domains")
}

func (s domainsEndpointStrategy) OrgDomainURL(orgGuid, name string) string {
	return buildURL(s.OrgDomainsURL(orgGuid), params{
		inlineRelationsDepth: 1,
		q:                    map[string]string{"name": name},
	})
}

func (s domainsEndpointStrategy) PrivateDomainsURL() string {
	return v2("domains")
}

func (s domainsEndpointStrategy) SharedDomainsURL() string {
	return v2("domains")
}

func (s domainsEndpointStrategy) PrivateDomainsByOrgURL(orgGuid string) string {
	return v2("domains")
}

func (s domainsEndpointStrategy) DeleteDomainURL(guid string) string {
	return buildURL(v2("domains", guid), params{recursive: true})
}

func (s domainsEndpointStrategy) DeleteSharedDomainURL(guid string) string {
	return buildURL(v2("domains", guid), params{recursive: true})
}

type separatedDomainsEndpointStrategy struct {
	domainsEndpointStrategy
}

func (s separatedDomainsEndpointStrategy) PrivateDomainsURL() string {
	return v2("private_domains")
}

func (s separatedDomainsEndpointStrategy) SharedDomainsURL() string {
	return v2("shared_domains")
}

func (s separatedDomainsEndpointStrategy) PrivateDomainsByOrgURL(orgGuid string) string {
	return v2("organizations", orgGuid, "private_domains")
}

func (s separatedDomainsEndpointStrategy) DeleteDomainURL(guid string) string {
	return buildURL(v2("private_domains", guid), params{recursive: true})
}

func (s separatedDomainsEndpointStrategy) DeleteSharedDomainURL(guid string) string {
	return buildURL(v2("shared_domains", guid), params{recursive: true})
}
<commit_msg>Stop using deprecated endpoints for domains.<commit_after>package strategy

type DomainsEndpointStrategy interface {
	OrgDomainURL(orgGuid, name string) string
	DomainURL(name string) string
	OrgDomainsURL(orgGuid string) string
	PrivateDomainsURL() string
	SharedDomainsURL() string
	DeleteDomainURL(guid string) string
	DeleteSharedDomainURL(guid string) string
	PrivateDomainsByOrgURL(guid string) string
}

type domainsEndpointStrategy struct{}

func (s domainsEndpointStrategy) DomainURL(name string) string {
	return buildURL(v2("domains"), params{
		inlineRelationsDepth: 1,
		q:                    map[string]string{"name": name},
	})
}

func (s domainsEndpointStrategy) OrgDomainsURL(orgGuid string) string {
	return v2("organizations", orgGuid, "domains")
}

func (s domainsEndpointStrategy) OrgDomainURL(orgGuid, name string) string {
	return buildURL(s.OrgDomainsURL(orgGuid), params{
		inlineRelationsDepth: 1,
		q:                    map[string]string{"name": name},
	})
}

func (s domainsEndpointStrategy) PrivateDomainsURL() string {
	return v2("domains")
}

func (s domainsEndpointStrategy) SharedDomainsURL() string {
	return v2("domains")
}

func (s domainsEndpointStrategy) PrivateDomainsByOrgURL(orgGuid string) string {
	return v2("domains")
}

func (s domainsEndpointStrategy) DeleteDomainURL(guid string) string {
	return buildURL(v2("domains", guid), params{recursive: true})
}

func (s domainsEndpointStrategy) DeleteSharedDomainURL(guid string) string {
	return buildURL(v2("domains", guid), params{recursive: true})
}

type separatedDomainsEndpointStrategy struct{}

func (s separatedDomainsEndpointStrategy) DomainURL(name string) string {
	return buildURL(v2("shared_domains"), params{
		q: map[string]string{"name": name},
	})
}

func (s separatedDomainsEndpointStrategy) OrgDomainsURL(orgGuid string) string {
	return v2("organizations", orgGuid, "private_domains")
}

func (s separatedDomainsEndpointStrategy) OrgDomainURL(orgGuid, name string) string {
	return buildURL(s.OrgDomainsURL(orgGuid), params{
		q: map[string]string{"name": name},
	})
}
func (s separatedDomainsEndpointStrategy) PrivateDomainsURL() string {
	return v2("private_domains")
}

func (s separatedDomainsEndpointStrategy) SharedDomainsURL() string {
	return v2("shared_domains")
}

func (s separatedDomainsEndpointStrategy) PrivateDomainsByOrgURL(orgGuid string) string {
	return v2("organizations", orgGuid, "private_domains")
}

func (s separatedDomainsEndpointStrategy) DeleteDomainURL(guid string) string {
	return buildURL(v2("private_domains", guid), params{recursive: true})
}

func (s separatedDomainsEndpointStrategy) DeleteSharedDomainURL(guid string) string {
	return buildURL(v2("shared_domains", guid), params{recursive: true})
}
<|endoftext|>


<commit_before>package main

import (
   "flag"
   "fmt"
   "encoding/json"
   "io/ioutil"
   "log"
   "os"
   "os/user"
   "path"
   "strings"
   "sync"
)

type config struct {
   User string
}

func loadConfig() (*config, error) {
   cfgfile, _ := configFile()
   bytes, err := ioutil.ReadFile(cfgfile)
   if err != nil {
      return nil, err
   }
   var cfg config
   err = json.Unmarshal(bytes, &cfg)
   return &cfg, err
}

func saveConfig(c *config) error {
   cfgfile, _ := configFile()
   bytes, err := json.MarshalIndent(c, "", "   ")
   if err != nil {
      return err
   }
   err = ioutil.WriteFile(cfgfile, bytes, 0600)
   return err
}

// A Command is an implementation of a dm command.
// This is borrowed largely from golang's cmd/go/main.go.
type Command struct {
   Run func(cfg *config, cmd *Command, args []string)
   UsageLine string
   Short string
   Long string
   Flag flag.FlagSet
}

func (c *Command) Name() string {
   name := c.UsageLine
   i := strings.Index(name, " ")
   if i >= 0 {
      name = name[:i]
   }
   return name
}

func (c *Command) Usage() {
   fmt.Fprintf(os.Stderr, "usage: %s\n\n", c.UsageLine)
   fmt.Fprintf(os.Stderr, "%s\n", strings.TrimSpace(c.Long))
   os.Exit(2)
}

func (c *Command) Runnable() bool {
   return c.Run != nil
}

// Available commands
var commands = []*Command{
   cmdUser,
   cmdSync,
   cmdFind,
}

var exitStatus = 0
var exitMu sync.Mutex

func setExitStatus(n int) {
   exitMu.Lock()
   if exitStatus < n {
      exitStatus = n
   }
   exitMu.Unlock()
}

func main() {
   var mainUser string  // -u user name

   flag.StringVar(&mainUser, "u", "", "")
   flag.Parse()
   args := flag.Args()

   if len(args) < 1 {
      usage()
   }

   // make working directory if it doesn't exist...or die trying
   makeWorkDir()

   cfg, err := loadConfig()
   if err != nil {
      fatalf("%s [dm.go]", err)
   }

   // command line flags override config file
   if mainUser != "" {
      cfg.User = mainUser
   }

   for _, cmd := range commands {
      if cmd.Name() == args[0] && cmd.Run != nil {
         cmd.Flag.Usage = func() { cmd.Usage() }
         cmd.Flag.Parse(args[1:])
         args = cmd.Flag.Args()
         cmd.Run(cfg, cmd, args)
         exit()
         return
      }
   }

   fmt.Fprintf(os.Stderr, "dm: unknown subcommand %q\n", args[0])
   setExitStatus(2)
   exit()
}

var atexitFuncs []func()

func atexit(f func()) {
   atexitFuncs = append(atexitFuncs, f)
}

func exit() {
   for _, f := range atexitFuncs {
      f()
   }
   os.Exit(exitStatus)
}

func userDir(user string) (string, error) {
   wrk, err := workDir()
   if err != nil {
      return "", err
   }
   return path.Join(wrk, user), nil
}

func makeUserDir(user string) {
   usr, err := userDir(user)
   if err != nil {
      fatalf("%s [dm.go]", err)
   }
   exists, err := isDir(usr)
   if err != nil {
      fatalf("%s [dm.go]", err)
   }
   if exists == false {
      err = os.MkdirAll(usr, 0700)
      if err != nil {
         fatalf("%s [dm.go]", err)
      }
   }
}

func homeDir() (string, error) {
   usr, err := user.Current()
   if err != nil {
      return "", err
   }
   return  usr.HomeDir, nil
}

func workDir() (string, error) {
   home, err := homeDir()
   if err != nil {
      return "", err
   }
   return path.Join(home, ".dailymile_cli"), nil
}

func makeWorkDir() {
   wrkdir, err := workDir()
   if err != nil {
      fatalf("%s [dm.go]", err)
   }

   exists, err := isDir(wrkdir)
   if err != nil {
      fatalf("%s [dm.go]", err)
   }

   if exists == false {
      err = os.MkdirAll(wrkdir, 0700)
      if err != nil {
         fatalf("%s [dm.go]", err)
      }
   }
}

func configFile() (string, error) {
   wrkdir, err := workDir()
   if err != nil {
      return "", err
   }
   cfgfile := path.Join(wrkdir, "config")
   return cfgfile, nil
}

func isDir(path string) (bool, error) {
   fi, err := os.Stat(path)
   if err == nil {
      return fi.IsDir(), nil
   } else if os.IsNotExist(err) {
      return false, nil
   }
   return false, err
}

func isFile(path string) (bool, error) {
   fi, err := os.Stat(path)
   if err == nil {
      return !fi.IsDir(), nil
   } else if os.IsNotExist(err) {
      return false, nil
   }
   return false, err
}

func fatalf(format string, args ...interface{}) {
   errorf(format, args...)
   exit()
}

func errorf(format string, args ...interface{}) {
   log.Printf(format, args...)
   setExitStatus(1)
}

var logf = log.Printf

func usage() {
   fmt.Fprintf(os.Stderr, "dm is a command line tool for Dailymile.com\n\n  usage: dm command [args]\n")
   os.Exit(2)
}
<commit_msg>Create default config file if one doesn't exist.<commit_after>package main

import (
   "flag"
   "fmt"
   "encoding/json"
   "io/ioutil"
   "log"
   "os"
   "os/user"
   "path"
   "strings"
   "sync"
)

type config struct {
   User string
}

var defaultConfig = &config{
   "",   // User
}

func loadConfig() (*config, error) {
   cfgfile, _ := configFile()
   bytes, err := ioutil.ReadFile(cfgfile)
   if err != nil {
      return nil, err
   }
   var cfg config
   err = json.Unmarshal(bytes, &cfg)
   return &cfg, err
}

func saveConfig(c *config) error {
   cfgfile, _ := configFile()
   bytes, err := json.MarshalIndent(c, "", "   ")
   if err != nil {
      return err
   }
   err = ioutil.WriteFile(cfgfile, bytes, 0600)
   return err
}

// A Command is an implementation of a dm command.
// This is borrowed largely from golang's cmd/go/main.go.
type Command struct {
   Run func(cfg *config, cmd *Command, args []string)
   UsageLine string
   Short string
   Long string
   Flag flag.FlagSet
}

func (c *Command) Name() string {
   name := c.UsageLine
   i := strings.Index(name, " ")
   if i >= 0 {
      name = name[:i]
   }
   return name
}

func (c *Command) Usage() {
   fmt.Fprintf(os.Stderr, "usage: %s\n\n", c.UsageLine)
   fmt.Fprintf(os.Stderr, "%s\n", strings.TrimSpace(c.Long))
   os.Exit(2)
}

func (c *Command) Runnable() bool {
   return c.Run != nil
}

// Available commands
var commands = []*Command{
   cmdUser,
   cmdSync,
   cmdFind,
}

var exitStatus = 0
var exitMu sync.Mutex

func setExitStatus(n int) {
   exitMu.Lock()
   if exitStatus < n {
      exitStatus = n
   }
   exitMu.Unlock()
}

func main() {
   var mainUser string  // -u user name

   flag.StringVar(&mainUser, "u", "", "")
   flag.Parse()
   args := flag.Args()

   if len(args) < 1 {
      usage()
   }

   // make working directory if it doesn't exist...or die trying
   makeWorkDir()

   cfg, err := loadConfig()
   if err != nil {
      if os.IsNotExist(err) {
         // create a default config file
         err = saveConfig(defaultConfig)
         if err != nil {
            fatalf("%s [dm.go - saveConfig]", err)
         }
         cfg = defaultConfig
      } else {
         fatalf("%s [dm.go - loadConfig]", err)
      }
   }

   // command line flags override config file
   if mainUser != "" {
      cfg.User = mainUser
   }

   if cfg.User == "" {
      fatalf(
`No user set.  Either use the 'dm user <user name>' command or
the '-u <user name>' command line argument.`)
   }

   for _, cmd := range commands {
      if cmd.Name() == args[0] && cmd.Run != nil {
         cmd.Flag.Usage = func() { cmd.Usage() }
         cmd.Flag.Parse(args[1:])
         args = cmd.Flag.Args()
         cmd.Run(cfg, cmd, args)
         exit()
         return
      }
   }

   fmt.Fprintf(os.Stderr, "dm: unknown subcommand %q\n", args[0])
   setExitStatus(2)
   exit()
}

var atexitFuncs []func()

func atexit(f func()) {
   atexitFuncs = append(atexitFuncs, f)
}

func exit() {
   for _, f := range atexitFuncs {
      f()
   }
   os.Exit(exitStatus)
}

func userDir(user string) (string, error) {
   wrk, err := workDir()
   if err != nil {
      return "", err
   }
   return path.Join(wrk, user), nil
}

func makeUserDir(user string) {
   usr, err := userDir(user)
   if err != nil {
      fatalf("%s [dm.go]", err)
   }
   exists, err := isDir(usr)
   if err != nil {
      fatalf("%s [dm.go]", err)
   }
   if exists == false {
      err = os.MkdirAll(usr, 0700)
      if err != nil {
         fatalf("%s [dm.go]", err)
      }
   }
}

func homeDir() (string, error) {
   usr, err := user.Current()
   if err != nil {
      return "", err
   }
   return  usr.HomeDir, nil
}

func workDir() (string, error) {
   home, err := homeDir()
   if err != nil {
      return "", err
   }
   return path.Join(home, ".dailymile_cli"), nil
}

func makeWorkDir() {
   wrkdir, err := workDir()
   if err != nil {
      fatalf("%s [dm.go]", err)
   }

   exists, err := isDir(wrkdir)
   if err != nil {
      fatalf("%s [dm.go]", err)
   }

   if exists == false {
      err = os.MkdirAll(wrkdir, 0700)
      if err != nil {
         fatalf("%s [dm.go]", err)
      }
   }
}

func configFile() (string, error) {
   wrkdir, err := workDir()
   if err != nil {
      return "", err
   }
   cfgfile := path.Join(wrkdir, "config")
   return cfgfile, nil
}

func isDir(path string) (bool, error) {
   fi, err := os.Stat(path)
   if err == nil {
      return fi.IsDir(), nil
   } else if os.IsNotExist(err) {
      return false, nil
   }
   return false, err
}

func isFile(path string) (bool, error) {
   fi, err := os.Stat(path)
   if err == nil {
      return !fi.IsDir(), nil
   } else if os.IsNotExist(err) {
      return false, nil
   }
   return false, err
}

func fatalf(format string, args ...interface{}) {
   errorf(format, args...)
   exit()
}

func errorf(format string, args ...interface{}) {
   log.Printf(format, args...)
   setExitStatus(1)
}

var logf = log.Printf

func usage() {
   fmt.Fprintf(os.Stderr, "dm is a command line tool for Dailymile.com\n\n  usage: dm command [args]\n")
   os.Exit(2)
}
<|endoftext|>


<commit_before>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package install

import (
	"archive/zip"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"testing"

	"github.com/getgauge/gauge/util"
	"github.com/getgauge/gauge/version"
	. "gopkg.in/check.v1"
)

func Test(t *testing.T) { TestingT(t) }

type MySuite struct{}

var _ = Suite(&MySuite{})

func (s *MySuite) TestFindVersion(c *C) {
	installDescription := createInstallDescriptionWithVersions("0.0.4", "0.6.7", "0.7.4", "3.6.5")
	versionInstall, err := installDescription.getVersion("0.7.4")
	c.Assert(err, Equals, nil)
	c.Assert(versionInstall.Version, Equals, "0.7.4")
}

func (s *MySuite) TestFindVersionFailing(c *C) {
	installDescription := createInstallDescriptionWithVersions("0.0.4", "0.6.7", "0.7.4", "3.6.5")
	_, err := installDescription.getVersion("0.9.4")
	c.Assert(err, NotNil)
}

func (s *MySuite) TestSortingVersionInstallDescriptionsInDecreasingVersionOrder(c *C) {
	installDescription := createInstallDescriptionWithVersions("5.8.8", "1.7.8", "4.8.9", "0.7.6", "3.5.6")
	installDescription.sortVersionInstallDescriptions()
	c.Assert(installDescription.Versions[0].Version, Equals, "5.8.8")
	c.Assert(installDescription.Versions[1].Version, Equals, "4.8.9")
	c.Assert(installDescription.Versions[2].Version, Equals, "3.5.6")
	c.Assert(installDescription.Versions[3].Version, Equals, "1.7.8")
	c.Assert(installDescription.Versions[4].Version, Equals, "0.7.6")
}

func (s *MySuite) TestFindingLatestCompatibleVersionSuccess(c *C) {
	installDescription := createInstallDescriptionWithVersions("5.8.8", "1.7.8", "4.8.9", "0.7.6")
	addVersionSupportToInstallDescription(installDescription,
		&version.VersionSupport{"0.0.2", "0.8.7"},
		&version.VersionSupport{"1.2.4", "1.2.6"},
		&version.VersionSupport{"0.9.8", "1.2.1"},
		&version.VersionSupport{Minimum: "0.7.7"})
	versionInstallDesc, err := installDescription.getLatestCompatibleVersionTo(&version.Version{1, 0, 0})
	c.Assert(err, Equals, nil)
	c.Assert(versionInstallDesc.Version, Equals, "4.8.9")
}

func (s *MySuite) TestFindingLatestCompatibleVersionFailing(c *C) {
	installDescription := createInstallDescriptionWithVersions("2.8.8", "0.7.8", "4.8.9", "1.7.6")
	addVersionSupportToInstallDescription(installDescription,
		&version.VersionSupport{"0.0.2", "0.8.7"},
		&version.VersionSupport{"1.2.4", "1.2.6"},
		&version.VersionSupport{"0.9.8", "1.0.0"},
		&version.VersionSupport{Minimum: "1.7.7"})
	_, err := installDescription.getLatestCompatibleVersionTo(&version.Version{1, 1, 0})
	c.Assert(err, NotNil)
}

func createInstallDescriptionWithVersions(versionNumbers ...string) *installDescription {
	var versionInstallDescriptions []versionInstallDescription
	for _, version := range versionNumbers {
		versionInstallDescriptions = append(versionInstallDescriptions, versionInstallDescription{Version: version})
	}
	return &installDescription{Name: "my-plugin", Versions: versionInstallDescriptions}
}

func addVersionSupportToInstallDescription(installDescription *installDescription, versionSupportList ...*version.VersionSupport) {
	for i := range installDescription.Versions {
		installDescription.Versions[i].GaugeVersionSupport = *versionSupportList[i]
	}
}

//func (s *MySuite) TestInstallGaugePluginFromNonExistingZipFile(c *C) {
//	result := InstallPluginFromZipFile(filepath.Join("test_resources", "notPresent.zip"), "ruby")
//	c.Assert(result.Error.Error(), Equals, fmt.Sprintf("ZipFile %s does not exist", filepath.Join("test_resources", "notPresent.zip")))
//}
//
func (s *MySuite) TestReturnsErrorInsteadOfPanicDuringZipFileInstall(c *C) {

	sourceFileName := filepath.Join("_testdata", "plugin.json")
	targetPathSingleDot := filepath.Join("_testdata", "zip_with_single_dot.zip")
	targetPathMultipleDots := filepath.Join("_testdata", "zip_with_multiple-dot.s.zip")
	err := createZipFromFile(sourceFileName, targetPathSingleDot)
	c.Assert(err, IsNil)
	defer os.Remove(targetPathSingleDot)

	err = createZipFromFile(sourceFileName, targetPathMultipleDots)
	c.Assert(err, IsNil)
	defer os.Remove(targetPathMultipleDots)

	result := InstallPluginFromZipFile(targetPathSingleDot, "ruby")
	c.Assert(result.Error.Error(), Equals, fmt.Sprintf("provided zip file is not a valid plugin of ruby"))

	result = InstallPluginFromZipFile(targetPathMultipleDots, "ruby")
	c.Assert(result.Error.Error(), Equals, fmt.Sprintf("provided plugin is not compatible with OS linux amd64"))
}

func (s *MySuite) TestGetVersionedPluginDirName(c *C) {
	name := getVersionedPluginDirName("abcd/foo/bar/html-report-2.0.1.nightly-2016-02-09-darwin.x86.zip")
	c.Assert(name, Equals, "2.0.1.nightly-2016-02-09")

	name = getVersionedPluginDirName("abcd/foo/bar/xml-report-2.0.1.nightly-2016-02-09-darwin.x86.zip")
	c.Assert(name, Equals, "2.0.1.nightly-2016-02-09")

	name = getVersionedPluginDirName("abcd/foo/bar/html-report-0.3.4-windows.x86_64.zip")
	c.Assert(name, Equals, "0.3.4")

	name = getVersionedPluginDirName("abcd/foo/bar/gauge-java-0.3.4.nightly-2016-02-09-linux.x86.zip")
	c.Assert(name, Equals, "0.3.4.nightly-2016-02-09")

	name = getVersionedPluginDirName("abcd/foo/bar/gauge-java-0.3.4-linux.x86_64.zip")
	c.Assert(name, Equals, "0.3.4")

	name = getVersionedPluginDirName("abcd/foo/gauge-ruby-0.1.2.nightly-2016-02-09-linux.x86.zip")
	c.Assert(name, Equals, "0.1.2.nightly-2016-02-09")

	if util.IsWindows() {
		name = getVersionedPluginDirName("C:\\Users\\apoorvam\\AppData\\Local\\Temp\\gauge_temp1456130044460213700\\gauge-java-0.3.4-windows.x86_64.zip")
		c.Assert(name, Equals, "0.3.4")
	}
}

func (s *MySuite) TestGetGaugePluginForJava(c *C) {
	path, _ := filepath.Abs(filepath.Join("_testdata", "java"))
	p, err := parsePluginJSON(path, "java")
	c.Assert(err, Equals, nil)
	c.Assert(p.ID, Equals, "java")
	c.Assert(p.Version, Equals, "0.3.4")
	c.Assert(p.Description, Equals, "Java support for gauge")
	c.Assert(p.PreInstall.Darwin[0], Equals, "pre install command")
	c.Assert(p.PreUnInstall.Darwin[0], Equals, "pre uninstall command")
	c.Assert(p.GaugeVersionSupport.Minimum, Equals, "0.3.0")
	c.Assert(p.GaugeVersionSupport.Maximum, Equals, "")
}

func (s *MySuite) TestGetGaugePluginForReportPlugin(c *C) {
	path, _ := filepath.Abs("_testdata")
	p, err := parsePluginJSON(path, "html-report")
	c.Assert(err, Equals, nil)
	c.Assert(p.ID, Equals, "html-report")
	c.Assert(p.Version, Equals, "2.0.1")
	c.Assert(p.Description, Equals, "Html reporting plugin")
	c.Assert(p.PreInstall.Darwin[0], Equals, "pre install command")
	c.Assert(p.PreUnInstall.Darwin[0], Equals, "pre uninstall command")
	c.Assert(p.GaugeVersionSupport.Minimum, Equals, "0.3.0")
	c.Assert(p.GaugeVersionSupport.Maximum, Equals, "")
}

func (s *MySuite) TestMatchesUninstallVersionIfUninstallPluginVersionIsNotGiven(c *C) {
	dirPath := "somepath"
	uninstallVersion := ""

	c.Assert(matchesUninstallVersion(dirPath, uninstallVersion), Equals, true)
}

func (s *MySuite) TestMatchesUninstallVersionIfUninstallPluginVersionMatches(c *C) {
	dirPath := "0.1.1-nightly-2016-05-05"
	uninstallVersion := "0.1.1-nightly-2016-05-05"

	c.Assert(matchesUninstallVersion(dirPath, uninstallVersion), Equals, true)
}

func (s *MySuite) TestMatchesUninstallVersionIfUninstallPluginVersionDoesntMatches(c *C) {
	dirPath := "0.1.1"
	uninstallVersion := "0.1.1-nightly-2016-05-05"

	c.Assert(matchesUninstallVersion(dirPath, uninstallVersion), Equals, false)
}

func (s *MySuite) TestIsPlatformIndependentZipFile(c *C) {
	javaReleased := "java-3.1.0.nightly-2017-02-08-darwin.x86_64.zip"
	csharpReleased := "gauge-csharp-0.10.1.zip"
	javaNightly := "gauge-java-3.1.0.nightly-2017-02-08-darwin.x86_64.zip"
	csharpNightly := "gauge-csharp-0.10.1.nightly-2017-02-17.zip"

	c.Assert(isPlatformIndependent(javaReleased), Equals, false)
	c.Assert(isPlatformIndependent(csharpReleased), Equals, true)
	c.Assert(isPlatformIndependent(javaNightly), Equals, false)
	c.Assert(isPlatformIndependent(csharpNightly), Equals, true)
}

func createZipFromFile(source, target string) error {
	zipfile, err := os.Create(target)
	if err != nil {
		return err
	}
	defer zipfile.Close()

	archive := zip.NewWriter(zipfile)
	defer archive.Close()

	info, err := os.Stat(source)
	if err != nil {
		return err
	}

	if info.IsDir() {
		return errors.New("file expected, directory found")
	}

	file, err := os.OpenFile(source, os.O_RDONLY, 0644)
	if err != nil {
		return err
	}
	defer file.Close()

	header, err := zip.FileInfoHeader(info)
	writer, err := archive.CreateHeader(header)
	if err != nil {
		return err
	}
	if err != nil {
		return err
	}

	_, err = io.Copy(writer, file)
	return err
}
<commit_msg>Avoid panic during plugin installation via zip file (#1487)<commit_after>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package install

import (
	"archive/zip"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"testing"

	"github.com/getgauge/gauge/util"
	"github.com/getgauge/gauge/version"
	. "gopkg.in/check.v1"
)

func Test(t *testing.T) { TestingT(t) }

type MySuite struct{}

var _ = Suite(&MySuite{})

func (s *MySuite) TestFindVersion(c *C) {
	installDescription := createInstallDescriptionWithVersions("0.0.4", "0.6.7", "0.7.4", "3.6.5")
	versionInstall, err := installDescription.getVersion("0.7.4")
	c.Assert(err, Equals, nil)
	c.Assert(versionInstall.Version, Equals, "0.7.4")
}

func (s *MySuite) TestFindVersionFailing(c *C) {
	installDescription := createInstallDescriptionWithVersions("0.0.4", "0.6.7", "0.7.4", "3.6.5")
	_, err := installDescription.getVersion("0.9.4")
	c.Assert(err, NotNil)
}

func (s *MySuite) TestSortingVersionInstallDescriptionsInDecreasingVersionOrder(c *C) {
	installDescription := createInstallDescriptionWithVersions("5.8.8", "1.7.8", "4.8.9", "0.7.6", "3.5.6")
	installDescription.sortVersionInstallDescriptions()
	c.Assert(installDescription.Versions[0].Version, Equals, "5.8.8")
	c.Assert(installDescription.Versions[1].Version, Equals, "4.8.9")
	c.Assert(installDescription.Versions[2].Version, Equals, "3.5.6")
	c.Assert(installDescription.Versions[3].Version, Equals, "1.7.8")
	c.Assert(installDescription.Versions[4].Version, Equals, "0.7.6")
}

func (s *MySuite) TestFindingLatestCompatibleVersionSuccess(c *C) {
	installDescription := createInstallDescriptionWithVersions("5.8.8", "1.7.8", "4.8.9", "0.7.6")
	addVersionSupportToInstallDescription(installDescription,
		&version.VersionSupport{"0.0.2", "0.8.7"},
		&version.VersionSupport{"1.2.4", "1.2.6"},
		&version.VersionSupport{"0.9.8", "1.2.1"},
		&version.VersionSupport{Minimum: "0.7.7"})
	versionInstallDesc, err := installDescription.getLatestCompatibleVersionTo(&version.Version{1, 0, 0})
	c.Assert(err, Equals, nil)
	c.Assert(versionInstallDesc.Version, Equals, "4.8.9")
}

func (s *MySuite) TestFindingLatestCompatibleVersionFailing(c *C) {
	installDescription := createInstallDescriptionWithVersions("2.8.8", "0.7.8", "4.8.9", "1.7.6")
	addVersionSupportToInstallDescription(installDescription,
		&version.VersionSupport{"0.0.2", "0.8.7"},
		&version.VersionSupport{"1.2.4", "1.2.6"},
		&version.VersionSupport{"0.9.8", "1.0.0"},
		&version.VersionSupport{Minimum: "1.7.7"})
	_, err := installDescription.getLatestCompatibleVersionTo(&version.Version{1, 1, 0})
	c.Assert(err, NotNil)
}

func createInstallDescriptionWithVersions(versionNumbers ...string) *installDescription {
	var versionInstallDescriptions []versionInstallDescription
	for _, version := range versionNumbers {
		versionInstallDescriptions = append(versionInstallDescriptions, versionInstallDescription{Version: version})
	}
	return &installDescription{Name: "my-plugin", Versions: versionInstallDescriptions}
}

func addVersionSupportToInstallDescription(installDescription *installDescription, versionSupportList ...*version.VersionSupport) {
	for i := range installDescription.Versions {
		installDescription.Versions[i].GaugeVersionSupport = *versionSupportList[i]
	}
}

func (s *MySuite) TestReturnsErrorInsteadOfPanicDuringZipFileInstall(c *C) {

	sourceFileName := filepath.Join("_testdata", "plugin.json")
	targetPathSingleDot := filepath.Join("_testdata", "zip_with_single_dot.zip")
	targetPathMultipleDots := filepath.Join("_testdata", "zip_with_multiple-dot.s.zip")
	err := createZipFromFile(sourceFileName, targetPathSingleDot)
	c.Assert(err, IsNil)
	defer os.Remove(targetPathSingleDot)

	err = createZipFromFile(sourceFileName, targetPathMultipleDots)
	c.Assert(err, IsNil)
	defer os.Remove(targetPathMultipleDots)

	result := InstallPluginFromZipFile(targetPathSingleDot, "ruby")
	c.Assert(result.Error.Error(), Equals, fmt.Sprintf("provided zip file is not a valid plugin of ruby"))

	result = InstallPluginFromZipFile(targetPathMultipleDots, "ruby")
	c.Assert(result.Error.Error(), Equals, fmt.Sprintf("provided plugin is not compatible with OS linux amd64"))
}

func (s *MySuite) TestGetVersionedPluginDirName(c *C) {
	name := getVersionedPluginDirName("abcd/foo/bar/html-report-2.0.1.nightly-2016-02-09-darwin.x86.zip")
	c.Assert(name, Equals, "2.0.1.nightly-2016-02-09")

	name = getVersionedPluginDirName("abcd/foo/bar/xml-report-2.0.1.nightly-2016-02-09-darwin.x86.zip")
	c.Assert(name, Equals, "2.0.1.nightly-2016-02-09")

	name = getVersionedPluginDirName("abcd/foo/bar/html-report-0.3.4-windows.x86_64.zip")
	c.Assert(name, Equals, "0.3.4")

	name = getVersionedPluginDirName("abcd/foo/bar/gauge-java-0.3.4.nightly-2016-02-09-linux.x86.zip")
	c.Assert(name, Equals, "0.3.4.nightly-2016-02-09")

	name = getVersionedPluginDirName("abcd/foo/bar/gauge-java-0.3.4-linux.x86_64.zip")
	c.Assert(name, Equals, "0.3.4")

	name = getVersionedPluginDirName("abcd/foo/gauge-ruby-0.1.2.nightly-2016-02-09-linux.x86.zip")
	c.Assert(name, Equals, "0.1.2.nightly-2016-02-09")

	if util.IsWindows() {
		name = getVersionedPluginDirName("C:\\Users\\apoorvam\\AppData\\Local\\Temp\\gauge_temp1456130044460213700\\gauge-java-0.3.4-windows.x86_64.zip")
		c.Assert(name, Equals, "0.3.4")
	}
}

func (s *MySuite) TestGetGaugePluginForJava(c *C) {
	path, _ := filepath.Abs(filepath.Join("_testdata", "java"))
	p, err := parsePluginJSON(path, "java")
	c.Assert(err, Equals, nil)
	c.Assert(p.ID, Equals, "java")
	c.Assert(p.Version, Equals, "0.3.4")
	c.Assert(p.Description, Equals, "Java support for gauge")
	c.Assert(p.PreInstall.Darwin[0], Equals, "pre install command")
	c.Assert(p.PreUnInstall.Darwin[0], Equals, "pre uninstall command")
	c.Assert(p.GaugeVersionSupport.Minimum, Equals, "0.3.0")
	c.Assert(p.GaugeVersionSupport.Maximum, Equals, "")
}

func (s *MySuite) TestGetGaugePluginForReportPlugin(c *C) {
	path, _ := filepath.Abs("_testdata")
	p, err := parsePluginJSON(path, "html-report")
	c.Assert(err, Equals, nil)
	c.Assert(p.ID, Equals, "html-report")
	c.Assert(p.Version, Equals, "2.0.1")
	c.Assert(p.Description, Equals, "Html reporting plugin")
	c.Assert(p.PreInstall.Darwin[0], Equals, "pre install command")
	c.Assert(p.PreUnInstall.Darwin[0], Equals, "pre uninstall command")
	c.Assert(p.GaugeVersionSupport.Minimum, Equals, "0.3.0")
	c.Assert(p.GaugeVersionSupport.Maximum, Equals, "")
}

func (s *MySuite) TestMatchesUninstallVersionIfUninstallPluginVersionIsNotGiven(c *C) {
	dirPath := "somepath"
	uninstallVersion := ""

	c.Assert(matchesUninstallVersion(dirPath, uninstallVersion), Equals, true)
}

func (s *MySuite) TestMatchesUninstallVersionIfUninstallPluginVersionMatches(c *C) {
	dirPath := "0.1.1-nightly-2016-05-05"
	uninstallVersion := "0.1.1-nightly-2016-05-05"

	c.Assert(matchesUninstallVersion(dirPath, uninstallVersion), Equals, true)
}

func (s *MySuite) TestMatchesUninstallVersionIfUninstallPluginVersionDoesntMatches(c *C) {
	dirPath := "0.1.1"
	uninstallVersion := "0.1.1-nightly-2016-05-05"

	c.Assert(matchesUninstallVersion(dirPath, uninstallVersion), Equals, false)
}

func (s *MySuite) TestIsPlatformIndependentZipFile(c *C) {
	javaReleased := "java-3.1.0.nightly-2017-02-08-darwin.x86_64.zip"
	csharpReleased := "gauge-csharp-0.10.1.zip"
	javaNightly := "gauge-java-3.1.0.nightly-2017-02-08-darwin.x86_64.zip"
	csharpNightly := "gauge-csharp-0.10.1.nightly-2017-02-17.zip"

	c.Assert(isPlatformIndependent(javaReleased), Equals, false)
	c.Assert(isPlatformIndependent(csharpReleased), Equals, true)
	c.Assert(isPlatformIndependent(javaNightly), Equals, false)
	c.Assert(isPlatformIndependent(csharpNightly), Equals, true)
}

func createZipFromFile(source, target string) error {
	zipfile, err := os.Create(target)
	if err != nil {
		return err
	}
	defer zipfile.Close()

	archive := zip.NewWriter(zipfile)
	defer archive.Close()

	info, err := os.Stat(source)
	if err != nil {
		return err
	}

	if info.IsDir() {
		return errors.New("file expected, directory found")
	}

	file, err := os.OpenFile(source, os.O_RDONLY, 0644)
	if err != nil {
		return err
	}
	defer file.Close()

	header, err := zip.FileInfoHeader(info)
	writer, err := archive.CreateHeader(header)
	if err != nil {
		return err
	}
	if err != nil {
		return err
	}

	_, err = io.Copy(writer, file)
	return err
}
<|endoftext|>


<commit_before>package birthdays

import (
	"log"
	"math/rand"
	"regexp"
	"strconv"
	"time"

	"github.com/asdine/storm"
	"github.com/asdine/storm/q"
	"github.com/bearbin/go-age"
	"github.com/tucnak/telebot"

	"github.com/focusshifter/muxgoob/registry"
)

type BirthdaysPlugin struct {
}

type BirthdayNotify struct {
	ID       int    `storm:"id,increment"`
	Username string `storm:"index"`
	Year     int
}

var db *storm.DB
var rng *rand.Rand
var birthdays map[string]time.Time

func init() {
	registry.RegisterPlugin(&BirthdaysPlugin{})
}

func (p *BirthdaysPlugin) Start(sharedDb *storm.DB) {
	db = sharedDb
	rng = rand.New(rand.NewSource(time.Now().UnixNano()))

	birthdays = make(map[string]time.Time)

	loc := registry.Config.TimeLoc

	for username, birthday := range registry.Config.Birthdays {
		t, _ := time.ParseInLocation("2006-01-02", birthday, loc)
		birthdays[username] = t
	}
}

func (p *BirthdaysPlugin) Process(message *telebot.Message) {
	todaysBirthday(message)
	nextBirthday(message)
}

func nextBirthday(message *telebot.Message) {
	bot := registry.Bot
	loc := registry.Config.TimeLoc

	birthdayExp := regexp.MustCompile(`(?i)^\!(др|birthda(y|ys))$`)

	switch {
	case birthdayExp.MatchString(message.Text):
		cur := time.Now().In(loc)
		curDay := cur.YearDay()

		diff := 365
		curDiff := 365
		curBirthday := ""
		curUsername := ""

		for username, birthday := range birthdays {
			diff = birthday.YearDay() - curDay
			if diff > 0 {
				if diff == curDiff {
					curUsername += ", @" + username
				} else if diff < curDiff {
					curDiff = diff
					curUsername = username
					curBirthday = birthday.Format("02.01")
				}
			}
		}

		if curUsername != "" {
			bot.Send(message.Chat, "Prepare the 🎂 for @"+curUsername+" on "+curBirthday, &telebot.SendOptions{})
		} else {
			bot.Send(message.Chat, "No upcoming birthdays", &telebot.SendOptions{})
		}
	}
}

func todaysBirthday(message *telebot.Message) {
	bot := registry.Bot
	loc := registry.Config.TimeLoc

	cur := time.Now().In(loc)

	for username, birthday := range birthdays {
		if birthday.YearDay() == cur.YearDay() && notMentioned(username, cur.Year(), message) {
			age := strconv.Itoa(age.AgeAt(birthday, cur))
			bot.Send(message.Chat, "Hooray! 🎉 @"+username+" is turning "+age+"! 🎂", &telebot.SendOptions{})
		}
	}
}

func notMentioned(username string, year int, message *telebot.Message) bool {
	chat := db.From(strconv.FormatInt(message.Chat.ID, 10))

	count, _ := chat.Select(q.And(q.Eq("Username", username), q.Eq("Year", year))).Count(&BirthdayNotify{})

	if count > 0 {
		return false
	}

	log.Println("Brithday: notify " + username)

	newNotify := BirthdayNotify{Username: username, Year: year}
	chat.Save(&newNotify)

	return true
}
<commit_msg>Leap years, anyone?<commit_after>package birthdays

import (
	"log"
	"math/rand"
	"regexp"
	"strconv"
	"time"

	"github.com/asdine/storm"
	"github.com/asdine/storm/q"
	"github.com/bearbin/go-age"
	"github.com/tucnak/telebot"

	"github.com/focusshifter/muxgoob/registry"
)

type BirthdaysPlugin struct {
}

type BirthdayNotify struct {
	ID       int    `storm:"id,increment"`
	Username string `storm:"index"`
	Year     int
}

var db *storm.DB
var rng *rand.Rand
var birthdays map[string]time.Time

func init() {
	registry.RegisterPlugin(&BirthdaysPlugin{})
}

func (p *BirthdaysPlugin) Start(sharedDb *storm.DB) {
	db = sharedDb
	rng = rand.New(rand.NewSource(time.Now().UnixNano()))

	birthdays = make(map[string]time.Time)

	loc := registry.Config.TimeLoc

	for username, birthday := range registry.Config.Birthdays {
		t, _ := time.ParseInLocation("2006-01-02", birthday, loc)
		birthdays[username] = t
	}
}

func (p *BirthdaysPlugin) Process(message *telebot.Message) {
	todaysBirthday(message)
	nextBirthday(message)
}

func nextBirthday(message *telebot.Message) {
	bot := registry.Bot
	loc := registry.Config.TimeLoc

	birthdayExp := regexp.MustCompile(`(?i)^\!(др|birthda(y|ys))$`)

	switch {
	case birthdayExp.MatchString(message.Text):
		cur := time.Now().In(loc)
		curDay := cur.YearDay()

		diff := time.Date(cur.Year(), time.December, 31, 0, 0, 0, 0, time.Local).YearDay()
		curDiff := diff
		curBirthday := ""
		curUsername := ""

		for username, birthday := range birthdays {
			diff = birthday.YearDay() - curDay
			if diff > 0 {
				if diff == curDiff {
					curUsername += ", @" + username
				} else if diff < curDiff {
					curDiff = diff
					curUsername = username
					curBirthday = birthday.Format("02.01")
				}
			}
		}

		if curUsername != "" {
			bot.Send(message.Chat, "Prepare the 🎂 for @"+curUsername+" on "+curBirthday, &telebot.SendOptions{})
		} else {
			bot.Send(message.Chat, "No upcoming birthdays", &telebot.SendOptions{})
		}
	}
}

func todaysBirthday(message *telebot.Message) {
	bot := registry.Bot
	loc := registry.Config.TimeLoc

	cur := time.Now().In(loc)

	for username, birthday := range birthdays {
		if birthday.YearDay() == cur.YearDay() && notMentioned(username, cur.Year(), message) {
			age := strconv.Itoa(age.AgeAt(birthday, cur))
			bot.Send(message.Chat, "Hooray! 🎉 @"+username+" is turning "+age+"! 🎂", &telebot.SendOptions{})
		}
	}
}

func notMentioned(username string, year int, message *telebot.Message) bool {
	chat := db.From(strconv.FormatInt(message.Chat.ID, 10))

	count, _ := chat.Select(q.And(q.Eq("Username", username), q.Eq("Year", year))).Count(&BirthdayNotify{})

	if count > 0 {
		return false
	}

	log.Println("Brithday: notify " + username)

	newNotify := BirthdayNotify{Username: username, Year: year}
	chat.Save(&newNotify)

	return true
}
<|endoftext|>


<commit_before>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package platformvm

import (
	"errors"
	"fmt"
	"time"

	"github.com/ava-labs/avalanchego/database"
	"github.com/ava-labs/avalanchego/database/versiondb"
	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/snow"
	"github.com/ava-labs/avalanchego/utils/codec"
	"github.com/ava-labs/avalanchego/utils/constants"
	"github.com/ava-labs/avalanchego/utils/crypto"
	"github.com/ava-labs/avalanchego/vms/components/avax"
	"github.com/ava-labs/avalanchego/vms/components/verify"
	"github.com/ava-labs/avalanchego/vms/secp256k1fx"

	safemath "github.com/ava-labs/avalanchego/utils/math"
)

var (
	errNilTx                     = errors.New("tx is nil")
	errWeightTooSmall            = errors.New("weight of this validator is too low")
	errWeightTooLarge            = errors.New("weight of this validator is too large")
	errStakeTooShort             = errors.New("staking period is too short")
	errStakeTooLong              = errors.New("staking period is too long")
	errInsufficientDelegationFee = errors.New("staker charges an insufficient delegation fee")
	errTooManyShares             = fmt.Errorf("a staker can only require at most %d shares from delegators", PercentDenominator)

	_ UnsignedProposalTx = &UnsignedAddValidatorTx{}
	_ TimedTx            = &UnsignedAddValidatorTx{}
)

// UnsignedAddValidatorTx is an unsigned addValidatorTx
type UnsignedAddValidatorTx struct {
	// Metadata, inputs and outputs
	BaseTx `serialize:"true"`
	// Describes the delegatee
	Validator Validator `serialize:"true" json:"validator"`
	// Where to send staked tokens when done validating
	Stake []*avax.TransferableOutput `serialize:"true" json:"stake"`
	// Where to send staking rewards when done validating
	RewardsOwner verify.Verifiable `serialize:"true" json:"rewardsOwner"`
	// Fee this validator charges delegators as a percentage, times 10,000
	// For example, if this validator has Shares=300,000 then they take 30% of rewards from delegators
	Shares uint32 `serialize:"true" json:"shares"`
}

// StartTime of this validator
func (tx *UnsignedAddValidatorTx) StartTime() time.Time {
	return tx.Validator.StartTime()
}

// EndTime of this validator
func (tx *UnsignedAddValidatorTx) EndTime() time.Time {
	return tx.Validator.EndTime()
}

// Weight of this validator
func (tx *UnsignedAddValidatorTx) Weight() uint64 {
	return tx.Validator.Weight()
}

// Verify return nil iff [tx] is valid
func (tx *UnsignedAddValidatorTx) Verify(
	ctx *snow.Context,
	c codec.Codec,
	minStake uint64,
	maxStake uint64,
	minStakeDuration time.Duration,
	maxStakeDuration time.Duration,
	minDelegationFee uint32,
) error {
	switch {
	case tx == nil:
		return errNilTx
	case tx.syntacticallyVerified: // already passed syntactic verification
		return nil
	case tx.Validator.Wght < minStake: // Ensure validator is staking at least the minimum amount
		return errWeightTooSmall
	case tx.Validator.Wght > maxStake: // Ensure validator isn't staking too much
		return errWeightTooLarge
	case tx.Shares > PercentDenominator: // Ensure delegators shares are in the allowed amount
		return errTooManyShares
	case tx.Shares < minDelegationFee:
		return errInsufficientDelegationFee
	}

	duration := tx.Validator.Duration()
	switch {
	case duration < minStakeDuration: // Ensure staking length is not too short
		return errStakeTooShort
	case duration > maxStakeDuration: // Ensure staking length is not too long
		return errStakeTooLong
	}

	if err := tx.BaseTx.Verify(ctx, c); err != nil {
		return fmt.Errorf("failed to verify BaseTx: %w", err)
	}
	if err := verify.All(&tx.Validator, tx.RewardsOwner); err != nil {
		return fmt.Errorf("failed to verify validator or rewards owner: %w", err)
	}

	totalStakeWeight := uint64(0)
	for _, out := range tx.Stake {
		if err := out.Verify(); err != nil {
			return fmt.Errorf("failed to verify output: %w", err)
		}
		newWeight, err := safemath.Add64(totalStakeWeight, out.Output().Amount())
		if err != nil {
			return err
		}
		totalStakeWeight = newWeight
	}

	switch {
	case !avax.IsSortedTransferableOutputs(tx.Stake, Codec):
		return errOutputsNotSorted
	case totalStakeWeight != tx.Validator.Wght:
		return errInvalidAmount
	}

	// cache that this is valid
	tx.syntacticallyVerified = true
	return nil
}

// SemanticVerify this transaction is valid.
func (tx *UnsignedAddValidatorTx) SemanticVerify(
	vm *VM,
	db database.Database,
	stx *Tx,
) (
	*versiondb.Database,
	*versiondb.Database,
	func() error,
	func() error,
	TxError,
) {
	// Verify the tx is well-formed
	if err := tx.Verify(
		vm.Ctx,
		vm.codec,
		vm.minValidatorStake,
		vm.maxValidatorStake,
		vm.minStakeDuration,
		vm.maxStakeDuration,
		vm.minDelegationFee,
	); err != nil {
		return nil, nil, nil, nil, permError{err}
	}

	// Ensure the proposed validator starts after the current time
	if currentTime, err := vm.getTimestamp(db); err != nil {
		return nil, nil, nil, nil, tempError{
			fmt.Errorf("failed to get timestamp: %w", err),
		}
	} else if startTime := tx.StartTime(); !currentTime.Before(startTime) {
		return nil, nil, nil, nil, permError{
			fmt.Errorf("validator's start time (%s) at or before current timestamp (%s)",
				startTime,
				currentTime,
			),
		}
	} else if startTime.After(currentTime.Add(maxFutureStartTime)) {
		return nil, nil, nil, nil, permError{
			fmt.Errorf(
				"validator start time (%s) more than two weeks after current chain timestamp (%s)",
				startTime,
				currentTime,
			),
		}
	}

	_, isValidator, err := vm.isValidator(db, constants.PrimaryNetworkID, tx.Validator.NodeID)
	if err != nil {
		return nil, nil, nil, nil, tempError{
			fmt.Errorf(
				"failed to get whether %s is a validator: %w",
				tx.Validator.NodeID,
				err,
			),
		}
	}
	if isValidator {
		return nil, nil, nil, nil, permError{
			fmt.Errorf(
				"validator %s already is already a primary network validator",
				tx.Validator.NodeID,
			),
		}
	}

	// Ensure that the period this validator validates the specified subnet
	// is a subnet of the time they will validate the primary network.
	_, willBeValidator, err := vm.willBeValidator(db, constants.PrimaryNetworkID, tx.Validator.NodeID)
	if err != nil {
		return nil, nil, nil, nil, tempError{
			fmt.Errorf(
				"failed to get whether %s will be a validator: %w",
				tx.Validator.NodeID,
				err,
			),
		}
	}
	if willBeValidator {
		return nil, nil, nil, nil, permError{
			fmt.Errorf(
				"validator %s already is already a primary network validator",
				tx.Validator.NodeID,
			),
		}
	}

	outs := make([]*avax.TransferableOutput, len(tx.Outs)+len(tx.Stake))
	copy(outs, tx.Outs)
	copy(outs[len(tx.Outs):], tx.Stake)

	// Verify the flowcheck
	if err := vm.semanticVerifySpend(db, tx, tx.Ins, outs, stx.Creds, 0, vm.Ctx.AVAXAssetID); err != nil {
		switch err.(type) {
		case permError:
			return nil, nil, nil, nil, permError{
				fmt.Errorf("failed semanticVerifySpend: %w", err),
			}
		default:
			return nil, nil, nil, nil, tempError{
				fmt.Errorf("failed semanticVerifySpend: %w", err),
			}
		}
	}

	txID := tx.ID()

	// Verify inputs/outputs and update the UTXO set
	onCommitDB := versiondb.New(db)
	// Consume the UTXOS
	if err := vm.consumeInputs(onCommitDB, tx.Ins); err != nil {
		return nil, nil, nil, nil, tempError{
			fmt.Errorf("failed to consume inputs: %w", err),
		}
	}
	// Produce the UTXOS
	if err := vm.produceOutputs(onCommitDB, txID, tx.Outs); err != nil {
		return nil, nil, nil, nil, tempError{
			fmt.Errorf("failed to produce outputs: %w", err),
		}
	}

	// Add validator to set of pending validators
	if err := vm.enqueueStaker(onCommitDB, constants.PrimaryNetworkID, stx); err != nil {
		return nil, nil, nil, nil, tempError{
			fmt.Errorf("failed to enqueue staker: %w", err),
		}
	}

	onAbortDB := versiondb.New(db)
	// Consume the UTXOS
	if err := vm.consumeInputs(onAbortDB, tx.Ins); err != nil {
		return nil, nil, nil, nil, tempError{
			fmt.Errorf("failed to consume inputs: %w", err),
		}
	}
	// Produce the UTXOS
	if err := vm.produceOutputs(onAbortDB, txID, outs); err != nil {
		return nil, nil, nil, nil, tempError{
			fmt.Errorf("failed to produce outputs: %w", err),
		}
	}

	return onCommitDB, onAbortDB, nil, nil, nil
}

// InitiallyPrefersCommit returns true if the proposed validators start time is
// after the current wall clock time,
func (tx *UnsignedAddValidatorTx) InitiallyPrefersCommit(vm *VM) bool {
	return tx.StartTime().After(vm.clock.Time())
}

// NewAddValidatorTx returns a new NewAddValidatorTx
func (vm *VM) newAddValidatorTx(
	stakeAmt, // Amount the delegator stakes
	startTime, // Unix time they start delegating
	endTime uint64, // Unix time they stop delegating
	nodeID ids.ShortID, // ID of the node we are delegating to
	rewardAddress ids.ShortID, // Address to send reward to, if applicable
	shares uint32, // 10,000 times percentage of reward taken from delegators
	keys []*crypto.PrivateKeySECP256K1R, // Keys providing the staked tokens
	changeAddr ids.ShortID, // Address to send change to, if there is any
) (*Tx, error) {
	ins, unlockedOuts, lockedOuts, signers, err := vm.stake(vm.DB, keys, stakeAmt, 0, changeAddr)
	if err != nil {
		return nil, fmt.Errorf("couldn't generate tx inputs/outputs: %w", err)
	}
	// Create the tx
	utx := &UnsignedAddValidatorTx{
		BaseTx: BaseTx{BaseTx: avax.BaseTx{
			NetworkID:    vm.Ctx.NetworkID,
			BlockchainID: vm.Ctx.ChainID,
			Ins:          ins,
			Outs:         unlockedOuts,
		}},
		Validator: Validator{
			NodeID: nodeID,
			Start:  startTime,
			End:    endTime,
			Wght:   stakeAmt,
		},
		Stake: lockedOuts,
		RewardsOwner: &secp256k1fx.OutputOwners{
			Locktime:  0,
			Threshold: 1,
			Addrs:     []ids.ShortID{rewardAddress},
		},
		Shares: shares,
	}
	tx := &Tx{UnsignedTx: utx}
	if err := tx.Sign(vm.codec, signers); err != nil {
		return nil, err
	}
	return tx, utx.Verify(
		vm.Ctx,
		vm.codec,
		vm.minValidatorStake,
		vm.maxValidatorStake,
		vm.minStakeDuration,
		vm.maxStakeDuration,
		vm.minDelegationFee,
	)
}
<commit_msg>Update verbiage.<commit_after>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package platformvm

import (
	"errors"
	"fmt"
	"time"

	"github.com/ava-labs/avalanchego/database"
	"github.com/ava-labs/avalanchego/database/versiondb"
	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/snow"
	"github.com/ava-labs/avalanchego/utils/codec"
	"github.com/ava-labs/avalanchego/utils/constants"
	"github.com/ava-labs/avalanchego/utils/crypto"
	"github.com/ava-labs/avalanchego/vms/components/avax"
	"github.com/ava-labs/avalanchego/vms/components/verify"
	"github.com/ava-labs/avalanchego/vms/secp256k1fx"

	safemath "github.com/ava-labs/avalanchego/utils/math"
)

var (
	errNilTx                     = errors.New("tx is nil")
	errWeightTooSmall            = errors.New("weight of this validator is too low")
	errWeightTooLarge            = errors.New("weight of this validator is too large")
	errStakeTooShort             = errors.New("staking period is too short")
	errStakeTooLong              = errors.New("staking period is too long")
	errInsufficientDelegationFee = errors.New("staker charges an insufficient delegation fee")
	errTooManyShares             = fmt.Errorf("a staker can only require at most %d shares from delegators", PercentDenominator)

	_ UnsignedProposalTx = &UnsignedAddValidatorTx{}
	_ TimedTx            = &UnsignedAddValidatorTx{}
)

// UnsignedAddValidatorTx is an unsigned addValidatorTx
type UnsignedAddValidatorTx struct {
	// Metadata, inputs and outputs
	BaseTx `serialize:"true"`
	// Describes the delegatee
	Validator Validator `serialize:"true" json:"validator"`
	// Where to send staked tokens when done validating
	Stake []*avax.TransferableOutput `serialize:"true" json:"stake"`
	// Where to send staking rewards when done validating
	RewardsOwner verify.Verifiable `serialize:"true" json:"rewardsOwner"`
	// Fee this validator charges delegators as a percentage, times 10,000
	// For example, if this validator has Shares=300,000 then they take 30% of rewards from delegators
	Shares uint32 `serialize:"true" json:"shares"`
}

// StartTime of this validator
func (tx *UnsignedAddValidatorTx) StartTime() time.Time {
	return tx.Validator.StartTime()
}

// EndTime of this validator
func (tx *UnsignedAddValidatorTx) EndTime() time.Time {
	return tx.Validator.EndTime()
}

// Weight of this validator
func (tx *UnsignedAddValidatorTx) Weight() uint64 {
	return tx.Validator.Weight()
}

// Verify return nil iff [tx] is valid
func (tx *UnsignedAddValidatorTx) Verify(
	ctx *snow.Context,
	c codec.Codec,
	minStake uint64,
	maxStake uint64,
	minStakeDuration time.Duration,
	maxStakeDuration time.Duration,
	minDelegationFee uint32,
) error {
	switch {
	case tx == nil:
		return errNilTx
	case tx.syntacticallyVerified: // already passed syntactic verification
		return nil
	case tx.Validator.Wght < minStake: // Ensure validator is staking at least the minimum amount
		return errWeightTooSmall
	case tx.Validator.Wght > maxStake: // Ensure validator isn't staking too much
		return errWeightTooLarge
	case tx.Shares > PercentDenominator: // Ensure delegators shares are in the allowed amount
		return errTooManyShares
	case tx.Shares < minDelegationFee:
		return errInsufficientDelegationFee
	}

	duration := tx.Validator.Duration()
	switch {
	case duration < minStakeDuration: // Ensure staking length is not too short
		return errStakeTooShort
	case duration > maxStakeDuration: // Ensure staking length is not too long
		return errStakeTooLong
	}

	if err := tx.BaseTx.Verify(ctx, c); err != nil {
		return fmt.Errorf("failed to verify BaseTx: %w", err)
	}
	if err := verify.All(&tx.Validator, tx.RewardsOwner); err != nil {
		return fmt.Errorf("failed to verify validator or rewards owner: %w", err)
	}

	totalStakeWeight := uint64(0)
	for _, out := range tx.Stake {
		if err := out.Verify(); err != nil {
			return fmt.Errorf("failed to verify output: %w", err)
		}
		newWeight, err := safemath.Add64(totalStakeWeight, out.Output().Amount())
		if err != nil {
			return err
		}
		totalStakeWeight = newWeight
	}

	switch {
	case !avax.IsSortedTransferableOutputs(tx.Stake, Codec):
		return errOutputsNotSorted
	case totalStakeWeight != tx.Validator.Wght:
		return errInvalidAmount
	}

	// cache that this is valid
	tx.syntacticallyVerified = true
	return nil
}

// SemanticVerify this transaction is valid.
func (tx *UnsignedAddValidatorTx) SemanticVerify(
	vm *VM,
	db database.Database,
	stx *Tx,
) (
	*versiondb.Database,
	*versiondb.Database,
	func() error,
	func() error,
	TxError,
) {
	// Verify the tx is well-formed
	if err := tx.Verify(
		vm.Ctx,
		vm.codec,
		vm.minValidatorStake,
		vm.maxValidatorStake,
		vm.minStakeDuration,
		vm.maxStakeDuration,
		vm.minDelegationFee,
	); err != nil {
		return nil, nil, nil, nil, permError{err}
	}

	// Ensure the proposed validator starts after the current time
	if currentTime, err := vm.getTimestamp(db); err != nil {
		return nil, nil, nil, nil, tempError{
			fmt.Errorf("failed to get timestamp: %w", err),
		}
	} else if startTime := tx.StartTime(); !currentTime.Before(startTime) {
		return nil, nil, nil, nil, permError{
			fmt.Errorf("validator's start time (%s) at or before current timestamp (%s)",
				startTime,
				currentTime,
			),
		}
	} else if startTime.After(currentTime.Add(maxFutureStartTime)) {
		return nil, nil, nil, nil, permError{
			fmt.Errorf(
				"validator start time (%s) more than two weeks after current chain timestamp (%s)",
				startTime,
				currentTime,
			),
		}
	}

	_, isValidator, err := vm.isValidator(db, constants.PrimaryNetworkID, tx.Validator.NodeID)
	if err != nil {
		return nil, nil, nil, nil, tempError{
			fmt.Errorf(
				"failed to get whether %s is a validator: %w",
				tx.Validator.NodeID,
				err,
			),
		}
	}
	if isValidator {
		return nil, nil, nil, nil, permError{
			fmt.Errorf(
				"validator %s is already a primary network validator",
				tx.Validator.NodeID,
			),
		}
	}

	// Ensure that the period this validator validates the specified subnet
	// is a subnet of the time they will validate the primary network.
	_, willBeValidator, err := vm.willBeValidator(db, constants.PrimaryNetworkID, tx.Validator.NodeID)
	if err != nil {
		return nil, nil, nil, nil, tempError{
			fmt.Errorf(
				"failed to get whether %s will be a validator: %w",
				tx.Validator.NodeID,
				err,
			),
		}
	}
	if willBeValidator {
		return nil, nil, nil, nil, permError{
			fmt.Errorf(
				"validator %s is already a primary network validator",
				tx.Validator.NodeID,
			),
		}
	}

	outs := make([]*avax.TransferableOutput, len(tx.Outs)+len(tx.Stake))
	copy(outs, tx.Outs)
	copy(outs[len(tx.Outs):], tx.Stake)

	// Verify the flowcheck
	if err := vm.semanticVerifySpend(db, tx, tx.Ins, outs, stx.Creds, 0, vm.Ctx.AVAXAssetID); err != nil {
		switch err.(type) {
		case permError:
			return nil, nil, nil, nil, permError{
				fmt.Errorf("failed semanticVerifySpend: %w", err),
			}
		default:
			return nil, nil, nil, nil, tempError{
				fmt.Errorf("failed semanticVerifySpend: %w", err),
			}
		}
	}

	txID := tx.ID()

	// Verify inputs/outputs and update the UTXO set
	onCommitDB := versiondb.New(db)
	// Consume the UTXOS
	if err := vm.consumeInputs(onCommitDB, tx.Ins); err != nil {
		return nil, nil, nil, nil, tempError{
			fmt.Errorf("failed to consume inputs: %w", err),
		}
	}
	// Produce the UTXOS
	if err := vm.produceOutputs(onCommitDB, txID, tx.Outs); err != nil {
		return nil, nil, nil, nil, tempError{
			fmt.Errorf("failed to produce outputs: %w", err),
		}
	}

	// Add validator to set of pending validators
	if err := vm.enqueueStaker(onCommitDB, constants.PrimaryNetworkID, stx); err != nil {
		return nil, nil, nil, nil, tempError{
			fmt.Errorf("failed to enqueue staker: %w", err),
		}
	}

	onAbortDB := versiondb.New(db)
	// Consume the UTXOS
	if err := vm.consumeInputs(onAbortDB, tx.Ins); err != nil {
		return nil, nil, nil, nil, tempError{
			fmt.Errorf("failed to consume inputs: %w", err),
		}
	}
	// Produce the UTXOS
	if err := vm.produceOutputs(onAbortDB, txID, outs); err != nil {
		return nil, nil, nil, nil, tempError{
			fmt.Errorf("failed to produce outputs: %w", err),
		}
	}

	return onCommitDB, onAbortDB, nil, nil, nil
}

// InitiallyPrefersCommit returns true if the proposed validators start time is
// after the current wall clock time,
func (tx *UnsignedAddValidatorTx) InitiallyPrefersCommit(vm *VM) bool {
	return tx.StartTime().After(vm.clock.Time())
}

// NewAddValidatorTx returns a new NewAddValidatorTx
func (vm *VM) newAddValidatorTx(
	stakeAmt, // Amount the delegator stakes
	startTime, // Unix time they start delegating
	endTime uint64, // Unix time they stop delegating
	nodeID ids.ShortID, // ID of the node we are delegating to
	rewardAddress ids.ShortID, // Address to send reward to, if applicable
	shares uint32, // 10,000 times percentage of reward taken from delegators
	keys []*crypto.PrivateKeySECP256K1R, // Keys providing the staked tokens
	changeAddr ids.ShortID, // Address to send change to, if there is any
) (*Tx, error) {
	ins, unlockedOuts, lockedOuts, signers, err := vm.stake(vm.DB, keys, stakeAmt, 0, changeAddr)
	if err != nil {
		return nil, fmt.Errorf("couldn't generate tx inputs/outputs: %w", err)
	}
	// Create the tx
	utx := &UnsignedAddValidatorTx{
		BaseTx: BaseTx{BaseTx: avax.BaseTx{
			NetworkID:    vm.Ctx.NetworkID,
			BlockchainID: vm.Ctx.ChainID,
			Ins:          ins,
			Outs:         unlockedOuts,
		}},
		Validator: Validator{
			NodeID: nodeID,
			Start:  startTime,
			End:    endTime,
			Wght:   stakeAmt,
		},
		Stake: lockedOuts,
		RewardsOwner: &secp256k1fx.OutputOwners{
			Locktime:  0,
			Threshold: 1,
			Addrs:     []ids.ShortID{rewardAddress},
		},
		Shares: shares,
	}
	tx := &Tx{UnsignedTx: utx}
	if err := tx.Sign(vm.codec, signers); err != nil {
		return nil, err
	}
	return tx, utx.Verify(
		vm.Ctx,
		vm.codec,
		vm.minValidatorStake,
		vm.maxValidatorStake,
		vm.minStakeDuration,
		vm.maxStakeDuration,
		vm.minDelegationFee,
	)
}
<|endoftext|>


<commit_before>package genfs

import (
	"bytes"
	"errors"
	"io"
	"net/http"
	"os"
	"path"
	"time"
)

// NewFS returns a new FS for the given files.
func NewFS(files ...*File) *FS {
	fs := &FS{
		l: make([]*File, len(files)),
		m: make(map[string]*File, len(files)),
	}
	for i, file := range files {
		// Clone file so that the same file can be used with multiple FS instances.
		clone := &File{}
		*clone = *file
		clone.fs = fs
		fs.m[file.path] = clone
		fs.l[i] = clone
	}
	return fs
}

// Dir returns a new FS for the given path.
func Dir(path string) (*FS, error) {
	files, err := Files(path, FilterNone)
	if err != nil {
		return nil, err
	}
	return NewFS(files...), nil
}

// FS is an in-memory file system that implements http.FileSystem.
type FS struct {
	m map[string]*File
	l []*File
}

// check interface compliance
var _ = http.FileSystem(&FS{})

// Open is part of http.FileSystem.
func (f *FS) Open(name string) (http.File, error) {
	name = path.Clean(name)
	file := f.m[name]
	if file == nil {
		// @TODO Path error?
		return nil, os.ErrNotExist
	}
	// Return a clone so that concurrently opened files can have their own read
	// state.
	clone := &File{}
	*clone = *file
	clone.Reader = bytes.NewReader(clone.data)
	return clone, nil
}

// NewFile returns a new in-memory file with the given properties.
func NewFile(path, name string, size int64, mode os.FileMode, t time.Time, isDir bool, data []byte) *File {
	return &File{
		path:    path,
		name:    name,
		size:    size,
		mode:    mode,
		modTime: t,
		isDir:   isDir,
		data:    data,
		Reader:  bytes.NewReader(data),
	}
}

// File is an in-memory file that implements http.File and os.FileInfo.
type File struct {
	io.Reader

	path    string
	name    string
	size    int64
	mode    os.FileMode
	modTime time.Time
	isDir   bool
	data    []byte

	fs *FS
}

// check interface compliance
var _ = os.FileInfo(&File{})
var _ = http.File(&File{})

// Close is part of http.File.
func (f *File) Close() error {
	// @TODO return err when closing twice?
	return nil
}

// Seek is part of http.File.
func (f *File) Seek(offset int64, whence int) (int64, error) {
	// @TODO(fg) implement
	return 0, nil
}

// Stat is part of http.File.
func (f *File) Stat() (os.FileInfo, error) {
	// @TODO(fg) implement
	return f, nil
}

// Readdir is part of http.File.
func (f *File) Readdir(count int) ([]os.FileInfo, error) {
	// @TODO(fg) support full Readdir semantics, including EOF, etc.
	if count != 0 {
		return nil, errors.New("only count=0 is supported right now")
	}
	var files []os.FileInfo
	// TODO(fg) make this faster using binary search or a tree structure
	for _, file := range f.fs.l {
		if path.Clean(path.Join(f.path, file.name)) == file.path {
			files = append(files, file)
		}
	}
	return files, nil
}

// IsDir is part of os.FileInfo.
func (f *File) IsDir() bool { return f.isDir }

// ModTime is part of os.FileInfo.
func (f *File) ModTime() time.Time { return f.modTime }

// Mode is part of os.FileInfo.
func (f *File) Mode() os.FileMode { return f.mode }

// Name is part of os.FileInfo.
func (f *File) Name() string { return f.name }

// Size is part of os.FileInfo.
func (f *File) Size() int64 { return f.size }

// Sys is part of os.FileInfo.
func (f *File) Sys() interface{} { return nil }

// Path returns the path of the file. It's needed for WriteSource.
func (f *File) Path() string { return f.path }

// String returns the file data as a string. It's needed for WriteSource.
func (f *File) String() string { return string(f.data) }

// MustTime parses and returns the given time as RFC3339 or panics.
func MustTime(s string) time.Time {
	t, err := time.Parse(time.RFC3339, s)
	if err != nil {
		panic(err)
	}
	return t
}
<commit_msg>support Readdir count = 0 (same as -1)<commit_after>package genfs

import (
	"bytes"
	"errors"
	"io"
	"net/http"
	"os"
	"path"
	"time"
)

// NewFS returns a new FS for the given files.
func NewFS(files ...*File) *FS {
	fs := &FS{
		l: make([]*File, len(files)),
		m: make(map[string]*File, len(files)),
	}
	for i, file := range files {
		// Clone file so that the same file can be used with multiple FS instances.
		clone := &File{}
		*clone = *file
		clone.fs = fs
		fs.m[file.path] = clone
		fs.l[i] = clone
	}
	return fs
}

// Dir returns a new FS for the given path.
func Dir(path string) (*FS, error) {
	files, err := Files(path, FilterNone)
	if err != nil {
		return nil, err
	}
	return NewFS(files...), nil
}

// FS is an in-memory file system that implements http.FileSystem.
type FS struct {
	m map[string]*File
	l []*File
}

// check interface compliance
var _ = http.FileSystem(&FS{})

// Open is part of http.FileSystem.
func (f *FS) Open(name string) (http.File, error) {
	name = path.Clean(name)
	file := f.m[name]
	if file == nil {
		// @TODO Path error?
		return nil, os.ErrNotExist
	}
	// Return a clone so that concurrently opened files can have their own read
	// state.
	clone := &File{}
	*clone = *file
	clone.Reader = bytes.NewReader(clone.data)
	return clone, nil
}

// NewFile returns a new in-memory file with the given properties.
func NewFile(path, name string, size int64, mode os.FileMode, t time.Time, isDir bool, data []byte) *File {
	return &File{
		path:    path,
		name:    name,
		size:    size,
		mode:    mode,
		modTime: t,
		isDir:   isDir,
		data:    data,
		Reader:  bytes.NewReader(data),
	}
}

// File is an in-memory file that implements http.File and os.FileInfo.
type File struct {
	io.Reader

	path    string
	name    string
	size    int64
	mode    os.FileMode
	modTime time.Time
	isDir   bool
	data    []byte

	fs *FS
}

// check interface compliance
var _ = os.FileInfo(&File{})
var _ = http.File(&File{})

// Close is part of http.File.
func (f *File) Close() error {
	// @TODO return err when closing twice?
	return nil
}

// Seek is part of http.File.
func (f *File) Seek(offset int64, whence int) (int64, error) {
	// @TODO(fg) implement
	return 0, nil
}

// Stat is part of http.File.
func (f *File) Stat() (os.FileInfo, error) {
	// @TODO(fg) implement
	return f, nil
}

// Readdir is part of http.File.
func (f *File) Readdir(count int) ([]os.FileInfo, error) {
	// @TODO(fg) support full Readdir semantics, including EOF, etc.
	if count <= 0 {
		return nil, errors.New("genfs: only count <= 0 is supported right now")
	}
	var files []os.FileInfo
	// TODO(fg) make this faster using binary search or a tree structure
	for _, file := range f.fs.l {
		if path.Clean(path.Join(f.path, file.name)) == file.path {
			files = append(files, file)
		}
	}
	return files, nil
}

// IsDir is part of os.FileInfo.
func (f *File) IsDir() bool { return f.isDir }

// ModTime is part of os.FileInfo.
func (f *File) ModTime() time.Time { return f.modTime }

// Mode is part of os.FileInfo.
func (f *File) Mode() os.FileMode { return f.mode }

// Name is part of os.FileInfo.
func (f *File) Name() string { return f.name }

// Size is part of os.FileInfo.
func (f *File) Size() int64 { return f.size }

// Sys is part of os.FileInfo.
func (f *File) Sys() interface{} { return nil }

// Path returns the path of the file. It's needed for WriteSource.
func (f *File) Path() string { return f.path }

// String returns the file data as a string. It's needed for WriteSource.
func (f *File) String() string { return string(f.data) }

// MustTime parses and returns the given time as RFC3339 or panics.
func MustTime(s string) time.Time {
	t, err := time.Parse(time.RFC3339, s)
	if err != nil {
		panic(err)
	}
	return t
}
<|endoftext|>


<commit_before>package fscache

import (
	"bytes"
	"crypto/md5"
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"

	"gopkg.in/djherbis/atime.v1"
	"gopkin.in/djherbis/stream.v1"
)

// FileSystem is used as the source for a Cache.
type FileSystem interface {
	// Reload should look through the FileSystem and call the suplied fn
	// with the key/filename pairs that are found.
	Reload(func(key, name string)) error

	// Create should return a new File using a name generated from the passed key.
	Create(key string) (stream.File, error)

	// Open takes a File.Name() and returns a concurrent-safe reader to the file.
	// The reader may return io.EOF when reaches the end of written content, but
	// if more content is written and Read is called again, it must continue
	// reading the data.
	Open(name string) (stream.File, error)

	// Remove takes a File.Name() and deletes the underlying file.
	// It does not have to worry about concurrent use, the Cache will wait
	// for all activity in the file to cease.
	Remove(name string) error

	// RemoveAll should empty the FileSystem of all files.
	RemoveAll() error

	// AccessTimes takes a File.Name() and returns the last time the file was read,
	// and the last time it was written to.
	// It will be used to check expiry of a file, and must be concurrent safe
	// with modifications to the FileSystem (writes, reads etc.)
	AccessTimes(name string) (rt, wt time.Time, err error)
}

type stdFs struct {
	root string
}

// NewFs returns a FileSystem rooted at directory dir.
// Dir is created with perms if it doesn't exist.
func NewFs(dir string, mode os.FileMode) (FileSystem, error) {
	return &stdFs{root: dir}, os.MkdirAll(dir, mode)
}

func (fs *stdFs) Reload(add func(key, name string)) error {
	files, err := ioutil.ReadDir(fs.root)
	if err != nil {
		return err
	}

	addfiles := make(map[string]struct {
		os.FileInfo
		key string
	})

	for _, f := range files {

		if strings.HasSuffix(f.Name(), ".key") {
			continue
		}

		key, err := fs.getKey(f.Name())
		if err != nil {
			return err
		}
		fi, ok := addfiles[key]

		if !ok || fi.ModTime().Before(f.ModTime()) {
			if ok {
				fs.Remove(fi.Name())
			}
			addfiles[key] = struct {
				os.FileInfo
				key string
			}{
				FileInfo: f,
				key:      key,
			}
		} else {
			fs.Remove(f.Name())
		}

	}

	for _, f := range addfiles {
		path, err := filepath.Abs(filepath.Join(fs.root, f.Name()))
		if err != nil {
			return err
		}
		add(f.key, path)
	}

	return nil
}

func (fs *stdFs) Create(name string) (stream.File, error) {
	name, err := fs.makeName(name)
	if err != nil {
		return nil, err
	}
	return fs.create(name)
}

func (fs *stdFs) create(name string) (stream.File, error) {
	return os.OpenFile(filepath.Join(fs.root, name), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
}

func (fs *stdFs) Open(name string) (stream.File, error) {
	return os.Open(name)
}

func (fs *stdFs) Remove(name string) error {
	os.Remove(fmt.Sprintf("%s.key", name))
	return os.Remove(name)
}

func (fs *stdFs) RemoveAll() error {
	return os.RemoveAll(fs.root)
}

func (fs *stdFs) AccessTimes(name string) (rt, wt time.Time, err error) {
	fi, err := os.Stat(name)
	if err != nil {
		return rt, wt, err
	}
	return atime.Get(fi), fi.ModTime(), nil
}

const (
	saltSize    = 8
	maxShort    = 20
	shortPrefix = "s"
	longPrefix  = "l"
)

func salt() string {
	buf := bytes.NewBufferString("")
	enc := base64.NewEncoder(base64.URLEncoding, buf)
	io.CopyN(enc, rand.Reader, saltSize)
	return buf.String()
}

func tob64(s string) string {
	buf := bytes.NewBufferString("")
	enc := base64.NewEncoder(base64.URLEncoding, buf)
	enc.Write([]byte(s))
	enc.Close()
	return buf.String()
}

func fromb64(s string) string {
	buf := bytes.NewBufferString(s)
	dec := base64.NewDecoder(base64.URLEncoding, buf)
	out := bytes.NewBufferString("")
	io.Copy(out, dec)
	return out.String()
}

func (fs *stdFs) makeName(key string) (string, error) {
	b64key := tob64(key)
	// short name
	if len(b64key) < maxShort {
		return fmt.Sprintf("%s%s%s", shortPrefix, salt(), b64key), nil
	}

	// long name
	hash := md5.Sum([]byte(key))
	name := fmt.Sprintf("%s%s%x", longPrefix, salt(), hash[:])
	f, err := fs.create(fmt.Sprintf("%s.key", name))
	if err != nil {
		return "", err
	}
	_, err = f.Write([]byte(key))
	f.Close()
	return name, err
}

func (fs *stdFs) getKey(name string) (string, error) {
	// short name
	if strings.HasPrefix(name, shortPrefix) {
		return fromb64(strings.TrimPrefix(name, shortPrefix)[saltSize:]), nil
	}

	// long name
	f, err := fs.Open(filepath.Join(fs.root, fmt.Sprintf("%s.key", name)))
	if err != nil {
		return "", err
	}
	defer f.Close()
	key, err := ioutil.ReadAll(f)
	if err != nil {
		return "", err
	}
	return string(key), nil
}
<commit_msg>typo fix<commit_after>package fscache

import (
	"bytes"
	"crypto/md5"
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"

	"gopkg.in/djherbis/atime.v1"
	"gopkg.in/djherbis/stream.v1"
)

// FileSystem is used as the source for a Cache.
type FileSystem interface {
	// Reload should look through the FileSystem and call the suplied fn
	// with the key/filename pairs that are found.
	Reload(func(key, name string)) error

	// Create should return a new File using a name generated from the passed key.
	Create(key string) (stream.File, error)

	// Open takes a File.Name() and returns a concurrent-safe reader to the file.
	// The reader may return io.EOF when reaches the end of written content, but
	// if more content is written and Read is called again, it must continue
	// reading the data.
	Open(name string) (stream.File, error)

	// Remove takes a File.Name() and deletes the underlying file.
	// It does not have to worry about concurrent use, the Cache will wait
	// for all activity in the file to cease.
	Remove(name string) error

	// RemoveAll should empty the FileSystem of all files.
	RemoveAll() error

	// AccessTimes takes a File.Name() and returns the last time the file was read,
	// and the last time it was written to.
	// It will be used to check expiry of a file, and must be concurrent safe
	// with modifications to the FileSystem (writes, reads etc.)
	AccessTimes(name string) (rt, wt time.Time, err error)
}

type stdFs struct {
	root string
}

// NewFs returns a FileSystem rooted at directory dir.
// Dir is created with perms if it doesn't exist.
func NewFs(dir string, mode os.FileMode) (FileSystem, error) {
	return &stdFs{root: dir}, os.MkdirAll(dir, mode)
}

func (fs *stdFs) Reload(add func(key, name string)) error {
	files, err := ioutil.ReadDir(fs.root)
	if err != nil {
		return err
	}

	addfiles := make(map[string]struct {
		os.FileInfo
		key string
	})

	for _, f := range files {

		if strings.HasSuffix(f.Name(), ".key") {
			continue
		}

		key, err := fs.getKey(f.Name())
		if err != nil {
			return err
		}
		fi, ok := addfiles[key]

		if !ok || fi.ModTime().Before(f.ModTime()) {
			if ok {
				fs.Remove(fi.Name())
			}
			addfiles[key] = struct {
				os.FileInfo
				key string
			}{
				FileInfo: f,
				key:      key,
			}
		} else {
			fs.Remove(f.Name())
		}

	}

	for _, f := range addfiles {
		path, err := filepath.Abs(filepath.Join(fs.root, f.Name()))
		if err != nil {
			return err
		}
		add(f.key, path)
	}

	return nil
}

func (fs *stdFs) Create(name string) (stream.File, error) {
	name, err := fs.makeName(name)
	if err != nil {
		return nil, err
	}
	return fs.create(name)
}

func (fs *stdFs) create(name string) (stream.File, error) {
	return os.OpenFile(filepath.Join(fs.root, name), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
}

func (fs *stdFs) Open(name string) (stream.File, error) {
	return os.Open(name)
}

func (fs *stdFs) Remove(name string) error {
	os.Remove(fmt.Sprintf("%s.key", name))
	return os.Remove(name)
}

func (fs *stdFs) RemoveAll() error {
	return os.RemoveAll(fs.root)
}

func (fs *stdFs) AccessTimes(name string) (rt, wt time.Time, err error) {
	fi, err := os.Stat(name)
	if err != nil {
		return rt, wt, err
	}
	return atime.Get(fi), fi.ModTime(), nil
}

const (
	saltSize    = 8
	maxShort    = 20
	shortPrefix = "s"
	longPrefix  = "l"
)

func salt() string {
	buf := bytes.NewBufferString("")
	enc := base64.NewEncoder(base64.URLEncoding, buf)
	io.CopyN(enc, rand.Reader, saltSize)
	return buf.String()
}

func tob64(s string) string {
	buf := bytes.NewBufferString("")
	enc := base64.NewEncoder(base64.URLEncoding, buf)
	enc.Write([]byte(s))
	enc.Close()
	return buf.String()
}

func fromb64(s string) string {
	buf := bytes.NewBufferString(s)
	dec := base64.NewDecoder(base64.URLEncoding, buf)
	out := bytes.NewBufferString("")
	io.Copy(out, dec)
	return out.String()
}

func (fs *stdFs) makeName(key string) (string, error) {
	b64key := tob64(key)
	// short name
	if len(b64key) < maxShort {
		return fmt.Sprintf("%s%s%s", shortPrefix, salt(), b64key), nil
	}

	// long name
	hash := md5.Sum([]byte(key))
	name := fmt.Sprintf("%s%s%x", longPrefix, salt(), hash[:])
	f, err := fs.create(fmt.Sprintf("%s.key", name))
	if err != nil {
		return "", err
	}
	_, err = f.Write([]byte(key))
	f.Close()
	return name, err
}

func (fs *stdFs) getKey(name string) (string, error) {
	// short name
	if strings.HasPrefix(name, shortPrefix) {
		return fromb64(strings.TrimPrefix(name, shortPrefix)[saltSize:]), nil
	}

	// long name
	f, err := fs.Open(filepath.Join(fs.root, fmt.Sprintf("%s.key", name)))
	if err != nil {
		return "", err
	}
	defer f.Close()
	key, err := ioutil.ReadAll(f)
	if err != nil {
		return "", err
	}
	return string(key), nil
}
<|endoftext|>


<commit_before>package executor

import (
	"fmt"
	. "github.com/smartystreets/goconvey/convey"
	"strings"
	"testing"
	"time"
)

const concurrentBatchSize = 2

func TestConcurrentTester(t *testing.T) {
	var fixture *TesterFixture

	Convey("Subject: Controlled execution of test packages", t, func() {
		fixture = NewTesterFixture()

		Convey("Whenever tests for each package are executed", func() {
			fixture.RunTests()

			Convey("The tester should build all dependencies of input packages",
				fixture.ShouldHaveRecordOfCompilationCommands)

			Convey("The tester should execute the tests in each package with the correct arguments",
				fixture.ShouldHaveRecordOfExecutionCommands)

			Convey("There should be a test output result for each package",
				fixture.ShouldHaveOneOutputPerInput)

			Convey("The output should be as expected",
				fixture.OutputShouldBeAsExpected)
		})

		Convey("When the tests for each package are executed synchronously", func() {
			fixture.InBatchesOf(1).RunTests()

			Convey("Each package should be run synchronously and in the given order",
				fixture.TestsShouldHaveRunContiguously)
		})

		Convey("When packages are tested concurrently", func() {
			fixture.InBatchesOf(concurrentBatchSize).RunTests()

			Convey("Packages should be arranged and tested in batches of the appropriate size",
				fixture.TestsShouldHaveRunInBatchesOfTwo)
		})
	})
}

type TesterFixture struct {
	tester       *ConcurrentTester
	shell        *TimedShell
	results      []string
	compilations []*ShellCommand
	executions   []*ShellCommand
	packages     []string
}

func NewTesterFixture() *TesterFixture {
	self := &TesterFixture{}
	self.shell = NewTimedShell()
	self.tester = NewConcurrentTester(self.shell)
	self.packages = []string{"a", "b", "c", "d", "e", "f"}
	return self
}

func (self *TesterFixture) InBatchesOf(batchSize int) *TesterFixture {
	self.tester.SetBatchSize(batchSize)
	return self
}

func (self *TesterFixture) RunTests() {
	self.results = self.tester.TestAll(self.packages)
	self.compilations = self.shell.Compilations()
	self.executions = self.shell.Executions()
}

func (self *TesterFixture) ShouldHaveRecordOfCompilationCommands() {
	for i, pkg := range self.packages {
		command := self.compilations[i].Command
		So(command, ShouldEqual, "go test -i "+pkg)
	}
}

func (self *TesterFixture) ShouldHaveRecordOfExecutionCommands() {
	for i, pkg := range self.packages {
		So(self.executions[i].Command, ShouldEqual, "go test -v -timeout=-42s "+pkg)
	}
}

func (self *TesterFixture) ShouldHaveOneOutputPerInput() {
	So(len(self.results), ShouldEqual, len(self.packages))
}

func (self *TesterFixture) OutputShouldBeAsExpected() {
	for i, _ := range self.packages {
		So(self.results[i], ShouldEqual, self.executions[i].Command)
	}
}

func (self *TesterFixture) TestsShouldHaveRunContiguously() {
	So(self.shell.MaxConcurrentCommands(), ShouldEqual, 1)

	for i := 0; i < len(self.executions)-1; i++ {
		current := self.executions[i]
		next := self.executions[i+1]
		So(current.Started, ShouldHappenBefore, next.Started)
		So(current.Ended, ShouldHappenOnOrBefore, next.Started)
	}
}

func (self *TesterFixture) TestsShouldHaveRunInBatchesOfTwo() {
	So(self.shell.MaxConcurrentCommands(), ShouldEqual, concurrentBatchSize)
}

/**** Fakes ****/

type ShellCommand struct {
	Command string
	Started time.Time
	Ended   time.Time
}

type TimedShell struct {
	executions   []*ShellCommand
	compilations []*ShellCommand
}

func (self *TimedShell) Compilations() []*ShellCommand {
	return self.compilations
}

func (self *TimedShell) Executions() []*ShellCommand {
	return self.executions
}

func (self *TimedShell) MaxConcurrentCommands() int {
	var concurrent int

	for x, current := range self.executions {
		concurrentWith_x := 1
		for y, comparison := range self.executions {
			if y == x {
				continue
			} else if concurrentWith(current, comparison) {
				concurrentWith_x++
			}
		}
		if concurrentWith_x > concurrent {
			concurrent = concurrentWith_x
		}
	}
	return concurrent
}

func concurrentWith(current, comparison *ShellCommand) bool {
	return comparison.Started.After(current.Started) && comparison.Started.Before(current.Ended)
}

func (self *TimedShell) Execute(name string, args ...string) (output string, err error) {
	command := self.composeCommand(name + " " + strings.Join(args, " "))
	output = command.Command

	if strings.Contains(command.Command, " -i ") {
		self.compilations = append(self.compilations, command)
	} else {
		self.executions = append(self.executions, command)
	}
	return
}
func (self *TimedShell) composeCommand(commandText string) *ShellCommand {
	start := time.Now()
	time.Sleep(nap)
	end := time.Now()
	return &ShellCommand{commandText, start, end}
}
func (self *TimedShell) Getenv(key string) string {
	panic("NOT SUPPORTED")
}
func (self *TimedShell) Setenv(key, value string) error {
	panic("NOT SUPPORTED")
}

func NewTimedShell() *TimedShell {
	self := &TimedShell{}
	self.executions = []*ShellCommand{}
	self.compilations = []*ShellCommand{}
	return self
}

var nap, _ = time.ParseDuration("10ms")
var _ = fmt.Sprintf("fmt")
<commit_msg>Moved a const.<commit_after>package executor

import (
	"fmt"
	. "github.com/smartystreets/goconvey/convey"
	"strings"
	"testing"
	"time"
)

func TestConcurrentTester(t *testing.T) {
	var fixture *TesterFixture

	Convey("Subject: Controlled execution of test packages", t, func() {
		fixture = NewTesterFixture()

		Convey("Whenever tests for each package are executed", func() {
			fixture.RunTests()

			Convey("The tester should build all dependencies of input packages",
				fixture.ShouldHaveRecordOfCompilationCommands)

			Convey("The tester should execute the tests in each package with the correct arguments",
				fixture.ShouldHaveRecordOfExecutionCommands)

			Convey("There should be a test output result for each package",
				fixture.ShouldHaveOneOutputPerInput)

			Convey("The output should be as expected",
				fixture.OutputShouldBeAsExpected)
		})

		Convey("When the tests for each package are executed synchronously", func() {
			fixture.InBatchesOf(1).RunTests()

			Convey("Each package should be run synchronously and in the given order",
				fixture.TestsShouldHaveRunContiguously)
		})

		Convey("When packages are tested concurrently", func() {
			fixture.InBatchesOf(concurrentBatchSize).RunTests()

			Convey("Packages should be arranged and tested in batches of the appropriate size",
				fixture.TestsShouldHaveRunInBatchesOfTwo)
		})
	})
}

const concurrentBatchSize = 2

type TesterFixture struct {
	tester       *ConcurrentTester
	shell        *TimedShell
	results      []string
	compilations []*ShellCommand
	executions   []*ShellCommand
	packages     []string
}

func NewTesterFixture() *TesterFixture {
	self := &TesterFixture{}
	self.shell = NewTimedShell()
	self.tester = NewConcurrentTester(self.shell)
	self.packages = []string{"a", "b", "c", "d", "e", "f"}
	return self
}

func (self *TesterFixture) InBatchesOf(batchSize int) *TesterFixture {
	self.tester.SetBatchSize(batchSize)
	return self
}

func (self *TesterFixture) RunTests() {
	self.results = self.tester.TestAll(self.packages)
	self.compilations = self.shell.Compilations()
	self.executions = self.shell.Executions()
}

func (self *TesterFixture) ShouldHaveRecordOfCompilationCommands() {
	for i, pkg := range self.packages {
		command := self.compilations[i].Command
		So(command, ShouldEqual, "go test -i "+pkg)
	}
}

func (self *TesterFixture) ShouldHaveRecordOfExecutionCommands() {
	for i, pkg := range self.packages {
		So(self.executions[i].Command, ShouldEqual, "go test -v -timeout=-42s "+pkg)
	}
}

func (self *TesterFixture) ShouldHaveOneOutputPerInput() {
	So(len(self.results), ShouldEqual, len(self.packages))
}

func (self *TesterFixture) OutputShouldBeAsExpected() {
	for i, _ := range self.packages {
		So(self.results[i], ShouldEqual, self.executions[i].Command)
	}
}

func (self *TesterFixture) TestsShouldHaveRunContiguously() {
	So(self.shell.MaxConcurrentCommands(), ShouldEqual, 1)

	for i := 0; i < len(self.executions)-1; i++ {
		current := self.executions[i]
		next := self.executions[i+1]
		So(current.Started, ShouldHappenBefore, next.Started)
		So(current.Ended, ShouldHappenOnOrBefore, next.Started)
	}
}

func (self *TesterFixture) TestsShouldHaveRunInBatchesOfTwo() {
	So(self.shell.MaxConcurrentCommands(), ShouldEqual, concurrentBatchSize)
}

/**** Fakes ****/

type ShellCommand struct {
	Command string
	Started time.Time
	Ended   time.Time
}

type TimedShell struct {
	executions   []*ShellCommand
	compilations []*ShellCommand
}

func (self *TimedShell) Compilations() []*ShellCommand {
	return self.compilations
}

func (self *TimedShell) Executions() []*ShellCommand {
	return self.executions
}

func (self *TimedShell) MaxConcurrentCommands() int {
	var concurrent int

	for x, current := range self.executions {
		concurrentWith_x := 1
		for y, comparison := range self.executions {
			if y == x {
				continue
			} else if concurrentWith(current, comparison) {
				concurrentWith_x++
			}
		}
		if concurrentWith_x > concurrent {
			concurrent = concurrentWith_x
		}
	}
	return concurrent
}

func concurrentWith(current, comparison *ShellCommand) bool {
	return comparison.Started.After(current.Started) && comparison.Started.Before(current.Ended)
}

func (self *TimedShell) Execute(name string, args ...string) (output string, err error) {
	command := self.composeCommand(name + " " + strings.Join(args, " "))
	output = command.Command

	if strings.Contains(command.Command, " -i ") {
		self.compilations = append(self.compilations, command)
	} else {
		self.executions = append(self.executions, command)
	}
	return
}
func (self *TimedShell) composeCommand(commandText string) *ShellCommand {
	start := time.Now()
	time.Sleep(nap)
	end := time.Now()
	return &ShellCommand{commandText, start, end}
}
func (self *TimedShell) Getenv(key string) string {
	panic("NOT SUPPORTED")
}
func (self *TimedShell) Setenv(key, value string) error {
	panic("NOT SUPPORTED")
}

func NewTimedShell() *TimedShell {
	self := &TimedShell{}
	self.executions = []*ShellCommand{}
	self.compilations = []*ShellCommand{}
	return self
}

var nap, _ = time.ParseDuration("10ms")
var _ = fmt.Sprintf("fmt")
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net"
	"strconv"
	"strings"
)

//The IP struct
type IP struct {
	Address     string
	CountryName string
	CommonName  string
	OrgName     string
	ServerName  string
	Delay       int
	Bandwidth   int
}

// The status of type IP
const (
	okIP = iota
	noIP
	errIP
)

//IPs []IP
type IPs []IP

//Len return the length of []IP
func (ips IPs) Len() int {
	return len(ips)
}

//Swap swap two value of []IP
func (ips IPs) Swap(i, j int) {
	ips[i], ips[j] = ips[j], ips[i]
}

//ByDelay sort by delay
type ByDelay struct {
	IPs
}

//Less return false if the first value less than the second one
func (s ByDelay) Less(i, j int) bool {
	return s.IPs[i].Delay < s.IPs[j].Delay
}

//ByBandwidth sort by bandwidth
type ByBandwidth struct {
	IPs
}

//Less return false if the first value less than the second one
func (s ByBandwidth) Less(i, j int) bool {
	return s.IPs[i].Bandwidth < s.IPs[j].Bandwidth
}

//get last ok ip
func getLastOkIP() []IP {
	m := make(map[string]IP)
	var checkedip IP
	var ips []IP
	if isFileExist(tmpOkIPFileName) {
		bytes, err := ioutil.ReadFile(tmpOkIPFileName)
		checkErr(fmt.Sprintf("read file %s error: ", tmpOkIPFileName), err, Error)
		lines := strings.Split(string(bytes), "\n")
		for _, line := range lines {
			ipInfo := strings.Split(line, " ")
			if len(ipInfo) == 6 {
				delay, err := strconv.Atoi(ipInfo[1][:len(ipInfo[1])-2])
				checkErr("delay conversion failed: ", err, Warning)
				bandwidth, err := strconv.Atoi(ipInfo[5][:len(ipInfo[5])-4])
				checkErr("bandwidth conversion failed: ", err, Warning)
				checkedip = IP{
					Address:     ipInfo[0],
					Delay:       delay,
					CommonName:  ipInfo[2],
					ServerName:  ipInfo[3],
					CountryName: ipInfo[4],
					Bandwidth:   bandwidth,
				}
				m[ipInfo[0]] = checkedip
			}
		}
	}
	for _, v := range m {
		ips = append(ips, v)
	}
	return ips
}

//get all google ip range from googleip.txt file
func getGoogleIPRange() []string {
	m := make(map[string]string)
	var ipRanges []string
	bytes, err := ioutil.ReadFile(googleIPFileName)
	checkErr(fmt.Sprintf("read file %s error: ", googleIPFileName), err, Error)

	lines := strings.Split(string(bytes), "\r\n")
	for _, line := range lines {
		if len(line) > 7 {
			m[line] = line
		}
	}
	for _, v := range m {
		ipRanges = append(ipRanges, v)
	}

	return ipRanges
}

/**
  Parse google ip range, support the following formats:
  1. xxx.xxx.xxx.xxx
  2. xxx.xxx.xxx.xxx/xx
  3. xxx.xxx.xxx.xxx-xxx.xxx.xxx.xxx
*/
func parseGoogleIPRange(ipRange string) []string {
	var ips []string
	if strings.Contains(ipRange, "/") {
		//CIDR: https://zh.wikipedia.org/wiki/%E6%97%A0%E7%B1%BB%E5%88%AB%E5%9F%9F%E9%97%B4%E8%B7%AF%E7%94%B1
		ip, ipNet, err := net.ParseCIDR(ipRange)
		checkErr(fmt.Sprintf("parse CIDR %s error: ", ipRange), err, Error)

		for iptmp := ip.Mask(ipNet.Mask); ipNet.Contains(iptmp); inc(iptmp) {
			ips = append(ips, iptmp.String())
		}
		// remove network address and broadcast address
		return ips[1 : len(ips)-1]
	} else if strings.Contains(ipRange, "-") {
		startIP := net.ParseIP(ipRange[:strings.Index(ipRange, "-")])
		endIP := net.ParseIP(ipRange[strings.Index(ipRange, "-")+1:])

		for ip := startIP; bytes.Compare(ip, endIP) <= 0; inc(ip) {
			ips = append(ips, ip.String())
		}
	} else {
		ips = append(ips, ipRange)
	}

	return ips
}

//get all google ip
func getGoogleIP() []string {
	var ips []string
	ipRanges := getGoogleIPRange()
	for _, ipRange := range ipRanges {
		ips = append(ips, parseGoogleIPRange(ipRange)...)
	}

	return ips
}

func inc(ip net.IP) {
	for j := len(ip) - 1; j >= 0; j-- {
		ip[j]++
		if ip[j] > 0 {
			break
		}
	}
}
<commit_msg>support line break CRLF and LF<commit_after>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net"
	"strconv"
	"strings"
)

//The IP struct
type IP struct {
	Address     string
	CountryName string
	CommonName  string
	OrgName     string
	ServerName  string
	Delay       int
	Bandwidth   int
}

// The status of type IP
const (
	okIP = iota
	noIP
	errIP
)

//IPs []IP
type IPs []IP

//Len return the length of []IP
func (ips IPs) Len() int {
	return len(ips)
}

//Swap swap two value of []IP
func (ips IPs) Swap(i, j int) {
	ips[i], ips[j] = ips[j], ips[i]
}

//ByDelay sort by delay
type ByDelay struct {
	IPs
}

//Less return false if the first value less than the second one
func (s ByDelay) Less(i, j int) bool {
	return s.IPs[i].Delay < s.IPs[j].Delay
}

//ByBandwidth sort by bandwidth
type ByBandwidth struct {
	IPs
}

//Less return false if the first value less than the second one
func (s ByBandwidth) Less(i, j int) bool {
	return s.IPs[i].Bandwidth < s.IPs[j].Bandwidth
}

//get last ok ip
func getLastOkIP() []IP {
	m := make(map[string]IP)
	var checkedip IP
	var ips []IP
	if isFileExist(tmpOkIPFileName) {
		bytes, err := ioutil.ReadFile(tmpOkIPFileName)
		checkErr(fmt.Sprintf("read file %s error: ", tmpOkIPFileName), err, Error)
		lines := strings.Split(string(bytes), "\n")
		for _, line := range lines {
			ipInfo := strings.Split(line, " ")
			if len(ipInfo) == 6 {
				delay, err := strconv.Atoi(ipInfo[1][:len(ipInfo[1])-2])
				checkErr("delay conversion failed: ", err, Warning)
				bandwidth, err := strconv.Atoi(ipInfo[5][:len(ipInfo[5])-4])
				checkErr("bandwidth conversion failed: ", err, Warning)
				checkedip = IP{
					Address:     ipInfo[0],
					Delay:       delay,
					CommonName:  ipInfo[2],
					ServerName:  ipInfo[3],
					CountryName: ipInfo[4],
					Bandwidth:   bandwidth,
				}
				m[ipInfo[0]] = checkedip
			}
		}
	}
	for _, v := range m {
		ips = append(ips, v)
	}
	return ips
}

//get all google ip range from googleip.txt file
func getGoogleIPRange() []string {
	m := make(map[string]string)
	var ipRanges []string
	bytes, err := ioutil.ReadFile(googleIPFileName)
	checkErr(fmt.Sprintf("read file %s error: ", googleIPFileName), err, Error)

	lines := strings.Split(string(bytes), "\n")
	for _, line := range lines {
		line = strings.Replace(line, "\r", "", -1)
		if len(line) > 7 {
			m[line] = line
		}
	}
	for _, v := range m {
		ipRanges = append(ipRanges, v)
	}

	return ipRanges
}

/**
  Parse google ip range, support the following formats:
  1. xxx.xxx.xxx.xxx
  2. xxx.xxx.xxx.xxx/xx
  3. xxx.xxx.xxx.xxx-xxx.xxx.xxx.xxx
*/
func parseGoogleIPRange(ipRange string) []string {
	var ips []string
	if strings.Contains(ipRange, "/") {
		//CIDR: https://zh.wikipedia.org/wiki/%E6%97%A0%E7%B1%BB%E5%88%AB%E5%9F%9F%E9%97%B4%E8%B7%AF%E7%94%B1
		ip, ipNet, err := net.ParseCIDR(ipRange)
		checkErr(fmt.Sprintf("parse CIDR %s error: ", ipRange), err, Error)

		for iptmp := ip.Mask(ipNet.Mask); ipNet.Contains(iptmp); inc(iptmp) {
			ips = append(ips, iptmp.String())
		}
		// remove network address and broadcast address
		return ips[1 : len(ips)-1]
	} else if strings.Contains(ipRange, "-") {
		startIP := net.ParseIP(ipRange[:strings.Index(ipRange, "-")])
		endIP := net.ParseIP(ipRange[strings.Index(ipRange, "-")+1:])

		for ip := startIP; bytes.Compare(ip, endIP) <= 0; inc(ip) {
			ips = append(ips, ip.String())
		}
	} else {
		ips = append(ips, ipRange)
	}

	return ips
}

//get all google ip
func getGoogleIP() []string {
	var ips []string
	ipRanges := getGoogleIPRange()
	for _, ipRange := range ipRanges {
		ips = append(ips, parseGoogleIPRange(ipRange)...)
	}

	return ips
}

func inc(ip net.IP) {
	for j := len(ip) - 1; j >= 0; j-- {
		ip[j]++
		if ip[j] > 0 {
			break
		}
	}
}
<|endoftext|>