<commit_before>package generatorControllers

import (
	. "eaciit/wfdemo-git/library/helper"
	. "eaciit/wfdemo-git/library/models"
	. "eaciit/wfdemo-git/processapp/summaryGenerator/controllers"
	"eaciit/wfdemo-git/web/helper"
	_ "fmt"
	"log"
	"os"
	_ "strings"
	"time"

	"strings"

	"github.com/eaciit/dbox"
	_ "github.com/eaciit/dbox/dbc/mongo"
	tk "github.com/eaciit/toolkit"
)

type GenScadaLast24 struct {
	*BaseController
}

func (d *GenScadaLast24) Generate(base *BaseController) {
	if base != nil {
		d.BaseController = base
		ctx, e := PrepareConnection()
		if e != nil {
			ErrorHandler(e, "Scada Summary")
			os.Exit(0)
		}

		d.BaseController.Ctx.DeleteMany(new(ScadaLastUpdate), dbox.And(dbox.Ne("_id", "")))

		projectList, _ := helper.GetProjectList()

		inprojectactive := func(str string) bool {
			for _, v := range projectList {
				if v.Value == str {
					return true
				}
			}
			return false
		}

		mapbudget := map[string]float64{}
		csrBudget, _ := ctx.NewQuery().From(new(ExpPValueModel).TableName()).
			Cursor(nil)

		budgets := make([]ExpPValueModel, 0)
		_ = csrBudget.Fetch(&budgets, 0, false)
		csrBudget.Close()

		for _, budget := range budgets {
			mapbudget[tk.Sprintf("%s_%d_75", budget.ProjectName, budget.MonthNo)] = budget.P75NetGenMWH
			mapbudget[tk.Sprintf("%s_%d_50", budget.ProjectName, budget.MonthNo)] = budget.P50NetGenMWH
			mapbudget[tk.Sprintf("%s_%d_90", budget.ProjectName, budget.MonthNo)] = budget.P90NetGenMWH
			if inprojectactive(budget.ProjectName) {
				mapbudget[tk.Sprintf("fleet_%d_75", budget.MonthNo)] = budget.P75NetGenMWH
				mapbudget[tk.Sprintf("fleet_%d_50", budget.MonthNo)] = budget.P50NetGenMWH
				mapbudget[tk.Sprintf("fleet_%d_90", budget.MonthNo)] = budget.P90NetGenMWH
			}
		}

		for _, proj := range d.BaseController.ProjectList {
			projectName := proj.Value
			turbineList := []TurbineOut{}

			if projectName != "Fleet" {
				turbineList, _ = helper.GetTurbineList([]interface{}{projectName})
			} else {
				turbineList, _ = helper.GetTurbineList(nil)
			}

			totalTurbine := len(turbineList)

			filter := dbox.Eq("available", 1)
			if projectName != "Fleet" {
				filter = dbox.And(dbox.Eq("projectname", projectName), filter)
			}

			/*for _, v := range filter {
				log.Printf(">> %#v \n", v)
			}*/

			csr, e := ctx.NewQuery().
				From(new(ScadaData).TableName()).
				Where(filter).
				Aggr(dbox.AggrMax, "$timestamp", "timestamp").
				Aggr(dbox.AggrMax, "$dateinfo.dateid", "dateid").
				Group("").
				Cursor(nil)

			if e != nil {
				log.Printf("Error: %v \n", e.Error())
			} else {
				datas := []tk.M{}
				e = csr.Fetch(&datas, 0, false)
				csr.Close()

				tk.Printf(">> %#v \n", datas)

				if len(datas) > 0 {
					dateId := datas[0].Get("dateid", time.Time{}).(time.Time).UTC()
					dtInfo := GetDateInfo(dateId)
					maxTimeStamp := datas[0].Get("timestamp", time.Time{}).(time.Time).UTC()

					var budgetCurrMonthDaily float64
					var budgetCurrMonthDaily50 float64
					var budgetCurrMonthDaily90 float64

					_id := tk.Sprintf("%s_%d", projectName, dateId.Month())
					if val, cond := mapbudget[_id+"_75"]; cond {
						budgetCurrMonths := val * 1000.0
						noOfDay := float64(daysIn(dateId.Month(), dateId.Year()))
						budgetCurrMonthDaily = tk.Div(budgetCurrMonths, noOfDay)
					}
					if val, cond := mapbudget[_id+"_50"]; cond {
						budgetCurrMonths := val * 1000.0
						noOfDay := float64(daysIn(dateId.Month(), dateId.Year()))
						budgetCurrMonthDaily50 = tk.Div(budgetCurrMonths, noOfDay)
					}
					if val, cond := mapbudget[_id+"_90"]; cond {
						budgetCurrMonths := val * 1000.0
						noOfDay := float64(daysIn(dateId.Month(), dateId.Year()))
						budgetCurrMonthDaily90 = tk.Div(budgetCurrMonths, noOfDay)
					}

					mdl := new(ScadaLastUpdate).New()

					if projectName != "Fleet" {
						mdl.ID = "SCADALASTUPDATE_" + strings.ToUpper(projectName)
						mdl.ProjectName = projectName
						mdl.NoOfProjects = 1
					} else {
						mdl.ID = "SCADALASTUPDATE_FLEET"
						mdl.ProjectName = "Fleet"
						mdl.NoOfProjects = len(d.BaseController.ProjectList) - 1
					}

					for _, t := range turbineList {
						mdl.TotalMaxCapacity += t.Capacity
					}

					mdl.TotalMaxCapacity = tk.ToFloat64(mdl.TotalMaxCapacity*1000.0, 2, tk.RoundingAuto)
					mdl.LastUpdate = maxTimeStamp
					mdl.DateInfo = dtInfo
					mdl.NoOfTurbines = totalTurbine

					items := make([]LastData24Hours, 0)
					cdatehour := dateId.UTC().Add(-1 * time.Hour)
					for i := 0; i < 24; i++ {
						cdatehour = cdatehour.Add(1 * time.Hour)

						// year := strconv.Itoa(dateId.Year())
						// month := dateId.Month().String()
						// day := strconv.Itoa(dateId.Day())
						// strTime := year + "-" + month + "-" + day + " " + strconv.Itoa(i) + ":00:00"
						// timeHr, _ := time.Parse("2006-January-2 15:04:05", strTime)

						// timeHrStart := timeHr.Add(-1 * time.Hour)

						filterSub := []*dbox.Filter{}
						filterSub = append(filterSub, dbox.Gt("timestamp", cdatehour.Add(time.Hour*-1)))
						filterSub = append(filterSub, dbox.Lte("timestamp", cdatehour))
						filterSub = append(filterSub, dbox.Eq("available", 1))

						if projectName != "Fleet" {
							filterSub = append(filterSub, dbox.Eq("projectname", projectName))
						}

						csr, e = ctx.NewQuery().From(new(ScadaData).TableName()).
							Where(dbox.And(filterSub...)).
							Aggr(dbox.AggrSum, "$power", "totalpower").
							Aggr(dbox.AggrSum, "$powerlost", "totalpowerlost").
							Aggr(dbox.AggrSum, "$energylost", "energylost").
							Aggr(dbox.AggrSum, "$denpower", "denpower").
							Aggr(dbox.AggrSum, "$oktime", "totaloktime").
							Aggr(dbox.AggrSum, "$griddowntime", "totalgriddowntime").
							Aggr(dbox.AggrAvr, "$windspeed", "avgwindspeed").
							Group("projectname").
							Cursor(nil)
						defer csr.Close()

						scadas := []tk.M{}
						e = csr.Fetch(&scadas, 0, false)

						var last LastData24Hours
						if len(scadas) > 0 {
							data := scadas[0]
							trueAvail := 0.0
							gridAvail := 0.0

							ipower := data["totalpower"]
							power := 0.0
							if ipower != nil {
								power = tk.ToFloat64(ipower, 6, tk.RoundingAuto)
							}

							ipotentialpower := data["denpower"]
							potentialpower := 0.0
							if ipotentialpower != nil {
								potentialpower = tk.ToFloat64(ipotentialpower, 6, tk.RoundingAuto)
							}

							iwindspeed := data["avgwindspeed"]
							windspeed := 0.0
							if iwindspeed != nil {
								windspeed = tk.ToFloat64(iwindspeed, 6, tk.RoundingAuto)
							}
							last.Hour = i
							last.TimeHour = cdatehour
							last.AvgWindSpeed = windspeed
							last.PowerKw = power
							last.EnergyKwh = power / 6
							last.Potential = potentialpower
							last.PotentialKwh = potentialpower / 6
							last.TrueAvail = trueAvail
							last.GridAvail = gridAvail
						} else {
							last.Hour = i
							last.TimeHour = cdatehour
							last.AvgWindSpeed = 0.0
							last.PowerKw = 0.0
							last.EnergyKwh = 0.0
							last.Potential = 0.0
							last.PotentialKwh = 0.0
							last.TrueAvail = 0.0
							last.GridAvail = 0.0
						}

						items = append(items, last)
					}

					match := tk.M{}

					match.Set("dateinfo.monthid", tk.M{}.Set("$eq", dtInfo.MonthId)).Set("available", tk.M{}.Set("$eq", 1))

					if projectName != "Fleet" {
						match.Set("projectname", projectName)
					}

					pipe := []tk.M{tk.M{}.Set("$match", match), tk.M{}.Set("$group", tk.M{}.Set("_id", "$dateinfo.dateid").Set("totalpower", tk.M{}.Set("$sum", "$power"))), tk.M{}.Set("$sort", tk.M{}.Set("_id", 1))}

					csr, _ := ctx.NewQuery().
						Command("pipe", pipe).
						From(new(ScadaData).TableName()).
						Cursor(nil)
					defer csr.Close()

					scadas := []tk.M{}
					e = csr.Fetch(&scadas, 0, false)

					item30s := make([]Last30Days, 0)
					dateData := dateId
					cummProd := 0.0
					cummBudget := 0.0
					cummBudget50 := 0.0
					cummBudget90 := 0.0
					for _, data := range scadas {
						dateData = data["_id"].(time.Time)
						var last30 Last30Days
						last30.DateId = dateData
						last30.DayNo = dateData.Day()

						currProd := 0.0
						currBudget := budgetCurrMonthDaily // 565160.32
						currBudget50 := budgetCurrMonthDaily50
						currBudget90 := budgetCurrMonthDaily90
						if data != nil {
							ipower := data.GetFloat64("totalpower")
							power := 0.0
							if ipower != nil {
								power = ipower
							}
							currProd = power / 6
						}
						cummProd = cummProd + currProd
						cummBudget = cummBudget + currBudget
						cummBudget50 += currBudget50
						cummBudget90 += currBudget90

						last30.CurrBudget = currBudget
						last30.CurrBudget50 = currBudget50
						last30.CurrBudget90 = currBudget90
						last30.CurrProduction = currProd
						last30.CumBudget = cummBudget / 1000000
						last30.CumBudget50 = cummBudget50 / 1000000
						last30.CumBudget90 = cummBudget90 / 1000000
						last30.CumProduction = cummProd / 1000000

						item30s = append(item30s, last30)

						dateData = dateId.Add(-1)
					}

					mdl.Productions = items
					mdl.CummulativeProductions = item30s

					d.BaseController.Ctx.Insert(mdl)
				}
			}
		}
	}
}
<commit_msg>bug #1, prev commit<commit_after>package generatorControllers

import (
	. "eaciit/wfdemo-git/library/helper"
	. "eaciit/wfdemo-git/library/models"
	. "eaciit/wfdemo-git/processapp/summaryGenerator/controllers"
	"eaciit/wfdemo-git/web/helper"
	_ "fmt"
	"log"
	"os"
	_ "strings"
	"time"

	"strings"

	"github.com/eaciit/dbox"
	_ "github.com/eaciit/dbox/dbc/mongo"
	tk "github.com/eaciit/toolkit"
)

type GenScadaLast24 struct {
	*BaseController
}

func (d *GenScadaLast24) Generate(base *BaseController) {
	if base != nil {
		d.BaseController = base
		ctx, e := PrepareConnection()
		if e != nil {
			ErrorHandler(e, "Scada Summary")
			os.Exit(0)
		}

		d.BaseController.Ctx.DeleteMany(new(ScadaLastUpdate), dbox.And(dbox.Ne("_id", "")))

		projectList, _ := helper.GetProjectList()

		inprojectactive := func(str string) bool {
			for _, v := range projectList {
				if v.Value == str {
					return true
				}
			}
			return false
		}

		mapbudget := map[string]float64{}
		csrBudget, _ := ctx.NewQuery().From(new(ExpPValueModel).TableName()).
			Cursor(nil)

		budgets := make([]ExpPValueModel, 0)
		_ = csrBudget.Fetch(&budgets, 0, false)
		csrBudget.Close()

		for _, budget := range budgets {
			mapbudget[tk.Sprintf("%s_%d_75", budget.ProjectName, budget.MonthNo)] = budget.P75NetGenMWH
			mapbudget[tk.Sprintf("%s_%d_50", budget.ProjectName, budget.MonthNo)] = budget.P50NetGenMWH
			mapbudget[tk.Sprintf("%s_%d_90", budget.ProjectName, budget.MonthNo)] = budget.P90NetGenMWH
			if inprojectactive(budget.ProjectName) {
				mapbudget[tk.Sprintf("fleet_%d_75", budget.MonthNo)] = budget.P75NetGenMWH
				mapbudget[tk.Sprintf("fleet_%d_50", budget.MonthNo)] = budget.P50NetGenMWH
				mapbudget[tk.Sprintf("fleet_%d_90", budget.MonthNo)] = budget.P90NetGenMWH
			}
		}

		for _, proj := range d.BaseController.ProjectList {
			projectName := proj.Value
			turbineList := []TurbineOut{}

			if projectName != "Fleet" {
				turbineList, _ = helper.GetTurbineList([]interface{}{projectName})
			} else {
				turbineList, _ = helper.GetTurbineList(nil)
			}

			totalTurbine := len(turbineList)

			filter := dbox.Eq("available", 1)
			if projectName != "Fleet" {
				filter = dbox.And(dbox.Eq("projectname", projectName), filter)
			}

			/*for _, v := range filter {
				log.Printf(">> %#v \n", v)
			}*/

			csr, e := ctx.NewQuery().
				From(new(ScadaData).TableName()).
				Where(filter).
				Aggr(dbox.AggrMax, "$timestamp", "timestamp").
				Aggr(dbox.AggrMax, "$dateinfo.dateid", "dateid").
				Group("").
				Cursor(nil)

			if e != nil {
				log.Printf("Error: %v \n", e.Error())
			} else {
				datas := []tk.M{}
				e = csr.Fetch(&datas, 0, false)
				csr.Close()

				tk.Printf(">> %#v \n", datas)

				if len(datas) > 0 {
					dateId := datas[0].Get("dateid", time.Time{}).(time.Time).UTC()
					dtInfo := GetDateInfo(dateId)
					maxTimeStamp := datas[0].Get("timestamp", time.Time{}).(time.Time).UTC()

					var budgetCurrMonthDaily float64
					var budgetCurrMonthDaily50 float64
					var budgetCurrMonthDaily90 float64

					_id := tk.Sprintf("%s_%d", projectName, dateId.Month())
					if val, cond := mapbudget[_id+"_75"]; cond {
						budgetCurrMonths := val * 1000.0
						noOfDay := float64(daysIn(dateId.Month(), dateId.Year()))
						budgetCurrMonthDaily = tk.Div(budgetCurrMonths, noOfDay)
					}
					if val, cond := mapbudget[_id+"_50"]; cond {
						budgetCurrMonths := val * 1000.0
						noOfDay := float64(daysIn(dateId.Month(), dateId.Year()))
						budgetCurrMonthDaily50 = tk.Div(budgetCurrMonths, noOfDay)
					}
					if val, cond := mapbudget[_id+"_90"]; cond {
						budgetCurrMonths := val * 1000.0
						noOfDay := float64(daysIn(dateId.Month(), dateId.Year()))
						budgetCurrMonthDaily90 = tk.Div(budgetCurrMonths, noOfDay)
					}

					mdl := new(ScadaLastUpdate).New()

					if projectName != "Fleet" {
						mdl.ID = "SCADALASTUPDATE_" + strings.ToUpper(projectName)
						mdl.ProjectName = projectName
						mdl.NoOfProjects = 1
					} else {
						mdl.ID = "SCADALASTUPDATE_FLEET"
						mdl.ProjectName = "Fleet"
						mdl.NoOfProjects = len(d.BaseController.ProjectList) - 1
					}

					for _, t := range turbineList {
						mdl.TotalMaxCapacity += t.Capacity
					}

					mdl.TotalMaxCapacity = tk.ToFloat64(mdl.TotalMaxCapacity*1000.0, 2, tk.RoundingAuto)
					mdl.LastUpdate = maxTimeStamp
					mdl.DateInfo = dtInfo
					mdl.NoOfTurbines = totalTurbine

					items := make([]LastData24Hours, 0)
					cdatehour := dateId.UTC().Add(-1 * time.Hour)
					for i := 0; i < 24; i++ {
						cdatehour = cdatehour.Add(1 * time.Hour)

						// year := strconv.Itoa(dateId.Year())
						// month := dateId.Month().String()
						// day := strconv.Itoa(dateId.Day())
						// strTime := year + "-" + month + "-" + day + " " + strconv.Itoa(i) + ":00:00"
						// timeHr, _ := time.Parse("2006-January-2 15:04:05", strTime)

						// timeHrStart := timeHr.Add(-1 * time.Hour)

						filterSub := []*dbox.Filter{}
						filterSub = append(filterSub, dbox.Gt("timestamp", cdatehour.Add(time.Hour*-1)))
						filterSub = append(filterSub, dbox.Lte("timestamp", cdatehour))
						filterSub = append(filterSub, dbox.Eq("available", 1))

						if projectName != "Fleet" {
							filterSub = append(filterSub, dbox.Eq("projectname", projectName))
						}

						csr, e = ctx.NewQuery().From(new(ScadaData).TableName()).
							Where(dbox.And(filterSub...)).
							Aggr(dbox.AggrSum, "$power", "totalpower").
							Aggr(dbox.AggrSum, "$powerlost", "totalpowerlost").
							Aggr(dbox.AggrSum, "$energylost", "energylost").
							Aggr(dbox.AggrSum, "$denpower", "denpower").
							Aggr(dbox.AggrSum, "$oktime", "totaloktime").
							Aggr(dbox.AggrSum, "$griddowntime", "totalgriddowntime").
							Aggr(dbox.AggrAvr, "$windspeed", "avgwindspeed").
							Group("projectname").
							Cursor(nil)
						defer csr.Close()

						scadas := []tk.M{}
						e = csr.Fetch(&scadas, 0, false)

						var last LastData24Hours
						if len(scadas) > 0 {
							data := scadas[0]
							trueAvail := 0.0
							gridAvail := 0.0

							ipower := data["totalpower"]
							power := 0.0
							if ipower != nil {
								power = tk.ToFloat64(ipower, 6, tk.RoundingAuto)
							}

							ipotentialpower := data["denpower"]
							potentialpower := 0.0
							if ipotentialpower != nil {
								potentialpower = tk.ToFloat64(ipotentialpower, 6, tk.RoundingAuto)
							}

							iwindspeed := data["avgwindspeed"]
							windspeed := 0.0
							if iwindspeed != nil {
								windspeed = tk.ToFloat64(iwindspeed, 6, tk.RoundingAuto)
							}
							last.Hour = i
							last.TimeHour = cdatehour
							last.AvgWindSpeed = windspeed
							last.PowerKw = power
							last.EnergyKwh = power / 6
							last.Potential = potentialpower
							last.PotentialKwh = potentialpower / 6
							last.TrueAvail = trueAvail
							last.GridAvail = gridAvail
						} else {
							last.Hour = i
							last.TimeHour = cdatehour
							last.AvgWindSpeed = 0.0
							last.PowerKw = 0.0
							last.EnergyKwh = 0.0
							last.Potential = 0.0
							last.PotentialKwh = 0.0
							last.TrueAvail = 0.0
							last.GridAvail = 0.0
						}

						items = append(items, last)
					}

					match := tk.M{}

					match.Set("dateinfo.monthid", tk.M{}.Set("$eq", dtInfo.MonthId)).Set("available", tk.M{}.Set("$eq", 1))

					if projectName != "Fleet" {
						match.Set("projectname", projectName)
					}

					pipe := []tk.M{tk.M{}.Set("$match", match), tk.M{}.Set("$group", tk.M{}.Set("_id", "$dateinfo.dateid").Set("totalpower", tk.M{}.Set("$sum", "$power"))), tk.M{}.Set("$sort", tk.M{}.Set("_id", 1))}

					csr, _ := ctx.NewQuery().
						Command("pipe", pipe).
						From(new(ScadaData).TableName()).
						Cursor(nil)
					defer csr.Close()

					scadas := []tk.M{}
					e = csr.Fetch(&scadas, 0, false)

					item30s := make([]Last30Days, 0)
					dateData := dateId
					cummProd := 0.0
					cummBudget := 0.0
					cummBudget50 := 0.0
					cummBudget90 := 0.0
					for _, data := range scadas {
						dateData = data["_id"].(time.Time)
						var last30 Last30Days
						last30.DateId = dateData
						last30.DayNo = dateData.Day()

						currProd := 0.0
						currBudget := budgetCurrMonthDaily // 565160.32
						currBudget50 := budgetCurrMonthDaily50
						currBudget90 := budgetCurrMonthDaily90
						if data != nil {
							ipower := data["totalpower"]
							power := 0.0
							if ipower != nil {
								power = data.GetFloat64("totalpower")
							}
							currProd = power / 6
						}
						cummProd = cummProd + currProd
						cummBudget = cummBudget + currBudget
						cummBudget50 += currBudget50
						cummBudget90 += currBudget90

						last30.CurrBudget = currBudget
						last30.CurrBudget50 = currBudget50
						last30.CurrBudget90 = currBudget90
						last30.CurrProduction = currProd
						last30.CumBudget = cummBudget / 1000000
						last30.CumBudget50 = cummBudget50 / 1000000
						last30.CumBudget90 = cummBudget90 / 1000000
						last30.CumProduction = cummProd / 1000000

						item30s = append(item30s, last30)

						dateData = dateId.Add(-1)
					}

					mdl.Productions = items
					mdl.CummulativeProductions = item30s

					d.BaseController.Ctx.Insert(mdl)
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>package vm

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"sync/atomic"
	"testing"

	"github.com/goby-lang/goby/compiler"
	"github.com/goby-lang/goby/compiler/bytecode"
	"github.com/goby-lang/goby/compiler/parser"
	"github.com/goby-lang/goby/vm/classes"
	"github.com/goby-lang/goby/vm/errors"
)

// Version stores current Goby version
const Version = "0.1.12"

// DefaultLibPath is used for overriding vm.libpath build-time.
var DefaultLibPath string

type isIndexTable struct {
	Data map[string]int
}

func newISIndexTable() *isIndexTable {
	return &isIndexTable{Data: make(map[string]int)}
}

type isTable map[string][]*instructionSet

type filename = string

var standardLibraries = map[string]func(*VM){
	"net/http":           initHTTPClass,
	"net/simple_server":  initSimpleServerClass,
	"uri":                initURIClass,
	"json":               initJSONClass,
	"concurrent/array":   initConcurrentArrayClass,
	"concurrent/hash":    initConcurrentHashClass,
	"concurrent/rw_lock": initConcurrentRWLockClass,
	"spec":               initSpecClass,
}

// VM represents a stack based virtual machine.
type VM struct {
	mainObj     *RObject
	mainThread  Thread
	objectClass *RClass
	// a map holds different types of instruction set tables
	isTables map[setType]isTable
	// method instruction set table
	methodISIndexTables map[filename]*isIndexTable
	// class instruction set table
	classISIndexTables map[filename]*isIndexTable
	// block instruction set table
	blockTables map[filename]map[string]*instructionSet
	// fileDir indicates executed file's directory
	fileDir string
	// args are command line arguments
	args []string

	// Goby vm uses libPath to find standard libraries written in Goby
	// the fallback order is:
	// 1. $GOBY_ROOT/lib
	// 2. /usr/local/Cellar/goby/VERSION/lib - installed via homebrew
	// 3. $GOPATH/src/github.com/goby-lang/goby/lib - development environment
	libPath string

	channelObjectMap *objectMap

	mode parser.Mode

	libFiles []string

	threadCount int64
}

// New initializes a vm to initialize state and returns it.
func New(fileDir string, args []string) (vm *VM, e error) {
	vm = &VM{args: args}
	vm.mainThread.vm = vm
	vm.threadCount++
	vm.mode = parser.NormalMode

	vm.methodISIndexTables = map[filename]*isIndexTable{
		fileDir: newISIndexTable(),
	}
	vm.classISIndexTables = map[filename]*isIndexTable{
		fileDir: newISIndexTable(),
	}
	vm.blockTables = make(map[filename]map[string]*instructionSet)
	vm.isTables = map[setType]isTable{
		bytecode.MethodDef: make(isTable),
		bytecode.ClassDef:  make(isTable),
	}
	vm.fileDir = fileDir

	err := vm.assignLibPath()

	if err != nil {
		return nil, err
	}

	vm.initConstants()
	vm.mainObj = vm.initMainObj()
	vm.channelObjectMap = &objectMap{store: &sync.Map{}}

	for _, fn := range vm.libFiles {
		err := vm.mainThread.execGobyLib(fn)
		if err != nil {
			fmt.Printf("An error occurs when loading lib file %s:\n", string(fn))
			fmt.Println(err.Error())
			break
		}
	}

	return
}

func (vm *VM) newThread() (t Thread) {
	t.vm = vm
	t.id = atomic.AddInt64(&vm.threadCount, 1)
	return
}

// vm.assignLibPath looks up and assigns vm.libPath
func (vm *VM) assignLibPath() (err error) {
	if DefaultLibPath != "" {
		vm.libPath = DefaultLibPath
		return
	}

	gobyRoot := os.Getenv("GOBY_ROOT")

	if len(gobyRoot) == 0 {
		// if GOBY_ROOT is not set, fallback to homebrew's path
		gobyRoot = fmt.Sprintf("/usr/local/Cellar/goby/%s", Version)

		// if it's not installed via homebrew, assume it's in development env and Goby's source is under GOPATH
		if _, err := os.Stat(gobyRoot); err != nil {
			path, _ := filepath.Abs(os.Getenv("GOPATH") + "/src/github.com/goby-lang/goby")

			if _, err = os.Stat(path); err != nil {
				return fmt.Errorf("You haven't set $GOBY_ROOT properly")
			}

			gobyRoot = path
		}
	}

	vm.libPath = filepath.Join(gobyRoot, "lib")

	return
}

// ExecInstructions accepts a sequence of bytecodes and use vm to evaluate them.
func (vm *VM) ExecInstructions(sets []*bytecode.InstructionSet, fn string) {
	translator := newInstructionTranslator(fn)
	translator.vm = vm
	translator.transferInstructionSets(sets)

	// Keep instruction set table updated after parsed new files.
	// TODO: Find more efficient way to do this.
	for setType, table := range translator.setTable {
		for name, is := range table {
			vm.isTables[setType][name] = is
		}
	}

	vm.blockTables[translator.filename] = translator.blockTable
	vm.SetClassISIndexTable(translator.filename)
	vm.SetMethodISIndexTable(translator.filename)

	cf := newNormalCallFrame(translator.program, translator.filename, 1)
	cf.self = vm.mainObj
	vm.mainThread.callFrameStack.push(cf)

	// here is the final destination of Goby errors at the VM level, and we don't deal with them at this point.
	// we only decide how the user program should react to them.
	// at vm level, we don't deal with the error itself
	// we only decide how the program should react to it
	defer func() {
		switch err := recover().(type) {
		// if the error is a true Go panic, Goby can't handle it properly and we should re-raise it again
		// it means Goby can't handle it properly and we should just let it crash
		case error:
			panic(err)

			// if the error is one of the Goby's errors, such as argument error, we need to handle it depending on the mode of execution.
			// we need to handle it depends on the type of program execution
		case *Error:

			// REPLMode: We handle the error inside the igb package, so don't need to do anything here
			// TestMode: We should preserve the vm as it is and inspect its state via test helpers, so don't need to do anything here either
			// NormalMode (normal file execution): we should print our the error and exit the program
			if vm.mode == parser.NormalMode {
				fmt.Fprintln(os.Stderr, err.Message())
				os.Exit(1)
			}
		}
	}()

	vm.mainThread.startFromTopFrame()
}

// SetClassISIndexTable adds new instruction set's index table to vm.classISIndexTables
func (vm *VM) SetClassISIndexTable(fn filename) {
	vm.classISIndexTables[fn] = newISIndexTable()
}

// SetMethodISIndexTable adds new instruction set's index table to vm.methodISIndexTables
func (vm *VM) SetMethodISIndexTable(fn filename) {
	vm.methodISIndexTables[fn] = newISIndexTable()
}

// main object singleton methods -----------------------------------------------------
func builtinMainObjSingletonMethods() []*BuiltinMethodObject {
	return []*BuiltinMethodObject{
		{
			Name: "to_s",
			Fn: func(receiver Object, sourceLine int, thread *Thread, objects []Object, frame *normalCallFrame) Object {
				return thread.vm.InitStringObject("main")

			},
		},
	}
}

func (vm *VM) initMainObj() *RObject {
	obj := vm.objectClass.initializeInstance()
	singletonClass := vm.initializeClass(fmt.Sprintf("#<Class:%s>", obj.ToString()))
	singletonClass.Methods.set("include", vm.TopLevelClass(classes.ClassClass).lookupMethod("include"))
	singletonClass.setBuiltinMethods(builtinMainObjSingletonMethods(), false)
	obj.singletonClass = singletonClass

	return obj
}

func (vm *VM) initConstants() {
	// Init Class and Object
	cClass := initClassClass()
	mClass := initModuleClass(cClass)
	vm.objectClass = initObjectClass(cClass)
	vm.TopLevelClass(classes.ObjectClass).setClassConstant(cClass)
	vm.TopLevelClass(classes.ObjectClass).setClassConstant(mClass)

	// Init builtin classes
	builtinClasses := []*RClass{
		vm.initIntegerClass(),
		vm.initFloatClass(),
		vm.initStringClass(),
		vm.initBoolClass(),
		vm.initNullClass(),
		vm.initArrayClass(),
		vm.initHashClass(),
		vm.initRangeClass(),
		vm.initMethodClass(),
		vm.initBlockClass(),
		vm.initChannelClass(),
		vm.initGoClass(),
		vm.initFileClass(),
		vm.initRegexpClass(),
		vm.initMatchDataClass(),
		vm.initGoMapClass(),
		vm.initDecimalClass(),
	}

	// Init error classes
	vm.initErrorClasses()

	for _, c := range builtinClasses {
		vm.objectClass.setClassConstant(c)
	}

	// Init ARGV
	args := []Object{}

	for _, arg := range vm.args {
		args = append(args, vm.InitStringObject(arg))
	}

	vm.objectClass.constants["ARGV"] = &Pointer{Target: vm.InitArrayObject(args)}

	// Init ENV
	envs := map[string]Object{}

	for _, e := range os.Environ() {
		pair := strings.Split(e, "=")
		envs[pair[0]] = vm.InitStringObject(pair[1])
	}

	vm.objectClass.constants["ENV"] = &Pointer{Target: vm.InitHashObject(envs)}
	vm.objectClass.constants["STDOUT"] = &Pointer{Target: vm.initFileObject(os.Stdout)}
	vm.objectClass.constants["STDERR"] = &Pointer{Target: vm.initFileObject(os.Stderr)}
	vm.objectClass.constants["STDIN"] = &Pointer{Target: vm.initFileObject(os.Stdin)}
}

// TopLevelClass returns a specified top-level class (stored under the Object constant)
func (vm *VM) TopLevelClass(cn string) *RClass {
	objClass := vm.objectClass

	if cn == classes.ObjectClass {
		return objClass
	}

	return objClass.constants[cn].Target.(*RClass)
}

func (vm *VM) currentFilePath() string {
	frame := vm.mainThread.callFrameStack.top()
	return frame.FileName()
}

// loadConstant makes sure we don't create a class twice.
func (vm *VM) loadConstant(name string, isModule bool) *RClass {
	var c *RClass
	var ptr *Pointer

	ptr = vm.objectClass.constants[name]

	if ptr == nil {
		if isModule {
			c = vm.initializeClass(name)
		} else {
			c = vm.initializeModule(name)
		}

		vm.objectClass.setClassConstant(c)
	} else {
		c = ptr.Target.(*RClass)
	}

	return c
}

func (vm *VM) lookupConstant(cf callFrame, constName string) (constant *Pointer) {
	var namespace *RClass
	var hasNamespace bool

	top := vm.mainThread.Stack.top()

	if top == nil {
		hasNamespace = false
	} else {
		namespace, hasNamespace = top.Target.(*RClass)
	}

	if hasNamespace {
		constant = namespace.lookupConstantUnderAllScope(constName)

		if constant != nil {
			return
		}
	}

	constant = cf.lookupConstantUnderAllScope(constName)

	if constant == nil {
		constant = vm.objectClass.constants[constName]
	}

	if constName == classes.ObjectClass {
		constant = &Pointer{Target: vm.objectClass}
	}

	return
}
func initTestVM() *VM {
	fn, err := os.Getwd()

	if err != nil {
		panic(err)
	}

	v, err := New(fn, []string{})

	if err != nil {
		panic(err)
	}

	v.mode = parser.TestMode
	return v
}

func getFilename() string {
	_, filename, _, _ := runtime.Caller(1)
	return filename
}

// ExecAndReturn is a test helper
func ExecAndReturn(t *testing.T, src string) Object {
	t.Helper()
	v := initTestVM()
	return v.testEval(t, src, getFilename())
}

func (vm *VM) testEval(t *testing.T, input, filepath string) Object {
	iss, err := compiler.CompileToInstructions(input, parser.TestMode)

	if err != nil {
		t.Helper()
		t.Errorf("Error when compiling input: %s", input)
		t.Fatal(err.Error())
	}

	vm.ExecInstructions(iss, filepath)

	return vm.mainThread.Stack.top().Target
}

func (vm *VM) checkArgTypes(args []Object, sourceLine int, types ...string) *Error {
	for i, expectedType := range types {
		className := args[i].Class().Name

		if className != expectedType {
			return vm.InitErrorObject(errors.TypeError, sourceLine, errors.WrongArgumentTypeFormat, expectedType, className)
		}
	}

	return nil
}
<commit_msg>Bump version to v0.1.13<commit_after>package vm

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"sync/atomic"
	"testing"

	"github.com/goby-lang/goby/compiler"
	"github.com/goby-lang/goby/compiler/bytecode"
	"github.com/goby-lang/goby/compiler/parser"
	"github.com/goby-lang/goby/vm/classes"
	"github.com/goby-lang/goby/vm/errors"
)

// Version stores current Goby version
const Version = "0.1.13"

// DefaultLibPath is used for overriding vm.libpath build-time.
var DefaultLibPath string

type isIndexTable struct {
	Data map[string]int
}

func newISIndexTable() *isIndexTable {
	return &isIndexTable{Data: make(map[string]int)}
}

type isTable map[string][]*instructionSet

type filename = string

var standardLibraries = map[string]func(*VM){
	"net/http":           initHTTPClass,
	"net/simple_server":  initSimpleServerClass,
	"uri":                initURIClass,
	"json":               initJSONClass,
	"concurrent/array":   initConcurrentArrayClass,
	"concurrent/hash":    initConcurrentHashClass,
	"concurrent/rw_lock": initConcurrentRWLockClass,
	"spec":               initSpecClass,
}

// VM represents a stack based virtual machine.
type VM struct {
	mainObj     *RObject
	mainThread  Thread
	objectClass *RClass
	// a map holds different types of instruction set tables
	isTables map[setType]isTable
	// method instruction set table
	methodISIndexTables map[filename]*isIndexTable
	// class instruction set table
	classISIndexTables map[filename]*isIndexTable
	// block instruction set table
	blockTables map[filename]map[string]*instructionSet
	// fileDir indicates executed file's directory
	fileDir string
	// args are command line arguments
	args []string

	// Goby vm uses libPath to find standard libraries written in Goby
	// the fallback order is:
	// 1. $GOBY_ROOT/lib
	// 2. /usr/local/Cellar/goby/VERSION/lib - installed via homebrew
	// 3. $GOPATH/src/github.com/goby-lang/goby/lib - development environment
	libPath string

	channelObjectMap *objectMap

	mode parser.Mode

	libFiles []string

	threadCount int64
}

// New initializes a vm to initialize state and returns it.
func New(fileDir string, args []string) (vm *VM, e error) {
	vm = &VM{args: args}
	vm.mainThread.vm = vm
	vm.threadCount++
	vm.mode = parser.NormalMode

	vm.methodISIndexTables = map[filename]*isIndexTable{
		fileDir: newISIndexTable(),
	}
	vm.classISIndexTables = map[filename]*isIndexTable{
		fileDir: newISIndexTable(),
	}
	vm.blockTables = make(map[filename]map[string]*instructionSet)
	vm.isTables = map[setType]isTable{
		bytecode.MethodDef: make(isTable),
		bytecode.ClassDef:  make(isTable),
	}
	vm.fileDir = fileDir

	err := vm.assignLibPath()

	if err != nil {
		return nil, err
	}

	vm.initConstants()
	vm.mainObj = vm.initMainObj()
	vm.channelObjectMap = &objectMap{store: &sync.Map{}}

	for _, fn := range vm.libFiles {
		err := vm.mainThread.execGobyLib(fn)
		if err != nil {
			fmt.Printf("An error occurs when loading lib file %s:\n", string(fn))
			fmt.Println(err.Error())
			break
		}
	}

	return
}

func (vm *VM) newThread() (t Thread) {
	t.vm = vm
	t.id = atomic.AddInt64(&vm.threadCount, 1)
	return
}

// vm.assignLibPath looks up and assigns vm.libPath
func (vm *VM) assignLibPath() (err error) {
	if DefaultLibPath != "" {
		vm.libPath = DefaultLibPath
		return
	}

	gobyRoot := os.Getenv("GOBY_ROOT")

	if len(gobyRoot) == 0 {
		// if GOBY_ROOT is not set, fallback to homebrew's path
		gobyRoot = fmt.Sprintf("/usr/local/Cellar/goby/%s", Version)

		// if it's not installed via homebrew, assume it's in development env and Goby's source is under GOPATH
		if _, err := os.Stat(gobyRoot); err != nil {
			path, _ := filepath.Abs(os.Getenv("GOPATH") + "/src/github.com/goby-lang/goby")

			if _, err = os.Stat(path); err != nil {
				return fmt.Errorf("You haven't set $GOBY_ROOT properly")
			}

			gobyRoot = path
		}
	}

	vm.libPath = filepath.Join(gobyRoot, "lib")

	return
}

// ExecInstructions accepts a sequence of bytecodes and use vm to evaluate them.
func (vm *VM) ExecInstructions(sets []*bytecode.InstructionSet, fn string) {
	translator := newInstructionTranslator(fn)
	translator.vm = vm
	translator.transferInstructionSets(sets)

	// Keep instruction set table updated after parsed new files.
	// TODO: Find more efficient way to do this.
	for setType, table := range translator.setTable {
		for name, is := range table {
			vm.isTables[setType][name] = is
		}
	}

	vm.blockTables[translator.filename] = translator.blockTable
	vm.SetClassISIndexTable(translator.filename)
	vm.SetMethodISIndexTable(translator.filename)

	cf := newNormalCallFrame(translator.program, translator.filename, 1)
	cf.self = vm.mainObj
	vm.mainThread.callFrameStack.push(cf)

	// here is the final destination of Goby errors at the VM level, and we don't deal with them at this point.
	// we only decide how the user program should react to them.
	// at vm level, we don't deal with the error itself
	// we only decide how the program should react to it
	defer func() {
		switch err := recover().(type) {
		// if the error is a true Go panic, Goby can't handle it properly and we should re-raise it again
		// it means Goby can't handle it properly and we should just let it crash
		case error:
			panic(err)

			// if the error is one of the Goby's errors, such as argument error, we need to handle it depending on the mode of execution.
			// we need to handle it depends on the type of program execution
		case *Error:

			// REPLMode: We handle the error inside the igb package, so don't need to do anything here
			// TestMode: We should preserve the vm as it is and inspect its state via test helpers, so don't need to do anything here either
			// NormalMode (normal file execution): we should print our the error and exit the program
			if vm.mode == parser.NormalMode {
				fmt.Fprintln(os.Stderr, err.Message())
				os.Exit(1)
			}
		}
	}()

	vm.mainThread.startFromTopFrame()
}

// SetClassISIndexTable adds new instruction set's index table to vm.classISIndexTables
func (vm *VM) SetClassISIndexTable(fn filename) {
	vm.classISIndexTables[fn] = newISIndexTable()
}

// SetMethodISIndexTable adds new instruction set's index table to vm.methodISIndexTables
func (vm *VM) SetMethodISIndexTable(fn filename) {
	vm.methodISIndexTables[fn] = newISIndexTable()
}

// main object singleton methods -----------------------------------------------------
func builtinMainObjSingletonMethods() []*BuiltinMethodObject {
	return []*BuiltinMethodObject{
		{
			Name: "to_s",
			Fn: func(receiver Object, sourceLine int, thread *Thread, objects []Object, frame *normalCallFrame) Object {
				return thread.vm.InitStringObject("main")

			},
		},
	}
}

func (vm *VM) initMainObj() *RObject {
	obj := vm.objectClass.initializeInstance()
	singletonClass := vm.initializeClass(fmt.Sprintf("#<Class:%s>", obj.ToString()))
	singletonClass.Methods.set("include", vm.TopLevelClass(classes.ClassClass).lookupMethod("include"))
	singletonClass.setBuiltinMethods(builtinMainObjSingletonMethods(), false)
	obj.singletonClass = singletonClass

	return obj
}

func (vm *VM) initConstants() {
	// Init Class and Object
	cClass := initClassClass()
	mClass := initModuleClass(cClass)
	vm.objectClass = initObjectClass(cClass)
	vm.TopLevelClass(classes.ObjectClass).setClassConstant(cClass)
	vm.TopLevelClass(classes.ObjectClass).setClassConstant(mClass)

	// Init builtin classes
	builtinClasses := []*RClass{
		vm.initIntegerClass(),
		vm.initFloatClass(),
		vm.initStringClass(),
		vm.initBoolClass(),
		vm.initNullClass(),
		vm.initArrayClass(),
		vm.initHashClass(),
		vm.initRangeClass(),
		vm.initMethodClass(),
		vm.initBlockClass(),
		vm.initChannelClass(),
		vm.initGoClass(),
		vm.initFileClass(),
		vm.initRegexpClass(),
		vm.initMatchDataClass(),
		vm.initGoMapClass(),
		vm.initDecimalClass(),
	}

	// Init error classes
	vm.initErrorClasses()

	for _, c := range builtinClasses {
		vm.objectClass.setClassConstant(c)
	}

	// Init ARGV
	args := []Object{}

	for _, arg := range vm.args {
		args = append(args, vm.InitStringObject(arg))
	}

	vm.objectClass.constants["ARGV"] = &Pointer{Target: vm.InitArrayObject(args)}

	// Init ENV
	envs := map[string]Object{}

	for _, e := range os.Environ() {
		pair := strings.Split(e, "=")
		envs[pair[0]] = vm.InitStringObject(pair[1])
	}

	vm.objectClass.constants["ENV"] = &Pointer{Target: vm.InitHashObject(envs)}
	vm.objectClass.constants["STDOUT"] = &Pointer{Target: vm.initFileObject(os.Stdout)}
	vm.objectClass.constants["STDERR"] = &Pointer{Target: vm.initFileObject(os.Stderr)}
	vm.objectClass.constants["STDIN"] = &Pointer{Target: vm.initFileObject(os.Stdin)}
}

// TopLevelClass returns a specified top-level class (stored under the Object constant)
func (vm *VM) TopLevelClass(cn string) *RClass {
	objClass := vm.objectClass

	if cn == classes.ObjectClass {
		return objClass
	}

	return objClass.constants[cn].Target.(*RClass)
}

func (vm *VM) currentFilePath() string {
	frame := vm.mainThread.callFrameStack.top()
	return frame.FileName()
}

// loadConstant makes sure we don't create a class twice.
func (vm *VM) loadConstant(name string, isModule bool) *RClass {
	var c *RClass
	var ptr *Pointer

	ptr = vm.objectClass.constants[name]

	if ptr == nil {
		if isModule {
			c = vm.initializeClass(name)
		} else {
			c = vm.initializeModule(name)
		}

		vm.objectClass.setClassConstant(c)
	} else {
		c = ptr.Target.(*RClass)
	}

	return c
}

func (vm *VM) lookupConstant(cf callFrame, constName string) (constant *Pointer) {
	var namespace *RClass
	var hasNamespace bool

	top := vm.mainThread.Stack.top()

	if top == nil {
		hasNamespace = false
	} else {
		namespace, hasNamespace = top.Target.(*RClass)
	}

	if hasNamespace {
		constant = namespace.lookupConstantUnderAllScope(constName)

		if constant != nil {
			return
		}
	}

	constant = cf.lookupConstantUnderAllScope(constName)

	if constant == nil {
		constant = vm.objectClass.constants[constName]
	}

	if constName == classes.ObjectClass {
		constant = &Pointer{Target: vm.objectClass}
	}

	return
}
func initTestVM() *VM {
	fn, err := os.Getwd()

	if err != nil {
		panic(err)
	}

	v, err := New(fn, []string{})

	if err != nil {
		panic(err)
	}

	v.mode = parser.TestMode
	return v
}

func getFilename() string {
	_, filename, _, _ := runtime.Caller(1)
	return filename
}

// ExecAndReturn is a test helper
func ExecAndReturn(t *testing.T, src string) Object {
	t.Helper()
	v := initTestVM()
	return v.testEval(t, src, getFilename())
}

func (vm *VM) testEval(t *testing.T, input, filepath string) Object {
	iss, err := compiler.CompileToInstructions(input, parser.TestMode)

	if err != nil {
		t.Helper()
		t.Errorf("Error when compiling input: %s", input)
		t.Fatal(err.Error())
	}

	vm.ExecInstructions(iss, filepath)

	return vm.mainThread.Stack.top().Target
}

func (vm *VM) checkArgTypes(args []Object, sourceLine int, types ...string) *Error {
	for i, expectedType := range types {
		className := args[i].Class().Name

		if className != expectedType {
			return vm.InitErrorObject(errors.TypeError, sourceLine, errors.WrongArgumentTypeFormat, expectedType, className)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package chord

import (
	"encoding/binary"
	"encoding/hex"
	"fmt"
	"log"
	"time"
)

// Converts the ID to string
func (vn *Vnode) String() string {
	return hex.Dump(vn.Id)
}

// Initializes a local vnode
func (vn *localVnode) init(idx int) error {
	// Generate an ID
	vn.genId(uint16(idx))

	// Initialize all state
	vn.successors = make([]*Vnode, vn.ring.config.NumSuccessors)
	vn.finger = make([]*Vnode, vn.ring.config.HashBits)

	// Register with the RPC mechanism
	vn.ring.transport.Register(&vn.Vnode, vn)
	return nil
}

// Schedules the Vnode to do regular maintenence
func (vn *localVnode) schedule() {
	// Setup our stabilize timer
	vn.timer = time.AfterFunc(randStabilize(vn.ring.config), vn.stabilize)
}

// Generates an ID for the node
func (vn *localVnode) genId(idx uint16) {
	// Use the hash funciton
	conf := vn.ring.config
	hash := conf.HashFunc()
	hash.Write([]byte(conf.Hostname))
	binary.Write(hash, binary.BigEndian, idx)

	// Use the hash as the ID
	vn.Id = hash.Sum(nil)
}

// Called to periodically stabilize the vnode
func (vn *localVnode) stabilize() {
	// Clear the timer
	vn.timer = nil

	// Check for shutdown
	if vn.ring.shutdown {
		return
	}

	// Setup the next stabilize timer
	defer vn.schedule()

	// Check for new successor
	if err := vn.checkNewSuccessor(); err != nil {
		log.Printf("[ERR] Error checking for new successor: %s", err)
	}

	// Notify the successor
	if err := vn.notifySuccessor(); err != nil {
		log.Printf("[ERR] Error notifying successor: %s", err)
	}

	// Finger table fix up
	if err := vn.fixFingerTable(); err != nil {
		log.Printf("[ERR] Error fixing finger table: %s", err)
	}

	// Check the predecessor
	if err := vn.checkPredecessor(); err != nil {
		log.Printf("[ERR] Error checking predecessor: %s", err)
	}

	// Set the last stabilized time
	vn.stabilized = time.Now()
}

// Checks for a new successor
func (vn *localVnode) checkNewSuccessor() error {
	// Ask our successor for it's predecessor
	trans := vn.ring.transport
	succ := vn.successors[0]
	maybe_suc, err := trans.GetPredecessor(succ)
	if err != nil {
		// Handle a dead successor
		if alive, _ := trans.Ping(succ); !alive {
			// Advance the successors list past the dead one...
			copy(vn.successors[0:], vn.successors[1:])
			vn.successors[len(vn.successors)-1] = nil
			return nil
		}
		return err
	}

	// Check if we should replace our successor
	if maybe_suc != nil && between(vn.Id, succ.Id, maybe_suc.Id) {
		// Check if new successor is alive before switching
		alive, err := trans.Ping(maybe_suc)
		if alive && err == nil {
			vn.successors[0] = maybe_suc
		} else {
			return err
		}
	}
	return nil
}

// RPC: Invoked to return out predecessor
func (vn *localVnode) GetPredecessor() (*Vnode, error) {
	return vn.predecessor, nil
}

// Notifies our successor of us, updates successor list
func (vn *localVnode) notifySuccessor() error {
	// Notify successor
	succ := vn.successors[0]
	succ_list, err := vn.ring.transport.Notify(succ, &vn.Vnode)
	if err != nil {
		return err
	}

	// Trim the successors list if too long
	max_succ := vn.ring.config.NumSuccessors
	if len(succ_list) > max_succ-1 {
		succ_list = succ_list[:max_succ-1]
	}

	// Update local successors list
	for idx, s := range succ_list {
		if s == nil {
			break
		}
		vn.successors[idx+1] = s
	}
	return nil
}

// RPC: Notify is invoked when a Vnode gets notified
func (vn *localVnode) Notify(maybe_pred *Vnode) ([]*Vnode, error) {
	// Check if we should update our predecessor
	if vn.predecessor == nil || between(vn.predecessor.Id, vn.Id, maybe_pred.Id) {
		vn.predecessor = maybe_pred
	}

	// Return our successors list
	return vn.successors, nil
}

// Fixes up the finger table
func (vn *localVnode) fixFingerTable() error {
	// Determine the offset
	hb := vn.ring.config.HashBits
	offset := powerOffset(vn.Id, vn.last_finger, hb)

	// Find the successor
	nodes, err := vn.FindSuccessors(1, offset)
	node := nodes[0]
	if node == nil || err != nil {
		return err
	}

	// Update the finger table
	vn.finger[vn.last_finger] = node

	// Try to skip as many finger entries as possible
	for {
		next := vn.last_finger + 1
		if next >= hb {
			break
		}
		offset := powerOffset(vn.Id, next, hb)

		// While the node is the successor, update the finger entries
		if betweenRightIncl(vn.Id, node.Id, offset) {
			vn.finger[next] = node
			vn.last_finger = next
		} else {
			break
		}
	}

	// Increment to the index to repair
	if vn.last_finger+1 == hb {
		vn.last_finger = 0
	} else {
		vn.last_finger++
	}

	return nil
}

// Checks the health of our predecessor
func (vn *localVnode) checkPredecessor() error {
	// Check predecessor
	if vn.predecessor != nil {
		res, err := vn.ring.transport.Ping(vn.predecessor)
		if err != nil {
			return err
		}

		// Predecessor is dead
		if !res {
			vn.predecessor = nil
		}
	}
	return nil
}

// Finds next N successors. N must be <= NumSuccessors
func (vn *localVnode) FindSuccessors(n int, key []byte) ([]*Vnode, error) {
	// Determine how many successors we know of
	successors := vn.knownSuccessors()

	// Check if the ID is between us and any successors
	for i := 0; i < max(successors-n, 1); i++ {
		if betweenRightIncl(vn.Id, vn.successors[i].Id, key) {
			return vn.successors[i : i+n], nil
		}
	}

	// Try the closest preceeding nodes
	cp := ClosestPreceedingVnodeIterator{}
	cp.init(vn, key)
	for {
		// Get the next closest node
		closest, err := cp.Next()
		if closest == nil {
			return nil, fmt.Errorf("Exhausted all preceeding nodes!")
		} else if err != nil {
			return nil, err
		}

		// Try that node, break on success
		res, err := vn.ring.transport.FindSuccessors(closest, n, key)
		if err == nil {
			return res, nil
		} else {
			log.Printf("[ERR] Failed to contact %s. Got %s", closest.String(), err)
		}
	}
}

// Determine how many successors we know of
func (vn *localVnode) knownSuccessors() (successors int) {
	for i := 0; i < len(vn.successors); i++ {
		if vn.successors[i] != nil {
			successors = i + 1
		}
	}
	return
}
<commit_msg>working on making more checkNewSuccessor() more tolerant of errors<commit_after>package chord

import (
	"encoding/binary"
	"encoding/hex"
	"fmt"
	"log"
	"time"
)

// Converts the ID to string
func (vn *Vnode) String() string {
	return hex.Dump(vn.Id)
}

// Initializes a local vnode
func (vn *localVnode) init(idx int) error {
	// Generate an ID
	vn.genId(uint16(idx))

	// Initialize all state
	vn.successors = make([]*Vnode, vn.ring.config.NumSuccessors)
	vn.finger = make([]*Vnode, vn.ring.config.HashBits)

	// Register with the RPC mechanism
	vn.ring.transport.Register(&vn.Vnode, vn)
	return nil
}

// Schedules the Vnode to do regular maintenence
func (vn *localVnode) schedule() {
	// Setup our stabilize timer
	vn.timer = time.AfterFunc(randStabilize(vn.ring.config), vn.stabilize)
}

// Generates an ID for the node
func (vn *localVnode) genId(idx uint16) {
	// Use the hash funciton
	conf := vn.ring.config
	hash := conf.HashFunc()
	hash.Write([]byte(conf.Hostname))
	binary.Write(hash, binary.BigEndian, idx)

	// Use the hash as the ID
	vn.Id = hash.Sum(nil)
}

// Called to periodically stabilize the vnode
func (vn *localVnode) stabilize() {
	// Clear the timer
	vn.timer = nil

	// Check for shutdown
	if vn.ring.shutdown {
		return
	}

	// Setup the next stabilize timer
	defer vn.schedule()

	// Check for new successor
	if err := vn.checkNewSuccessor(); err != nil {
		log.Printf("[ERR] Error checking for new successor: %s", err)
	}

	// Notify the successor
	if err := vn.notifySuccessor(); err != nil {
		log.Printf("[ERR] Error notifying successor: %s", err)
	}

	// Finger table fix up
	if err := vn.fixFingerTable(); err != nil {
		log.Printf("[ERR] Error fixing finger table: %s", err)
	}

	// Check the predecessor
	if err := vn.checkPredecessor(); err != nil {
		log.Printf("[ERR] Error checking predecessor: %s", err)
	}

	// Set the last stabilized time
	vn.stabilized = time.Now()
}

// Checks for a new successor
func (vn *localVnode) checkNewSuccessor() error {
	// Ask our successor for it's predecessor
	trans := vn.ring.transport

CHECK_NEW_SUC:
	succ := vn.successors[0]
	maybe_suc, err := trans.GetPredecessor(succ)
	if err != nil {
		// Check if we have succ list, try to contact next live succ
		known := vn.knownSuccessors()
		if known > 1 {
			for i := 0; i < known; i++ {
				if alive, _ := trans.Ping(vn.successors[0]); !alive {
					// Advance the successors list past the dead one
					copy(vn.successors[0:], vn.successors[1:])
					vn.successors[known-1-i] = nil
				} else {
					// Found live successor, check for new one
					goto CHECK_NEW_SUC
				}
			}
		}
		return err
	}

	// Check if we should replace our successor
	if maybe_suc != nil && between(vn.Id, succ.Id, maybe_suc.Id) {
		// Check if new successor is alive before switching
		alive, err := trans.Ping(maybe_suc)
		if alive && err == nil {
			vn.successors[0] = maybe_suc
		} else {
			return err
		}
	}
	return nil
}

// RPC: Invoked to return out predecessor
func (vn *localVnode) GetPredecessor() (*Vnode, error) {
	return vn.predecessor, nil
}

// Notifies our successor of us, updates successor list
func (vn *localVnode) notifySuccessor() error {
	// Notify successor
	succ := vn.successors[0]
	succ_list, err := vn.ring.transport.Notify(succ, &vn.Vnode)
	if err != nil {
		return err
	}

	// Trim the successors list if too long
	max_succ := vn.ring.config.NumSuccessors
	if len(succ_list) > max_succ-1 {
		succ_list = succ_list[:max_succ-1]
	}

	// Update local successors list
	for idx, s := range succ_list {
		if s == nil {
			break
		}
		vn.successors[idx+1] = s
	}
	return nil
}

// RPC: Notify is invoked when a Vnode gets notified
func (vn *localVnode) Notify(maybe_pred *Vnode) ([]*Vnode, error) {
	// Check if we should update our predecessor
	if vn.predecessor == nil || between(vn.predecessor.Id, vn.Id, maybe_pred.Id) {
		vn.predecessor = maybe_pred
	}

	// Return our successors list
	return vn.successors, nil
}

// Fixes up the finger table
func (vn *localVnode) fixFingerTable() error {
	// Determine the offset
	hb := vn.ring.config.HashBits
	offset := powerOffset(vn.Id, vn.last_finger, hb)

	// Find the successor
	nodes, err := vn.FindSuccessors(1, offset)
	node := nodes[0]
	if node == nil || err != nil {
		return err
	}

	// Update the finger table
	vn.finger[vn.last_finger] = node

	// Try to skip as many finger entries as possible
	for {
		next := vn.last_finger + 1
		if next >= hb {
			break
		}
		offset := powerOffset(vn.Id, next, hb)

		// While the node is the successor, update the finger entries
		if betweenRightIncl(vn.Id, node.Id, offset) {
			vn.finger[next] = node
			vn.last_finger = next
		} else {
			break
		}
	}

	// Increment to the index to repair
	if vn.last_finger+1 == hb {
		vn.last_finger = 0
	} else {
		vn.last_finger++
	}

	return nil
}

// Checks the health of our predecessor
func (vn *localVnode) checkPredecessor() error {
	// Check predecessor
	if vn.predecessor != nil {
		res, err := vn.ring.transport.Ping(vn.predecessor)
		if err != nil {
			return err
		}

		// Predecessor is dead
		if !res {
			vn.predecessor = nil
		}
	}
	return nil
}

// Finds next N successors. N must be <= NumSuccessors
func (vn *localVnode) FindSuccessors(n int, key []byte) ([]*Vnode, error) {
	// Determine how many successors we know of
	successors := vn.knownSuccessors()

	// Check if the ID is between us and any successors
	for i := 0; i < max(successors-n, 1); i++ {
		if betweenRightIncl(vn.Id, vn.successors[i].Id, key) {
			return vn.successors[i : i+n], nil
		}
	}

	// Try the closest preceeding nodes
	cp := ClosestPreceedingVnodeIterator{}
	cp.init(vn, key)
	for {
		// Get the next closest node
		closest, err := cp.Next()
		if closest == nil {
			return nil, fmt.Errorf("Exhausted all preceeding nodes!")
		} else if err != nil {
			return nil, err
		}

		// Try that node, break on success
		res, err := vn.ring.transport.FindSuccessors(closest, n, key)
		if err == nil {
			return res, nil
		} else {
			log.Printf("[ERR] Failed to contact %s. Got %s", closest.String(), err)
		}
	}
}

// Determine how many successors we know of
func (vn *localVnode) knownSuccessors() (successors int) {
	for i := 0; i < len(vn.successors); i++ {
		if vn.successors[i] != nil {
			successors = i + 1
		}
	}
	return
}
<|endoftext|>


<commit_before>// Discordgo - Discord bindings for Go
// Available at https://github.com/bwmarrin/discordgo

// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains code related to Discord voice suppport

package discordgo

import (
	"encoding/binary"
	"encoding/json"
	"fmt"
	"net"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/gorilla/websocket"
)

// ------------------------------------------------------------------------------------------------
// Code related to both Voice Websocket and UDP connections.
// ------------------------------------------------------------------------------------------------

// A Voice struct holds all data and functions related to Discord Voice support.
type Voice struct {
	sync.Mutex             // future use
	Ready      bool        // If true, voice is ready to send/receive audio
	Debug      bool        // If true, print extra logging
	OP2        *voiceOP2   // exported for dgvoice, may change.
	OpusSend   chan []byte // Chan for sending opus audio
	OpusRecv   chan []byte // Chan for receiving opus audio
	//	FrameRate  int         // This can be used to set the FrameRate of Opus data
	//	FrameSize  int         // This can be used to set the FrameSize of Opus data

	wsConn  *websocket.Conn
	UDPConn *net.UDPConn // this will become unexported soon.

	sessionID string
	token     string
	endpoint  string
	guildID   string
	channelID string
	userID    string
}

// ------------------------------------------------------------------------------------------------
// Code related to the Voice websocket connection
// ------------------------------------------------------------------------------------------------

// A voiceOP2 stores the data for the voice operation 2 websocket event
// which is sort of like the voice READY packet
type voiceOP2 struct {
	SSRC              uint32        `json:"ssrc"`
	Port              int           `json:"port"`
	Modes             []string      `json:"modes"`
	HeartbeatInterval time.Duration `json:"heartbeat_interval"`
}

type voiceHandshakeData struct {
	ServerID  string `json:"server_id"`
	UserID    string `json:"user_id"`
	SessionID string `json:"session_id"`
	Token     string `json:"token"`
}

type voiceHandshakeOp struct {
	Op   int                `json:"op"` // Always 0
	Data voiceHandshakeData `json:"d"`
}

// Open opens a voice connection.  This should be called
// after VoiceChannelJoin is used and the data VOICE websocket events
// are captured.
func (v *Voice) Open() (err error) {

	// TODO: How do we handle changing channels?

	// Don't open a websocket if one is already open
	if v.wsConn != nil {
		return
	}

	// Connect to Voice Websocket
	vg := fmt.Sprintf("wss://%s", strings.TrimSuffix(v.endpoint, ":80"))
	v.wsConn, _, err = websocket.DefaultDialer.Dial(vg, nil)
	if err != nil {
		fmt.Println("VOICE error opening websocket:", err)
		return
	}

	data := voiceHandshakeOp{0, voiceHandshakeData{v.guildID, v.userID, v.sessionID, v.token}}

	err = v.wsConn.WriteJSON(data)
	if err != nil {
		fmt.Println("VOICE error sending init packet:", err)
		return
	}

	// Start a listening for voice websocket events
	// TODO add a check here to make sure Listen worked by monitoring
	// a chan or bool?
	go v.wsListen()

	return
}

// Close closes the voice connection
func (v *Voice) Close() {

	if v.UDPConn != nil {
		err := v.UDPConn.Close()
		if err != nil {
			fmt.Println("error closing udp connection: ", err)
		}
	}

	if v.wsConn != nil {
		err := v.wsConn.Close()
		if err != nil {
			fmt.Println("error closing websocket connection: ", err)
		}
	}
}

// wsListen listens on the voice websocket for messages and passes them
// to the voice event handler.  This is automatically called by the Open func
func (v *Voice) wsListen() {

	for {
		messageType, message, err := v.wsConn.ReadMessage()
		if err != nil {
			// TODO: Handle this problem better.
			// TODO: needs proper logging
			fmt.Println("Voice Listen Error:", err)
			break
		}

		// Pass received message to voice event handler
		go v.wsEvent(messageType, message)
	}

	return
}

// wsEvent handles any voice websocket events. This is only called by the
// wsListen() function.
func (v *Voice) wsEvent(messageType int, message []byte) {

	if v.Debug {
		fmt.Println("wsEvent received: ", messageType)
		printJSON(message)
	}

	var e Event
	if err := json.Unmarshal(message, &e); err != nil {
		fmt.Println("wsEvent Unmarshall error: ", err)
		return
	}

	switch e.Operation {

	case 2: // READY

		v.OP2 = &voiceOP2{}
		if err := json.Unmarshal(e.RawData, v.OP2); err != nil {
			fmt.Println("voiceWS.onEvent OP2 Unmarshall error: ", err)
			printJSON(e.RawData) // TODO: Better error logging
			return
		}

		// Start the voice websocket heartbeat to keep the connection alive
		go v.wsHeartbeat(v.OP2.HeartbeatInterval)
		// TODO monitor a chan/bool to verify this was successful

		// Start the UDP connection
		err := v.udpOpen()
		if err != nil {
			fmt.Println("Error opening udp connection: ", err)
			return
		}

		// Start the opusSender.
		// TODO: Should we allow 48000/960 values to be user defined?
		v.OpusSend = make(chan []byte, 2)
		go v.opusSender(v.OpusSend, 48000, 960)

		return

	case 3: // HEARTBEAT response
		// add code to use this to track latency?
		return

	case 4:
		// TODO

	case 5:
		// SPEAKING TRUE/FALSE NOTIFICATION
		/*
			{
				"user_id": "1238921738912",
				"ssrc": 2,
				"speaking": false
			}
		*/

	default:
		fmt.Println("UNKNOWN VOICE OP: ", e.Operation)
		printJSON(e.RawData)
	}

	return
}

type voiceHeartbeatOp struct {
	Op   int `json:"op"` // Always 3
	Data int `json:"d"`
}

// wsHeartbeat sends regular heartbeats to voice Discord so it knows the client
// is still connected.  If you do not send these heartbeats Discord will
// disconnect the websocket connection after a few seconds.
func (v *Voice) wsHeartbeat(i time.Duration) {

	ticker := time.NewTicker(i * time.Millisecond)
	for {
		err := v.wsConn.WriteJSON(voiceHeartbeatOp{3, int(time.Now().Unix())})
		if err != nil {
			v.Ready = false
			fmt.Println("wsHeartbeat send error: ", err)
			return // TODO better logging
		}
		<-ticker.C
	}
}

type voiceSpeakingData struct {
	Speaking bool `json:"speaking"`
	Delay    int  `json:"delay"`
}

type voiceSpeakingOp struct {
	Op   int               `json:"op"` // Always 5
	Data voiceSpeakingData `json:"d"`
}

// Speaking sends a speaking notification to Discord over the voice websocket.
// This must be sent as true prior to sending audio and should be set to false
// once finished sending audio.
//  b  : Send true if speaking, false if not.
func (v *Voice) Speaking(b bool) (err error) {

	if v.wsConn == nil {
		return fmt.Errorf("No Voice websocket.")
	}

	data := voiceSpeakingOp{5, voiceSpeakingData{b, 0}}
	err = v.wsConn.WriteJSON(data)
	if err != nil {
		fmt.Println("Speaking() write json error:", err)
		return
	}

	return
}

// ------------------------------------------------------------------------------------------------
// Code related to the Voice UDP connection
// ------------------------------------------------------------------------------------------------

type voiceUDPData struct {
	Address string `json:"address"` // Public IP of machine running this code
	Port    uint16 `json:"port"`    // UDP Port of machine running this code
	Mode    string `json:"mode"`    // plain or ?  (plain or encrypted)
}

type voiceUDPD struct {
	Protocol string       `json:"protocol"` // Always "udp" ?
	Data     voiceUDPData `json:"data"`
}

type voiceUDPOp struct {
	Op   int       `json:"op"` // Always 1
	Data voiceUDPD `json:"d"`
}

// udpOpen opens a UDP connection to the voice server and completes the
// initial required handshake.  This connection is left open in the session
// and can be used to send or receive audio.  This should only be called
// from voice.wsEvent OP2
func (v *Voice) udpOpen() (err error) {

	host := fmt.Sprintf("%s:%d", strings.TrimSuffix(v.endpoint, ":80"), v.OP2.Port)
	addr, err := net.ResolveUDPAddr("udp", host)
	if err != nil {
		fmt.Println("udpOpen resolve addr error: ", err)
		// TODO better logging
		return
	}

	v.UDPConn, err = net.DialUDP("udp", nil, addr)
	if err != nil {
		fmt.Println("udpOpen dial udp error: ", err)
		// TODO better logging
		return
	}

	// Create a 70 byte array and put the SSRC code from the Op 2 Voice event
	// into it.  Then send that over the UDP connection to Discord
	sb := make([]byte, 70)
	binary.BigEndian.PutUint32(sb, v.OP2.SSRC)
	_, err = v.UDPConn.Write(sb)
	if err != nil {
		fmt.Println("udpOpen udp write error : ", err)
		// TODO better logging
		return
	}

	// Create a 70 byte array and listen for the initial handshake response
	// from Discord.  Once we get it parse the IP and PORT information out
	// of the response.  This should be our public IP and PORT as Discord
	// saw us.
	rb := make([]byte, 70)
	rlen, _, err := v.UDPConn.ReadFromUDP(rb)
	if err != nil {
		fmt.Println("udpOpen udp read error : ", err)
		// TODO better logging
		return
	}
	if rlen < 70 {
		fmt.Println("Voice RLEN should be 70 but isn't")
	}

	// Loop over position 4 though 20 to grab the IP address
	// Should never be beyond position 20.
	var ip string
	for i := 4; i < 20; i++ {
		if rb[i] == 0 {
			break
		}
		ip += string(rb[i])
	}

	// Grab port from position 68 and 69
	port := binary.LittleEndian.Uint16(rb[68:70])

	// Take the data from above and send it back to Discord to finalize
	// the UDP connection handshake.
	data := voiceUDPOp{1, voiceUDPD{"udp", voiceUDPData{ip, port, "plain"}}}

	err = v.wsConn.WriteJSON(data)
	if err != nil {
		fmt.Println("udpOpen write json error:", err)
		return
	}

	// start udpKeepAlive
	go v.udpKeepAlive(5 * time.Second)
	// TODO: find a way to check that it fired off okay

	return
}

// udpKeepAlive sends a udp packet to keep the udp connection open
// This is still a bit of a "proof of concept"
func (v *Voice) udpKeepAlive(i time.Duration) {

	var err error
	var sequence uint64

	packet := make([]byte, 8)

	ticker := time.NewTicker(i)
	for {
		// TODO: Add a way to break from loop

		binary.LittleEndian.PutUint64(packet, sequence)
		sequence++

		_, err = v.UDPConn.Write(packet)
		if err != nil {
			fmt.Println("udpKeepAlive udp write error : ", err)
			return
		}
		<-ticker.C
	}
}

// opusSender will listen on the given channel and send any
// pre-encoded opus audio to Discord.  Supposedly.
func (v *Voice) opusSender(opus <-chan []byte, rate, size int) {

	// TODO: Better checking to prevent this from running more than
	// one instance at a time.
	v.Lock()
	if opus == nil {
		v.Unlock()
		return
	}
	v.Unlock()

	runtime.LockOSThread()

	// Voice is now ready to receive audio packets
	// TODO: this needs reviewed as I think there must be a better way.
	v.Ready = true
	defer func() { v.Ready = false }()

	var sequence uint16
	var timestamp uint32
	udpHeader := make([]byte, 12)

	// build the parts that don't change in the udpHeader
	udpHeader[0] = 0x80
	udpHeader[1] = 0x78
	binary.BigEndian.PutUint32(udpHeader[8:], v.OP2.SSRC)

	// start a send loop that loops until buf chan is closed
	ticker := time.NewTicker(time.Millisecond * time.Duration(size/(rate/1000)))
	for {

		// Add sequence and timestamp to udpPacket
		binary.BigEndian.PutUint16(udpHeader[2:], sequence)
		binary.BigEndian.PutUint32(udpHeader[4:], timestamp)

		// Get data from chan.  If chan is closed, return.
		recvbuf, ok := <-opus
		if !ok {
			return
		}

		// Combine the UDP Header and the opus data
		sendbuf := append(udpHeader, recvbuf...)

		// block here until we're exactly at the right time :)
		// Then send rtp audio packet to Discord over UDP
		<-ticker.C
		_, err := v.UDPConn.Write(sendbuf)

		if err != nil {
			fmt.Println("error writing to udp connection: ", err)
		}

		if (sequence) == 0xFFFF {
			sequence = 0
		} else {
			sequence++
		}

		if (timestamp + uint32(size)) >= 0xFFFFFFFF {
			timestamp = 0
		} else {
			timestamp += uint32(size)
		}
	}
}
<commit_msg>Added opusReceiver.<commit_after>// Discordgo - Discord bindings for Go
// Available at https://github.com/bwmarrin/discordgo

// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains code related to Discord voice suppport

package discordgo

import (
	"encoding/binary"
	"encoding/json"
	"fmt"
	"net"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/gorilla/websocket"
)

// ------------------------------------------------------------------------------------------------
// Code related to both Voice Websocket and UDP connections.
// ------------------------------------------------------------------------------------------------

// A Voice struct holds all data and functions related to Discord Voice support.
type Voice struct {
	sync.Mutex              // future use
	Ready      bool         // If true, voice is ready to send/receive audio
	Debug      bool         // If true, print extra logging
	OP2        *voiceOP2    // exported for dgvoice, may change.
	OpusSend   chan []byte  // Chan for sending opus audio
	OpusRecv   chan *Packet // Chan for receiving opus audio
	//	FrameRate  int         // This can be used to set the FrameRate of Opus data
	//	FrameSize  int         // This can be used to set the FrameSize of Opus data

	wsConn  *websocket.Conn
	UDPConn *net.UDPConn // this will become unexported soon.

	sessionID string
	token     string
	endpoint  string
	guildID   string
	channelID string
	userID    string
}

// ------------------------------------------------------------------------------------------------
// Code related to the Voice websocket connection
// ------------------------------------------------------------------------------------------------

// A voiceOP2 stores the data for the voice operation 2 websocket event
// which is sort of like the voice READY packet
type voiceOP2 struct {
	SSRC              uint32        `json:"ssrc"`
	Port              int           `json:"port"`
	Modes             []string      `json:"modes"`
	HeartbeatInterval time.Duration `json:"heartbeat_interval"`
}

type voiceHandshakeData struct {
	ServerID  string `json:"server_id"`
	UserID    string `json:"user_id"`
	SessionID string `json:"session_id"`
	Token     string `json:"token"`
}

type voiceHandshakeOp struct {
	Op   int                `json:"op"` // Always 0
	Data voiceHandshakeData `json:"d"`
}

// Open opens a voice connection.  This should be called
// after VoiceChannelJoin is used and the data VOICE websocket events
// are captured.
func (v *Voice) Open() (err error) {

	// TODO: How do we handle changing channels?

	// Don't open a websocket if one is already open
	if v.wsConn != nil {
		return
	}

	// Connect to Voice Websocket
	vg := fmt.Sprintf("wss://%s", strings.TrimSuffix(v.endpoint, ":80"))
	v.wsConn, _, err = websocket.DefaultDialer.Dial(vg, nil)
	if err != nil {
		fmt.Println("VOICE error opening websocket:", err)
		return
	}

	data := voiceHandshakeOp{0, voiceHandshakeData{v.guildID, v.userID, v.sessionID, v.token}}

	err = v.wsConn.WriteJSON(data)
	if err != nil {
		fmt.Println("VOICE error sending init packet:", err)
		return
	}

	// Start a listening for voice websocket events
	// TODO add a check here to make sure Listen worked by monitoring
	// a chan or bool?
	go v.wsListen()

	return
}

// Close closes the voice connection
func (v *Voice) Close() {

	if v.UDPConn != nil {
		err := v.UDPConn.Close()
		if err != nil {
			fmt.Println("error closing udp connection: ", err)
		}
	}

	if v.wsConn != nil {
		err := v.wsConn.Close()
		if err != nil {
			fmt.Println("error closing websocket connection: ", err)
		}
	}
}

// wsListen listens on the voice websocket for messages and passes them
// to the voice event handler.  This is automatically called by the Open func
func (v *Voice) wsListen() {

	for {
		messageType, message, err := v.wsConn.ReadMessage()
		if err != nil {
			// TODO: Handle this problem better.
			// TODO: needs proper logging
			fmt.Println("Voice Listen Error:", err)
			break
		}

		// Pass received message to voice event handler
		go v.wsEvent(messageType, message)
	}

	return
}

// wsEvent handles any voice websocket events. This is only called by the
// wsListen() function.
func (v *Voice) wsEvent(messageType int, message []byte) {

	if v.Debug {
		fmt.Println("wsEvent received: ", messageType)
		printJSON(message)
	}

	var e Event
	if err := json.Unmarshal(message, &e); err != nil {
		fmt.Println("wsEvent Unmarshall error: ", err)
		return
	}

	switch e.Operation {

	case 2: // READY

		v.OP2 = &voiceOP2{}
		if err := json.Unmarshal(e.RawData, v.OP2); err != nil {
			fmt.Println("voiceWS.onEvent OP2 Unmarshall error: ", err)
			printJSON(e.RawData) // TODO: Better error logging
			return
		}

		// Start the voice websocket heartbeat to keep the connection alive
		go v.wsHeartbeat(v.OP2.HeartbeatInterval)
		// TODO monitor a chan/bool to verify this was successful

		// Start the UDP connection
		err := v.udpOpen()
		if err != nil {
			fmt.Println("Error opening udp connection: ", err)
			return
		}

		// Start the opusSender.
		// TODO: Should we allow 48000/960 values to be user defined?
		v.OpusSend = make(chan []byte, 2)
		go v.opusSender(v.OpusSend, 48000, 960)

		// Start the opusReceiver
		v.OpusRecv = make(chan *Packet, 2)
		go v.opusReceiver(v.OpusRecv)
		return

	case 3: // HEARTBEAT response
		// add code to use this to track latency?
		return

	case 4:
		// TODO

	case 5:
		// SPEAKING TRUE/FALSE NOTIFICATION
		/*
			{
				"user_id": "1238921738912",
				"ssrc": 2,
				"speaking": false
			}
		*/

	default:
		fmt.Println("UNKNOWN VOICE OP: ", e.Operation)
		printJSON(e.RawData)
	}

	return
}

type voiceHeartbeatOp struct {
	Op   int `json:"op"` // Always 3
	Data int `json:"d"`
}

// wsHeartbeat sends regular heartbeats to voice Discord so it knows the client
// is still connected.  If you do not send these heartbeats Discord will
// disconnect the websocket connection after a few seconds.
func (v *Voice) wsHeartbeat(i time.Duration) {

	ticker := time.NewTicker(i * time.Millisecond)
	for {
		err := v.wsConn.WriteJSON(voiceHeartbeatOp{3, int(time.Now().Unix())})
		if err != nil {
			v.Ready = false
			fmt.Println("wsHeartbeat send error: ", err)
			return // TODO better logging
		}
		<-ticker.C
	}
}

type voiceSpeakingData struct {
	Speaking bool `json:"speaking"`
	Delay    int  `json:"delay"`
}

type voiceSpeakingOp struct {
	Op   int               `json:"op"` // Always 5
	Data voiceSpeakingData `json:"d"`
}

// Speaking sends a speaking notification to Discord over the voice websocket.
// This must be sent as true prior to sending audio and should be set to false
// once finished sending audio.
//  b  : Send true if speaking, false if not.
func (v *Voice) Speaking(b bool) (err error) {

	if v.wsConn == nil {
		return fmt.Errorf("No Voice websocket.")
	}

	data := voiceSpeakingOp{5, voiceSpeakingData{b, 0}}
	err = v.wsConn.WriteJSON(data)
	if err != nil {
		fmt.Println("Speaking() write json error:", err)
		return
	}

	return
}

// ------------------------------------------------------------------------------------------------
// Code related to the Voice UDP connection
// ------------------------------------------------------------------------------------------------

type voiceUDPData struct {
	Address string `json:"address"` // Public IP of machine running this code
	Port    uint16 `json:"port"`    // UDP Port of machine running this code
	Mode    string `json:"mode"`    // plain or ?  (plain or encrypted)
}

type voiceUDPD struct {
	Protocol string       `json:"protocol"` // Always "udp" ?
	Data     voiceUDPData `json:"data"`
}

type voiceUDPOp struct {
	Op   int       `json:"op"` // Always 1
	Data voiceUDPD `json:"d"`
}

// udpOpen opens a UDP connection to the voice server and completes the
// initial required handshake.  This connection is left open in the session
// and can be used to send or receive audio.  This should only be called
// from voice.wsEvent OP2
func (v *Voice) udpOpen() (err error) {

	host := fmt.Sprintf("%s:%d", strings.TrimSuffix(v.endpoint, ":80"), v.OP2.Port)
	addr, err := net.ResolveUDPAddr("udp", host)
	if err != nil {
		fmt.Println("udpOpen resolve addr error: ", err)
		// TODO better logging
		return
	}

	v.UDPConn, err = net.DialUDP("udp", nil, addr)
	if err != nil {
		fmt.Println("udpOpen dial udp error: ", err)
		// TODO better logging
		return
	}

	// Create a 70 byte array and put the SSRC code from the Op 2 Voice event
	// into it.  Then send that over the UDP connection to Discord
	sb := make([]byte, 70)
	binary.BigEndian.PutUint32(sb, v.OP2.SSRC)
	_, err = v.UDPConn.Write(sb)
	if err != nil {
		fmt.Println("udpOpen udp write error : ", err)
		// TODO better logging
		return
	}

	// Create a 70 byte array and listen for the initial handshake response
	// from Discord.  Once we get it parse the IP and PORT information out
	// of the response.  This should be our public IP and PORT as Discord
	// saw us.
	rb := make([]byte, 70)
	rlen, _, err := v.UDPConn.ReadFromUDP(rb)
	if err != nil {
		fmt.Println("udpOpen udp read error : ", err)
		// TODO better logging
		return
	}
	if rlen < 70 {
		fmt.Println("Voice RLEN should be 70 but isn't")
	}

	// Loop over position 4 though 20 to grab the IP address
	// Should never be beyond position 20.
	var ip string
	for i := 4; i < 20; i++ {
		if rb[i] == 0 {
			break
		}
		ip += string(rb[i])
	}

	// Grab port from position 68 and 69
	port := binary.LittleEndian.Uint16(rb[68:70])

	// Take the data from above and send it back to Discord to finalize
	// the UDP connection handshake.
	data := voiceUDPOp{1, voiceUDPD{"udp", voiceUDPData{ip, port, "plain"}}}

	err = v.wsConn.WriteJSON(data)
	if err != nil {
		fmt.Println("udpOpen write json error:", err)
		return
	}

	// start udpKeepAlive
	go v.udpKeepAlive(5 * time.Second)
	// TODO: find a way to check that it fired off okay

	return
}

// udpKeepAlive sends a udp packet to keep the udp connection open
// This is still a bit of a "proof of concept"
func (v *Voice) udpKeepAlive(i time.Duration) {

	var err error
	var sequence uint64

	packet := make([]byte, 8)

	ticker := time.NewTicker(i)
	for {
		// TODO: Add a way to break from loop

		binary.LittleEndian.PutUint64(packet, sequence)
		sequence++

		_, err = v.UDPConn.Write(packet)
		if err != nil {
			fmt.Println("udpKeepAlive udp write error : ", err)
			return
		}
		<-ticker.C
	}
}

// opusSender will listen on the given channel and send any
// pre-encoded opus audio to Discord.  Supposedly.
func (v *Voice) opusSender(opus <-chan []byte, rate, size int) {

	// TODO: Better checking to prevent this from running more than
	// one instance at a time.
	v.Lock()
	if opus == nil {
		v.Unlock()
		return
	}
	v.Unlock()

	runtime.LockOSThread()

	// Voice is now ready to receive audio packets
	// TODO: this needs reviewed as I think there must be a better way.
	v.Ready = true
	defer func() { v.Ready = false }()

	var sequence uint16
	var timestamp uint32
	udpHeader := make([]byte, 12)

	// build the parts that don't change in the udpHeader
	udpHeader[0] = 0x80
	udpHeader[1] = 0x78
	binary.BigEndian.PutUint32(udpHeader[8:], v.OP2.SSRC)

	// start a send loop that loops until buf chan is closed
	ticker := time.NewTicker(time.Millisecond * time.Duration(size/(rate/1000)))
	for {

		// Add sequence and timestamp to udpPacket
		binary.BigEndian.PutUint16(udpHeader[2:], sequence)
		binary.BigEndian.PutUint32(udpHeader[4:], timestamp)

		// Get data from chan.  If chan is closed, return.
		recvbuf, ok := <-opus
		if !ok {
			return
		}

		// Combine the UDP Header and the opus data
		sendbuf := append(udpHeader, recvbuf...)

		// block here until we're exactly at the right time :)
		// Then send rtp audio packet to Discord over UDP
		<-ticker.C
		_, err := v.UDPConn.Write(sendbuf)

		if err != nil {
			fmt.Println("error writing to udp connection: ", err)
		}

		if (sequence) == 0xFFFF {
			sequence = 0
		} else {
			sequence++
		}

		if (timestamp + uint32(size)) >= 0xFFFFFFFF {
			timestamp = 0
		} else {
			timestamp += uint32(size)
		}
	}
}

type Packet struct {
	SSRC      uint32
	Sequence  uint16
	Timestamp uint32
	Type      []byte
	Opus      []byte
	PCM       []int16
}

// opusReceiver listens on the UDP socket for incomming packets
// and sends them across the given channel
// NOTE :: This function may change names later.
func (v *Voice) opusReceiver(c chan *Packet) {

	// TODO: Better checking to prevent this from running more than
	// one instance at a time.
	v.Lock()
	if c == nil {
		v.Unlock()
		return
	}
	v.Unlock()

	p := Packet{}
	recvbuf := make([]byte, 1024)

	for {
		rlen, err := v.UDPConn.Read(recvbuf)
		if err != nil {
			fmt.Println("opusReceiver UDP Read error:", err)
			return
		}

		// For now, skip anything except audio.
		if rlen < 12 || recvbuf[0] != 0x80 {
			continue
		}

		p.Type = recvbuf[0:2]
		p.Sequence = binary.BigEndian.Uint16(recvbuf[2:4])
		p.Timestamp = binary.BigEndian.Uint32(recvbuf[4:8])
		p.SSRC = binary.BigEndian.Uint32(recvbuf[8:12])
		p.Opus = recvbuf[12:rlen]

		if c != nil {
			c <- &p
		}
	}
}
<|endoftext|>


<commit_before>package es_generate

import (
	"github.com/watermint/toolbox/essentials/go/es_project"
	"github.com/watermint/toolbox/infra/control/app_control"
	"github.com/watermint/toolbox/infra/recipe/rc_recipe"
	"github.com/watermint/toolbox/quality/recipe/qtr_endtoend"
	"reflect"
	"testing"
)

func TestScannerImpl(t *testing.T) {
	rr, err := es_project.DetectRepositoryRoot()
	if err != nil {
		t.Error(err)
		return
	}
	qtr_endtoend.TestWithControl(t, func(ctl app_control.Control) {
		sc, err := NewScanner(ctl, rr)
		if err != nil {
			t.Error(err)
			return
		}
		sc = sc.ExcludeTest().PathFilterPrefix("recipe")

		sts, err := sc.FindStructHasPrefix("Msg")
		if err != nil {
			t.Error(err)
			return
		}

		if len(sts) < 1 {
			t.Error("not found")
		}

		sts, err = sc.FindStructImplements(reflect.TypeOf((*rc_recipe.Recipe)(nil)).Elem())
		if err != nil {
			t.Error(err)
		}
		if len(sts) < 1 {
			t.Error("not found")
		}
	})
}
<commit_msg>skip time consuming tests<commit_after>package es_generate

import (
	"github.com/watermint/toolbox/essentials/go/es_project"
	"github.com/watermint/toolbox/infra/control/app_control"
	"github.com/watermint/toolbox/infra/recipe/rc_recipe"
	"github.com/watermint/toolbox/quality/infra/qt_endtoend"
	"github.com/watermint/toolbox/quality/recipe/qtr_endtoend"
	"reflect"
	"testing"
)

func TestScannerImpl(t *testing.T) {
	if qt_endtoend.IsSkipEndToEndTest() {
		t.Skipped()
		return
	}
	rr, err := es_project.DetectRepositoryRoot()
	if err != nil {
		t.Error(err)
		return
	}
	qtr_endtoend.TestWithControl(t, func(ctl app_control.Control) {
		sc, err := NewScanner(ctl, rr)
		if err != nil {
			t.Error(err)
			return
		}
		sc = sc.ExcludeTest().PathFilterPrefix("recipe")

		sts, err := sc.FindStructHasPrefix("Msg")
		if err != nil {
			t.Error(err)
			return
		}

		if len(sts) < 1 {
			t.Error("not found")
		}

		sts, err = sc.FindStructImplements(reflect.TypeOf((*rc_recipe.Recipe)(nil)).Elem())
		if err != nil {
			t.Error(err)
		}
		if len(sts) < 1 {
			t.Error("not found")
		}
	})
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"strconv"
	"strings"
)

// Version ...
type Version struct {
	Major uint64
	Minor uint64
	Patch uint64
}

func (v Version) String() string {
	s := make([]byte, 0, 5)
	s = strconv.AppendUint(s, v.Major, 10)
	s = append(s, '.')
	s = strconv.AppendUint(s, v.Minor, 10)
	s = append(s, '.')
	s = strconv.AppendUint(s, v.Patch, 10)

	return string(s)
}

func bumpVersion(v Version, bumpLevel string) (Version, error) {

	if bumpLevel == "patch" {
		v.Patch++
	} else if bumpLevel == "minor" {
		v.Minor++
		v.Patch = 0
	} else if bumpLevel == "major" {
		v.Major++
		v.Minor = 0
		v.Patch = 0
	} else {
		return Version{}, fmt.Errorf("Bump level '%s' unknown", bumpLevel)
	}

	return v, nil
}

func gitCommit(path, bumpLevel, newVersion, gitRemotes string, gitPush bool) {

	// git add
	fmt.Println("Adding changes to Git index")
	os.Chdir(path)
	_, err := exec.Command("git", "add", "metadata.rb").Output()
	if err != nil {
		log.Fatal(err)
	}

	if gitPush {

		// git commit
		fmt.Println("Committing changes")
		commitMessage := fmt.Sprintf("%s bump to version %s", bumpLevel, newVersion)
		_, err := exec.Command("git", "commit", "-m", commitMessage, "-n").Output()
		if err != nil {
			log.Fatal(err)
		}

		// git tag
		fmt.Printf("Adding tag '%s'\n", newVersion)
		tagMessage := fmt.Sprintf("%s", newVersion)
		_, err = exec.Command("git", "tag", "-a", newVersion, "-m", tagMessage).Output()
		if err != nil {
			log.Fatal(err)
		}

		// git push to gitRemotes
		remotes := strings.Split(gitRemotes, ",")
		for i := range remotes {
			remote := strings.TrimSpace(remotes[i])
			fmt.Printf("Pushing changes to '%s'\n", remote)
			_, err = exec.Command("git", "push", remote, "master", "--follow-tags").Output()
			if err != nil {
				log.Fatal(err)
			}
		}

	}
}

func parseVersion(version string) (Version, error) {
	versionArray := strings.SplitN(version, ".", 3)
	if len(versionArray) != 3 {
		return Version{}, fmt.Errorf("No major/minor/patch elements found")
	}

	var err error
	v := Version{}

	// major
	v.Major, err = strconv.ParseUint(versionArray[0], 10, 64)
	if err != nil {
		return Version{}, err
	}

	// minor
	v.Minor, err = strconv.ParseUint(versionArray[1], 10, 64)
	if err != nil {
		return Version{}, err
	}

	// patch
	v.Patch, err = strconv.ParseUint(versionArray[2], 10, 64)
	if err != nil {
		return Version{}, err
	}

	return v, nil
}

func metadata(path, bumpLevel string) string {
	path += "/metadata.rb"
	input, err := ioutil.ReadFile(path)
	if err != nil {
		log.Fatalln(err)
	}

	lines := strings.Split(string(input), "\n")
	newVersion := Version{}

	for i, line := range lines {
		if strings.Contains(line, "version") {
			lineArray := strings.Split(line, "'")
			fmt.Printf("Current version: %s\n", lineArray[1])

			version, err := parseVersion(lineArray[1])
			if err != nil {
				log.Fatalln(err)
			}

			newVersion, err = bumpVersion(version, bumpLevel)
			if err != nil {
				log.Fatalln(err)
			}

			lineArray[1] = newVersion.String()
			fmt.Printf("New version: %s\n", lineArray[1])

			lines[i] = strings.Join(lineArray, "'")
		}
	}

	output := strings.Join(lines, "\n")
	err = ioutil.WriteFile(path, []byte(output), 0644)
	if err != nil {
		log.Fatalln(err)
	}

	return newVersion.String()
}

func main() {
	path := flag.String("path", "", "Full or relative path to the cookbook directory. REQUIRED.")
	bumpLevel := flag.String("bump-level", "patch", "Version level to bump the cookbook")
	gitPush := flag.Bool("git-push", true, "Whether or not changes should be committed.")
	gitRemotes := flag.String("git-remotes", "upstream, origin", "Comma separated list of Git remotes")
	flag.Parse()

	if *path == "" {
		flag.PrintDefaults()
		os.Exit(1)
	}

	newVersion := metadata(*path, *bumpLevel)
	gitCommit(*path, *bumpLevel, newVersion, *gitRemotes, *gitPush)
}
<commit_msg>Support double quotes for the version value on metadata.rb file<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"strconv"
	"strings"
)

// Version ...
type Version struct {
	Major uint64
	Minor uint64
	Patch uint64
}

func (v Version) String() string {
	s := make([]byte, 0, 5)
	s = strconv.AppendUint(s, v.Major, 10)
	s = append(s, '.')
	s = strconv.AppendUint(s, v.Minor, 10)
	s = append(s, '.')
	s = strconv.AppendUint(s, v.Patch, 10)

	return string(s)
}

func bumpVersion(v Version, bumpLevel string) (Version, error) {

	if bumpLevel == "patch" {
		v.Patch++
	} else if bumpLevel == "minor" {
		v.Minor++
		v.Patch = 0
	} else if bumpLevel == "major" {
		v.Major++
		v.Minor = 0
		v.Patch = 0
	} else {
		return Version{}, fmt.Errorf("Bump level '%s' unknown", bumpLevel)
	}

	return v, nil
}

func gitCommit(path, bumpLevel, newVersion, gitRemotes string, gitPush bool) {

	// git add
	fmt.Println("Adding changes to Git index")
	os.Chdir(path)
	_, err := exec.Command("git", "add", "metadata.rb").Output()
	if err != nil {
		log.Fatal(err)
	}

	if gitPush {

		// git commit
		fmt.Println("Committing changes")
		commitMessage := fmt.Sprintf("%s bump to version %s", bumpLevel, newVersion)
		_, err := exec.Command("git", "commit", "-m", commitMessage, "-n").Output()
		if err != nil {
			log.Fatal(err)
		}

		// git tag
		fmt.Printf("Adding tag '%s'\n", newVersion)
		tagMessage := fmt.Sprintf("%s", newVersion)
		_, err = exec.Command("git", "tag", "-a", newVersion, "-m", tagMessage).Output()
		if err != nil {
			log.Fatal(err)
		}

		// git push to gitRemotes
		remotes := strings.Split(gitRemotes, ",")
		for i := range remotes {
			remote := strings.TrimSpace(remotes[i])
			fmt.Printf("Pushing changes to '%s'\n", remote)
			_, err = exec.Command("git", "push", remote, "master", "--follow-tags").Output()
			if err != nil {
				log.Fatal(err)
			}
		}

	}
}

func parseVersion(version string) (Version, error) {
	versionArray := strings.SplitN(version, ".", 3)
	if len(versionArray) != 3 {
		return Version{}, fmt.Errorf("No major/minor/patch elements found")
	}

	var err error
	v := Version{}

	// major
	v.Major, err = strconv.ParseUint(versionArray[0], 10, 64)
	if err != nil {
		return Version{}, err
	}

	// minor
	v.Minor, err = strconv.ParseUint(versionArray[1], 10, 64)
	if err != nil {
		return Version{}, err
	}

	// patch
	v.Patch, err = strconv.ParseUint(versionArray[2], 10, 64)
	if err != nil {
		return Version{}, err
	}

	return v, nil
}

func metadata(path, bumpLevel string) string {
	path += "/metadata.rb"
	input, err := ioutil.ReadFile(path)
	if err != nil {
		log.Fatalln(err)
	}

	lines := strings.Split(string(input), "\n")
	newVersion := Version{}

	for i, line := range lines {
		if strings.Contains(line, "version") {
			line = strings.Replace(line, "\"", "'", 2)
			lineArray := strings.Split(line, "'")
			fmt.Printf("Current version: %s\n", lineArray[1])

			version, err := parseVersion(lineArray[1])
			if err != nil {
				log.Fatalln(err)
			}

			newVersion, err = bumpVersion(version, bumpLevel)
			if err != nil {
				log.Fatalln(err)
			}

			lineArray[1] = newVersion.String()
			fmt.Printf("New version: %s\n", lineArray[1])

			lines[i] = strings.Join(lineArray, "'")
		}
	}

	output := strings.Join(lines, "\n")
	err = ioutil.WriteFile(path, []byte(output), 0644)
	if err != nil {
		log.Fatalln(err)
	}

	return newVersion.String()
}

func main() {
	path := flag.String("path", "", "Full or relative path to the cookbook directory. REQUIRED.")
	bumpLevel := flag.String("bump-level", "patch", "Version level to bump the cookbook")
	gitPush := flag.Bool("git-push", true, "Whether or not changes should be committed.")
	gitRemotes := flag.String("git-remotes", "upstream, origin", "Comma separated list of Git remotes")
	flag.Parse()

	if *path == "" {
		flag.PrintDefaults()
		os.Exit(1)
	}

	newVersion := metadata(*path, *bumpLevel)
	gitCommit(*path, *bumpLevel, newVersion, *gitRemotes, *gitPush)
}
<|endoftext|>


<commit_before>package bitballoon

import (
	"bytes"
	"fmt"
	"net/http"
	"reflect"
	"strings"
	"testing"
)

func TestDeploysService_List(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/api/v1/deploys", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		fmt.Fprint(w, `[{"id":"first"},{"id":"second"}]`)
	})

	deploys, _, err := client.Deploys.List(&ListOptions{})
	if err != nil {
		t.Errorf("Deploys.List returned an error: %v", err)
	}

	expected := []Deploy{{Id: "first"}, {Id: "second"}}
	if !reflect.DeepEqual(deploys, expected) {
		t.Errorf("Expected Deploys.List to return %v, returned %v", expected, deploys)
	}
}

func TestDeploysService_List_For_Site(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/api/v1/sites/first-site/deploys", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		fmt.Fprint(w, `[{"id":"first"},{"id":"second"}]`)
	})

	site := &Site{Id: "first-site", client: client}
	site.Deploys = &DeploysService{client: client, site: site}

	deploys, _, err := site.Deploys.List(&ListOptions{})
	if err != nil {
		t.Errorf("Deploys.List returned an error: %v", err)
	}

	expected := []Deploy{{Id: "first"}, {Id: "second"}}
	if !reflect.DeepEqual(deploys, expected) {
		t.Errorf("Expected Deploys.List to return %v, returned %v", expected, deploys)
	}
}

func TestDeploysService_Get(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/api/v1/deploys/my-deploy", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		fmt.Fprint(w, `{"id":"my-deploy"}`)
	})

	deploy, _, err := client.Deploys.Get("my-deploy")
	if err != nil {
		t.Errorf("Sites.Get returned an error: %v", err)
	}

	if deploy.Id != "my-deploy" {
		t.Errorf("Expected Sites.Get to return my-deploy, returned %v", deploy.Id)
	}
}

func TestDeploysService_Create(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/api/v1/sites/my-site/deploys", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "POST")
		buf := new(bytes.Buffer)
		buf.ReadFrom(r.Body)

		expected := `{"files":{"index.html":"3c7d0500e11e9eb9954ad3d9c2a1bd8b0fa06d88","style.css":"7b797fc1c66448cd8685c5914a571763e8a213da"},"Draft":false}`
		if expected != strings.TrimSpace(buf.String()) {
			t.Errorf("Expected JSON: %v\nGot JSON: %v", expected, buf.String())
		}
		fmt.Fprint(w, `{"id":"my-deploy"})`)
	})

	site := &Site{Id: "my-site"}
	deploys := &DeploysService{client: client, site: site}
	deploy, _, err := deploys.Create("test-site/folder")

	if err != nil {
		t.Errorf("Deploys.Create returned and error: %v", err)
	}

	if deploy.Id != "my-deploy" {
		t.Errorf("Expected Deploys.Create to return my-deploy, returned %v", deploy.Id)
	}
}

func TestDeploysService_CreateDraft(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/api/v1/sites/my-site/deploys", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "POST")
		buf := new(bytes.Buffer)
		buf.ReadFrom(r.Body)

		expected := `{"files":{"index.html":"3c7d0500e11e9eb9954ad3d9c2a1bd8b0fa06d88","style.css":"7b797fc1c66448cd8685c5914a571763e8a213da"},"Draft":true}`
		if expected != strings.TrimSpace(buf.String()) {
			t.Errorf("Expected JSON: %v\nGot JSON: %v", expected, buf.String())
		}
		fmt.Fprint(w, `{"id":"my-deploy"})`)
	})

	site := &Site{Id: "my-site"}
	deploys := &DeploysService{client: client, site: site}
	deploy, _, err := deploys.CreateDraft("test-site/folder")

	if err != nil {
		t.Errorf("Deploys.Create returned and error: %v", err)
	}

	if deploy.Id != "my-deploy" {
		t.Errorf("Expected Deploys.Create to return my-deploy, returned %v", deploy.Id)
	}
}

func TestDeploysService_Create_Zip(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/api/v1/sites/my-site/deploys", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "POST")
		r.ParseForm()
		if _,ok := r.Form["draft"]; ok {
			t.Errorf("Draft should not be a query parameter for a normal deploy")
		}
		if r.Header["Content-Type"][0] != "application/zip" {
			t.Errorf("Deploying a zip should set the content type to application/zip")
		}
		fmt.Fprint(w, `{"id":"my-deploy"})`)
	})

	site := &Site{Id: "my-site"}
	deploys := &DeploysService{client: client, site: site}
	deploy, _, err := deploys.Create("test-site/archive.zip")

	if err != nil {
		t.Errorf("Deploys.Create returned and error: %v", err)
	}

	if deploy.Id != "my-deploy" {
		t.Errorf("Expected Deploys.Create to return my-deploy, returned %v", deploy.Id)
	}
}

func TestDeploysService_CreateDraft_Zip(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/api/v1/sites/my-site/deploys", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "POST")
		r.ParseForm()
		if val,ok := r.Form["draft"]; ok == false || val[0] != "true" {
			t.Errorf("Draft should be a true parameter for a draft deploy")
		}
		if r.Header["Content-Type"][0] != "application/zip" {
			t.Errorf("Deploying a zip should set the content type to application/zip")
		}
		fmt.Fprint(w, `{"id":"my-deploy"})`)
	})

	site := &Site{Id: "my-site"}
	deploys := &DeploysService{client: client, site: site}
	deploy, _, err := deploys.CreateDraft("test-site/archive.zip")

	if err != nil {
		t.Errorf("Deploys.Create returned and error: %v", err)
	}

	if deploy.Id != "my-deploy" {
		t.Errorf("Expected Deploys.Create to return my-deploy, returned %v", deploy.Id)
	}
}
<commit_msg>Executed go fmt to fix styling issues<commit_after>package bitballoon

import (
	"bytes"
	"fmt"
	"net/http"
	"reflect"
	"strings"
	"testing"
)

func TestDeploysService_List(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/api/v1/deploys", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		fmt.Fprint(w, `[{"id":"first"},{"id":"second"}]`)
	})

	deploys, _, err := client.Deploys.List(&ListOptions{})
	if err != nil {
		t.Errorf("Deploys.List returned an error: %v", err)
	}

	expected := []Deploy{{Id: "first"}, {Id: "second"}}
	if !reflect.DeepEqual(deploys, expected) {
		t.Errorf("Expected Deploys.List to return %v, returned %v", expected, deploys)
	}
}

func TestDeploysService_List_For_Site(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/api/v1/sites/first-site/deploys", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		fmt.Fprint(w, `[{"id":"first"},{"id":"second"}]`)
	})

	site := &Site{Id: "first-site", client: client}
	site.Deploys = &DeploysService{client: client, site: site}

	deploys, _, err := site.Deploys.List(&ListOptions{})
	if err != nil {
		t.Errorf("Deploys.List returned an error: %v", err)
	}

	expected := []Deploy{{Id: "first"}, {Id: "second"}}
	if !reflect.DeepEqual(deploys, expected) {
		t.Errorf("Expected Deploys.List to return %v, returned %v", expected, deploys)
	}
}

func TestDeploysService_Get(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/api/v1/deploys/my-deploy", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		fmt.Fprint(w, `{"id":"my-deploy"}`)
	})

	deploy, _, err := client.Deploys.Get("my-deploy")
	if err != nil {
		t.Errorf("Sites.Get returned an error: %v", err)
	}

	if deploy.Id != "my-deploy" {
		t.Errorf("Expected Sites.Get to return my-deploy, returned %v", deploy.Id)
	}
}

func TestDeploysService_Create(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/api/v1/sites/my-site/deploys", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "POST")
		buf := new(bytes.Buffer)
		buf.ReadFrom(r.Body)

		expected := `{"files":{"index.html":"3c7d0500e11e9eb9954ad3d9c2a1bd8b0fa06d88","style.css":"7b797fc1c66448cd8685c5914a571763e8a213da"},"Draft":false}`
		if expected != strings.TrimSpace(buf.String()) {
			t.Errorf("Expected JSON: %v\nGot JSON: %v", expected, buf.String())
		}
		fmt.Fprint(w, `{"id":"my-deploy"})`)
	})

	site := &Site{Id: "my-site"}
	deploys := &DeploysService{client: client, site: site}
	deploy, _, err := deploys.Create("test-site/folder")

	if err != nil {
		t.Errorf("Deploys.Create returned and error: %v", err)
	}

	if deploy.Id != "my-deploy" {
		t.Errorf("Expected Deploys.Create to return my-deploy, returned %v", deploy.Id)
	}
}

func TestDeploysService_CreateDraft(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/api/v1/sites/my-site/deploys", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "POST")
		buf := new(bytes.Buffer)
		buf.ReadFrom(r.Body)

		expected := `{"files":{"index.html":"3c7d0500e11e9eb9954ad3d9c2a1bd8b0fa06d88","style.css":"7b797fc1c66448cd8685c5914a571763e8a213da"},"Draft":true}`
		if expected != strings.TrimSpace(buf.String()) {
			t.Errorf("Expected JSON: %v\nGot JSON: %v", expected, buf.String())
		}
		fmt.Fprint(w, `{"id":"my-deploy"})`)
	})

	site := &Site{Id: "my-site"}
	deploys := &DeploysService{client: client, site: site}
	deploy, _, err := deploys.CreateDraft("test-site/folder")

	if err != nil {
		t.Errorf("Deploys.Create returned and error: %v", err)
	}

	if deploy.Id != "my-deploy" {
		t.Errorf("Expected Deploys.Create to return my-deploy, returned %v", deploy.Id)
	}
}

func TestDeploysService_Create_Zip(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/api/v1/sites/my-site/deploys", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "POST")
		r.ParseForm()
		if _, ok := r.Form["draft"]; ok {
			t.Errorf("Draft should not be a query parameter for a normal deploy")
		}
		if r.Header["Content-Type"][0] != "application/zip" {
			t.Errorf("Deploying a zip should set the content type to application/zip")
		}
		fmt.Fprint(w, `{"id":"my-deploy"})`)
	})

	site := &Site{Id: "my-site"}
	deploys := &DeploysService{client: client, site: site}
	deploy, _, err := deploys.Create("test-site/archive.zip")

	if err != nil {
		t.Errorf("Deploys.Create returned and error: %v", err)
	}

	if deploy.Id != "my-deploy" {
		t.Errorf("Expected Deploys.Create to return my-deploy, returned %v", deploy.Id)
	}
}

func TestDeploysService_CreateDraft_Zip(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/api/v1/sites/my-site/deploys", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "POST")
		r.ParseForm()
		if val, ok := r.Form["draft"]; ok == false || val[0] != "true" {
			t.Errorf("Draft should be a true parameter for a draft deploy")
		}
		if r.Header["Content-Type"][0] != "application/zip" {
			t.Errorf("Deploying a zip should set the content type to application/zip")
		}
		fmt.Fprint(w, `{"id":"my-deploy"})`)
	})

	site := &Site{Id: "my-site"}
	deploys := &DeploysService{client: client, site: site}
	deploy, _, err := deploys.CreateDraft("test-site/archive.zip")

	if err != nil {
		t.Errorf("Deploys.Create returned and error: %v", err)
	}

	if deploy.Id != "my-deploy" {
		t.Errorf("Expected Deploys.Create to return my-deploy, returned %v", deploy.Id)
	}
}
<|endoftext|>