<commit_before>package main

import (
	"../geolib"
	"bufio"
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"time"
)

type GeoMetaData struct {
	DataSetName  string    `json:"ds_name"`
	TimeStamps      []string         `json:"timestamps"`
	FileNameFields map[string]string `json:"filename_fields"`
	Polygon        json.RawMessage   `json:"polygon"`
	RasterCount  int       `json:"raster_count"`
	Type           string            `json:"array_type"`
	XSize          int               `json:"x_size"`
	YSize          int               `json:"y_size"`
	ProjWKT        string            `json:"proj_wkt"`
	GeoTransform   []float64         `json:"geotransform"`
}

type GeoFile struct {
	Driver   string        `json:"file_type"`
	DataSets []GeoMetaData `json:"geo_metadata"`
}

var parserStrings map[string]string = map[string]string{"landsat": `LC(?P<mission>\d)(?P<path>\d\d\d)(?P<row>\d\d\d)(?P<year>\d\d\d\d)(?P<julian_day>\d\d\d)(?P<processing_level>[a-zA-Z0-9]+)_(?P<band>[a-zA-Z0-9]+)`,
				  "modis1": `M(?P<satellite>[OD|YD])(?P<product>[0-9]+_[A-Z0-9]+).A[0-9]+.[0-9]+.(?P<collection_version>\d\d\d).(?P<year>\d\d\d\d)(?P<julian_day>\d\d\d)(?P<hour>\d\d)(?P<minute>\d\d)(?P<second>\d\d)`,
				  "modis2": `MCD43A4.A[0-9]+.(?P<horizontal>h\d\d)(?P<vertical>v\d\d).(?P<resolution>\d\d\d).(?P<year>\d\d\d\d)(?P<julian_day>\d\d\d)(?P<hour>\d\d)(?P<minute>\d\d)(?P<second>\d\d)`,
				  "agdc_landsat1": `LS(?P<mission>\d)_(?P<sensor>[A-Z]+)_(?P<correction>[A-Z]+)_(?P<epsg>\d+)_(?P<x_coord>-?\d+)_(?P<y_coord>-?\d+)_(?P<year>\d\d\d\d).`,}

var parsers map[string]*regexp.Regexp = map[string]*regexp.Regexp{}
//var timeExtractors map[string]func(map[string] string) time.Time = map[string]func(map[string] string) time.Time{"landsat":landsatTime, "modis1": modisTime, "modis2": modisTime}

func init() {
	for key, value := range(parserStrings) {
		parsers[key] = regexp.MustCompile(value)
	}
}

func parseName(filePath string) (map[string]string, time.Time) {

	for _, r := range(parsers) {
		_, fileName := filepath.Split(filePath)

		if (r.MatchString(fileName)) {
			match := r.FindStringSubmatch(fileName)

			result := make(map[string]string)
			for i, name := range r.SubexpNames() {
				if i != 0 {
					result[name] = match[i]
				}
			}

			return result, parseTime(result)
		}
	}
	return nil, time.Time{}
}

func parseTime(nameFields map[string]string) time.Time {
	if _, ok := nameFields["year"]; ok {
		year, _ := strconv.Atoi(nameFields["year"])
		t := time.Date(year, 0, 0, 0, 0, 0, 0, time.UTC)
		if _, ok := nameFields["julian_day"]; ok {
			julianDay, _ := strconv.Atoi(nameFields["julian_day"])
			t = t.Add(time.Hour * 24 * time.Duration(julianDay))
		}
		if _, ok := nameFields["hour"]; ok {
			hour, _ := strconv.Atoi(nameFields["hour"])
			t = t.Add(time.Hour * time.Duration(hour))
		}
		if _, ok := nameFields["minute"]; ok {
			minute, _ := strconv.Atoi(nameFields["minute"])
			t = t.Add(time.Minute * time.Duration(minute))
		}
		if _, ok := nameFields["second"]; ok {
			second, _ := strconv.Atoi(nameFields["second"])
			t = t.Add(time.Second * time.Duration(second))
		}

		return t
	}
	return time.Time{}
}

func main() {
	s := bufio.NewScanner(os.Stdin)
	for s.Scan() {
		parts := strings.Split(s.Text(), "\t")
		if len(parts) != 2 {
			fmt.Printf("Input not recognised: %s\n", s.Text())
		}

		gdalFile := geolib.GDALFile{}
		err := json.Unmarshal([]byte(parts[1]), &gdalFile)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

		geoFile := GeoFile{Driver: gdalFile.Driver}

		nameFields, timeStamp := parseName(parts[0])

		for _, ds := range gdalFile.DataSets {
			if ds.ProjWKT != "" {
				poly := geolib.GetPolygon(ds.ProjWKT, ds.GeoTransform, ds.XSize, ds.YSize)
				polyWGS84 := poly.ReprojectToWGS84()

				var times []string
				if nc_times, ok := ds.Extras["nc_times"]; ok {
					times = nc_times
				} else {
					times = []string{timeStamp.Format("2006-01-02T15:04:05Z")}
				}

				geoFile.DataSets = append(geoFile.DataSets, GeoMetaData{DataSetName: ds.DataSetName, TimeStamps: times, FileNameFields: nameFields, Polygon: json.RawMessage(polyWGS84.ToGeoJSON()), RasterCount: ds.RasterCount, Type: ds.Type, XSize: ds.XSize, YSize: ds.YSize, ProjWKT: ds.ProjWKT, GeoTransform: ds.GeoTransform})

			}
		}
		out, err := json.Marshal(&geoFile)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		fmt.Printf("%s\tgdal\t%s\n", parts[0], string(out))

	}
}
<commit_msg>report unparsed files on stderr<commit_after>package main

import (
	"../geolib"
	"bufio"
	"encoding/json"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"time"
)

type GeoMetaData struct {
	DataSetName    string            `json:"ds_name"`
	TimeStamps     []string          `json:"timestamps"`
	FileNameFields map[string]string `json:"filename_fields"`
	Polygon        json.RawMessage   `json:"polygon"`
	RasterCount    int               `json:"raster_count"`
	Type           string            `json:"array_type"`
	XSize          int               `json:"x_size"`
	YSize          int               `json:"y_size"`
	ProjWKT        string            `json:"proj_wkt"`
	GeoTransform   []float64         `json:"geotransform"`
}

type GeoFile struct {
	Driver   string        `json:"file_type"`
	DataSets []GeoMetaData `json:"geo_metadata"`
}

//MCD12Q1.A2001001.h00v10.051.2014287161527.hdf
//MCD43A4.A2001337.h29v11.005.2007082233323.hdf
//MCD43A2.A2003025.h13v04.005.2007275170736.hdf

var parserStrings map[string]string = map[string]string{"landsat": `LC(?P<mission>\d)(?P<path>\d\d\d)(?P<row>\d\d\d)(?P<year>\d\d\d\d)(?P<julian_day>\d\d\d)(?P<processing_level>[a-zA-Z0-9]+)_(?P<band>[a-zA-Z0-9]+)`,
	"modis1":        `M(?P<satellite>[OD|YD])(?P<product>[0-9]+_[A-Z0-9]+).A[0-9]+.[0-9]+.(?P<collection_version>\d\d\d).(?P<year>\d\d\d\d)(?P<julian_day>\d\d\d)(?P<hour>\d\d)(?P<minute>\d\d)(?P<second>\d\d)`,
	"modis2":        `^(?P<product>MCD\d\d[A-Z]\d).A[0-9]+.(?P<horizontal>h\d\d)(?P<vertical>v\d\d).(?P<resolution>\d\d\d).(?P<year>\d\d\d\d)(?P<julian_day>\d\d\d)(?P<hour>\d\d)(?P<minute>\d\d)(?P<second>\d\d)`,
	"agdc_landsat1": `LS(?P<mission>\d)_(?P<sensor>[A-Z]+)_(?P<correction>[A-Z]+)_(?P<epsg>\d+)_(?P<x_coord>-?\d+)_(?P<y_coord>-?\d+)_(?P<year>\d\d\d\d).`}

var parsers map[string]*regexp.Regexp = map[string]*regexp.Regexp{}

//var timeExtractors map[string]func(map[string] string) time.Time = map[string]func(map[string] string) time.Time{"landsat":landsatTime, "modis1": modisTime, "modis2": modisTime}

func init() {
	for key, value := range parserStrings {
		parsers[key] = regexp.MustCompile(value)
	}
}

func parseName(filePath string) (map[string]string, time.Time) {

	for _, r := range parsers {
		_, fileName := filepath.Split(filePath)

		if r.MatchString(fileName) {
			match := r.FindStringSubmatch(fileName)

			result := make(map[string]string)
			for i, name := range r.SubexpNames() {
				if i != 0 {
					result[name] = match[i]
				}
			}

			return result, parseTime(result)
		}
	}

	return nil, time.Time{}
}

func parseTime(nameFields map[string]string) time.Time {
	if _, ok := nameFields["year"]; ok {
		year, _ := strconv.Atoi(nameFields["year"])
		t := time.Date(year, 0, 0, 0, 0, 0, 0, time.UTC)
		if _, ok := nameFields["julian_day"]; ok {
			julianDay, _ := strconv.Atoi(nameFields["julian_day"])
			t = t.Add(time.Hour * 24 * time.Duration(julianDay))
		}
		if _, ok := nameFields["hour"]; ok {
			hour, _ := strconv.Atoi(nameFields["hour"])
			t = t.Add(time.Hour * time.Duration(hour))
		}
		if _, ok := nameFields["minute"]; ok {
			minute, _ := strconv.Atoi(nameFields["minute"])
			t = t.Add(time.Minute * time.Duration(minute))
		}
		if _, ok := nameFields["second"]; ok {
			second, _ := strconv.Atoi(nameFields["second"])
			t = t.Add(time.Second * time.Duration(second))
		}

		return t
	}
	return time.Time{}
}

func main() {
	s := bufio.NewScanner(os.Stdin)
	for s.Scan() {
		parts := strings.Split(s.Text(), "\t")
		if len(parts) != 2 {
			fmt.Printf("Input not recognised: %s\n", s.Text())
		}

		gdalFile := geolib.GDALFile{}
		err := json.Unmarshal([]byte(parts[1]), &gdalFile)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

		geoFile := GeoFile{Driver: gdalFile.Driver}

		nameFields, timeStamp := parseName(parts[0])

		if nameFields != nil {

			for _, ds := range gdalFile.DataSets {
				if ds.ProjWKT != "" {
					poly := geolib.GetPolygon(ds.ProjWKT, ds.GeoTransform, ds.XSize, ds.YSize)
					polyWGS84 := poly.ReprojectToWGS84()

					var times []string
					if nc_times, ok := ds.Extras["nc_times"]; ok {
						times = nc_times
					} else {
						times = []string{timeStamp.Format("2006-01-02T15:04:05Z")}
					}

					geoFile.DataSets = append(geoFile.DataSets, GeoMetaData{DataSetName: ds.DataSetName, TimeStamps: times, FileNameFields: nameFields, Polygon: json.RawMessage(polyWGS84.ToGeoJSON()), RasterCount: ds.RasterCount, Type: ds.Type, XSize: ds.XSize, YSize: ds.YSize, ProjWKT: ds.ProjWKT, GeoTransform: ds.GeoTransform})

				}
			}
			out, err := json.Marshal(&geoFile)
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
			fmt.Printf("%s\tgdal\t%s\n", parts[0], string(out))
		} else {
			log.Printf("%s non parseable", parts[0])
		}
	}
}
<|endoftext|>


<commit_before>package blockdiag

import (
	"sort"
	"strings"
	"testing"
)

func TestShouldParser(t *testing.T) {
	for _, test := range []struct {
		description string
		input       string
		nodes       []string
		edges       []string
		attributes  map[string]string
	}{
		{
			"Empty diagram",
			`
blockdiag {}
`,
			[]string{},
			[]string{},
			map[string]string{},
		},
		{
			"Single Node",
			`
blockdiag {
	A;
}
`,
			[]string{"A"},
			[]string{},
			map[string]string{},
		},
		{
			// TODO Add test case for node chain without tailing ;
			"Node chain",
			`
blockdiag {
	A -> B;
}
`,
			[]string{"A", "B"},
			[]string{"A|B"},
			map[string]string{},
		},
		{
			"Multiple chains, using same nodes",
			`
blockdiag {
	A -> B -> C;
	A -> D;
}
`,
			[]string{"A", "B", "C", "D"},
			[]string{"A|B", "A|D", "B|C"},
			map[string]string{},
		},
		{
			"Self reference",
			`
blockdiag {
	A -> A;
}
`,
			[]string{"A"},
			[]string{"A|A"},
			map[string]string{},
		},
		{
			"Comment",
			`
# Comment
blockdiag # Comment
{
# Comment
	A; # Comment
# Comment
} # Comment
`,
			[]string{"A"},
			[]string{},
			map[string]string{},
		},
		{
			"Multi Char Node Names",
			`
blockdiag
{
	MultiCharNodeName1;
}
`,
			[]string{"MultiCharNodeName1"},
			[]string{},
			map[string]string{},
		},
		{
			"Digramm Attributes",
			`
blockdiag
{
	node_width = 128;
	A;
}
`,
			[]string{"A"},
			[]string{},
			map[string]string{
				"node_width": "128",
			},
		},
		{
			"Digramm type 'diagram'",
			`
diagram
{
	A;
}
`,
			[]string{"A"},
			[]string{},
			map[string]string{},
		},
	} {
		got, err := ParseReader("shouldparse.diag", strings.NewReader(test.input))
		if err != nil {
			t.Fatalf("%s: parse error: %s with input %s", test.description, err, test.input)
		}
		gotDiag, ok := got.(Diag)
		if !ok {
			t.Fatalf("%s: assertion error: %s should parse to diag", test.description, test.input)
		}
		if gotDiag.NodesString() != strings.Join(test.nodes, ", ") {
			t.Fatalf("%s: nodes error: %s, expected '%s', got: '%s'", test.description, test.input, strings.Join(test.nodes, ", "), gotDiag.NodesString())
		}
		if gotDiag.EdgesString() != strings.Join(test.edges, ", ") {
			t.Fatalf("%s edges error: %s, expected '%s', got: '%s'", test.description, test.input, strings.Join(test.edges, ", "), gotDiag.EdgesString())
		}

		var attributes []string
		for key, value := range test.attributes {
			attributes = append(attributes, key+"="+value)
		}
		sort.Strings(attributes)
		if gotDiag.AttributesString() != strings.Join(attributes, "\n") {
			t.Fatalf("%s attributes error: %s, expected '%s', got: '%s'", test.description, test.input, strings.Join(attributes, "\n"), gotDiag.AttributesString())
		}
	}
}

func TestShouldNotParse(t *testing.T) {
	for _, test := range []struct {
		description string
		input       string
	}{
		{
			"No block",
			`
blockdiag
`,
		},
	} {
		_, err := ParseReader("shouldnotparse.diag", strings.NewReader(test.input))
		if err == nil {
			t.Fatalf("%s: should not parse, but didn't give an error with input %s", test.description, test.input)
		}
	}
}

func TestCircular(t *testing.T) {
	for _, test := range []struct {
		input    string
		circular bool
	}{
		{
			`
blockdiag{
	A;
}
`,
			false,
		},
		{
			`
blockdiag{
	A -> B -> C;
}
`,
			false,
		},
		{
			`
blockdiag{
	A -> A;
}
`,
			false,
		},
		{
			`
blockdiag{
	A -> B -> C -> A;
}
`,
			true,
		},
	} {
		got, err := ParseReader("shouldnotparse.diag", strings.NewReader(test.input))
		if err != nil {
			t.Fatalf("should not parse, but didn't give an error with input %s", test.input)
		}
		gotDiag, ok := got.(Diag)
		if !ok {
			t.Fatalf("assertion error: %s should parse to diag", test.input)
		}
		if gotDiag.FindCircular() != test.circular {
			t.Fatalf("expect %s to be circular == %t", test.input, test.circular)
		}
	}
}

func TestGetStartNodes(t *testing.T) {
	for _, test := range []struct {
		input      string
		startNodes []string
	}{
		{
			`
blockdiag{
	A -> B -> C;
}
`,
			[]string{"A"},
		},
		{
			`
blockdiag {
	A -> B -> C;
	D;
	E -> F;
}
`,
			[]string{"A", "D", "E"},
		},
		{
			`
blockdiag {
	D;
	E -> F;
	A -> B -> C;
}
`,
			[]string{"A", "D", "E"},
		},
	} {
		got, err := ParseReader("placeingrid.diag", strings.NewReader(test.input))
		if err != nil {
			t.Fatalf("should not parse, but didn't give an error with input %s", test.input)
		}
		gotDiag, ok := got.(Diag)
		if !ok {
			t.Fatalf("assertion error: %s should parse to diag", test.input)
		}
		// if gotDiag.PlaceInGrid() != test.circular {
		// 	t.Fatalf("expect %s to be circular == %t", test.input, test.circular)
		// }
		startNodes := gotDiag.getStartNodes()
		if len(startNodes) != len(test.startNodes) {
			t.Fatalf("Start Nodes count wrong, expected: %s, got: %s", strings.Join(test.startNodes, ", "), startNodes)
		}
		sort.Sort(startNodes)
		sort.Strings(test.startNodes)
		for i, n := range startNodes {
			if n.Name != test.startNodes[i] {
				t.Fatalf("Start Nodes do not match, expected: %s, got: %s", strings.Join(test.startNodes, ", "), startNodes)
			}
		}
	}
}

func TestPlaceInGrid(t *testing.T) {
	for _, test := range []struct {
		input string
	}{
		{
			`
blockdiag{
	A -> B -> C;
}
`,
		},
		{
			`
blockdiag{
	A -> B -> C;
	B -> D;
	A -> E -> C;
}
`,
		},
	} {
		got, err := ParseReader("placeingrid.diag", strings.NewReader(test.input))
		if err != nil {
			t.Fatalf("should not parse, but didn't give an error with input %s", test.input)
		}
		gotDiag, ok := got.(Diag)
		if !ok {
			t.Fatalf("assertion error: %s should parse to diag", test.input)
		}
		// if gotDiag.PlaceInGrid() != test.circular {
		// 	t.Fatalf("expect %s to be circular == %t", test.input, test.circular)
		// }
		gotDiag.PlaceInGrid()
		t.Logf("%s\n", gotDiag.GridString())
	}
}
<commit_msg>Updated test, sort no longer needed<commit_after>package blockdiag

import (
	"sort"
	"strings"
	"testing"
)

func TestShouldParser(t *testing.T) {
	for _, test := range []struct {
		description string
		input       string
		nodes       []string
		edges       []string
		attributes  map[string]string
	}{
		{
			"Empty diagram",
			`
blockdiag {}
`,
			[]string{},
			[]string{},
			map[string]string{},
		},
		{
			"Single Node",
			`
blockdiag {
	A;
}
`,
			[]string{"A"},
			[]string{},
			map[string]string{},
		},
		{
			// TODO Add test case for node chain without tailing ;
			"Node chain",
			`
blockdiag {
	A -> B;
}
`,
			[]string{"A", "B"},
			[]string{"A|B"},
			map[string]string{},
		},
		{
			"Multiple chains, using same nodes",
			`
blockdiag {
	A -> B -> C;
	A -> D;
}
`,
			[]string{"A", "B", "C", "D"},
			[]string{"A|B", "A|D", "B|C"},
			map[string]string{},
		},
		{
			"Self reference",
			`
blockdiag {
	A -> A;
}
`,
			[]string{"A"},
			[]string{"A|A"},
			map[string]string{},
		},
		{
			"Comment",
			`
# Comment
blockdiag # Comment
{
# Comment
	A; # Comment
# Comment
} # Comment
`,
			[]string{"A"},
			[]string{},
			map[string]string{},
		},
		{
			"Multi Char Node Names",
			`
blockdiag
{
	MultiCharNodeName1;
}
`,
			[]string{"MultiCharNodeName1"},
			[]string{},
			map[string]string{},
		},
		{
			"Digramm Attributes",
			`
blockdiag
{
	node_width = 128;
	A;
}
`,
			[]string{"A"},
			[]string{},
			map[string]string{
				"node_width": "128",
			},
		},
		{
			"Digramm type 'diagram'",
			`
diagram
{
	A;
}
`,
			[]string{"A"},
			[]string{},
			map[string]string{},
		},
	} {
		got, err := ParseReader("shouldparse.diag", strings.NewReader(test.input))
		if err != nil {
			t.Fatalf("%s: parse error: %s with input %s", test.description, err, test.input)
		}
		gotDiag, ok := got.(Diag)
		if !ok {
			t.Fatalf("%s: assertion error: %s should parse to diag", test.description, test.input)
		}
		if gotDiag.NodesString() != strings.Join(test.nodes, ", ") {
			t.Fatalf("%s: nodes error: %s, expected '%s', got: '%s'", test.description, test.input, strings.Join(test.nodes, ", "), gotDiag.NodesString())
		}
		if gotDiag.EdgesString() != strings.Join(test.edges, ", ") {
			t.Fatalf("%s edges error: %s, expected '%s', got: '%s'", test.description, test.input, strings.Join(test.edges, ", "), gotDiag.EdgesString())
		}

		var attributes []string
		for key, value := range test.attributes {
			attributes = append(attributes, key+"="+value)
		}
		sort.Strings(attributes)
		if gotDiag.AttributesString() != strings.Join(attributes, "\n") {
			t.Fatalf("%s attributes error: %s, expected '%s', got: '%s'", test.description, test.input, strings.Join(attributes, "\n"), gotDiag.AttributesString())
		}
	}
}

func TestShouldNotParse(t *testing.T) {
	for _, test := range []struct {
		description string
		input       string
	}{
		{
			"No block",
			`
blockdiag
`,
		},
	} {
		_, err := ParseReader("shouldnotparse.diag", strings.NewReader(test.input))
		if err == nil {
			t.Fatalf("%s: should not parse, but didn't give an error with input %s", test.description, test.input)
		}
	}
}

func TestCircular(t *testing.T) {
	for _, test := range []struct {
		input    string
		circular bool
	}{
		{
			`
blockdiag{
	A;
}
`,
			false,
		},
		{
			`
blockdiag{
	A -> B -> C;
}
`,
			false,
		},
		{
			`
blockdiag{
	A -> A;
}
`,
			false,
		},
		{
			`
blockdiag{
	A -> B -> C -> A;
}
`,
			true,
		},
	} {
		got, err := ParseReader("shouldnotparse.diag", strings.NewReader(test.input))
		if err != nil {
			t.Fatalf("should not parse, but didn't give an error with input %s", test.input)
		}
		gotDiag, ok := got.(Diag)
		if !ok {
			t.Fatalf("assertion error: %s should parse to diag", test.input)
		}
		if gotDiag.FindCircular() != test.circular {
			t.Fatalf("expect %s to be circular == %t", test.input, test.circular)
		}
	}
}

func TestGetStartNodes(t *testing.T) {
	for _, test := range []struct {
		input      string
		startNodes []string
	}{
		{
			`
blockdiag{
	A -> B -> C;
}
`,
			[]string{"A"},
		},
		{
			`
blockdiag {
	A -> B -> C;
	D;
	E -> F;
}
`,
			[]string{"A", "D", "E"},
		},
		{
			`
blockdiag {
	D;
	E -> F;
	A -> B -> C;
}
`,
			[]string{"A", "D", "E"},
		},
	} {
		got, err := ParseReader("placeingrid.diag", strings.NewReader(test.input))
		if err != nil {
			t.Fatalf("should not parse, but didn't give an error with input %s", test.input)
		}
		gotDiag, ok := got.(Diag)
		if !ok {
			t.Fatalf("assertion error: %s should parse to diag", test.input)
		}
		// if gotDiag.PlaceInGrid() != test.circular {
		// 	t.Fatalf("expect %s to be circular == %t", test.input, test.circular)
		// }
		startNodes := gotDiag.getStartNodes()
		if len(startNodes) != len(test.startNodes) {
			t.Fatalf("Start Nodes count wrong, expected: %s, got: %s", strings.Join(test.startNodes, ", "), startNodes)
		}
		sort.Strings(test.startNodes)
		for i, n := range startNodes {
			if n.Name != test.startNodes[i] {
				t.Fatalf("Start Nodes do not match, expected: %s, got: %s", strings.Join(test.startNodes, ", "), startNodes)
			}
		}
	}
}

func TestPlaceInGrid(t *testing.T) {
	for _, test := range []struct {
		input string
	}{
		{
			`
blockdiag{
	A -> B -> C;
}
`,
		},
		{
			`
blockdiag{
	A -> B -> C;
	B -> D;
	A -> E -> C;
}
`,
		},
	} {
		got, err := ParseReader("placeingrid.diag", strings.NewReader(test.input))
		if err != nil {
			t.Fatalf("should not parse, but didn't give an error with input %s", test.input)
		}
		gotDiag, ok := got.(Diag)
		if !ok {
			t.Fatalf("assertion error: %s should parse to diag", test.input)
		}
		// if gotDiag.PlaceInGrid() != test.circular {
		// 	t.Fatalf("expect %s to be circular == %t", test.input, test.circular)
		// }
		gotDiag.PlaceInGrid()
		t.Logf("%s\n", gotDiag.GridString())
	}
}
<|endoftext|>


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

package tcp_test

import (
	"crypto/tls"
	"encoding/json"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"runtime"
	"testing"
	"time"

	"github.com/mikioh/tcp"
)

var (
	host = "www.google.com"
	url  = "https://www.google.com/robots.txt"
	tt   *testing.T
)

func TestInfoWithGoogle(t *testing.T) {
	switch runtime.GOOS {
	case "freebsd", "linux":
	default:
		t.Skipf("not supported on %s/%s", runtime.GOOS, runtime.GOARCH)
	}

	tt = t
	tr := &http.Transport{
		Dial:            dialWithTCPConnMonitor,
		TLSClientConfig: &tls.Config{ServerName: host},
	}
	client := http.Client{Transport: tr}
	resp, err := client.Get(url)
	if err != nil {
		t.Fatal(err)
	}
	defer resp.Body.Close()
	if _, err := io.Copy(ioutil.Discard, resp.Body); err != nil {
		t.Fatal(err)
	}
}

func dialWithTCPConnMonitor(network, address string) (net.Conn, error) {
	d := net.Dialer{DualStack: true}
	c, err := d.Dial(network, address)
	if err != nil {
		return nil, err
	}
	tc, err := tcp.NewConn(c)
	if err != nil {
		c.Close()
		return nil, err
	}
	go tcpConnMonitor(tc)
	return &tc.TCPConn, nil
}

func tcpConnMonitor(c *tcp.Conn) {
	tt.Logf("%v -> %v", c.LocalAddr(), c.RemoteAddr())
	for {
		ti, err := c.Info()
		if err != nil {
			if runtime.GOOS == "linux" && runtime.GOARCH == "386" {
				tt.Log(err)
			} else {
				tt.Error(err)
			}
			return
		}
		text, err := json.Marshal(ti)
		if err != nil {
			tt.Error(err)
			return
		}
		tt.Log(string(text))
		time.Sleep(10 * time.Millisecond)
	}
}
<commit_msg>tcp: and more<commit_after>// Copyright 2014 Mikio Hara. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tcp_test

import (
	"crypto/tls"
	"encoding/json"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"runtime"
	"testing"
	"time"

	"github.com/mikioh/tcp"
)

var (
	host = "www.google.com"
	url  = "https://www.google.com/robots.txt"
	tt   *testing.T
)

func TestInfoWithGoogle(t *testing.T) {
	switch runtime.GOOS {
	case "freebsd", "linux":
	default:
		t.Skipf("not supported on %s/%s", runtime.GOOS, runtime.GOARCH)
	}

	tt = t
	tr := &http.Transport{
		Dial:            dialWithTCPConnMonitor,
		TLSClientConfig: &tls.Config{ServerName: host},
	}
	client := http.Client{Transport: tr}
	resp, err := client.Get(url)
	if err != nil {
		t.Fatal(err)
	}
	defer resp.Body.Close()
	if _, err := io.Copy(ioutil.Discard, resp.Body); err != nil {
		t.Fatal(err)
	}
}

func dialWithTCPConnMonitor(network, address string) (net.Conn, error) {
	d := net.Dialer{DualStack: true}
	c, err := d.Dial(network, address)
	if err != nil {
		return nil, err
	}
	tc, err := tcp.NewConn(c)
	if err != nil {
		c.Close()
		return nil, err
	}
	go tcpConnMonitor(tc)
	return &tc.TCPConn, nil
}

func tcpConnMonitor(c *tcp.Conn) {
	tt.Logf("%v -> %v", c.LocalAddr(), c.RemoteAddr())
	for {
		ti, err := c.Info()
		if err != nil {
			if runtime.GOOS == "linux" && runtime.GOARCH == "386" {
				tt.Log(err)
			} else {
				tt.Error(err)
			}
			return
		}
		text, err := json.Marshal(ti)
		if err != nil {
			tt.Error(err)
			return
		}
		tt.Log(string(text))
		time.Sleep(5 * time.Millisecond)
	}
}
<|endoftext|>


<commit_before>
package core

import ("fmt"
	"os"
	"strconv"
	"runtime"
	"math"
	"bytes"
	"time"
	"reflect"
	"github.com/jackyb/go-sdl2/sdl"
	"github.com/op/go-nanomsg"
	"github.com/fire/go-ogre3d"
	"github.com/jmckaskill/go-capnproto")

type InputState struct {
	yawSens float32
	pitchSens float32
	orientationFactor float32 // +1/-1 easy switch between look around and manipulate something
	yaw float32 // degrees, modulo [-180,180] range
	pitch float32 // degrees, clamped [-90,90] range
	roll float32
	orientation ogre.Quaternion // current orientation
}

func InitCore() {
	var gameThreadParams GameThreadParams
	gameThreadParams.start = time.Now() // There's an small time before this variable is initalized,
	// it probably doesn't matter... Someone timed Go initalization at 1.94us on Linux.
	sdl.Init(sdl.INIT_EVERYTHING)
	window := sdl.CreateWindow("es_core::SDL",
		sdl.WINDOWPOS_UNDEFINED,
		sdl.WINDOWPOS_UNDEFINED,
		800,
		600,
		sdl.WINDOW_OPENGL|sdl.WINDOW_SHOWN)
	if window == nil {
		panic(fmt.Sprintf("sdl.CreateWindow failed: %s\n", sdl.GetError()))
	}
	defer sdl.Quit()
	glContext := sdl.GL_CreateContext(window)

	var info sdl.SysWMInfo
	if !window.GetWMInfo(&info) {
		panic(fmt.Sprintf("window.GetWMInfo failed.\n"))
	}
	var version sdl.Version
	sdl.GetVersion(&version)

	fmt.Printf("Sdl Major Version: %d\n", version.Major)
	fmt.Printf("Sdl Minor Version: %d\n", version.Minor)
	fmt.Printf("Sdl Patch level: %d\n", version.Patch)
	fmt.Printf("Sdl Subsystem: %s\n", getSubsystemString(info))
	root := ogre.NewRoot("", "", "ogre.log")
	defer root.Destroy()
	wd, err := os.Getwd()
	if err != nil {
		panic(err)
	}
	if runtime.GOOS == "windows" {
		root.LoadPlugin(wd  + "/RenderSystem_GL3Plus")
		}
	if runtime.GOOS == "darwin" {
		root.LoadPlugin(wd  + "/../frameworks/RenderSystem_GL3Plus")
	}

	renderers := root.GetAvailableRenderers()
	if renderers.RenderSystemListSize() != 1 {

		panic(fmt.Sprintf("Failed to initalize RendererRenderSystem_GL"))
	}
	root.SetRenderSystem(renderers.RenderSystemListGet(0))
	root.Initialise(false, "es_core::ogre")
	params := ogre.CreateNameValuePairList()
	if runtime.GOOS == "windows" {
		params.AddPair("externalGLControl", "1")
		params.AddPair("externalGLContext", strconv.FormatUint(uint64(uintptr(glContext)), 10))

		windowsInfo := info.GetWindowsInfo()
		windowString := strconv.FormatUint(uint64(uintptr(windowsInfo.Window)), 10)
		params.AddPair("externalWindowHandle", windowString)
	}
	if runtime.GOOS == "darwin" {
		params.AddPair("externalGLControl", "1")
		params.AddPair("externalGLContext", strconv.FormatUint(uint64(uintptr(glContext)), 10))
		params.AddPair("macAPI", "cocoa")
		cocoaInfo := info.GetCocoaInfo()
		params.AddPair("externalWindowHandle", strconv.FormatUint(uint64(*(*uint32)(cocoaInfo.Window)), 10))
	}

	renderWindow := root.CreateRenderWindow("es_core::ogre", 800, 600, false, params)
	if runtime.GOOS == "darwin" {
		// I suspect triple buffering is on by default, which makes vsync pointless?
		// except maybe for poorly implemented render loops which will then be forced to wait
		// window->SetVSyncEnabled(false)
	} else {
		// NOTE: SDL_GL_SWAP_CONTROL was SDL 1.2 and has been retired
		sdl.GL_SetSwapInterval(1);
	}

	renderWindow.SetVisible(true)

	nnGameSocket, err := nanomsg.NewSocket(nanomsg.AF_SP, nanomsg.BUS)
        if err != nil {
                panic(err)
        }
        _, err = nnGameSocket.Bind("tcp://127.0.0.1:60206")
        if err != nil {
                panic(err)
        }

	nnRenderSocket, err := nanomsg.NewSocket(nanomsg.AF_SP, nanomsg.BUS)
	if err != nil {
                panic(err)
        }
        _, err = nnRenderSocket.Bind("tcp://127.0.0.1:60207")
        if err != nil {
                panic(err)
        }

	nnInputPub, err := nanomsg.NewSocket(nanomsg.AF_SP, nanomsg.PUB)
        if err != nil {
                panic(err)
        }
        _, err = nnInputPub.Bind("tcp://127.0.0.1:60208")
        if err != nil {
                panic(err)
        }

	nnInputPull, err := nanomsg.NewSocket(nanomsg.AF_SP, nanomsg.PULL)
        if err != nil {
                panic(err)
        }
        _, err = nnInputPull.Bind("tcp://127.0.0.1:60209")
        if err != nil {
                panic(err)
        }
	go gameThread(gameThreadParams)
	var renderThreadParams RenderThreadParams
	renderThreadParams.start = gameThreadParams.start
	renderThreadParams.root = root
	renderThreadParams.window = window
	renderThreadParams.glContext = glContext
	renderThreadParams.ogreWindow = renderWindow

	sdl.GL_MakeCurrent(window, nil)
	go renderThread(renderThreadParams)

	window.SetGrab(true)
	sdl.SetRelativeMouseMode(true)

	shutdownRequested := false
	var is InputState
	is.yawSens = 0.1
	is.yaw = 0.0
	is.pitchSens = 0.1
	is.pitch = 0.0
	is.roll = 0.0
	is.orientation = ogre.CreateQuaternion()
	is.orientationFactor = -1.0 // Look around config

	for !shutdownRequested /* && SDL_GetTicks() < MAX_RUN_TIME */ {
		// We wait here.
		b, err := nnInputPull.Recv(0)
		if err != nil {
			fmt.Printf("%s\n", err)
		}
		s, _, err := capn.ReadFromMemoryZeroCopy(b)
		if err != nil {
			fmt.Printf("Read error %v\n", err)
			return
		}
		state := ReadRootState(s)
		fmt.Printf("Game push received:\n")
		// poll for events before processing the request
		// NOTE: this is how SDL builds the internal mouse and keyboard state
		// TODO: done this way does not meet the objectives of smooth, frame independent mouse view control,
		// Plus it throws some latency into the calling thread

		for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
			switch t := event.(type) {
			default:
				fmt.Printf("SDL_Event: %s\n", reflect.TypeOf(t).String()[5:]);
			case *sdl.KeyDownEvent:
				fmt.Printf("SDL keyboard event:\n")
			case *sdl.KeyUpEvent:
				fmt.Printf("SDL keyboard event:\n")
				if t.Keysym.Scancode == sdl.SCANCODE_ESCAPE {
					sendShutdown(nnRenderSocket, nnGameSocket)
					shutdownRequested = true
				}
			case *sdl.MouseMotionEvent:
				// + when manipulating an object, - when doing a first person view .. needs to be configurable?
				is.yaw += is.orientationFactor * is.yawSens * float32(t.XRel)
				if is.yaw >= 0.0 {
					is.yaw = float32(math.Mod(float64(is.yaw) + 180.0, 360.0) - 180.0)
				} else {
					is.yaw = float32(math.Mod(float64(is.yaw) - 180.0, 360.0) + 180.0)
				}
				// + when manipulating an object, - when doing a first person view .. needs to be configurable?
				is.pitch += is.orientationFactor * is.pitchSens * float32(t.YRel)
				if is.pitch > 90.0 {
					is.pitch = 90.0
				} else if ( is.pitch < -90.0 ) {
					is.pitch = -90.0
				}
				// build a quaternion of the current orientation
				var r ogre.Matrix3
				r.FromEulerAnglesYXZ( deg2Rad(is.yaw), deg2Rad(is.pitch), deg2Rad(is.roll))
				is.orientation.FromRotationMatrix(r)
			case *sdl.MouseButtonEvent:
				fmt.Printf("SDL mouse button event:\n")
			case *sdl.QuitEvent:
			    // push a shutdown on the control socket, game and render will pick it up later
				// NOTE: if the message patterns change we may still have to deal with hangs here
				sendShutdown(nnRenderSocket, nnGameSocket)
				shutdownRequested = true
			}
		}
		switch {
		// we are ready to process the request now
		case state.Mouse():
			buttons := sdl.GetMouseState(nil, nil)
			fmt.Printf("buttons: %d\n", buttons)
			s := capn.NewBuffer(nil)
			ms := NewRootInputMouse(s)
			ms.SetW(is.orientation.W())
			ms.SetX(is.orientation.X())
			ms.SetY(is.orientation.Y())
			ms.SetZ(is.orientation.Z())
			ms.SetButtons(buttons)
			buf := bytes.Buffer{}
			s.WriteTo(&buf)
			nnInputPub.Send(append([]byte("input.mouse:"), buf.Bytes()...), 0)
			fmt.Printf("Mouse input sent.\n")

		case state.Kb():
		// looking at a few hardcoded keys for now
		// NOTE: I suspect it would be perfectly safe to grab that pointer once, and read it from a different thread?
			state := sdl.GetKeyboardState()
			t := capn.NewBuffer(nil)
			kbs := NewRootInputKb(t)
			kbs.SetW(state[sdl.SCANCODE_W] != 0)
			kbs.SetA(state[sdl.SCANCODE_A] != 0)
			kbs.SetS(state[sdl.SCANCODE_S] != 0)
			kbs.SetD(state[sdl.SCANCODE_D] != 0)
			kbs.SetSpace(state[sdl.SCANCODE_SPACE] != 0)
			kbs.SetLalt(state[sdl.SCANCODE_LALT] != 0)
			b := bytes.Buffer{}
			t.WriteTo(&b)
			nnInputPub.Send(append([]byte("input.kb:"), b.Bytes()...), 0)
			fmt.Printf("Keyboard input sent.\n")

		case state.MouseReset():
			var q ogre.Quaternion;
			is.orientation = q.FromValues(state.Quaternion().W(), state.Quaternion().X(),
				state.Quaternion().Y(), state.Quaternion().Z())
			var r ogre.Matrix3
			is.orientation.ToRotationMatrix(&r)
			var rfYAngle, rfPAngle, rfRAngle float32
			r.ToEulerAnglesYXZ(&rfYAngle, &rfPAngle, &rfRAngle)
			is.yaw = rad2Deg(rfYAngle)
			is.pitch = rad2Deg(rfPAngle)
			is.roll = rad2Deg(rfRAngle)
		case state.ConfigLookAround():
			if state.LookAround().ManipulateObject() {
				fmt.Printf("Input configuration: manipulate object\n");
				is.orientationFactor = 1.0;
			} else {
				fmt.Printf("Input configuration: look around\n");
				is.orientationFactor = -1.0
			}
		}
	}
	if !shutdownRequested {
      sendShutdown(nnRenderSocket, nnGameSocket)
      shutdownRequested = true
    }
    // make the GL context again before proceeding with the teardown
    if runtime.GOOS != "darwin" {
		sdl.GL_MakeCurrent(window, glContext)
	}
    waitShutdown(nnInputPull, &gameThreadParams)
}

func deg2Rad(deg float32) float32 {
	return deg * math.Pi / 180
}

func rad2Deg (rad float32) float32 {
	return rad * 180 / math.Pi
}

func sendShutdown(nnRenderSocket *nanomsg.Socket, nnGameSocket *nanomsg.Socket) {
	s := capn.NewBuffer(nil)
	stop := NewRootStop(s)
	stop.SetStop(true)
	buf := bytes.Buffer{}
	s.WriteTo(&buf)
	fmt.Printf("Render socket shutdown.\n")
	nnRenderSocket.Send(buf.Bytes(), 0)
	fmt.Printf("Game socket shutdown.\n")
	nnGameSocket.Send(buf.Bytes(), 0)
}

func waitShutdown(nnInputPull *nanomsg.Socket, params *GameThreadParams) {
	// For now, loop the input thread for a bit to flush out any events
	continueTime := time.Since(params.start) + 500 * time.Millisecond  // An eternity.
	for time.Since(params.start) < continueTime {
		msg, _ := nnInputPull.Recv(nanomsg.DontWait)
		if msg == nil {
			sdl.Delay(10)
		}
	}
}

func getSubsystemString(info sdl.SysWMInfo) string {
	switch info.Subsystem {
	case 0:
	    return "Unknown"
	case 1:
		return "Windows"
	case 2:
		return "X11"
	case 3:
		return "DirectFB"
	case 4:
		return "Cocoa"
	case 5:
		return "UiKit"
	}
	return "Unknown"
}
<commit_msg>Uppercase text.<commit_after>
package core

import ("fmt"
	"os"
	"strconv"
	"runtime"
	"math"
	"bytes"
	"time"
	"reflect"
	"github.com/jackyb/go-sdl2/sdl"
	"github.com/op/go-nanomsg"
	"github.com/fire/go-ogre3d"
	"github.com/jmckaskill/go-capnproto")

type InputState struct {
	yawSens float32
	pitchSens float32
	orientationFactor float32 // +1/-1 easy switch between look around and manipulate something
	yaw float32 // degrees, modulo [-180,180] range
	pitch float32 // degrees, clamped [-90,90] range
	roll float32
	orientation ogre.Quaternion // current orientation
}

func InitCore() {
	var gameThreadParams GameThreadParams
	gameThreadParams.start = time.Now() // There's an small time before this variable is initalized,
	// it probably doesn't matter... Someone timed Go initalization at 1.94us on Linux.
	sdl.Init(sdl.INIT_EVERYTHING)
	window := sdl.CreateWindow("es_core::SDL",
		sdl.WINDOWPOS_UNDEFINED,
		sdl.WINDOWPOS_UNDEFINED,
		800,
		600,
		sdl.WINDOW_OPENGL|sdl.WINDOW_SHOWN)
	if window == nil {
		panic(fmt.Sprintf("sdl.CreateWindow failed: %s\n", sdl.GetError()))
	}
	defer sdl.Quit()
	glContext := sdl.GL_CreateContext(window)

	var info sdl.SysWMInfo
	if !window.GetWMInfo(&info) {
		panic(fmt.Sprintf("window.GetWMInfo failed.\n"))
	}
	var version sdl.Version
	sdl.GetVersion(&version)

	fmt.Printf("Sdl Major Version: %d\n", version.Major)
	fmt.Printf("Sdl Minor Version: %d\n", version.Minor)
	fmt.Printf("Sdl Patch level: %d\n", version.Patch)
	fmt.Printf("Sdl Subsystem: %s\n", getSubsystemString(info))
	root := ogre.NewRoot("", "", "ogre.log")
	defer root.Destroy()
	wd, err := os.Getwd()
	if err != nil {
		panic(err)
	}
	if runtime.GOOS == "windows" {
		root.LoadPlugin(wd  + "/RenderSystem_GL3Plus")
		}
	if runtime.GOOS == "darwin" {
		root.LoadPlugin(wd  + "/../frameworks/RenderSystem_GL3Plus")
	}

	renderers := root.GetAvailableRenderers()
	if renderers.RenderSystemListSize() != 1 {

		panic(fmt.Sprintf("Failed to initalize RendererRenderSystem_GL"))
	}
	root.SetRenderSystem(renderers.RenderSystemListGet(0))
	root.Initialise(false, "es_core::ogre")
	params := ogre.CreateNameValuePairList()
	if runtime.GOOS == "windows" {
		params.AddPair("externalGLControl", "1")
		params.AddPair("externalGLContext", strconv.FormatUint(uint64(uintptr(glContext)), 10))

		windowsInfo := info.GetWindowsInfo()
		windowString := strconv.FormatUint(uint64(uintptr(windowsInfo.Window)), 10)
		params.AddPair("externalWindowHandle", windowString)
	}
	if runtime.GOOS == "darwin" {
		params.AddPair("externalGLControl", "1")
		params.AddPair("externalGLContext", strconv.FormatUint(uint64(uintptr(glContext)), 10))
		params.AddPair("macAPI", "cocoa")
		cocoaInfo := info.GetCocoaInfo()
		params.AddPair("externalWindowHandle", strconv.FormatUint(uint64(*(*uint32)(cocoaInfo.Window)), 10))
	}

	renderWindow := root.CreateRenderWindow("es_core::ogre", 800, 600, false, params)
	if runtime.GOOS == "darwin" {
		// I suspect triple buffering is on by default, which makes vsync pointless?
		// except maybe for poorly implemented render loops which will then be forced to wait
		// window->SetVSyncEnabled(false)
	} else {
		// NOTE: SDL_GL_SWAP_CONTROL was SDL 1.2 and has been retired
		sdl.GL_SetSwapInterval(1);
	}

	renderWindow.SetVisible(true)

	nnGameSocket, err := nanomsg.NewSocket(nanomsg.AF_SP, nanomsg.BUS)
        if err != nil {
                panic(err)
        }
        _, err = nnGameSocket.Bind("tcp://127.0.0.1:60206")
        if err != nil {
                panic(err)
        }

	nnRenderSocket, err := nanomsg.NewSocket(nanomsg.AF_SP, nanomsg.BUS)
	if err != nil {
                panic(err)
        }
        _, err = nnRenderSocket.Bind("tcp://127.0.0.1:60207")
        if err != nil {
                panic(err)
        }

	nnInputPub, err := nanomsg.NewSocket(nanomsg.AF_SP, nanomsg.PUB)
        if err != nil {
                panic(err)
        }
        _, err = nnInputPub.Bind("tcp://127.0.0.1:60208")
        if err != nil {
                panic(err)
        }

	nnInputPull, err := nanomsg.NewSocket(nanomsg.AF_SP, nanomsg.PULL)
        if err != nil {
                panic(err)
        }
        _, err = nnInputPull.Bind("tcp://127.0.0.1:60209")
        if err != nil {
                panic(err)
        }
	go gameThread(gameThreadParams)
	var renderThreadParams RenderThreadParams
	renderThreadParams.start = gameThreadParams.start
	renderThreadParams.root = root
	renderThreadParams.window = window
	renderThreadParams.glContext = glContext
	renderThreadParams.ogreWindow = renderWindow

	sdl.GL_MakeCurrent(window, nil)
	go renderThread(renderThreadParams)

	window.SetGrab(true)
	sdl.SetRelativeMouseMode(true)

	shutdownRequested := false
	var is InputState
	is.yawSens = 0.1
	is.yaw = 0.0
	is.pitchSens = 0.1
	is.pitch = 0.0
	is.roll = 0.0
	is.orientation = ogre.CreateQuaternion()
	is.orientationFactor = -1.0 // Look around config

	for !shutdownRequested /* && SDL_GetTicks() < MAX_RUN_TIME */ {
		// We wait here.
		b, err := nnInputPull.Recv(0)
		if err != nil {
			fmt.Printf("%s\n", err)
		}
		s, _, err := capn.ReadFromMemoryZeroCopy(b)
		if err != nil {
			fmt.Printf("Read error %v\n", err)
			return
		}
		state := ReadRootState(s)
		fmt.Printf("Game push received:\n")
		// poll for events before processing the request
		// NOTE: this is how SDL builds the internal mouse and keyboard state
		// TODO: done this way does not meet the objectives of smooth, frame independent mouse view control,
		// Plus it throws some latency into the calling thread

		for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
			switch t := event.(type) {
			default:
				fmt.Printf("SDL_Event: %s\n", reflect.TypeOf(t).String()[5:]);
			case *sdl.KeyDownEvent:
				fmt.Printf("SDL keyboard event:\n")
			case *sdl.KeyUpEvent:
				fmt.Printf("SDL keyboard event:\n")
				if t.Keysym.Scancode == sdl.SCANCODE_ESCAPE {
					sendShutdown(nnRenderSocket, nnGameSocket)
					shutdownRequested = true
				}
			case *sdl.MouseMotionEvent:
				// + when manipulating an object, - when doing a first person view .. needs to be configurable?
				is.yaw += is.orientationFactor * is.yawSens * float32(t.XRel)
				if is.yaw >= 0.0 {
					is.yaw = float32(math.Mod(float64(is.yaw) + 180.0, 360.0) - 180.0)
				} else {
					is.yaw = float32(math.Mod(float64(is.yaw) - 180.0, 360.0) + 180.0)
				}
				// + when manipulating an object, - when doing a first person view .. needs to be configurable?
				is.pitch += is.orientationFactor * is.pitchSens * float32(t.YRel)
				if is.pitch > 90.0 {
					is.pitch = 90.0
				} else if ( is.pitch < -90.0 ) {
					is.pitch = -90.0
				}
				// build a quaternion of the current orientation
				var r ogre.Matrix3
				r.FromEulerAnglesYXZ( deg2Rad(is.yaw), deg2Rad(is.pitch), deg2Rad(is.roll))
				is.orientation.FromRotationMatrix(r)
			case *sdl.MouseButtonEvent:
				fmt.Printf("SDL mouse button event:\n")
			case *sdl.QuitEvent:
			    // push a shutdown on the control socket, game and render will pick it up later
				// NOTE: if the message patterns change we may still have to deal with hangs here
				sendShutdown(nnRenderSocket, nnGameSocket)
				shutdownRequested = true
			}
		}
		switch {
		// we are ready to process the request now
		case state.Mouse():
			buttons := sdl.GetMouseState(nil, nil)
			fmt.Printf("Buttons: %d\n", buttons)
			s := capn.NewBuffer(nil)
			ms := NewRootInputMouse(s)
			ms.SetW(is.orientation.W())
			ms.SetX(is.orientation.X())
			ms.SetY(is.orientation.Y())
			ms.SetZ(is.orientation.Z())
			ms.SetButtons(buttons)
			buf := bytes.Buffer{}
			s.WriteTo(&buf)
			nnInputPub.Send(append([]byte("input.mouse:"), buf.Bytes()...), 0)
			fmt.Printf("Mouse input sent.\n")

		case state.Kb():
		// looking at a few hardcoded keys for now
		// NOTE: I suspect it would be perfectly safe to grab that pointer once, and read it from a different thread?
			state := sdl.GetKeyboardState()
			t := capn.NewBuffer(nil)
			kbs := NewRootInputKb(t)
			kbs.SetW(state[sdl.SCANCODE_W] != 0)
			kbs.SetA(state[sdl.SCANCODE_A] != 0)
			kbs.SetS(state[sdl.SCANCODE_S] != 0)
			kbs.SetD(state[sdl.SCANCODE_D] != 0)
			kbs.SetSpace(state[sdl.SCANCODE_SPACE] != 0)
			kbs.SetLalt(state[sdl.SCANCODE_LALT] != 0)
			b := bytes.Buffer{}
			t.WriteTo(&b)
			nnInputPub.Send(append([]byte("input.kb:"), b.Bytes()...), 0)
			fmt.Printf("Keyboard input sent.\n")

		case state.MouseReset():
			var q ogre.Quaternion;
			is.orientation = q.FromValues(state.Quaternion().W(), state.Quaternion().X(),
				state.Quaternion().Y(), state.Quaternion().Z())
			var r ogre.Matrix3
			is.orientation.ToRotationMatrix(&r)
			var rfYAngle, rfPAngle, rfRAngle float32
			r.ToEulerAnglesYXZ(&rfYAngle, &rfPAngle, &rfRAngle)
			is.yaw = rad2Deg(rfYAngle)
			is.pitch = rad2Deg(rfPAngle)
			is.roll = rad2Deg(rfRAngle)
		case state.ConfigLookAround():
			if state.LookAround().ManipulateObject() {
				fmt.Printf("Input configuration: manipulate object\n");
				is.orientationFactor = 1.0;
			} else {
				fmt.Printf("Input configuration: look around\n");
				is.orientationFactor = -1.0
			}
		}
	}
	if !shutdownRequested {
      sendShutdown(nnRenderSocket, nnGameSocket)
      shutdownRequested = true
    }
    // make the GL context again before proceeding with the teardown
    if runtime.GOOS != "darwin" {
		sdl.GL_MakeCurrent(window, glContext)
	}
    waitShutdown(nnInputPull, &gameThreadParams)
}

func deg2Rad(deg float32) float32 {
	return deg * math.Pi / 180
}

func rad2Deg (rad float32) float32 {
	return rad * 180 / math.Pi
}

func sendShutdown(nnRenderSocket *nanomsg.Socket, nnGameSocket *nanomsg.Socket) {
	s := capn.NewBuffer(nil)
	stop := NewRootStop(s)
	stop.SetStop(true)
	buf := bytes.Buffer{}
	s.WriteTo(&buf)
	fmt.Printf("Render socket shutdown.\n")
	nnRenderSocket.Send(buf.Bytes(), 0)
	fmt.Printf("Game socket shutdown.\n")
	nnGameSocket.Send(buf.Bytes(), 0)
}

func waitShutdown(nnInputPull *nanomsg.Socket, params *GameThreadParams) {
	// For now, loop the input thread for a bit to flush out any events
	continueTime := time.Since(params.start) + 500 * time.Millisecond  // An eternity.
	for time.Since(params.start) < continueTime {
		msg, _ := nnInputPull.Recv(nanomsg.DontWait)
		if msg == nil {
			sdl.Delay(10)
		}
	}
}

func getSubsystemString(info sdl.SysWMInfo) string {
	switch info.Subsystem {
	case 0:
	    return "Unknown"
	case 1:
		return "Windows"
	case 2:
		return "X11"
	case 3:
		return "DirectFB"
	case 4:
		return "Cocoa"
	case 5:
		return "UiKit"
	}
	return "Unknown"
}
<|endoftext|>


<commit_before>package fire

import (
	"fmt"
	"io"
	"sort"
	"time"

	"github.com/fatih/color"
	"github.com/labstack/echo"
	"github.com/mattn/go-colorable"
)

// A ComponentInfo is returned by a component to describe itself.
type ComponentInfo struct {
	// The name of the component.
	Name string

	// The settings it is using.
	Settings Map
}

var _ InspectorComponent = (*Inspector)(nil)

// An Inspector can be used during development to print the applications
// component stack, the route table and log requests to writer.
type Inspector struct {
	Writer io.Writer
}

// DefaultInspector creates and returns a new inspector that writes to stdout.
func DefaultInspector() *Inspector {
	return NewInspector(colorable.NewColorableStdout())
}

// NewInspector creates and returns a new inspector.
func NewInspector(writer io.Writer) *Inspector {
	return &Inspector{
		Writer: writer,
	}
}

// Describe implements the Component interface.
func (i *Inspector) Describe() ComponentInfo {
	return ComponentInfo{
		Name: "Inspector",
	}
}

// Register implements the RoutableComponent interface.
func (i *Inspector) Register(router *echo.Echo) {
	router.Use(i.requestLogger)
}

// Before implements the InspectorComponent interface.
func (i *Inspector) Before(stage Phase, app *Application) {
	switch stage {
	case Registration:
		fmt.Fprintln(i.Writer, color.YellowString("==> Application booting..."))

		fmt.Fprintln(i.Writer, color.YellowString("==> Mounted components:"))
		i.printComponents(app.Components())

		fmt.Fprintln(i.Writer, color.YellowString("==> Registering routable components..."))
	case Setup:
		fmt.Fprintln(i.Writer, color.YellowString("==> Setting up bootable components..."))
	case Run:
		fmt.Fprintln(i.Writer, color.YellowString("==> Registered routes:"))
		i.printRoutes(app.Router())

		fmt.Fprintln(i.Writer, color.YellowString("==> Application is ready to go!"))
		fmt.Fprintln(i.Writer, color.CyanString("Visit: %s", app.BaseURL()))
	case Teardown:
		fmt.Fprintln(i.Writer, color.YellowString("==> Application is stopping..."))
		fmt.Fprintln(i.Writer, color.YellowString("==> Terminating bootable components..."))
	case Termination:
		fmt.Fprintln(i.Writer, color.YellowString("==> Application has been terminated."))
	}
}

// Report implements the ReporterComponent interface.
func (i *Inspector) Report(err error) error {
	fmt.Fprintf(i.Writer, color.RedString("   ERR  \"%s\"\n", err))
	return nil
}

func (i *Inspector) printComponents(components []Component) {
	// inspect all components
	for _, component := range components {
		// get component info
		info := component.Describe()

		// print name
		fmt.Fprintln(i.Writer, color.CyanString("[%s]", info.Name))

		// prepare settings
		var settings []string

		// print settings
		for name, value := range info.Settings {
			settings = append(settings, fmt.Sprintf("  - %s: %s", name, value))
		}

		// sort settings
		sort.Strings(settings)

		// print settings
		for _, setting := range settings {
			fmt.Fprintln(i.Writer, color.BlueString(setting))
		}
	}
}

func (i *Inspector) printRoutes(router *echo.Echo) {
	// prepare routes
	var routes []string

	// add all routes as string
	for _, route := range router.Routes() {
		routes = append(routes, fmt.Sprintf("%6s  %-30s", route.Method, route.Path))
	}

	// sort routes
	sort.Strings(routes)

	// print routes
	for _, route := range routes {
		fmt.Fprintln(i.Writer, color.BlueString(route))
	}
}

func (i *Inspector) requestLogger(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) error {
		req := c.Request()
		res := c.Response()

		// save start
		start := time.Now()

		// call next handler
		if err := next(c); err != nil {
			c.Error(err)
		}

		// get request duration
		duration := time.Since(start).String()

		// log request
		fmt.Fprintf(i.Writer, "%s  %s\n   %s  %s\n", color.GreenString("%6s", req.Method()), req.URL().Path(), color.MagentaString("%d", res.Status()), duration)

		return nil
	}
}
<commit_msg>fix format<commit_after>package fire

import (
	"fmt"
	"io"
	"sort"
	"time"

	"github.com/fatih/color"
	"github.com/labstack/echo"
	"github.com/mattn/go-colorable"
)

// A ComponentInfo is returned by a component to describe itself.
type ComponentInfo struct {
	// The name of the component.
	Name string

	// The settings it is using.
	Settings Map
}

var _ InspectorComponent = (*Inspector)(nil)

// An Inspector can be used during development to print the applications
// component stack, the route table and log requests to writer.
type Inspector struct {
	Writer io.Writer
}

// DefaultInspector creates and returns a new inspector that writes to stdout.
func DefaultInspector() *Inspector {
	return NewInspector(colorable.NewColorableStdout())
}

// NewInspector creates and returns a new inspector.
func NewInspector(writer io.Writer) *Inspector {
	return &Inspector{
		Writer: writer,
	}
}

// Describe implements the Component interface.
func (i *Inspector) Describe() ComponentInfo {
	return ComponentInfo{
		Name: "Inspector",
	}
}

// Register implements the RoutableComponent interface.
func (i *Inspector) Register(router *echo.Echo) {
	router.Use(i.requestLogger)
}

// Before implements the InspectorComponent interface.
func (i *Inspector) Before(stage Phase, app *Application) {
	switch stage {
	case Registration:
		fmt.Fprintln(i.Writer, color.YellowString("==> Application booting..."))

		fmt.Fprintln(i.Writer, color.YellowString("==> Mounted components:"))
		i.printComponents(app.Components())

		fmt.Fprintln(i.Writer, color.YellowString("==> Registering routable components..."))
	case Setup:
		fmt.Fprintln(i.Writer, color.YellowString("==> Setting up bootable components..."))
	case Run:
		fmt.Fprintln(i.Writer, color.YellowString("==> Registered routes:"))
		i.printRoutes(app.Router())

		fmt.Fprintln(i.Writer, color.YellowString("==> Application is ready to go!"))
		fmt.Fprintln(i.Writer, color.YellowString("==> Visit: %s", app.BaseURL()))
	case Teardown:
		fmt.Fprintln(i.Writer, color.YellowString("==> Application is stopping..."))
		fmt.Fprintln(i.Writer, color.YellowString("==> Terminating bootable components..."))
	case Termination:
		fmt.Fprintln(i.Writer, color.YellowString("==> Application has been terminated."))
	}
}

// Report implements the ReporterComponent interface.
func (i *Inspector) Report(err error) error {
	fmt.Fprintf(i.Writer, color.RedString("   ERR  \"%s\"\n", err))
	return nil
}

func (i *Inspector) printComponents(components []Component) {
	// inspect all components
	for _, component := range components {
		// get component info
		info := component.Describe()

		// print name
		fmt.Fprintln(i.Writer, color.CyanString("[%s]", info.Name))

		// prepare settings
		var settings []string

		// print settings
		for name, value := range info.Settings {
			settings = append(settings, fmt.Sprintf("  - %s: %s", name, value))
		}

		// sort settings
		sort.Strings(settings)

		// print settings
		for _, setting := range settings {
			fmt.Fprintln(i.Writer, color.BlueString(setting))
		}
	}
}

func (i *Inspector) printRoutes(router *echo.Echo) {
	// prepare routes
	var routes []string

	// add all routes as string
	for _, route := range router.Routes() {
		routes = append(routes, fmt.Sprintf("%6s  %-30s", route.Method, route.Path))
	}

	// sort routes
	sort.Strings(routes)

	// print routes
	for _, route := range routes {
		fmt.Fprintln(i.Writer, color.BlueString(route))
	}
}

func (i *Inspector) requestLogger(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) error {
		req := c.Request()
		res := c.Response()

		// save start
		start := time.Now()

		// call next handler
		if err := next(c); err != nil {
			c.Error(err)
		}

		// get request duration
		duration := time.Since(start).String()

		// log request
		fmt.Fprintf(i.Writer, "%s  %s\n   %s  %s\n", color.GreenString("%6s", req.Method()), req.URL().Path(), color.MagentaString("%d", res.Status()), duration)

		return nil
	}
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"errors"
	"log"
	"testing"
	"time"

	pbd "github.com/brotherlogic/discovery/proto"
	pb "github.com/brotherlogic/gobuildmaster/proto"
	pbs "github.com/brotherlogic/gobuildslave/proto"
)

type testChecker struct {
	machines []*pbs.Config
	running  bool
}

func (t testChecker) assess(ctx context.Context, server string) (*pbs.JobList, *pbs.Config) {
	log.Printf("ASSESS %v", server)
	if server == "server1" {
		return &pbs.JobList{Details: []*pbs.JobDetails{&pbs.JobDetails{Spec: &pbs.JobSpec{Name: "test1"}}}}, t.machines[0]
	}
	return &pbs.JobList{Details: []*pbs.JobDetails{&pbs.JobDetails{Spec: &pbs.JobSpec{Name: "test1"}}}}, t.machines[1]
}

func (t testChecker) discover() *pbd.ServiceList {
	return &pbd.ServiceList{Services: []*pbd.RegistryEntry{&pbd.RegistryEntry{Identifier: "server1", Name: "gobuildslave"}, &pbd.RegistryEntry{Identifier: "server2", Name: "gobuildslave"}}}
}

func (t testChecker) getprev() []string {
	return make([]string, 0)
}

func (t testChecker) setprev(v []string) {
	// Do nothing
}

func (t testChecker) master(entry *pbd.RegistryEntry, master bool) (bool, error) {
	// Do nothing
	return true, nil
}

func TestPullData(t *testing.T) {
	status, _ := getFleetStatus(context.Background(), &testChecker{machines: []*pbs.Config{&pbs.Config{}, &pbs.Config{}}})
	if val, ok := status["server1"]; !ok || len(val.Details) != 1 {
		t.Errorf("Status has come back bad: %v", status)
	}
}

func TestFleetCount(t *testing.T) {
	status, _ := getFleetStatus(context.Background(), &testChecker{machines: []*pbs.Config{&pbs.Config{}, &pbs.Config{}}, running: false})
	if val, ok := status["server2"]; !ok || len(val.Details) != 1 {
		t.Errorf("Status has come back good when not running: %v", val)
	}
}

func TestLoadMainConfig(t *testing.T) {
	c, err := loadConfig()
	if err != nil {
		t.Errorf("Config load failed: %v", err)
	}

	found := false
	for _, i := range c.Nintents {
		if i.Job.Name == "buildserver" {
			found = true
		}
	}
	if !found {
		t.Errorf("Cannot find buildserver: %v", c.Nintents)
	}

	log.Printf("CONFIG = %v", c)
}

func TestRunJob(t *testing.T) {
	i1 := &pb.Intent{Spec: &pbs.JobSpec{Name: "testing"}, Count: 2}
	jobs := runJobs(&pb.Config{Intents: []*pb.Intent{i1}})
	log.Printf("RUN ON: %v", jobs)
	if len(jobs) != 2 || jobs[0].Name != "testing" || jobs[1].Name != "testing" {
		t.Errorf("Run jobs produced bad result: %v", jobs)
	}
}

func TestDiff(t *testing.T) {
	i1 := &pb.Intent{Spec: &pbs.JobSpec{Name: "testing"}, Count: 1}
	c1 := &pb.Config{Intents: []*pb.Intent{i1}}
	c2 := &pb.Config{Intents: []*pb.Intent{}}

	diff := configDiff(c1, c2)
	if len(diff.Intents) != 1 || diff.Intents[0].Spec.Name != "testing" {
		t.Errorf("Error in diff: %v", diff)
	}
}

func TestDiffWhenMatch(t *testing.T) {
	i1 := &pb.Intent{Spec: &pbs.JobSpec{Name: "testing"}, Count: 1}
	c1 := &pb.Config{Intents: []*pb.Intent{i1}}
	c2 := &pb.Config{Intents: []*pb.Intent{i1}}

	diff := configDiff(c1, c2)
	if len(diff.Intents) != 0 && diff.Intents[0].Count != 0 {
		t.Errorf("Error in diff: %v", diff)
	}
}

func TestLoadOntoDiskMachine(t *testing.T) {
	conf := &pbs.JobSpec{Name: "needsdisk", Disk: 1024}

	machine1 := &pbs.Config{Disk: 100}
	machine2 := &pbs.Config{Disk: 2000}

	server := chooseServer(context.Background(), conf, testChecker{machines: []*pbs.Config{machine1, machine2}})
	if server != "server2" {
		t.Errorf("Failed to select correct server: %v", server)
	}
}

func TestLoadOntoAlreadyRunning(t *testing.T) {
	conf := &pbs.JobSpec{Name: "test1"}

	server := chooseServer(context.Background(), conf, testChecker{machines: []*pbs.Config{&pbs.Config{Disk: 100}, &pbs.Config{Disk: 1000}}})
	if server != "" {
		t.Errorf("Failed to select correct server: %v", server)
	}
}

func TestLoadOntoExternalMachine(t *testing.T) {
	conf := &pbs.JobSpec{Name: "needsexternal", External: true}

	machine1 := &pbs.Config{Disk: 100, External: false}
	machine2 := &pbs.Config{Disk: 100, External: true}

	server := chooseServer(context.Background(), conf, testChecker{machines: []*pbs.Config{machine1, machine2}})
	if server != "server2" {
		t.Errorf("Failed to select correct server: %v", server)
	}
}

func TestDoubleLoadServer(t *testing.T) {
	conf := &pbs.JobSpec{Name: "test1"}
	machine1 := &pbs.Config{Disk: 100}
	machine2 := &pbs.Config{Disk: 100}
	server := chooseServer(context.Background(), conf, testChecker{machines: []*pbs.Config{machine1, machine2}})

	if server == "server1" {
		t.Errorf("Loaded on server1 even though job was running there: %v", server)
	}
}

func TestMissServer(t *testing.T) {
	conf := &pbs.JobSpec{Name: "needsdisk", Disk: 1024}

	machine1 := &pbs.Config{Disk: 100}
	machine2 := &pbs.Config{Disk: 100}

	server := chooseServer(context.Background(), conf, testChecker{machines: []*pbs.Config{machine1, machine2}})
	if server != "" {
		t.Errorf("Found a server even though one is not there: %v", server)
	}
}

type testGetter struct {
	failGetSlaves bool
	failGetJobs   bool
	running       map[string][]*pbs.JobAssignment
	config        map[string][]*pbs.Requirement
}

func (t *testGetter) getSlaves() (*pbd.ServiceList, error) {
	if t.failGetSlaves {
		return &pbd.ServiceList{}, errors.New("Built to fail")
	}

	list := &pbd.ServiceList{}
	for key := range t.running {
		list.Services = append(list.Services, &pbd.RegistryEntry{Identifier: key})
	}
	return list, nil
}

func (t *testGetter) getJobs(ctx context.Context, e *pbd.RegistryEntry) ([]*pbs.JobAssignment, error) {
	if t.failGetJobs {
		return []*pbs.JobAssignment{}, errors.New("Built to fail")
	}

	if val, ok := t.running[e.Identifier]; ok {
		return val, nil
	}
	return make([]*pbs.JobAssignment, 0), nil
}

func (t *testGetter) getConfig(ctx context.Context, e *pbd.RegistryEntry) ([]*pbs.Requirement, error) {
	if val, ok := t.config[e.Identifier]; ok {
		return val, nil
	}
	return make([]*pbs.Requirement, 0), nil
}

func TestFirstSelect(t *testing.T) {
	s := InitTestServer()
	tg := &testGetter{running: make(map[string][]*pbs.JobAssignment)}
	tg.running["badserver"] = []*pbs.JobAssignment{&pbs.JobAssignment{Job: &pbs.Job{Name: "runner"}}}
	tg.running["goodserver"] = []*pbs.JobAssignment{}

	server := s.selectServer(context.Background(), &pbs.Job{Name: "runner"}, tg)
	if server != "goodserver" {
		t.Errorf("Wrong server selected: %v", server)
	}
}

func TestDiskSelect(t *testing.T) {
	s := InitTestServer()
	tg := &testGetter{running: make(map[string][]*pbs.JobAssignment)}
	tg.running["badserver"] = []*pbs.JobAssignment{&pbs.JobAssignment{Job: &pbs.Job{Name: "runner"}}}
	tg.running["goodserver"] = []*pbs.JobAssignment{}

	server := s.selectServer(context.Background(), &pbs.Job{Name: "runner"}, tg)
	if server != "goodserver" {
		t.Errorf("Wrong server selected: %v", server)
	}
}

func TestReqSelectWithLimits(t *testing.T) {
	s := InitTestServer()
	tg := &testGetter{running: make(map[string][]*pbs.JobAssignment), config: make(map[string][]*pbs.Requirement)}
	tg.running["badserver"] = []*pbs.JobAssignment{}
	tg.running["goodserver"] = []*pbs.JobAssignment{}
	tg.config["goodserver"] = []*pbs.Requirement{&pbs.Requirement{Category: pbs.RequirementCategory_DISK, Properties: "backup"}}

	server := s.selectServer(context.Background(), &pbs.Job{Name: "runner", Requirements: []*pbs.Requirement{&pbs.Requirement{Category: pbs.RequirementCategory_DISK, Properties: "maindisk"}}}, tg)
	if server != "" {
		t.Errorf("Wrong server selected: %v", server)
	}
}

func TestReqSelect(t *testing.T) {
	s := InitTestServer()
	tg := &testGetter{running: make(map[string][]*pbs.JobAssignment), config: make(map[string][]*pbs.Requirement)}
	tg.running["goodserver"] = []*pbs.JobAssignment{}
	tg.config["goodserver"] = []*pbs.Requirement{&pbs.Requirement{Category: pbs.RequirementCategory_DISK, Properties: "maindisk"}}

	server := s.selectServer(context.Background(), &pbs.Job{Name: "runner", Requirements: []*pbs.Requirement{&pbs.Requirement{Category: pbs.RequirementCategory_DISK, Properties: "maindisk"}}}, tg)
	if server != "goodserver" {
		t.Errorf("Wrong server selected: %v", server)
	}
}

func TestReqSelectExternal(t *testing.T) {
	s := InitTestServer()
	tg := &testGetter{running: make(map[string][]*pbs.JobAssignment), config: make(map[string][]*pbs.Requirement)}
	tg.running["discover"] = []*pbs.JobAssignment{}
	tg.config["discover"] = []*pbs.Requirement{&pbs.Requirement{Category: pbs.RequirementCategory_EXTERNAL, Properties: "external_ready"}}

	server := s.selectServer(context.Background(), &pbs.Job{Name: "runner", Requirements: []*pbs.Requirement{&pbs.Requirement{Category: pbs.RequirementCategory_EXTERNAL, Properties: "external_ready"}}}, tg)
	if server != "discover" {
		t.Errorf("Wrong server selected: %v", server)
	}
}

func TestReqSelectFail(t *testing.T) {
	s := InitTestServer()
	tg := &testGetter{running: make(map[string][]*pbs.JobAssignment), config: make(map[string][]*pbs.Requirement)}
	tg.running["goodserver"] = []*pbs.JobAssignment{}
	tg.config["goodserver"] = []*pbs.Requirement{&pbs.Requirement{Category: pbs.RequirementCategory_DISK, Properties: "maindisker"}, &pbs.Requirement{Category: pbs.RequirementCategory_ACCESS_POINT, Properties: "70:3A:CB:17:CF:BB"}}

	server := s.selectServer(context.Background(), &pbs.Job{Name: "runner", Requirements: []*pbs.Requirement{&pbs.Requirement{Category: pbs.RequirementCategory_DISK, Properties: "maindisk"}}}, tg)
	if server != "" {
		t.Errorf("Wrong server selected: %v", server)
	}
}

func TestAddAccessPoints(t *testing.T) {
	s := InitTestServer()
	s.accessPoints["blah"] = time.Now().Add(-time.Hour * 25)
	for _, str := range []string{"70:3A:CB:17:CF:BB", "70:3A:CB:17:CC:D3", "70:3A:CB:17:CE:E3", "70:3A:CB:17:CF:BF", "blah", "70:3A:CB:17:CC:CF"} {
		s.addAccessPoint(context.Background(), str)
	}
}
<commit_msg>Only check load<commit_after>package main

import (
	"context"
	"errors"
	"log"
	"testing"
	"time"

	pbd "github.com/brotherlogic/discovery/proto"
	pb "github.com/brotherlogic/gobuildmaster/proto"
	pbs "github.com/brotherlogic/gobuildslave/proto"
)

type testChecker struct {
	machines []*pbs.Config
	running  bool
}

func (t testChecker) assess(ctx context.Context, server string) (*pbs.JobList, *pbs.Config) {
	log.Printf("ASSESS %v", server)
	if server == "server1" {
		return &pbs.JobList{Details: []*pbs.JobDetails{&pbs.JobDetails{Spec: &pbs.JobSpec{Name: "test1"}}}}, t.machines[0]
	}
	return &pbs.JobList{Details: []*pbs.JobDetails{&pbs.JobDetails{Spec: &pbs.JobSpec{Name: "test1"}}}}, t.machines[1]
}

func (t testChecker) discover() *pbd.ServiceList {
	return &pbd.ServiceList{Services: []*pbd.RegistryEntry{&pbd.RegistryEntry{Identifier: "server1", Name: "gobuildslave"}, &pbd.RegistryEntry{Identifier: "server2", Name: "gobuildslave"}}}
}

func (t testChecker) getprev() []string {
	return make([]string, 0)
}

func (t testChecker) setprev(v []string) {
	// Do nothing
}

func (t testChecker) master(entry *pbd.RegistryEntry, master bool) (bool, error) {
	// Do nothing
	return true, nil
}

func TestPullData(t *testing.T) {
	status, _ := getFleetStatus(context.Background(), &testChecker{machines: []*pbs.Config{&pbs.Config{}, &pbs.Config{}}})
	if val, ok := status["server1"]; !ok || len(val.Details) != 1 {
		t.Errorf("Status has come back bad: %v", status)
	}
}

func TestFleetCount(t *testing.T) {
	status, _ := getFleetStatus(context.Background(), &testChecker{machines: []*pbs.Config{&pbs.Config{}, &pbs.Config{}}, running: false})
	if val, ok := status["server2"]; !ok || len(val.Details) != 1 {
		t.Errorf("Status has come back good when not running: %v", val)
	}
}

func TestLoadMainConfig(t *testing.T) {
	c, err := loadConfig()
	if err != nil {
		t.Errorf("Config load failed: %v", err)
	}
}

func TestRunJob(t *testing.T) {
	i1 := &pb.Intent{Spec: &pbs.JobSpec{Name: "testing"}, Count: 2}
	jobs := runJobs(&pb.Config{Intents: []*pb.Intent{i1}})
	log.Printf("RUN ON: %v", jobs)
	if len(jobs) != 2 || jobs[0].Name != "testing" || jobs[1].Name != "testing" {
		t.Errorf("Run jobs produced bad result: %v", jobs)
	}
}

func TestDiff(t *testing.T) {
	i1 := &pb.Intent{Spec: &pbs.JobSpec{Name: "testing"}, Count: 1}
	c1 := &pb.Config{Intents: []*pb.Intent{i1}}
	c2 := &pb.Config{Intents: []*pb.Intent{}}

	diff := configDiff(c1, c2)
	if len(diff.Intents) != 1 || diff.Intents[0].Spec.Name != "testing" {
		t.Errorf("Error in diff: %v", diff)
	}
}

func TestDiffWhenMatch(t *testing.T) {
	i1 := &pb.Intent{Spec: &pbs.JobSpec{Name: "testing"}, Count: 1}
	c1 := &pb.Config{Intents: []*pb.Intent{i1}}
	c2 := &pb.Config{Intents: []*pb.Intent{i1}}

	diff := configDiff(c1, c2)
	if len(diff.Intents) != 0 && diff.Intents[0].Count != 0 {
		t.Errorf("Error in diff: %v", diff)
	}
}

func TestLoadOntoDiskMachine(t *testing.T) {
	conf := &pbs.JobSpec{Name: "needsdisk", Disk: 1024}

	machine1 := &pbs.Config{Disk: 100}
	machine2 := &pbs.Config{Disk: 2000}

	server := chooseServer(context.Background(), conf, testChecker{machines: []*pbs.Config{machine1, machine2}})
	if server != "server2" {
		t.Errorf("Failed to select correct server: %v", server)
	}
}

func TestLoadOntoAlreadyRunning(t *testing.T) {
	conf := &pbs.JobSpec{Name: "test1"}

	server := chooseServer(context.Background(), conf, testChecker{machines: []*pbs.Config{&pbs.Config{Disk: 100}, &pbs.Config{Disk: 1000}}})
	if server != "" {
		t.Errorf("Failed to select correct server: %v", server)
	}
}

func TestLoadOntoExternalMachine(t *testing.T) {
	conf := &pbs.JobSpec{Name: "needsexternal", External: true}

	machine1 := &pbs.Config{Disk: 100, External: false}
	machine2 := &pbs.Config{Disk: 100, External: true}

	server := chooseServer(context.Background(), conf, testChecker{machines: []*pbs.Config{machine1, machine2}})
	if server != "server2" {
		t.Errorf("Failed to select correct server: %v", server)
	}
}

func TestDoubleLoadServer(t *testing.T) {
	conf := &pbs.JobSpec{Name: "test1"}
	machine1 := &pbs.Config{Disk: 100}
	machine2 := &pbs.Config{Disk: 100}
	server := chooseServer(context.Background(), conf, testChecker{machines: []*pbs.Config{machine1, machine2}})

	if server == "server1" {
		t.Errorf("Loaded on server1 even though job was running there: %v", server)
	}
}

func TestMissServer(t *testing.T) {
	conf := &pbs.JobSpec{Name: "needsdisk", Disk: 1024}

	machine1 := &pbs.Config{Disk: 100}
	machine2 := &pbs.Config{Disk: 100}

	server := chooseServer(context.Background(), conf, testChecker{machines: []*pbs.Config{machine1, machine2}})
	if server != "" {
		t.Errorf("Found a server even though one is not there: %v", server)
	}
}

type testGetter struct {
	failGetSlaves bool
	failGetJobs   bool
	running       map[string][]*pbs.JobAssignment
	config        map[string][]*pbs.Requirement
}

func (t *testGetter) getSlaves() (*pbd.ServiceList, error) {
	if t.failGetSlaves {
		return &pbd.ServiceList{}, errors.New("Built to fail")
	}

	list := &pbd.ServiceList{}
	for key := range t.running {
		list.Services = append(list.Services, &pbd.RegistryEntry{Identifier: key})
	}
	return list, nil
}

func (t *testGetter) getJobs(ctx context.Context, e *pbd.RegistryEntry) ([]*pbs.JobAssignment, error) {
	if t.failGetJobs {
		return []*pbs.JobAssignment{}, errors.New("Built to fail")
	}

	if val, ok := t.running[e.Identifier]; ok {
		return val, nil
	}
	return make([]*pbs.JobAssignment, 0), nil
}

func (t *testGetter) getConfig(ctx context.Context, e *pbd.RegistryEntry) ([]*pbs.Requirement, error) {
	if val, ok := t.config[e.Identifier]; ok {
		return val, nil
	}
	return make([]*pbs.Requirement, 0), nil
}

func TestFirstSelect(t *testing.T) {
	s := InitTestServer()
	tg := &testGetter{running: make(map[string][]*pbs.JobAssignment)}
	tg.running["badserver"] = []*pbs.JobAssignment{&pbs.JobAssignment{Job: &pbs.Job{Name: "runner"}}}
	tg.running["goodserver"] = []*pbs.JobAssignment{}

	server := s.selectServer(context.Background(), &pbs.Job{Name: "runner"}, tg)
	if server != "goodserver" {
		t.Errorf("Wrong server selected: %v", server)
	}
}

func TestDiskSelect(t *testing.T) {
	s := InitTestServer()
	tg := &testGetter{running: make(map[string][]*pbs.JobAssignment)}
	tg.running["badserver"] = []*pbs.JobAssignment{&pbs.JobAssignment{Job: &pbs.Job{Name: "runner"}}}
	tg.running["goodserver"] = []*pbs.JobAssignment{}

	server := s.selectServer(context.Background(), &pbs.Job{Name: "runner"}, tg)
	if server != "goodserver" {
		t.Errorf("Wrong server selected: %v", server)
	}
}

func TestReqSelectWithLimits(t *testing.T) {
	s := InitTestServer()
	tg := &testGetter{running: make(map[string][]*pbs.JobAssignment), config: make(map[string][]*pbs.Requirement)}
	tg.running["badserver"] = []*pbs.JobAssignment{}
	tg.running["goodserver"] = []*pbs.JobAssignment{}
	tg.config["goodserver"] = []*pbs.Requirement{&pbs.Requirement{Category: pbs.RequirementCategory_DISK, Properties: "backup"}}

	server := s.selectServer(context.Background(), &pbs.Job{Name: "runner", Requirements: []*pbs.Requirement{&pbs.Requirement{Category: pbs.RequirementCategory_DISK, Properties: "maindisk"}}}, tg)
	if server != "" {
		t.Errorf("Wrong server selected: %v", server)
	}
}

func TestReqSelect(t *testing.T) {
	s := InitTestServer()
	tg := &testGetter{running: make(map[string][]*pbs.JobAssignment), config: make(map[string][]*pbs.Requirement)}
	tg.running["goodserver"] = []*pbs.JobAssignment{}
	tg.config["goodserver"] = []*pbs.Requirement{&pbs.Requirement{Category: pbs.RequirementCategory_DISK, Properties: "maindisk"}}

	server := s.selectServer(context.Background(), &pbs.Job{Name: "runner", Requirements: []*pbs.Requirement{&pbs.Requirement{Category: pbs.RequirementCategory_DISK, Properties: "maindisk"}}}, tg)
	if server != "goodserver" {
		t.Errorf("Wrong server selected: %v", server)
	}
}

func TestReqSelectExternal(t *testing.T) {
	s := InitTestServer()
	tg := &testGetter{running: make(map[string][]*pbs.JobAssignment), config: make(map[string][]*pbs.Requirement)}
	tg.running["discover"] = []*pbs.JobAssignment{}
	tg.config["discover"] = []*pbs.Requirement{&pbs.Requirement{Category: pbs.RequirementCategory_EXTERNAL, Properties: "external_ready"}}

	server := s.selectServer(context.Background(), &pbs.Job{Name: "runner", Requirements: []*pbs.Requirement{&pbs.Requirement{Category: pbs.RequirementCategory_EXTERNAL, Properties: "external_ready"}}}, tg)
	if server != "discover" {
		t.Errorf("Wrong server selected: %v", server)
	}
}

func TestReqSelectFail(t *testing.T) {
	s := InitTestServer()
	tg := &testGetter{running: make(map[string][]*pbs.JobAssignment), config: make(map[string][]*pbs.Requirement)}
	tg.running["goodserver"] = []*pbs.JobAssignment{}
	tg.config["goodserver"] = []*pbs.Requirement{&pbs.Requirement{Category: pbs.RequirementCategory_DISK, Properties: "maindisker"}, &pbs.Requirement{Category: pbs.RequirementCategory_ACCESS_POINT, Properties: "70:3A:CB:17:CF:BB"}}

	server := s.selectServer(context.Background(), &pbs.Job{Name: "runner", Requirements: []*pbs.Requirement{&pbs.Requirement{Category: pbs.RequirementCategory_DISK, Properties: "maindisk"}}}, tg)
	if server != "" {
		t.Errorf("Wrong server selected: %v", server)
	}
}

func TestAddAccessPoints(t *testing.T) {
	s := InitTestServer()
	s.accessPoints["blah"] = time.Now().Add(-time.Hour * 25)
	for _, str := range []string{"70:3A:CB:17:CF:BB", "70:3A:CB:17:CC:D3", "70:3A:CB:17:CE:E3", "70:3A:CB:17:CF:BF", "blah", "70:3A:CB:17:CC:CF"} {
		s.addAccessPoint(context.Background(), str)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"time"

	pbd "github.com/brotherlogic/discovery/proto"
	pb "github.com/brotherlogic/goserver/proto"
	"golang.org/x/net/context"
)

func (s *Server) updateWorld(ctx context.Context, server *pbd.RegistryEntry) error {
	s.serverMap[server.Identifier] = time.Now()

	jobs, err := s.getter.getJobs(ctx, server)
	if err != nil {
		return err
	}

	s.slaveMap[server.GetIdentifier()] = []string{}
	for _, job := range jobs {
		s.slaveMap[server.GetIdentifier()] = append(s.slaveMap[server.GetIdentifier()], job.GetJob().GetName())
	}
	return nil
}

func (s *Server) buildWorld(ctx context.Context) error {
	s.worldMutex.Lock()
	s.world = make(map[string]map[string]struct{})
	slaves, err := s.getter.getSlaves()
	if err != nil {
		s.worldMutex.Unlock()
		return err
	}
	s.worldMutex.Unlock()

	for _, server := range slaves.GetServices() {
		err := s.updateWorld(ctx, server)
		if err != nil {
			return err
		}
	}

	// Rebuild world
	s.worldMutex.Lock()
	for server, jobs := range s.slaveMap {
		for _, job := range jobs {
			if _, ok := s.world[job]; !ok {
				s.world[job] = make(map[string]struct{})
			}
			s.world[job][server] = struct{}{}
		}
	}
	s.worldMutex.Unlock()

	s.lastWorldRun = time.Now().Unix()

	for server, seen := range s.serverMap {
		if time.Now().Sub(seen) > s.timeChange {
			info, _ := s.State(ctx, &pb.Empty{})
			infoString := ""
			for _, str := range info.GetStates() {
				infoString += fmt.Sprintf("%v = %v\n", str.Key, str)
			}
			s.RaiseIssue(ctx, "Missing Server", fmt.Sprintf("%v is missing duration: %v.\n%v", server, time.Now().Sub(seen), infoString), false)
		}
	}

	for job, versions := range s.world {
		count := int32(0)
		found := false
		for _, cjob := range s.config.Nintents {
			if cjob.Job.Name == job {
				found = true
				count = cjob.Count
			}
		}

		if !found {
			s.Log(fmt.Sprintf("Could not find %v", job))
		}

		if count > 0 && int32(len(versions))-count > 1 {
			s.RaiseIssue(ctx, "Too many jobs", fmt.Sprintf("%v has too many versions running", job), false)
		}
	}

	return nil
}
<commit_msg>Only clears world once servers are processed. This closes #519<commit_after>package main

import (
	"fmt"
	"time"

	pbd "github.com/brotherlogic/discovery/proto"
	pb "github.com/brotherlogic/goserver/proto"
	"golang.org/x/net/context"
)

func (s *Server) updateWorld(ctx context.Context, server *pbd.RegistryEntry) error {
	s.serverMap[server.Identifier] = time.Now()

	jobs, err := s.getter.getJobs(ctx, server)
	if err != nil {
		return err
	}

	s.slaveMap[server.GetIdentifier()] = []string{}
	for _, job := range jobs {
		s.slaveMap[server.GetIdentifier()] = append(s.slaveMap[server.GetIdentifier()], job.GetJob().GetName())
	}
	return nil
}

func (s *Server) buildWorld(ctx context.Context) error {
	slaves, err := s.getter.getSlaves()
	if err != nil {
		return err
	}

	for _, server := range slaves.GetServices() {
		err := s.updateWorld(ctx, server)
		if err != nil {
			return err
		}
	}

	// Rebuild world
	s.worldMutex.Lock()
	s.world = make(map[string]map[string]struct{})
	for server, jobs := range s.slaveMap {
		for _, job := range jobs {
			if _, ok := s.world[job]; !ok {
				s.world[job] = make(map[string]struct{})
			}
			s.world[job][server] = struct{}{}
		}
	}
	s.worldMutex.Unlock()
	s.lastWorldRun = time.Now().Unix()

	for server, seen := range s.serverMap {
		if time.Now().Sub(seen) > s.timeChange {
			info, _ := s.State(ctx, &pb.Empty{})
			infoString := ""
			for _, str := range info.GetStates() {
				infoString += fmt.Sprintf("%v = %v\n", str.Key, str)
			}
			s.RaiseIssue(ctx, "Missing Server", fmt.Sprintf("%v is missing duration: %v.\n%v", server, time.Now().Sub(seen), infoString), false)
		}
	}

	for job, versions := range s.world {
		count := int32(0)
		found := false
		for _, cjob := range s.config.Nintents {
			if cjob.Job.Name == job {
				found = true
				count = cjob.Count
			}
		}

		if !found {
			s.Log(fmt.Sprintf("Could not find %v", job))
		}

		if count > 0 && int32(len(versions))-count > 1 {
			s.RaiseIssue(ctx, "Too many jobs", fmt.Sprintf("%v has too many versions running", job), false)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>// +build darwin freebsd dragonfly netbsd openbsd linux

package reuseport

import (
	"context"
	"net"
	"os"
	"strconv"
	"syscall"
	"time"

	"github.com/jbenet/go-reuseport/singlepoll"
	sockaddrnet "github.com/jbenet/go-sockaddr/net"
)

const (
	filePrefix = "port."
)

// Wrapper around the socket system call that marks the returned file
// descriptor as nonblocking and close-on-exec.
func socket(family, socktype, protocol int) (fd int, err error) {
	syscall.ForkLock.RLock()
	fd, err = syscall.Socket(family, socktype, protocol)
	if err == nil {
		syscall.CloseOnExec(fd)
	}
	syscall.ForkLock.RUnlock()

	if err != nil {
		return -1, err
	}

	// cant set it until after connect
	// if err = syscall.SetNonblock(fd, true); err != nil {
	// 	syscall.Close(fd)
	// 	return -1, err
	// }

	if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, soReuseAddr, 1); err != nil {
		syscall.Close(fd)
		return -1, err
	}

	if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, soReusePort, 1); err != nil {
		syscall.Close(fd)
		return -1, err
	}

	// set setLinger to 5 as reusing exact same (srcip:srcport, dstip:dstport)
	// will otherwise fail on connect.
	if err = setLinger(fd, 5); err != nil {
		syscall.Close(fd)
		return -1, err
	}

	return fd, nil
}

func dial(ctx context.Context, dialer net.Dialer, netw, addr string) (c net.Conn, err error) {
	var (
		fd             int
		lfamily        int
		rfamily        int
		socktype       int
		lprotocol      int
		rprotocol      int
		file           *os.File
		deadline       time.Time
		remoteSockaddr syscall.Sockaddr
		localSockaddr  syscall.Sockaddr
	)

	netAddr, err := ResolveAddr(netw, addr)
	if err != nil {
		return nil, err
	}

	switch netAddr.(type) {
	case *net.TCPAddr, *net.UDPAddr:
	default:
		return nil, ErrUnsupportedProtocol
	}

	switch {
	case !dialer.Deadline.IsZero():
		deadline = dialer.Deadline
	case dialer.Timeout != 0:
		deadline = time.Now().Add(dialer.Timeout)
	}

	ctxdeadline, ok := ctx.Deadline()
	if ok && ctxdeadline.Before(deadline) {
		deadline = ctxdeadline
	}

	localSockaddr = sockaddrnet.NetAddrToSockaddr(dialer.LocalAddr)
	remoteSockaddr = sockaddrnet.NetAddrToSockaddr(netAddr)

	rfamily = sockaddrnet.NetAddrAF(netAddr)
	rprotocol = sockaddrnet.NetAddrIPPROTO(netAddr)
	socktype = sockaddrnet.NetAddrSOCK(netAddr)

	if dialer.LocalAddr != nil {
		switch dialer.LocalAddr.(type) {
		case *net.TCPAddr, *net.UDPAddr:
		default:
			return nil, ErrUnsupportedProtocol
		}

		// check family and protocols match.
		lfamily = sockaddrnet.NetAddrAF(dialer.LocalAddr)
		lprotocol = sockaddrnet.NetAddrIPPROTO(dialer.LocalAddr)
		if lfamily != rfamily || lprotocol != rprotocol {
			return nil, &net.AddrError{Err: "unexpected address type", Addr: netAddr.String()}
		}
	}

	// look at dialTCP in http://golang.org/src/net/tcpsock_posix.go  .... !
	// here we just try again 3 times.
	for i := 0; i < 3; i++ {
		if !deadline.IsZero() && deadline.Before(time.Now()) {
			err = errTimeout
			break
		}

		if fd, err = socket(rfamily, socktype, rprotocol); err != nil {
			return nil, err
		}

		if localSockaddr != nil {
			if err = syscall.Bind(fd, localSockaddr); err != nil {
				syscall.Close(fd)
				return nil, err
			}
		}

		if err = syscall.SetNonblock(fd, true); err != nil {
			syscall.Close(fd)
			return nil, err
		}

		if err = connect(ctx, fd, remoteSockaddr, deadline); err != nil {
			syscall.Close(fd)
			if ctx.Err() != nil {
				return nil, ctx.Err()
			}
			continue // try again.
		}

		break
	}
	if err != nil {
		return nil, err
	}

	if rprotocol == syscall.IPPROTO_TCP {
		//  by default golang/net sets TCP no delay to true.
		if err = setNoDelay(fd, true); err != nil {
			syscall.Close(fd)
			return nil, err
		}
	}

	// File Name get be nil
	file = os.NewFile(uintptr(fd), filePrefix+strconv.Itoa(os.Getpid()))
	if c, err = net.FileConn(file); err != nil {
		syscall.Close(fd)
		return nil, err
	}

	if err = file.Close(); err != nil {
		syscall.Close(fd)
		c.Close()
		return nil, err
	}

	// c = wrapConnWithRemoteAddr(c, netAddr)
	return c, err
}

func listen(netw, addr string) (fd int, err error) {
	var (
		family   int
		socktype int
		protocol int
		sockaddr syscall.Sockaddr
	)

	netAddr, err := ResolveAddr(netw, addr)
	if err != nil {
		return -1, err
	}

	switch netAddr.(type) {
	case *net.TCPAddr, *net.UDPAddr:
	default:
		return -1, ErrUnsupportedProtocol
	}

	family = sockaddrnet.NetAddrAF(netAddr)
	protocol = sockaddrnet.NetAddrIPPROTO(netAddr)
	sockaddr = sockaddrnet.NetAddrToSockaddr(netAddr)
	socktype = sockaddrnet.NetAddrSOCK(netAddr)

	if fd, err = socket(family, socktype, protocol); err != nil {
		return -1, err
	}

	if err = syscall.Bind(fd, sockaddr); err != nil {
		syscall.Close(fd)
		return -1, err
	}

	if protocol == syscall.IPPROTO_TCP {
		//  by default golang/net sets TCP no delay to true.
		if err = setNoDelay(fd, true); err != nil {
			syscall.Close(fd)
			return -1, err
		}
	}

	if err = syscall.SetNonblock(fd, true); err != nil {
		syscall.Close(fd)
		return -1, err
	}

	return fd, nil
}

func listenStream(netw, addr string) (l net.Listener, err error) {
	var (
		file *os.File
	)

	fd, err := listen(netw, addr)
	if err != nil {
		return nil, err
	}

	// Set backlog size to the maximum
	if err = syscall.Listen(fd, syscall.SOMAXCONN); err != nil {
		syscall.Close(fd)
		return nil, err
	}

	file = os.NewFile(uintptr(fd), filePrefix+strconv.Itoa(os.Getpid()))
	if l, err = net.FileListener(file); err != nil {
		syscall.Close(fd)
		return nil, err
	}

	if err = file.Close(); err != nil {
		syscall.Close(fd)
		l.Close()
		return nil, err
	}

	return l, err
}

func listenPacket(netw, addr string) (p net.PacketConn, err error) {
	var (
		file *os.File
	)

	fd, err := listen(netw, addr)
	if err != nil {
		return nil, err
	}

	file = os.NewFile(uintptr(fd), filePrefix+strconv.Itoa(os.Getpid()))
	if p, err = net.FilePacketConn(file); err != nil {
		syscall.Close(fd)
		return nil, err
	}

	if err = file.Close(); err != nil {
		syscall.Close(fd)
		p.Close()
		return nil, err
	}

	return p, err
}

func listenUDP(netw, addr string) (c net.Conn, err error) {
	var (
		file *os.File
	)

	fd, err := listen(netw, addr)
	if err != nil {
		return nil, err
	}

	file = os.NewFile(uintptr(fd), filePrefix+strconv.Itoa(os.Getpid()))
	if c, err = net.FileConn(file); err != nil {
		syscall.Close(fd)
		return nil, err
	}

	if err = file.Close(); err != nil {
		syscall.Close(fd)
		return nil, err
	}

	return c, err
}

// this is close to the connect() function inside stdlib/net
func connect(ctx context.Context, fd int, ra syscall.Sockaddr, deadline time.Time) error {
	if !deadline.IsZero() {
		ctx, _ = context.WithDeadline(ctx, deadline)
	}

	switch err := syscall.Connect(fd, ra); err {
	case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
	case nil, syscall.EISCONN:
		if !deadline.IsZero() && deadline.Before(time.Now()) {
			return errTimeout
		}
		if ctx.Err() != nil {
			return ctx.Err()
		}
		return nil
	default:
		return err
	}

	for {
		if err := singlepoll.PollPark(ctx, fd, "w"); err != nil {
			return err
		}

		// if err := fd.pd.WaitWrite(); err != nil {
		// 	return err
		// }
		// i'd use the above fd.pd.WaitWrite to poll io correctly, just like net sockets...
		// but of course, it uses the damn runtime_* functions that _cannot_ be used by
		// non-go-stdlib source... seriously guys, this is not nice.
		// we're relegated to using syscall.Select (what nightmare that is) or using
		// a simple but totally bogus time-based wait. such garbage.
		var nerr int
		var err error
		nerr, err = syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_ERROR)
		if err != nil {
			return err
		}
		switch err = syscall.Errno(nerr); err {
		case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
			continue
		case syscall.Errno(0), syscall.EISCONN:
			if !deadline.IsZero() && deadline.Before(time.Now()) {
				return errTimeout
			}
			return nil
		default:
			return err
		}
	}
}

var errTimeout = &timeoutError{}

type timeoutError struct{}

func (e *timeoutError) Error() string   { return "i/o timeout" }
func (e *timeoutError) Timeout() bool   { return true }
func (e *timeoutError) Temporary() bool { return true }
<commit_msg>Remove old comment<commit_after>// +build darwin freebsd dragonfly netbsd openbsd linux

package reuseport

import (
	"context"
	"net"
	"os"
	"strconv"
	"syscall"
	"time"

	"github.com/jbenet/go-reuseport/singlepoll"
	sockaddrnet "github.com/jbenet/go-sockaddr/net"
)

const (
	filePrefix = "port."
)

// Wrapper around the socket system call that marks the returned file
// descriptor as nonblocking and close-on-exec.
func socket(family, socktype, protocol int) (fd int, err error) {
	syscall.ForkLock.RLock()
	fd, err = syscall.Socket(family, socktype, protocol)
	if err == nil {
		syscall.CloseOnExec(fd)
	}
	syscall.ForkLock.RUnlock()

	if err != nil {
		return -1, err
	}

	// cant set it until after connect
	// if err = syscall.SetNonblock(fd, true); err != nil {
	// 	syscall.Close(fd)
	// 	return -1, err
	// }

	if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, soReuseAddr, 1); err != nil {
		syscall.Close(fd)
		return -1, err
	}

	if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, soReusePort, 1); err != nil {
		syscall.Close(fd)
		return -1, err
	}

	// set setLinger to 5 as reusing exact same (srcip:srcport, dstip:dstport)
	// will otherwise fail on connect.
	if err = setLinger(fd, 5); err != nil {
		syscall.Close(fd)
		return -1, err
	}

	return fd, nil
}

func dial(ctx context.Context, dialer net.Dialer, netw, addr string) (c net.Conn, err error) {
	var (
		fd             int
		lfamily        int
		rfamily        int
		socktype       int
		lprotocol      int
		rprotocol      int
		file           *os.File
		deadline       time.Time
		remoteSockaddr syscall.Sockaddr
		localSockaddr  syscall.Sockaddr
	)

	netAddr, err := ResolveAddr(netw, addr)
	if err != nil {
		return nil, err
	}

	switch netAddr.(type) {
	case *net.TCPAddr, *net.UDPAddr:
	default:
		return nil, ErrUnsupportedProtocol
	}

	switch {
	case !dialer.Deadline.IsZero():
		deadline = dialer.Deadline
	case dialer.Timeout != 0:
		deadline = time.Now().Add(dialer.Timeout)
	}

	ctxdeadline, ok := ctx.Deadline()
	if ok && ctxdeadline.Before(deadline) {
		deadline = ctxdeadline
	}

	localSockaddr = sockaddrnet.NetAddrToSockaddr(dialer.LocalAddr)
	remoteSockaddr = sockaddrnet.NetAddrToSockaddr(netAddr)

	rfamily = sockaddrnet.NetAddrAF(netAddr)
	rprotocol = sockaddrnet.NetAddrIPPROTO(netAddr)
	socktype = sockaddrnet.NetAddrSOCK(netAddr)

	if dialer.LocalAddr != nil {
		switch dialer.LocalAddr.(type) {
		case *net.TCPAddr, *net.UDPAddr:
		default:
			return nil, ErrUnsupportedProtocol
		}

		// check family and protocols match.
		lfamily = sockaddrnet.NetAddrAF(dialer.LocalAddr)
		lprotocol = sockaddrnet.NetAddrIPPROTO(dialer.LocalAddr)
		if lfamily != rfamily || lprotocol != rprotocol {
			return nil, &net.AddrError{Err: "unexpected address type", Addr: netAddr.String()}
		}
	}

	// look at dialTCP in http://golang.org/src/net/tcpsock_posix.go  .... !
	// here we just try again 3 times.
	for i := 0; i < 3; i++ {
		if !deadline.IsZero() && deadline.Before(time.Now()) {
			err = errTimeout
			break
		}

		if fd, err = socket(rfamily, socktype, rprotocol); err != nil {
			return nil, err
		}

		if localSockaddr != nil {
			if err = syscall.Bind(fd, localSockaddr); err != nil {
				syscall.Close(fd)
				return nil, err
			}
		}

		if err = syscall.SetNonblock(fd, true); err != nil {
			syscall.Close(fd)
			return nil, err
		}

		if err = connect(ctx, fd, remoteSockaddr, deadline); err != nil {
			syscall.Close(fd)
			if ctx.Err() != nil {
				return nil, ctx.Err()
			}
			continue // try again.
		}

		break
	}
	if err != nil {
		return nil, err
	}

	if rprotocol == syscall.IPPROTO_TCP {
		//  by default golang/net sets TCP no delay to true.
		if err = setNoDelay(fd, true); err != nil {
			syscall.Close(fd)
			return nil, err
		}
	}

	file = os.NewFile(uintptr(fd), filePrefix+strconv.Itoa(os.Getpid()))
	if c, err = net.FileConn(file); err != nil {
		syscall.Close(fd)
		return nil, err
	}

	if err = file.Close(); err != nil {
		syscall.Close(fd)
		c.Close()
		return nil, err
	}

	// c = wrapConnWithRemoteAddr(c, netAddr)
	return c, err
}

func listen(netw, addr string) (fd int, err error) {
	var (
		family   int
		socktype int
		protocol int
		sockaddr syscall.Sockaddr
	)

	netAddr, err := ResolveAddr(netw, addr)
	if err != nil {
		return -1, err
	}

	switch netAddr.(type) {
	case *net.TCPAddr, *net.UDPAddr:
	default:
		return -1, ErrUnsupportedProtocol
	}

	family = sockaddrnet.NetAddrAF(netAddr)
	protocol = sockaddrnet.NetAddrIPPROTO(netAddr)
	sockaddr = sockaddrnet.NetAddrToSockaddr(netAddr)
	socktype = sockaddrnet.NetAddrSOCK(netAddr)

	if fd, err = socket(family, socktype, protocol); err != nil {
		return -1, err
	}

	if err = syscall.Bind(fd, sockaddr); err != nil {
		syscall.Close(fd)
		return -1, err
	}

	if protocol == syscall.IPPROTO_TCP {
		//  by default golang/net sets TCP no delay to true.
		if err = setNoDelay(fd, true); err != nil {
			syscall.Close(fd)
			return -1, err
		}
	}

	if err = syscall.SetNonblock(fd, true); err != nil {
		syscall.Close(fd)
		return -1, err
	}

	return fd, nil
}

func listenStream(netw, addr string) (l net.Listener, err error) {
	var (
		file *os.File
	)

	fd, err := listen(netw, addr)
	if err != nil {
		return nil, err
	}

	// Set backlog size to the maximum
	if err = syscall.Listen(fd, syscall.SOMAXCONN); err != nil {
		syscall.Close(fd)
		return nil, err
	}

	file = os.NewFile(uintptr(fd), filePrefix+strconv.Itoa(os.Getpid()))
	if l, err = net.FileListener(file); err != nil {
		syscall.Close(fd)
		return nil, err
	}

	if err = file.Close(); err != nil {
		syscall.Close(fd)
		l.Close()
		return nil, err
	}

	return l, err
}

func listenPacket(netw, addr string) (p net.PacketConn, err error) {
	var (
		file *os.File
	)

	fd, err := listen(netw, addr)
	if err != nil {
		return nil, err
	}

	file = os.NewFile(uintptr(fd), filePrefix+strconv.Itoa(os.Getpid()))
	if p, err = net.FilePacketConn(file); err != nil {
		syscall.Close(fd)
		return nil, err
	}

	if err = file.Close(); err != nil {
		syscall.Close(fd)
		p.Close()
		return nil, err
	}

	return p, err
}

func listenUDP(netw, addr string) (c net.Conn, err error) {
	var (
		file *os.File
	)

	fd, err := listen(netw, addr)
	if err != nil {
		return nil, err
	}

	file = os.NewFile(uintptr(fd), filePrefix+strconv.Itoa(os.Getpid()))
	if c, err = net.FileConn(file); err != nil {
		syscall.Close(fd)
		return nil, err
	}

	if err = file.Close(); err != nil {
		syscall.Close(fd)
		return nil, err
	}

	return c, err
}

// this is close to the connect() function inside stdlib/net
func connect(ctx context.Context, fd int, ra syscall.Sockaddr, deadline time.Time) error {
	if !deadline.IsZero() {
		ctx, _ = context.WithDeadline(ctx, deadline)
	}

	switch err := syscall.Connect(fd, ra); err {
	case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
	case nil, syscall.EISCONN:
		if !deadline.IsZero() && deadline.Before(time.Now()) {
			return errTimeout
		}
		if ctx.Err() != nil {
			return ctx.Err()
		}
		return nil
	default:
		return err
	}

	for {
		if err := singlepoll.PollPark(ctx, fd, "w"); err != nil {
			return err
		}

		// if err := fd.pd.WaitWrite(); err != nil {
		// 	return err
		// }
		// i'd use the above fd.pd.WaitWrite to poll io correctly, just like net sockets...
		// but of course, it uses the damn runtime_* functions that _cannot_ be used by
		// non-go-stdlib source... seriously guys, this is not nice.
		// we're relegated to using syscall.Select (what nightmare that is) or using
		// a simple but totally bogus time-based wait. such garbage.
		var nerr int
		var err error
		nerr, err = syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_ERROR)
		if err != nil {
			return err
		}
		switch err = syscall.Errno(nerr); err {
		case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
			continue
		case syscall.Errno(0), syscall.EISCONN:
			if !deadline.IsZero() && deadline.Before(time.Now()) {
				return errTimeout
			}
			return nil
		default:
			return err
		}
	}
}

var errTimeout = &timeoutError{}

type timeoutError struct{}

func (e *timeoutError) Error() string   { return "i/o timeout" }
func (e *timeoutError) Timeout() bool   { return true }
func (e *timeoutError) Temporary() bool { return true }
<|endoftext|>