<commit_before>package bolster_test

import (
	"flag"
	"reflect"
	"testing"

	"github.com/kylelemons/godebug/pretty"
	"github.com/nochso/bolster"
	"github.com/nochso/bolster/internal"
)

var updateGold = flag.Bool("update", false, "update golden test files")

func TestTx_Insert_withoutAutoincrement(t *testing.T) {
	st, closer := internal.OpenTestStore(t)
	defer closer()

	t.Run("withoutRegistration", func(t *testing.T) {
		err := st.Write(func(tx *bolster.Tx) error {
			return tx.Insert(&structWithID{})
		})
		if err == nil {
			t.Error("expected error, got nil")
		} else {
			t.Log(err)
		}
	})
	err := st.Register(structWithID{})
	if err != nil {
		t.Error(err)
	}
	t.Run("first", func(t *testing.T) {
		err := st.Write(func(tx *bolster.Tx) error {
			return tx.Insert(&structWithID{})
		})
		if err != nil {
			t.Error(err)
		}
		internal.GoldStore(t, st, *updateGold)
	})
	t.Run("duplicate", func(t *testing.T) {
		err := st.Write(func(tx *bolster.Tx) error {
			return tx.Insert(&structWithID{})
		})
		if err == nil {
			t.Error("expected error, got nil")
		} else {
			t.Log(err)
		}
		internal.GoldStore(t, st, *updateGold)
	})
	t.Run("duplicateLazy", func(t *testing.T) {
		err := st.Write(func(tx *bolster.Tx) error {
			tx.Insert(&structWithID{})
			tx.Insert(&structWithID{})
			tx.Insert(&structWithID{})
			return nil
		})
		if err == nil {
			t.Error("expected error, got nil")
		} else {
			t.Log(err)
		}
		internal.GoldStore(t, st, *updateGold)
	})
	t.Run("withoutPointer", func(t *testing.T) {
		err := st.Write(func(tx *bolster.Tx) error {
			return tx.Insert(structWithID{})
		})
		if err == nil {
			t.Error("expected error, got nil")
		} else {
			t.Log(err)
		}
	})
	t.Run("pointerToNonStruct", func(t *testing.T) {
		err := st.Write(func(tx *bolster.Tx) error {
			return tx.Insert(new(int))
		})
		if err == nil {
			t.Error("expected error, got nil")
		} else {
			t.Log(err)
		}
	})
	t.Run("surrounding", func(t *testing.T) {
		err := st.Write(func(tx *bolster.Tx) error {
			for i := -5; i < 6; i++ {
				if i == 0 {
					continue
				}
				err := tx.Insert(&structWithID{ID: i})
				if err != nil {
					return err
				}
			}
			return nil
		})
		if err != nil {
			t.Error(err)
		}
		internal.GoldStore(t, st, *updateGold)
	})
}

type structWithAutoincrement struct {
	ID int `bolster:"inc"`
}

func TestTx_Insert_withAutoincrement(t *testing.T) {
	st, closer := internal.OpenTestStore(t)
	defer closer()

	err := st.Register(structWithAutoincrement{})
	if err != nil {
		t.Error(err)
	}
	t.Run("single", func(t *testing.T) {
		exp := &structWithAutoincrement{ID: 1}
		act := &structWithAutoincrement{}
		err = st.Write(func(tx *bolster.Tx) error {
			return tx.Insert(act)
		})
		if err != nil {
			t.Error(err)
		}
		if !reflect.DeepEqual(exp, act) {
			t.Error(pretty.Compare(act, exp))
		}
		internal.GoldStore(t, st, *updateGold)
	})
	t.Run("multipleInSingleTransaction", func(t *testing.T) {
		actuals := make([]structWithAutoincrement, 4)
		err = st.Write(func(tx *bolster.Tx) error {
			for i, _ := range actuals {
				tx.Insert(&actuals[i])
			}
			return nil
		})
		if err != nil {
			t.Error(err)
		}
		for _, act := range actuals {
			if act.ID == 0 {
				t.Errorf("expected ID of struct to be set, got %d", act.ID)
			}
		}
		internal.GoldStore(t, st, *updateGold)
	})
}

func TestTx_Get(t *testing.T) {
	st, closer := internal.OpenTestStore(t)
	defer closer()

	err := st.Register(&structWithID{})
	if err != nil {
		t.Error(err)
	}
	exp := &structWithID{ID: 2}
	err = st.Write(func(tx *bolster.Tx) error {
		return tx.Insert(exp)
	})
	if err != nil {
		t.Error(err)
	}
	t.Run("NotFound", func(t *testing.T) {
		act := &structWithID{}
		err := st.Read(func(tx *bolster.Tx) error {
			return tx.Get(act, 1)
		})
		if err != bolster.ErrNotFound {
			t.Errorf("expected ErrNotFound, got %v", err)
		} else {
			t.Log(err)
		}
	})
	t.Run("Success", func(t *testing.T) {
		act := &structWithID{}
		err = st.Read(func(tx *bolster.Tx) error {
			return tx.Get(act, 2)
		})
		if err != nil {
			t.Error(err)
		}
		if !reflect.DeepEqual(act, exp) {
			t.Error(pretty.Compare(act, exp))
		}
	})
	t.Run("wrongTypeOfID", func(t *testing.T) {
		err := st.Read(func(tx *bolster.Tx) error {
			return tx.Get(&structWithID{}, "1")
		})
		if err == nil {
			t.Error("expected error, got nil")
		} else {
			t.Log(err)
		}
	})
	t.Run("withoutPointer", func(t *testing.T) {
		err := st.Read(func(tx *bolster.Tx) error {
			return tx.Get(structWithID{}, "1")
		})
		if err == nil {
			t.Error("expected error, got nil")
		} else {
			t.Log(err)
		}
	})
}
<commit_msg>gofmt -s<commit_after>package bolster_test

import (
	"flag"
	"reflect"
	"testing"

	"github.com/kylelemons/godebug/pretty"
	"github.com/nochso/bolster"
	"github.com/nochso/bolster/internal"
)

var updateGold = flag.Bool("update", false, "update golden test files")

func TestTx_Insert_withoutAutoincrement(t *testing.T) {
	st, closer := internal.OpenTestStore(t)
	defer closer()

	t.Run("withoutRegistration", func(t *testing.T) {
		err := st.Write(func(tx *bolster.Tx) error {
			return tx.Insert(&structWithID{})
		})
		if err == nil {
			t.Error("expected error, got nil")
		} else {
			t.Log(err)
		}
	})
	err := st.Register(structWithID{})
	if err != nil {
		t.Error(err)
	}
	t.Run("first", func(t *testing.T) {
		err := st.Write(func(tx *bolster.Tx) error {
			return tx.Insert(&structWithID{})
		})
		if err != nil {
			t.Error(err)
		}
		internal.GoldStore(t, st, *updateGold)
	})
	t.Run("duplicate", func(t *testing.T) {
		err := st.Write(func(tx *bolster.Tx) error {
			return tx.Insert(&structWithID{})
		})
		if err == nil {
			t.Error("expected error, got nil")
		} else {
			t.Log(err)
		}
		internal.GoldStore(t, st, *updateGold)
	})
	t.Run("duplicateLazy", func(t *testing.T) {
		err := st.Write(func(tx *bolster.Tx) error {
			tx.Insert(&structWithID{})
			tx.Insert(&structWithID{})
			tx.Insert(&structWithID{})
			return nil
		})
		if err == nil {
			t.Error("expected error, got nil")
		} else {
			t.Log(err)
		}
		internal.GoldStore(t, st, *updateGold)
	})
	t.Run("withoutPointer", func(t *testing.T) {
		err := st.Write(func(tx *bolster.Tx) error {
			return tx.Insert(structWithID{})
		})
		if err == nil {
			t.Error("expected error, got nil")
		} else {
			t.Log(err)
		}
	})
	t.Run("pointerToNonStruct", func(t *testing.T) {
		err := st.Write(func(tx *bolster.Tx) error {
			return tx.Insert(new(int))
		})
		if err == nil {
			t.Error("expected error, got nil")
		} else {
			t.Log(err)
		}
	})
	t.Run("surrounding", func(t *testing.T) {
		err := st.Write(func(tx *bolster.Tx) error {
			for i := -5; i < 6; i++ {
				if i == 0 {
					continue
				}
				err := tx.Insert(&structWithID{ID: i})
				if err != nil {
					return err
				}
			}
			return nil
		})
		if err != nil {
			t.Error(err)
		}
		internal.GoldStore(t, st, *updateGold)
	})
}

type structWithAutoincrement struct {
	ID int `bolster:"inc"`
}

func TestTx_Insert_withAutoincrement(t *testing.T) {
	st, closer := internal.OpenTestStore(t)
	defer closer()

	err := st.Register(structWithAutoincrement{})
	if err != nil {
		t.Error(err)
	}
	t.Run("single", func(t *testing.T) {
		exp := &structWithAutoincrement{ID: 1}
		act := &structWithAutoincrement{}
		err = st.Write(func(tx *bolster.Tx) error {
			return tx.Insert(act)
		})
		if err != nil {
			t.Error(err)
		}
		if !reflect.DeepEqual(exp, act) {
			t.Error(pretty.Compare(act, exp))
		}
		internal.GoldStore(t, st, *updateGold)
	})
	t.Run("multipleInSingleTransaction", func(t *testing.T) {
		actuals := make([]structWithAutoincrement, 4)
		err = st.Write(func(tx *bolster.Tx) error {
			for i := range actuals {
				tx.Insert(&actuals[i])
			}
			return nil
		})
		if err != nil {
			t.Error(err)
		}
		for _, act := range actuals {
			if act.ID == 0 {
				t.Errorf("expected ID of struct to be set, got %d", act.ID)
			}
		}
		internal.GoldStore(t, st, *updateGold)
	})
}

func TestTx_Get(t *testing.T) {
	st, closer := internal.OpenTestStore(t)
	defer closer()

	err := st.Register(&structWithID{})
	if err != nil {
		t.Error(err)
	}
	exp := &structWithID{ID: 2}
	err = st.Write(func(tx *bolster.Tx) error {
		return tx.Insert(exp)
	})
	if err != nil {
		t.Error(err)
	}
	t.Run("NotFound", func(t *testing.T) {
		act := &structWithID{}
		err := st.Read(func(tx *bolster.Tx) error {
			return tx.Get(act, 1)
		})
		if err != bolster.ErrNotFound {
			t.Errorf("expected ErrNotFound, got %v", err)
		} else {
			t.Log(err)
		}
	})
	t.Run("Success", func(t *testing.T) {
		act := &structWithID{}
		err = st.Read(func(tx *bolster.Tx) error {
			return tx.Get(act, 2)
		})
		if err != nil {
			t.Error(err)
		}
		if !reflect.DeepEqual(act, exp) {
			t.Error(pretty.Compare(act, exp))
		}
	})
	t.Run("wrongTypeOfID", func(t *testing.T) {
		err := st.Read(func(tx *bolster.Tx) error {
			return tx.Get(&structWithID{}, "1")
		})
		if err == nil {
			t.Error("expected error, got nil")
		} else {
			t.Log(err)
		}
	})
	t.Run("withoutPointer", func(t *testing.T) {
		err := st.Read(func(tx *bolster.Tx) error {
			return tx.Get(structWithID{}, "1")
		})
		if err == nil {
			t.Error("expected error, got nil")
		} else {
			t.Log(err)
		}
	})
}
<|endoftext|>


<commit_before>package partners

import (
	"fmt"
	"log"
	"net/url"
	"time"

	"github.com/brnstz/bus/internal/conf"
	"github.com/brnstz/bus/internal/etc"
	"github.com/brnstz/bus/internal/models"

	"github.com/brnstz/bus/internal/partners/nyct_subway"
	"github.com/brnstz/bus/internal/partners/transit_realtime"

	"github.com/golang/protobuf/proto"
)

var (
	esiURL = "http://datamine.mta.info/mta_esi.php"

	mtaSubwayRouteToFeed = map[string]string{
		"1":  "1",
		"2":  "1",
		"3":  "1",
		"4":  "1",
		"5":  "1",
		"6":  "1",
		"6X": "1",
		"S":  "1",
		"GS": "1",
		"L":  "2",
		"SI": "11",
	}
)

type mtaNYCSubway struct{}

func (p mtaNYCSubway) IsLive() bool {
	return true
}

// getURL returns the URL for getting this routeID's feed. Second return
// value is false if there is no feed to get.
func (p mtaNYCSubway) getURL(routeID string) (string, bool) {
	var u string

	// Get the feed for this route, if there is one. Otherwise, nothing
	// to return.
	feed, exists := mtaSubwayRouteToFeed[routeID]
	if !exists {
		return "", false
	}

	// Construct URL and call external API, possibly getting cached
	// value.
	q := url.Values{}
	q.Set("key", conf.Partner.DatamineAPIKey)
	q.Set("feed_id", feed)
	u = fmt.Sprint(esiURL, "?", q.Encode())

	return u, true
}

func (p mtaNYCSubway) Precache(agencyID, routeID string, directionID int) error {
	k := fmt.Sprintf("%v|%v|%v", agencyID, routeID, directionID)

	u, exists := p.getURL(routeID)
	if !exists {
		return nil
	}

	// Since the URL we call is the same no matter which direction, arbirarily
	// decide to ignore one of the directions.
	if directionID == 1 {
		return nil
	}

	// Also feed ID "1" applies to multiple routes. We only need to cache one.
	// Arbitrarily choose the "1" route.
	if mtaSubwayRouteToFeed[routeID] == "1" && routeID != "1" {
		return nil
	}

	_, err := etc.RedisCacheURL(u)
	if err != nil {
		log.Println("can't cache live subway response", err)
		return err
	}

	// attempt to parse response to ensure it is valid
	_, _, err = p.Live(agencyID, routeID, "", directionID)
	if err != nil {
		log.Println("can't parse response", err)
		return err
	}

	log.Println("succesfully saved", k)

	return nil
}

func (p mtaNYCSubway) Live(agencyID, routeID, stopID string, directionID int) (d []*models.Departure, v []models.Vehicle, err error) {
	now := time.Now()

	u, exists := p.getURL(routeID)
	if !exists {
		return
	}

	b, err := etc.RedisGet(u)
	if err != nil {
		log.Println("can't get live subways", err)
		return
	}

	// Load the protobuf struct
	tr := &transit_realtime.FeedMessage{}
	err = proto.Unmarshal(b, tr)
	if err != nil {
		log.Println("can't unmarshal", err)
		return
	}

	// Look at each message in the feed
	for _, e := range tr.Entity {

		// Get some updates
		tripUpdate := e.GetTripUpdate()
		trip := tripUpdate.GetTrip()
		stopTimeUpdates := tripUpdate.GetStopTimeUpdate()

		// Ensure we have at least one stop time update
		if len(stopTimeUpdates) < 1 {
			continue
		}

		// Check the first stop time update to see if it's express or not
		var updateEvent interface{}
		firstUpdate := stopTimeUpdates[0]

		// Get the NYC extension so we can see if the Trip is "assigned"
		// yet. If it's assigned, we'll put the vehicle on the map.
		updateEvent, err = proto.GetExtension(
			firstUpdate, nyct_subway.E_NyctStopTimeUpdate,
		)
		if err != nil {
			log.Println("can't get extension", err)
			return
		}
		nycEvent, ok := updateEvent.(*nyct_subway.NyctStopTimeUpdate)
		if !ok {
			log.Println("can't coerce to nyct_subway.NyctStopTimeUpdate")
			return
		}

		var feedRouteID string

		switch nycEvent.GetScheduledTrack() {

		case "2", "3", "M":
			// Express track. Special case for 6X.
			if trip.GetRouteId() == "6" {
				feedRouteID = trip.GetRouteId() + "X"
			} else {
				feedRouteID = trip.GetRouteId()
			}
		default:
			// not express track
			feedRouteID = trip.GetRouteId()
		}

		if feedRouteID != routeID {
			continue
		}

		// If we have at least one stopTimeUpdate (already checked before) and
		// the trip is non-nil, we can get the NYCT extensions.
		if trip != nil {
			var event interface{}

			// Get the NYC extension so we can see if the Trip is "assigned"
			// yet. If it's assigned, we'll put the vehicle on the map.
			event, err = proto.GetExtension(
				trip, nyct_subway.E_NyctTripDescriptor,
			)
			if err != nil {
				log.Println("can't get extension", err)
				return
			}
			nycTrip, ok := event.(*nyct_subway.NyctTripDescriptor)
			if !ok {
				log.Println("can't coerce to nyct_subway.NyctTripDescriptor")
				return
			}

			// The first update in an entity is the stop where the train will
			// next be. Include only "assigned" trips, which are those that
			// are about to start.
			if nycTrip.GetIsAssigned() {
				var vehicle models.Vehicle

				// Get a "vehicle" with the lat/lon of the update's stop
				// (*not* the stop of our request)
				vehicle, err = models.GetVehicle(
					agencyID, routeID,
					stopTimeUpdates[0].GetStopId(),
					directionID,
				)
				if err != nil {
					// FIXME: why are these showing up again?
					//log.Println("can't get vehicle", err)

				} else {
					vehicle.Live = true
					v = append(v, vehicle)
				}
			}
		}

		// Go through all updates to check for our stop ID's departure time.
		for _, u := range stopTimeUpdates {
			// If this is our stop, then get the departure time.
			if u.GetStopId() == stopID {
				dtime := time.Unix(u.GetDeparture().GetTime(), 0)
				if dtime.After(now) {
					d = append(d,
						&models.Departure{
							Time:   dtime,
							TripID: trip.GetTripId(),
							Live:   true,
						},
					)
				}
			}
		}
	}

	return
}
<commit_msg>adding lines<commit_after>package partners

import (
	"fmt"
	"log"
	"net/url"
	"time"

	"github.com/brnstz/bus/internal/conf"
	"github.com/brnstz/bus/internal/etc"
	"github.com/brnstz/bus/internal/models"

	"github.com/brnstz/bus/internal/partners/nyct_subway"
	"github.com/brnstz/bus/internal/partners/transit_realtime"

	"github.com/golang/protobuf/proto"
)

var (
	esiURL = "http://datamine.mta.info/mta_esi.php"

	mtaSubwayRouteToFeed = map[string]string{
		"1":  "1",
		"2":  "1",
		"3":  "1",
		"4":  "1",
		"5":  "1",
		"6":  "1",
		"6X": "1",
		"S":  "1",
		"GS": "1",
		"L":  "2",
		"SI": "11",
		"N":  "16",
		"Q":  "16",
		"R":  "16",
		"W":  "16",
		"B":  "21",
		"D":  "21",
		"C":  "26",
	}
)

type mtaNYCSubway struct{}

func (p mtaNYCSubway) IsLive() bool {
	return true
}

// getURL returns the URL for getting this routeID's feed. Second return
// value is false if there is no feed to get.
func (p mtaNYCSubway) getURL(routeID string) (string, bool) {
	var u string

	// Get the feed for this route, if there is one. Otherwise, nothing
	// to return.
	feed, exists := mtaSubwayRouteToFeed[routeID]
	if !exists {
		return "", false
	}

	// Construct URL and call external API, possibly getting cached
	// value.
	q := url.Values{}
	q.Set("key", conf.Partner.DatamineAPIKey)
	q.Set("feed_id", feed)
	u = fmt.Sprint(esiURL, "?", q.Encode())

	return u, true
}

func (p mtaNYCSubway) Precache(agencyID, routeID string, directionID int) error {
	k := fmt.Sprintf("%v|%v|%v", agencyID, routeID, directionID)

	u, exists := p.getURL(routeID)
	if !exists {
		return nil
	}

	// Since the URL we call is the same no matter which direction, arbirarily
	// decide to ignore one of the directions.
	if directionID == 1 {
		return nil
	}

	// Also feed ID "1" applies to multiple routes. We only need to cache one.
	// Arbitrarily choose the "1" route.
	if mtaSubwayRouteToFeed[routeID] == "1" && routeID != "1" {
		return nil
	}

	_, err := etc.RedisCacheURL(u)
	if err != nil {
		log.Println("can't cache live subway response", err)
		return err
	}

	// attempt to parse response to ensure it is valid
	_, _, err = p.Live(agencyID, routeID, "", directionID)
	if err != nil {
		log.Println("can't parse response", err)
		return err
	}

	log.Println("succesfully saved", k)

	return nil
}

func (p mtaNYCSubway) Live(agencyID, routeID, stopID string, directionID int) (d []*models.Departure, v []models.Vehicle, err error) {
	now := time.Now()

	u, exists := p.getURL(routeID)
	if !exists {
		return
	}

	b, err := etc.RedisGet(u)
	if err != nil {
		log.Println("can't get live subways", err)
		return
	}

	// Load the protobuf struct
	tr := &transit_realtime.FeedMessage{}
	err = proto.Unmarshal(b, tr)
	if err != nil {
		log.Println("can't unmarshal", err)
		return
	}

	// Look at each message in the feed
	for _, e := range tr.Entity {

		// Get some updates
		tripUpdate := e.GetTripUpdate()
		trip := tripUpdate.GetTrip()
		stopTimeUpdates := tripUpdate.GetStopTimeUpdate()

		// Ensure we have at least one stop time update
		if len(stopTimeUpdates) < 1 {
			continue
		}

		// Check the first stop time update to see if it's express or not
		var updateEvent interface{}
		firstUpdate := stopTimeUpdates[0]

		// Get the NYC extension so we can see if the Trip is "assigned"
		// yet. If it's assigned, we'll put the vehicle on the map.
		updateEvent, err = proto.GetExtension(
			firstUpdate, nyct_subway.E_NyctStopTimeUpdate,
		)
		if err != nil {
			log.Println("can't get extension", err)
			return
		}
		nycEvent, ok := updateEvent.(*nyct_subway.NyctStopTimeUpdate)
		if !ok {
			log.Println("can't coerce to nyct_subway.NyctStopTimeUpdate")
			return
		}

		var feedRouteID string

		switch nycEvent.GetScheduledTrack() {

		case "2", "3", "M":
			// Express track. Special case for 6X.
			if trip.GetRouteId() == "6" {
				feedRouteID = trip.GetRouteId() + "X"
			} else {
				feedRouteID = trip.GetRouteId()
			}
		default:
			// not express track
			feedRouteID = trip.GetRouteId()
		}

		if feedRouteID != routeID {
			continue
		}

		// If we have at least one stopTimeUpdate (already checked before) and
		// the trip is non-nil, we can get the NYCT extensions.
		if trip != nil {
			var event interface{}

			// Get the NYC extension so we can see if the Trip is "assigned"
			// yet. If it's assigned, we'll put the vehicle on the map.
			event, err = proto.GetExtension(
				trip, nyct_subway.E_NyctTripDescriptor,
			)
			if err != nil {
				log.Println("can't get extension", err)
				return
			}
			nycTrip, ok := event.(*nyct_subway.NyctTripDescriptor)
			if !ok {
				log.Println("can't coerce to nyct_subway.NyctTripDescriptor")
				return
			}

			// The first update in an entity is the stop where the train will
			// next be. Include only "assigned" trips, which are those that
			// are about to start.
			if nycTrip.GetIsAssigned() {
				var vehicle models.Vehicle

				// Get a "vehicle" with the lat/lon of the update's stop
				// (*not* the stop of our request)
				vehicle, err = models.GetVehicle(
					agencyID, routeID,
					stopTimeUpdates[0].GetStopId(),
					directionID,
				)
				if err != nil {
					// FIXME: why are these showing up again?
					//log.Println("can't get vehicle", err)

				} else {
					vehicle.Live = true
					v = append(v, vehicle)
				}
			}
		}

		// Go through all updates to check for our stop ID's departure time.
		for _, u := range stopTimeUpdates {
			// If this is our stop, then get the departure time.
			if u.GetStopId() == stopID {
				dtime := time.Unix(u.GetDeparture().GetTime(), 0)
				if dtime.After(now) {
					d = append(d,
						&models.Departure{
							Time:   dtime,
							TripID: trip.GetTripId(),
							Live:   true,
						},
					)
				}
			}
		}
	}

	return
}
<|endoftext|>


<commit_before>// Copyright 2016 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build !js

package glfw

import (
	"fmt"
	"unsafe"

	"golang.org/x/sys/windows"

	"github.com/hajimehoshi/ebiten/internal/glfw"
)

const (
	smCyCaption             = 4
	monitorDefaultToNearest = 2
)

type rect struct {
	left   int32
	top    int32
	right  int32
	bottom int32
}

type monitorInfo struct {
	cbSize    uint32
	rcMonitor rect
	rcWork    rect
	dwFlags   uint32
}

var (
	// user32 is defined at hideconsole_windows.go
	procGetSystemMetrics    = user32.NewProc("GetSystemMetrics")
	procGetActiveWindow     = user32.NewProc("GetActiveWindow")
	procGetForegroundWindow = user32.NewProc("GetForegroundWindow")
	procMonitorFromWindow   = user32.NewProc("MonitorFromWindow")
	procGetMonitorInfoW     = user32.NewProc("GetMonitorInfoW")
)

func getSystemMetrics(nIndex int) (int, error) {
	r, _, e := procGetSystemMetrics.Call(uintptr(nIndex))
	if e != nil && e.(windows.Errno) != 0 {
		return 0, fmt.Errorf("ui: GetSystemMetrics failed: error code: %d", e)
	}
	return int(r), nil
}

func getActiveWindow() (uintptr, error) {
	r, _, e := procGetActiveWindow.Call()
	if e != nil && e.(windows.Errno) != 0 {
		return 0, fmt.Errorf("ui: GetActiveWindow failed: error code: %d", e)
	}
	return r, nil
}

func getForegroundWindow() (uintptr, error) {
	r, _, e := procGetForegroundWindow.Call()
	if e != nil && e.(windows.Errno) != 0 {
		return 0, fmt.Errorf("ui: GetForegroundWindow failed: error code: %d", e)
	}
	return r, nil
}

func monitorFromWindow(hwnd uintptr, dwFlags uint32) (uintptr, error) {
	r, _, e := procMonitorFromWindow.Call(hwnd, uintptr(dwFlags))
	if e != nil && e.(windows.Errno) != 0 {
		return 0, fmt.Errorf("ui: MonitorFromWindow failed: error code: %d", e)
	}
	if r == 0 {
		return 0, fmt.Errorf("ui: MonitorFromWindow failed: returned value: %d", r)
	}
	return r, nil
}

func getMonitorInfoW(hMonitor uintptr, lpmi *monitorInfo) error {
	r, _, e := procGetMonitorInfoW.Call(hMonitor, uintptr(unsafe.Pointer(lpmi)))
	if e != nil && e.(windows.Errno) != 0 {
		return fmt.Errorf("ui: GetMonitorInfoW failed: error code: %d", e)
	}
	if r == 0 {
		return fmt.Errorf("ui: GetMonitorInfoW failed: returned value: %d", r)
	}
	return nil
}

func (u *UserInterface) glfwScale() float64 {
	return u.deviceScaleFactor()
}

func (u *UserInterface) adjustWindowPosition(x, y int) (int, int) {
	mx, my := currentMonitor(u.window).GetPos()
	// As the video width/height might be wrong,
	// adjust x/y at least to enable to handle the window (#328)
	if x < mx {
		x = mx
	}
	t, err := getSystemMetrics(smCyCaption)
	if err != nil {
		panic(err)
	}
	if y < my+t {
		y = my + t
	}
	return x, y
}

func currentMonitorByOS(_ *glfw.Window) *glfw.Monitor {
	// TODO: Should we return nil here?
	w, err := getActiveWindow()
	if err != nil {
		panic(err)
	}

	if w == 0 {
		// There is no window at launching, but there is a hidden initialized window.
		// Get the foreground window, that is common among multiple processes.
		w, err = getForegroundWindow()
		if err != nil {
			panic(err)
		}
		if w == 0 {
			// GetForegroundWindow can return null according to the document.
			return nil
		}
	}

	// Get the current monitor by the window handle instead of the window position. It is because the window
	// position is not relaiable in some cases e.g. when the window is put across multiple monitors.

	m, err := monitorFromWindow(w, monitorDefaultToNearest)
	if err != nil {
		// monitorFromWindow can return error on Wine. Ignore this.
		return nil
	}

	mi := monitorInfo{}
	mi.cbSize = uint32(unsafe.Sizeof(mi))
	if err := getMonitorInfoW(m, &mi); err != nil {
		panic(err)
	}

	x, y := int(mi.rcMonitor.left), int(mi.rcMonitor.top)
	for _, m := range glfw.GetMonitors() {
		mx, my := m.GetPos()
		if mx == x && my == y {
			return m
		}
	}
	return nil
}

func (u *UserInterface) nativeWindow() unsafe.Pointer {
	return u.window.GetWin32Window()
}
<commit_msg>uidriver/glfw: Update comments<commit_after>// Copyright 2016 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build !js

package glfw

import (
	"fmt"
	"unsafe"

	"golang.org/x/sys/windows"

	"github.com/hajimehoshi/ebiten/internal/glfw"
)

const (
	smCyCaption             = 4
	monitorDefaultToNearest = 2
)

type rect struct {
	left   int32
	top    int32
	right  int32
	bottom int32
}

type monitorInfo struct {
	cbSize    uint32
	rcMonitor rect
	rcWork    rect
	dwFlags   uint32
}

var (
	// user32 is defined at hideconsole_windows.go
	procGetSystemMetrics    = user32.NewProc("GetSystemMetrics")
	procGetActiveWindow     = user32.NewProc("GetActiveWindow")
	procGetForegroundWindow = user32.NewProc("GetForegroundWindow")
	procMonitorFromWindow   = user32.NewProc("MonitorFromWindow")
	procGetMonitorInfoW     = user32.NewProc("GetMonitorInfoW")
)

func getSystemMetrics(nIndex int) (int, error) {
	r, _, e := procGetSystemMetrics.Call(uintptr(nIndex))
	if e != nil && e.(windows.Errno) != 0 {
		return 0, fmt.Errorf("ui: GetSystemMetrics failed: error code: %d", e)
	}
	return int(r), nil
}

func getActiveWindow() (uintptr, error) {
	r, _, e := procGetActiveWindow.Call()
	if e != nil && e.(windows.Errno) != 0 {
		return 0, fmt.Errorf("ui: GetActiveWindow failed: error code: %d", e)
	}
	return r, nil
}

func getForegroundWindow() (uintptr, error) {
	r, _, e := procGetForegroundWindow.Call()
	if e != nil && e.(windows.Errno) != 0 {
		return 0, fmt.Errorf("ui: GetForegroundWindow failed: error code: %d", e)
	}
	return r, nil
}

func monitorFromWindow(hwnd uintptr, dwFlags uint32) (uintptr, error) {
	r, _, e := procMonitorFromWindow.Call(hwnd, uintptr(dwFlags))
	if e != nil && e.(windows.Errno) != 0 {
		return 0, fmt.Errorf("ui: MonitorFromWindow failed: error code: %d", e)
	}
	if r == 0 {
		return 0, fmt.Errorf("ui: MonitorFromWindow failed: returned value: %d", r)
	}
	return r, nil
}

func getMonitorInfoW(hMonitor uintptr, lpmi *monitorInfo) error {
	r, _, e := procGetMonitorInfoW.Call(hMonitor, uintptr(unsafe.Pointer(lpmi)))
	if e != nil && e.(windows.Errno) != 0 {
		return fmt.Errorf("ui: GetMonitorInfoW failed: error code: %d", e)
	}
	if r == 0 {
		return fmt.Errorf("ui: GetMonitorInfoW failed: returned value: %d", r)
	}
	return nil
}

func (u *UserInterface) glfwScale() float64 {
	return u.deviceScaleFactor()
}

func (u *UserInterface) adjustWindowPosition(x, y int) (int, int) {
	mx, my := currentMonitor(u.window).GetPos()
	// As the video width/height might be wrong,
	// adjust x/y at least to enable to handle the window (#328)
	if x < mx {
		x = mx
	}
	t, err := getSystemMetrics(smCyCaption)
	if err != nil {
		panic(err)
	}
	if y < my+t {
		y = my + t
	}
	return x, y
}

func currentMonitorByOS(_ *glfw.Window) *glfw.Monitor {
	// TODO: Should we return nil here?
	w, err := getActiveWindow()
	if err != nil {
		panic(err)
	}

	if w == 0 {
		// The active window doesn't exist when launching, or the application is runnable on unfocused.
		// Get the foreground window, that is common among multiple processes.
		w, err = getForegroundWindow()
		if err != nil {
			panic(err)
		}
		if w == 0 {
			// GetForegroundWindow can return null according to the document.
			return nil
		}
	}

	// Get the current monitor by the window handle instead of the window position. It is because the window
	// position is not relaiable in some cases e.g. when the window is put across multiple monitors.

	m, err := monitorFromWindow(w, monitorDefaultToNearest)
	if err != nil {
		// monitorFromWindow can return error on Wine. Ignore this.
		return nil
	}

	mi := monitorInfo{}
	mi.cbSize = uint32(unsafe.Sizeof(mi))
	if err := getMonitorInfoW(m, &mi); err != nil {
		panic(err)
	}

	x, y := int(mi.rcMonitor.left), int(mi.rcMonitor.top)
	for _, m := range glfw.GetMonitors() {
		mx, my := m.GetPos()
		if mx == x && my == y {
			return m
		}
	}
	return nil
}

func (u *UserInterface) nativeWindow() unsafe.Pointer {
	return u.window.GetWin32Window()
}
<|endoftext|>


<commit_before>package org

import (
	"path"
	"regexp"
	"strings"
	"unicode"
)

type Text struct{ Content string }

type LineBreak struct{ Count int }
type ExplicitLineBreak struct{}

type Emphasis struct {
	Kind    string
	Content []Node
}

type FootnoteLink struct {
	Name       string
	Definition *FootnoteDefinition
}

type RegularLink struct {
	Protocol    string
	Description []Node
	URL         string
	AutoLink    bool
}

var validURLCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:/?#[]@!$&'()*+,;="
var autolinkProtocols = regexp.MustCompile(`(https?|ftp|file)`)
var imageExtensionRegexp = regexp.MustCompile(`^[.](png|gif|jpe?g|svg|tiff?)$`)
var videoExtensionRegexp = regexp.MustCompile(`^[.](webm|mp4)$`)

var subScriptSuperScriptRegexp = regexp.MustCompile(`([_^])\{(.*?)\}`)
var footnoteRegexp = regexp.MustCompile(`\[fn:([\w-]+?)(:(.*?))?\]`)

func (d *Document) parseInline(input string) (nodes []Node) {
	previous, current := 0, 0
	for current < len(input) {
		rewind, consumed, node := 0, 0, (Node)(nil)
		switch input[current] {
		case '^':
			consumed, node = d.parseSubOrSuperScript(input, current)
		case '_':
			consumed, node = d.parseSubScriptOrEmphasis(input, current)
		case '*', '/', '=', '~', '+':
			consumed, node = d.parseEmphasis(input, current)
		case '[':
			consumed, node = d.parseRegularLinkOrFootnoteReference(input, current)
		case '\\':
			consumed, node = d.parseExplicitLineBreak(input, current)
		case '\n':
			consumed, node = d.parseLineBreak(input, current)
		case ':':
			rewind, consumed, node = d.parseAutoLink(input, current)
			current -= rewind
		}
		if consumed != 0 {
			if current > previous {
				nodes = append(nodes, Text{input[previous:current]})
			}
			if node != nil {
				nodes = append(nodes, node)
			}
			current += consumed
			previous = current
		} else {
			current++
		}
	}

	if previous < len(input) {
		nodes = append(nodes, Text{input[previous:]})
	}
	return nodes
}

func (d *Document) parseLineBreak(input string, start int) (int, Node) {
	i := start
	for ; input[i] == '\n'; i++ {
	}
	return i - start, LineBreak{i - start}
}

func (d *Document) parseExplicitLineBreak(input string, start int) (int, Node) {
	if start == 0 || input[start-1] == '\n' || start+1 >= len(input) || input[start+1] != '\\' {
		return 0, nil
	}
	for i := start + 2; ; i++ {
		if i == len(input)-1 || input[i] == '\n' {
			return i + 1 - start, ExplicitLineBreak{}
		}
		if !unicode.IsSpace(rune(input[i])) {
			break
		}
	}
	return 0, nil
}

func (d *Document) parseSubOrSuperScript(input string, start int) (int, Node) {
	if m := subScriptSuperScriptRegexp.FindStringSubmatch(input[start:]); m != nil {
		return len(m[2]) + 3, Emphasis{m[1] + "{}", []Node{Text{m[2]}}}
	}
	return 0, nil
}

func (d *Document) parseSubScriptOrEmphasis(input string, start int) (int, Node) {
	if consumed, node := d.parseSubOrSuperScript(input, start); consumed != 0 {
		return consumed, node
	}
	return d.parseEmphasis(input, start)
}

func (d *Document) parseRegularLinkOrFootnoteReference(input string, start int) (int, Node) {
	if len(input[start:]) >= 2 && input[start] == '[' && input[start+1] == '[' {
		return d.parseRegularLink(input, start)
	} else if len(input[start:]) >= 1 && input[start] == '[' {
		return d.parseFootnoteReference(input, start)
	}
	return 0, nil
}

func (d *Document) parseFootnoteReference(input string, start int) (int, Node) {
	if m := footnoteRegexp.FindStringSubmatch(input[start:]); m != nil {
		name, definition := m[1], m[3]
		link := FootnoteLink{name, nil}
		if definition != "" {
			link.Definition = &FootnoteDefinition{name, []Node{Paragraph{d.parseInline(definition)}}, true}
			d.Footnotes.add(name, link.Definition)
		}
		return len(m[0]), link
	}
	return 0, nil
}

func (d *Document) parseAutoLink(input string, start int) (int, int, Node) {
	if !d.AutoLink || len(input[start:]) < 3 || input[start+1] != '/' || input[start+2] != '/' {
		return 0, 0, nil
	}
	protocolStart, protocol := start-1, ""
	for ; protocolStart > 0 && unicode.IsLetter(rune(input[protocolStart])); protocolStart-- {
	}
	if m := autolinkProtocols.FindStringSubmatch(input[protocolStart:start]); m != nil {
		protocol = m[1]
	} else {
		return 0, 0, nil
	}
	end := start
	for ; end < len(input) && strings.ContainsRune(validURLCharacters, rune(input[end])); end++ {
	}
	path := input[start:end]
	if path == "://" {
		return 0, 0, nil
	}
	return len(protocol), len(path + protocol), RegularLink{protocol, nil, protocol + path, true}
}

func (d *Document) parseRegularLink(input string, start int) (int, Node) {
	if len(input[start:]) == 0 || input[start+1] != '[' {
		return 0, nil
	}
	input = input[start:]
	end := strings.Index(input, "]]")
	if end == -1 {
		return 0, nil
	}
	rawLinkParts := strings.Split(input[2:end], "][")
	description, link := ([]Node)(nil), rawLinkParts[0]
	if len(rawLinkParts) == 2 {
		link, description = rawLinkParts[0], d.parseInline(rawLinkParts[1])
	}
	consumed := end + 2
	protocol, linkParts := "", strings.SplitN(link, ":", 2)
	if len(linkParts) == 2 {
		protocol = linkParts[0]
	}
	return consumed, RegularLink{protocol, description, link, false}
}

func (d *Document) parseEmphasis(input string, start int) (int, Node) {
	marker, i := input[start], start
	if !hasValidPreAndBorderChars(input, i) {
		return 0, nil
	}
	for i, consumedNewLines := i+1, 0; i < len(input) && consumedNewLines <= d.MaxEmphasisNewLines; i++ {
		if input[i] == '\n' {
			consumedNewLines++
		}

		if input[i] == marker && i != start+1 && hasValidPostAndBorderChars(input, i) {
			return i + 1 - start, Emphasis{input[start : start+1], d.parseInline(input[start+1 : i])}
		}
	}
	return 0, nil
}

// see org-emphasis-regexp-components (emacs elisp variable)

func hasValidPreAndBorderChars(input string, i int) bool {
	return (i+1 >= len(input) || isValidBorderChar(rune(input[i+1]))) && (i == 0 || isValidPreChar(rune(input[i-1])))
}

func hasValidPostAndBorderChars(input string, i int) bool {
	return (i == 0 || isValidBorderChar(rune(input[i-1]))) && (i+1 >= len(input) || isValidPostChar(rune(input[i+1])))
}

func isValidPreChar(r rune) bool {
	return unicode.IsSpace(r) || strings.ContainsRune(`-({'"`, r)
}

func isValidPostChar(r rune) bool {
	return unicode.IsSpace(r) || strings.ContainsRune(`-.,:!?;'")}[`, r)
}

func isValidBorderChar(r rune) bool { return !unicode.IsSpace(r) }

func (l RegularLink) Kind() string {
	if p := l.Protocol; l.Description != nil || (p != "" && p != "file" && p != "http" && p != "https") {
		return "regular"
	}
	if imageExtensionRegexp.MatchString(path.Ext(l.URL)) {
		return "image"
	}
	if videoExtensionRegexp.MatchString(path.Ext(l.URL)) {
		return "video"
	}
	return "regular"
}
<commit_msg>Fix inline parseLineBreak: Handle end of input<commit_after>package org

import (
	"path"
	"regexp"
	"strings"
	"unicode"
)

type Text struct{ Content string }

type LineBreak struct{ Count int }
type ExplicitLineBreak struct{}

type Emphasis struct {
	Kind    string
	Content []Node
}

type FootnoteLink struct {
	Name       string
	Definition *FootnoteDefinition
}

type RegularLink struct {
	Protocol    string
	Description []Node
	URL         string
	AutoLink    bool
}

var validURLCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:/?#[]@!$&'()*+,;="
var autolinkProtocols = regexp.MustCompile(`(https?|ftp|file)`)
var imageExtensionRegexp = regexp.MustCompile(`^[.](png|gif|jpe?g|svg|tiff?)$`)
var videoExtensionRegexp = regexp.MustCompile(`^[.](webm|mp4)$`)

var subScriptSuperScriptRegexp = regexp.MustCompile(`([_^])\{(.*?)\}`)
var footnoteRegexp = regexp.MustCompile(`\[fn:([\w-]+?)(:(.*?))?\]`)

func (d *Document) parseInline(input string) (nodes []Node) {
	previous, current := 0, 0
	for current < len(input) {
		rewind, consumed, node := 0, 0, (Node)(nil)
		switch input[current] {
		case '^':
			consumed, node = d.parseSubOrSuperScript(input, current)
		case '_':
			consumed, node = d.parseSubScriptOrEmphasis(input, current)
		case '*', '/', '=', '~', '+':
			consumed, node = d.parseEmphasis(input, current)
		case '[':
			consumed, node = d.parseRegularLinkOrFootnoteReference(input, current)
		case '\\':
			consumed, node = d.parseExplicitLineBreak(input, current)
		case '\n':
			consumed, node = d.parseLineBreak(input, current)
		case ':':
			rewind, consumed, node = d.parseAutoLink(input, current)
			current -= rewind
		}
		if consumed != 0 {
			if current > previous {
				nodes = append(nodes, Text{input[previous:current]})
			}
			if node != nil {
				nodes = append(nodes, node)
			}
			current += consumed
			previous = current
		} else {
			current++
		}
	}

	if previous < len(input) {
		nodes = append(nodes, Text{input[previous:]})
	}
	return nodes
}

func (d *Document) parseLineBreak(input string, start int) (int, Node) {
	i := start
	for ; i < len(input) && input[i] == '\n'; i++ {
	}
	return i - start, LineBreak{i - start}
}

func (d *Document) parseExplicitLineBreak(input string, start int) (int, Node) {
	if start == 0 || input[start-1] == '\n' || start+1 >= len(input) || input[start+1] != '\\' {
		return 0, nil
	}
	for i := start + 2; ; i++ {
		if i == len(input)-1 || input[i] == '\n' {
			return i + 1 - start, ExplicitLineBreak{}
		}
		if !unicode.IsSpace(rune(input[i])) {
			break
		}
	}
	return 0, nil
}

func (d *Document) parseSubOrSuperScript(input string, start int) (int, Node) {
	if m := subScriptSuperScriptRegexp.FindStringSubmatch(input[start:]); m != nil {
		return len(m[2]) + 3, Emphasis{m[1] + "{}", []Node{Text{m[2]}}}
	}
	return 0, nil
}

func (d *Document) parseSubScriptOrEmphasis(input string, start int) (int, Node) {
	if consumed, node := d.parseSubOrSuperScript(input, start); consumed != 0 {
		return consumed, node
	}
	return d.parseEmphasis(input, start)
}

func (d *Document) parseRegularLinkOrFootnoteReference(input string, start int) (int, Node) {
	if len(input[start:]) >= 2 && input[start] == '[' && input[start+1] == '[' {
		return d.parseRegularLink(input, start)
	} else if len(input[start:]) >= 1 && input[start] == '[' {
		return d.parseFootnoteReference(input, start)
	}
	return 0, nil
}

func (d *Document) parseFootnoteReference(input string, start int) (int, Node) {
	if m := footnoteRegexp.FindStringSubmatch(input[start:]); m != nil {
		name, definition := m[1], m[3]
		link := FootnoteLink{name, nil}
		if definition != "" {
			link.Definition = &FootnoteDefinition{name, []Node{Paragraph{d.parseInline(definition)}}, true}
			d.Footnotes.add(name, link.Definition)
		}
		return len(m[0]), link
	}
	return 0, nil
}

func (d *Document) parseAutoLink(input string, start int) (int, int, Node) {
	if !d.AutoLink || len(input[start:]) < 3 || input[start+1] != '/' || input[start+2] != '/' {
		return 0, 0, nil
	}
	protocolStart, protocol := start-1, ""
	for ; protocolStart > 0 && unicode.IsLetter(rune(input[protocolStart])); protocolStart-- {
	}
	if m := autolinkProtocols.FindStringSubmatch(input[protocolStart:start]); m != nil {
		protocol = m[1]
	} else {
		return 0, 0, nil
	}
	end := start
	for ; end < len(input) && strings.ContainsRune(validURLCharacters, rune(input[end])); end++ {
	}
	path := input[start:end]
	if path == "://" {
		return 0, 0, nil
	}
	return len(protocol), len(path + protocol), RegularLink{protocol, nil, protocol + path, true}
}

func (d *Document) parseRegularLink(input string, start int) (int, Node) {
	if len(input[start:]) == 0 || input[start+1] != '[' {
		return 0, nil
	}
	input = input[start:]
	end := strings.Index(input, "]]")
	if end == -1 {
		return 0, nil
	}
	rawLinkParts := strings.Split(input[2:end], "][")
	description, link := ([]Node)(nil), rawLinkParts[0]
	if len(rawLinkParts) == 2 {
		link, description = rawLinkParts[0], d.parseInline(rawLinkParts[1])
	}
	consumed := end + 2
	protocol, linkParts := "", strings.SplitN(link, ":", 2)
	if len(linkParts) == 2 {
		protocol = linkParts[0]
	}
	return consumed, RegularLink{protocol, description, link, false}
}

func (d *Document) parseEmphasis(input string, start int) (int, Node) {
	marker, i := input[start], start
	if !hasValidPreAndBorderChars(input, i) {
		return 0, nil
	}
	for i, consumedNewLines := i+1, 0; i < len(input) && consumedNewLines <= d.MaxEmphasisNewLines; i++ {
		if input[i] == '\n' {
			consumedNewLines++
		}

		if input[i] == marker && i != start+1 && hasValidPostAndBorderChars(input, i) {
			return i + 1 - start, Emphasis{input[start : start+1], d.parseInline(input[start+1 : i])}
		}
	}
	return 0, nil
}

// see org-emphasis-regexp-components (emacs elisp variable)

func hasValidPreAndBorderChars(input string, i int) bool {
	return (i+1 >= len(input) || isValidBorderChar(rune(input[i+1]))) && (i == 0 || isValidPreChar(rune(input[i-1])))
}

func hasValidPostAndBorderChars(input string, i int) bool {
	return (i == 0 || isValidBorderChar(rune(input[i-1]))) && (i+1 >= len(input) || isValidPostChar(rune(input[i+1])))
}

func isValidPreChar(r rune) bool {
	return unicode.IsSpace(r) || strings.ContainsRune(`-({'"`, r)
}

func isValidPostChar(r rune) bool {
	return unicode.IsSpace(r) || strings.ContainsRune(`-.,:!?;'")}[`, r)
}

func isValidBorderChar(r rune) bool { return !unicode.IsSpace(r) }

func (l RegularLink) Kind() string {
	if p := l.Protocol; l.Description != nil || (p != "" && p != "file" && p != "http" && p != "https") {
		return "regular"
	}
	if imageExtensionRegexp.MatchString(path.Ext(l.URL)) {
		return "image"
	}
	if videoExtensionRegexp.MatchString(path.Ext(l.URL)) {
		return "video"
	}
	return "regular"
}
<|endoftext|>


<commit_before>package git

import (
	"io/ioutil"
	"path/filepath"
	"strings"
	"testing"

	. "github.com/smartystreets/goconvey/convey"
	"polydawn.net/repeatr/io"
	"polydawn.net/repeatr/testutil"
)

//func TestCoreCompliance(t *testing.T) {
//	Convey("Spec Compliance: Git Transmat", t, testutil.WithTmpdir(func() {
//		// Nope.
//		// Most of the core compliance tests require round-trip;
//		// we can't satisfy any of those because we don't (yet) support scan with git.
//	}))
//}

func TestGitLocalFileInputCompat(t *testing.T) {
	// note that this test eschews use of regular file fixtures for a few reasons:
	//  - because it's capable of working without root if it doesn't try to chown
	//  - because we're doing custom content anyway so we have multiple commits
	//  both of these could be addressed with upgrades to filefixtures in the future.
	Convey("Given a local git repo", t, testutil.Requires(
		testutil.WithTmpdir(func() {
			var dataHash_1 integrity.CommitID
			var dataHash_2 integrity.CommitID
			var dataHash_3 integrity.CommitID
			git.Bake("init", "--", "repo-a").RunAndReport()
			testutil.UsingDir("repo-a", func() {
				git.Bake("commit", "--allow-empty", "-m", "testrepo-a initial commit").RunAndReport()
				dataHash_1 = integrity.CommitID(strings.Trim(git.Bake("rev-parse", "HEAD").Output(), "\n"))
				ioutil.WriteFile("file-a", []byte("abcd"), 0644)
				git.Bake("add", ".").RunAndReport()
				git.Bake("commit", "-m", "testrepo-a commit 1").RunAndReport()
				dataHash_2 = integrity.CommitID(strings.Trim(git.Bake("rev-parse", "HEAD").Output(), "\n"))
				ioutil.WriteFile("file-e", []byte("efghi"), 0644)
				git.Bake("add", ".").RunAndReport()
				git.Bake("commit", "-m", "testrepo-a commit 2").RunAndReport()
				dataHash_3 = integrity.CommitID(strings.Trim(git.Bake("rev-parse", "HEAD").Output(), "\n"))
			})

			transmat := New("./workdir")

			Convey("Materialization should be able to produce the latest commit", FailureContinues, func() {
				uris := []integrity.SiloURI{integrity.SiloURI("./repo-a")}
				// materialize from the ID returned by foreign git
				arena := transmat.Materialize(Kind, dataHash_3, uris, integrity.AcceptHashMismatch)
				// assert hash match
				// (normally survival would attest this, but we used the `AcceptHashMismatch` to supress panics in the name of letting the test see more after failures.)
				So(arena.Hash(), ShouldEqual, dataHash_3)
				// check filesystem to loosely match the original fixture
				So(filepath.Join(arena.Path(), "file-a"), testutil.ShouldBeFile)
				So(filepath.Join(arena.Path(), "file-e"), testutil.ShouldBeFile)
				So(filepath.Join(arena.Path(), ".git"), testutil.ShouldBeNotFile)
			})

			Convey("Materialization should be able to produce older commits", FailureContinues, func() {
				uris := []integrity.SiloURI{integrity.SiloURI("./repo-a")}
				// materialize from the ID returned by foreign git
				arena := transmat.Materialize(Kind, dataHash_2, uris, integrity.AcceptHashMismatch)
				// assert hash match
				// (normally survival would attest this, but we used the `AcceptHashMismatch` to supress panics in the name of letting the test see more after failures.)
				So(arena.Hash(), ShouldEqual, dataHash_2)
				// check filesystem to loosely match the original fixture
				So(filepath.Join(arena.Path(), "file-a"), testutil.ShouldBeFile)
				So(filepath.Join(arena.Path(), "file-e"), testutil.ShouldBeNotFile)
				So(filepath.Join(arena.Path(), ".git"), testutil.ShouldBeNotFile)
			})
		})),
	)

	// TODO you really should do this with a fixture loop
	// but that does also leave questions about multi-commits, branches, etc.
	// so do both i guess.
	//filefixture.Beta.Create("repo-a")
}
<commit_msg>Set an author and email when creating git fixtures<commit_after>package git

import (
	"io/ioutil"
	"path/filepath"
	"strings"
	"testing"

	"github.com/polydawn/gosh"
	. "github.com/smartystreets/goconvey/convey"
	"polydawn.net/repeatr/io"
	"polydawn.net/repeatr/testutil"
)

//func TestCoreCompliance(t *testing.T) {
//	Convey("Spec Compliance: Git Transmat", t, testutil.WithTmpdir(func() {
//		// Nope.
//		// Most of the core compliance tests require round-trip;
//		// we can't satisfy any of those because we don't (yet) support scan with git.
//	}))
//}

func TestGitLocalFileInputCompat(t *testing.T) {
	// note that this test eschews use of regular file fixtures for a few reasons:
	//  - because it's capable of working without root if it doesn't try to chown
	//  - because we're doing custom content anyway so we have multiple commits
	//  both of these could be addressed with upgrades to filefixtures in the future.
	Convey("Given a local git repo", t, testutil.Requires(
		testutil.WithTmpdir(func() {
			git := git.Bake(gosh.Opts{Env: map[string]string{
				"GIT_AUTHOR_NAME":     "repeatr",
				"GIT_AUTHOR_EMAIL":    "repeatr",
				"GIT_COMMITTER_NAME":  "repeatr",
				"GIT_COMMITTER_EMAIL": "repeatr",
			}})
			var dataHash_1 integrity.CommitID
			var dataHash_2 integrity.CommitID
			var dataHash_3 integrity.CommitID
			git.Bake("init", "--", "repo-a").RunAndReport()
			testutil.UsingDir("repo-a", func() {
				git.Bake("commit", "--allow-empty", "-m", "testrepo-a initial commit").RunAndReport()
				dataHash_1 = integrity.CommitID(strings.Trim(git.Bake("rev-parse", "HEAD").Output(), "\n"))
				ioutil.WriteFile("file-a", []byte("abcd"), 0644)
				git.Bake("add", ".").RunAndReport()
				git.Bake("commit", "-m", "testrepo-a commit 1").RunAndReport()
				dataHash_2 = integrity.CommitID(strings.Trim(git.Bake("rev-parse", "HEAD").Output(), "\n"))
				ioutil.WriteFile("file-e", []byte("efghi"), 0644)
				git.Bake("add", ".").RunAndReport()
				git.Bake("commit", "-m", "testrepo-a commit 2").RunAndReport()
				dataHash_3 = integrity.CommitID(strings.Trim(git.Bake("rev-parse", "HEAD").Output(), "\n"))
			})

			transmat := New("./workdir")

			Convey("Materialization should be able to produce the latest commit", FailureContinues, func() {
				uris := []integrity.SiloURI{integrity.SiloURI("./repo-a")}
				// materialize from the ID returned by foreign git
				arena := transmat.Materialize(Kind, dataHash_3, uris, integrity.AcceptHashMismatch)
				// assert hash match
				// (normally survival would attest this, but we used the `AcceptHashMismatch` to supress panics in the name of letting the test see more after failures.)
				So(arena.Hash(), ShouldEqual, dataHash_3)
				// check filesystem to loosely match the original fixture
				So(filepath.Join(arena.Path(), "file-a"), testutil.ShouldBeFile)
				So(filepath.Join(arena.Path(), "file-e"), testutil.ShouldBeFile)
				So(filepath.Join(arena.Path(), ".git"), testutil.ShouldBeNotFile)
			})

			Convey("Materialization should be able to produce older commits", FailureContinues, func() {
				uris := []integrity.SiloURI{integrity.SiloURI("./repo-a")}
				// materialize from the ID returned by foreign git
				arena := transmat.Materialize(Kind, dataHash_2, uris, integrity.AcceptHashMismatch)
				// assert hash match
				// (normally survival would attest this, but we used the `AcceptHashMismatch` to supress panics in the name of letting the test see more after failures.)
				So(arena.Hash(), ShouldEqual, dataHash_2)
				// check filesystem to loosely match the original fixture
				So(filepath.Join(arena.Path(), "file-a"), testutil.ShouldBeFile)
				So(filepath.Join(arena.Path(), "file-e"), testutil.ShouldBeNotFile)
				So(filepath.Join(arena.Path(), ".git"), testutil.ShouldBeNotFile)
			})
		})),
	)

	// TODO you really should do this with a fixture loop
	// but that does also leave questions about multi-commits, branches, etc.
	// so do both i guess.
	//filefixture.Beta.Create("repo-a")
}
<|endoftext|>


<commit_before>package main
import "fmt"
import "net/http"
improt "url"

type Gok struct {
    w http.ResponseWriter;
    r *http.Request;
};

func (gok *Gok) Echo(a ...interface{}) {
    fmt.Fprint(w, a...);
}
/*- >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> $_SERVER <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< -*/
func (gok *Gok) ServerSelf() string {
    return gok.Url.Path;
}
func (gok *Gok) ServerHttpUserAgent() string {
    return r.Header["User-Agent"];
}
func (gok *Gok) ServerHttps() bool {
    return r.URL.Scheme == "https";
}
func (gok *Gok) ServerRemoteAddr() string {
    return strings.Split(r.RemoteAddr, ":")[0];
}
func (gok *Gok) ServerRemotePort() string {
    return strings.Split(r.RmoteAddr, ":")[1];
}
func (gok *Gok) ServerPort() int {
    return 80;
}
func (gok *Gok) ServerHttpAcceptEncoding() string {
    return r.Header["Accept-Encoding"];
}
func (gok *Gok) ServerProtocol() string {
    return r.Proto;
}
func (gok *Gok) ServerRequestMethod() string {
    return r.Method;
}
func (gok *Gok) ServerQueryString() string {
    return r.URL.RawQuery
}
func (gok *Gok) ServerHttpAccept() string {
    return r.Header["Accept"];
}
func (gok *Gok) ServerHttpAcceptCharset() string {
    return r.Header["Accept-Charset"];
}
func (gok *Gok) ServerHttpAcceptLanguage() string {
    return r.Header["Accept-Language"];
}
func (gok *Gok) ServerHttpConnection() string {
    return r.Header["Connection"];
}
func (gok *Gok) ServerHttpHost() string {
    return r.Header["HOST"];
}

/*- >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> $_GET && $_POST <<<<<<<<<<<<<<<<<<<<<<<<<<< -*/

func (gok *Gok) Post(string) []byte {}
func (gok *Gok) Get(string) string {}

/*- $_COOKIE -*/
func (gok *Gok) Cookie(string) string {}
func (gok *Gok) SetCookie(string, string) {}
/*- $_FILE -*/
func (gok *Gok) File(string) (string, uint32) {}
/*- Headers -*/
func (gok *Gok) RequestHeader() map[string]string {}
func (gok *Gok) ResponseHeader() map[string]string {}
/*- Request/Writer -*/
func (gok *Gok) RequestWriter() http.ResponseWriter {}
func (gok *Gok) HttpRequest() *http.Request {}
<commit_msg>standard<commit_after>package main
import "fmt"
import "net/http"
improt "url"
import "time"

type Gok struct {
    w http.ResponseWriter;
    r *http.Request;
};

func (gok *Gok) Echo(a ...interface{}) {
    fmt.Fprint(w, a...);
}
/*- >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> $_SERVER <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< -*/
func (gok *Gok) ServerSelf() string {
    return gok.Url.Path;
}
func (gok *Gok) ServerHttpUserAgent() string {
    return r.Header["User-Agent"];
}
func (gok *Gok) ServerHttps() bool {
    return r.URL.Scheme == "https";
}
func (gok *Gok) ServerRemoteAddr() string {
    return strings.Split(r.RemoteAddr, ":")[0];
}
func (gok *Gok) ServerRemotePort() string {
    return strings.Split(r.RmoteAddr, ":")[1];
}
func (gok *Gok) ServerPort() int {
    return 80;
}
func (gok *Gok) ServerHttpAcceptEncoding() string {
    return r.Header["Accept-Encoding"];
}
func (gok *Gok) ServerProtocol() string {
    return r.Proto;
}
func (gok *Gok) ServerRequestMethod() string {
    return r.Method;
}
func (gok *Gok) ServerQueryString() string {
    return r.URL.RawQuery
}
func (gok *Gok) ServerHttpAccept() string {
    return r.Header["Accept"];
}
func (gok *Gok) ServerHttpAcceptCharset() string {
    return r.Header["Accept-Charset"];
}
func (gok *Gok) ServerHttpAcceptLanguage() string {
    return r.Header["Accept-Language"];
}
func (gok *Gok) ServerHttpConnection() string {
    return r.Header["Connection"];
}
func (gok *Gok) ServerHttpHost() string {
    return r.Header["HOST"];
}

/*- >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> $_GET && $_POST <<<<<<<<<<<<<<<<<<<<<<<<<<< -*/

func (gok *Gok) Post(name string) []byte {
    return nil;
}
func (gok *Gok) Get(name string) string {
    return "";
}

/*- >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  $_COOKIE <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< -*/
func (gok *Gok) Cookie(name string) string {
    return "";
}
func (gok *Gok) SetCookie(name string, value string, expires *time.Time) {
}
func (gok *Gok) SetCookie_4(name string, value string, expires *time.Time, path string){
}
func (gok *Gok) SetCookie_5(name string, value string, expires *time.Time, path string,
                            domain string){
}
func (gok *Gok) SetCookie_7(name string, value string, expires *time.Time, path string,
                            domain string, secure bool, httpOnly bool) {
}
func (gok *Gok) UnSetCookie(name string) {
}

/*- $_FILE -*/
func (gok *Gok) File(string) (string, uint32) { return "", 0; }

/*- Headers -*/
func (gok *Gok) RequestHeader() map[string]string { return nil; }
func (gok *Gok) ResponseHeader() map[string]string { return nil; }

/*- Request/Writer -*/
func (gok *Gok) RequestWriter() http.ResponseWriter { return w; }
func (gok *Gok) HttpRequest() *http.Request { return r; }
<|endoftext|>


<commit_before>package precache

import (
	"log"
	"time"

	"github.com/brnstz/bus/internal/conf"
	"github.com/brnstz/bus/internal/etc"
	"github.com/brnstz/bus/internal/models"
	"github.com/brnstz/bus/internal/partners"
)

var (

	// total simultaneous network connections
	maxWorkers = 200

	// max workers per a single agency
	maxWorkersAgency = 20

	// delay is the time to wait between requests for the same route
	delay = time.Duration(60) * time.Second

	// error delay
	errDelay = time.Duration(10) * time.Second

	// size of each precacheRequest's channel
	size = 50000
)

type precacheRequest struct {
	partner     partners.P
	agencyID    string
	routeID     string
	directionID int
	result      chan error
}

// routeWorker is
func routeWorker(ch chan precacheRequest, p partners.P, agencyID string, routeID string, directionID int) {
	var err error

	lastSuccess := time.Now()
	ttlDur := time.Duration(conf.Cache.RedisTTL) * time.Second

	// Loop forever, constantly getting new updates
	for {

		// Create a request
		req := precacheRequest{
			partner:     p,
			agencyID:    agencyID,
			routeID:     routeID,
			directionID: directionID,
			result:      make(chan error, 1),
		}

		log.Println("sending req", req)
		// Send it to an agencyWorker
		ch <- req
		log.Println("sent req", req)

		// Wait for the response
		err = <-req.result
		log.Println("got response", req)

		now := time.Now()
		diff := now.Sub(lastSuccess)

		// If the time between successes is greater than cache duration,
		// we should warn that we have a problem
		if diff > ttlDur {
			log.Println("%v %v %v worker took %v to run, longer than ttl of %v",
				agencyID, routeID, directionID, diff, ttlDur,
			)
		}

		if err != nil {
			log.Println("error getting response", err)
			time.Sleep(errDelay)
		} else {
			lastSuccess = now
			time.Sleep(delay)
		}
	}
}

// agencyWorker calls Precache on each incoming request
func agencyWorker(ch chan precacheRequest) {
	log.Println("ready for work sarge")
	for req := range ch {
		log.Println("i got one")
		req.result <- req.partner.Precache(req.agencyID, req.routeID, req.directionID)
		log.Println("i did it")
	}
}

func Precache() {
	// Go through each agency we support
	for _, agencyID := range conf.Partner.AgencyIDs {

		// Create a channel for this agency
		ch := make(chan precacheRequest, size)

		// Create a number of workers for this agency
		for i := 0; i < maxWorkersAgency; i++ {
			go agencyWorker(ch)
		}

		// Get all the routes for this agency
		routes, err := models.GetAllRoutes(etc.DBConn, agencyID)
		if err != nil {
			log.Println("can't get routes", err)
			continue
		}

		for _, route := range routes {
			for dir := 0; dir <= 1; dir++ {

				p, err := partners.Find(*route)
				if err == partners.ErrNoPartner {
					continue
				}
				if err != nil {
					log.Fatal("error getting partner", err)
				}

				// Create a routeWorker for every combination
				// FIXME: problem with this is... we'll never
				// create new goroutines when they are updated in db
				go routeWorker(ch, p, route.AgencyID, route.RouteID, dir)
			}
		}
	}

	// FIXME: wait forever
	select {}
}
<commit_msg>comments and minor fixes<commit_after>package precache

import (
	"log"
	"time"

	"github.com/brnstz/bus/internal/conf"
	"github.com/brnstz/bus/internal/etc"
	"github.com/brnstz/bus/internal/models"
	"github.com/brnstz/bus/internal/partners"
)

var (

	// max workers per a single agency
	maxWorkersAgency = 10

	// delay is the time to wait between requests for the same route
	delay = time.Duration(60) * time.Second

	// error delay is the time we wait before hitting the API again
	// if we get an error
	errDelay = time.Duration(10) * time.Second

	// size of each precacheRequest's channel
	size = 50000
)

type precacheRequest struct {
	partner     partners.P
	agencyID    string
	routeID     string
	directionID int
	result      chan error
}

// routeWorker runs forever for this partner/agency/route/direction, sending
// a new precacheRequest to the channel ch, delaying between each request.
// The goal is to make a request from the partner before the TTL
// runs out.
func routeWorker(ch chan precacheRequest, p partners.P, agencyID string, routeID string, directionID int) {
	var err error

	// Assume last success was now
	lastSuccess := time.Now()

	// Convert RedisTTL to a duration
	ttlDur := time.Duration(conf.Cache.RedisTTL) * time.Second

	// Loop forever, constantly getting new updates
	for {

		// Create a request
		req := precacheRequest{
			partner:     p,
			agencyID:    agencyID,
			routeID:     routeID,
			directionID: directionID,
			result:      make(chan error, 1),
		}

		// Send it to an agencyWorker
		ch <- req

		// Wait for the response
		err = <-req.result

		// Record current time and difference
		now := time.Now()
		diff := now.Sub(lastSuccess)

		// If the time between successes is greater than cache duration,
		// we should warn that we have a problem
		if diff > ttlDur {
			log.Println("%v %v %v worker took %v to run, longer than ttl of %v",
				agencyID, routeID, directionID, diff, ttlDur,
			)
		}

		if err != nil {
			log.Println("error getting response", err)
			time.Sleep(errDelay)
		} else {
			lastSuccess = now
			time.Sleep(delay)
		}
	}
}

// agencyWorker calls Precache on each incoming request
func agencyWorker(ch chan precacheRequest) {
	for req := range ch {
		req.result <- req.partner.Precache(req.agencyID, req.routeID, req.directionID)
	}
}

func Precache() {
	// Go through each agency we support
	for _, agencyID := range conf.Partner.AgencyIDs {

		// Create a channel for this agency
		ch := make(chan precacheRequest, size)

		// Create a number of workers for this agency
		for i := 0; i < maxWorkersAgency; i++ {
			go agencyWorker(ch)
		}

		// Get all the routes for this agency
		routes, err := models.GetAllRoutes(etc.DBConn, agencyID)
		if err != nil {
			log.Println("can't get routes", err)
			continue
		}

		for _, route := range routes {

			// Get the partner for each route
			p, err := partners.Find(*route)
			if err == partners.ErrNoPartner {
				// If there's no partner, just ignore it
				continue
			}
			if err != nil {
				// It's a fatal error the precacher if it can't
				// one of its configured partners
				log.Fatal("error getting partner", err)
			}

			for dir := 0; dir <= 1; dir++ {
				// Create a routeWorker for every combination of
				// route / direction. For some APIs, the response for each
				// direction is no different. It's up to the partner to ignore
				// one of the requests.

				// FIXME: problem with this is... we'll never
				// create new goroutines when they are updated in db
				go routeWorker(ch, p, route.AgencyID, route.RouteID, dir)
			}
		}
	}

	// FIXME: wait forever
	select {}
}
<|endoftext|>


<commit_before>package persist

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"github.com/rivine/rivine/build"
)

// TestLogger checks that the basic functions of the file logger work as
// designed.
func TestLogger(t *testing.T) {
	// Create a folder for the log file.
	testdir := build.TempDir(persistDir, t.Name())
	err := os.MkdirAll(testdir, 0700)
	if err != nil {
		t.Fatal(err)
	}

	// Create the logger.
	logFilename := filepath.Join(testdir, "test.log")
	fl, err := NewFileLogger(logFilename)
	if err != nil {
		t.Fatal(err)
	}

	// Write an example statement, and then close the logger.
	fl.Println("TEST: this should get written to the logfile")
	err = fl.Close()
	if err != nil {
		t.Fatal(err)
	}

	// Check that data was written to the log file. There should be three
	// lines, one for startup, the example line, and one to close the logger.
	expectedSubstring := []string{"STARTUP", "TEST", "SHUTDOWN", ""} // file ends with a newline
	fileData, err := ioutil.ReadFile(logFilename)
	if err != nil {
		t.Fatal(err)
	}
	fileLines := strings.Split(string(fileData), "\n")
	for i, line := range fileLines {
		if !strings.Contains(string(line), expectedSubstring[i]) {
			t.Error("did not find the expected message in the logger")
		}
	}
	if len(fileLines) != 4 { // file ends with a newline
		t.Error("logger did not create the correct number of lines:", len(fileLines))
	}
}

// TestLoggerCritical prints a critical message from the logger.
func TestLoggerCritical(t *testing.T) {
	// Create a folder for the log file.
	testdir := build.TempDir(persistDir, t.Name())
	err := os.MkdirAll(testdir, 0700)
	if err != nil {
		t.Fatal(err)
	}

	// Create the logger.
	logFilename := filepath.Join(testdir, "test.log")
	fl, err := NewFileLogger(logFilename)
	if err != nil {
		t.Fatal(err)
	}

	// Write a catch for a panic that should trigger when logger.Critical is
	// called.
	defer func() {
		r := recover()
		if r == nil {
			t.Error("critical message was not thrown in a panic")
		}

		// Close the file logger to clean up the test.
		err = fl.Close()
		if err != nil {
			t.Fatal(err)
		}
	}()
	fl.Critical("a critical message")
}
<commit_msg>skip TestLogger as the sync syscall can take >5s<commit_after>package persist

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"github.com/rivine/rivine/build"
)

// TestLogger checks that the basic functions of the file logger work as
// designed.
func TestLogger(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}

	// Create a folder for the log file.
	testdir := build.TempDir(persistDir, t.Name())
	err := os.MkdirAll(testdir, 0700)
	if err != nil {
		t.Fatal(err)
	}

	// Create the logger.
	logFilename := filepath.Join(testdir, "test.log")
	fl, err := NewFileLogger(logFilename)
	if err != nil {
		t.Fatal(err)
	}

	// Write an example statement, and then close the logger.
	fl.Println("TEST: this should get written to the logfile")
	err = fl.Close()
	if err != nil {
		t.Fatal(err)
	}

	// Check that data was written to the log file. There should be three
	// lines, one for startup, the example line, and one to close the logger.
	expectedSubstring := []string{"STARTUP", "TEST", "SHUTDOWN", ""} // file ends with a newline
	fileData, err := ioutil.ReadFile(logFilename)
	if err != nil {
		t.Fatal(err)
	}
	fileLines := strings.Split(string(fileData), "\n")
	for i, line := range fileLines {
		if !strings.Contains(string(line), expectedSubstring[i]) {
			t.Error("did not find the expected message in the logger")
		}
	}
	if len(fileLines) != 4 { // file ends with a newline
		t.Error("logger did not create the correct number of lines:", len(fileLines))
	}
}

// TestLoggerCritical prints a critical message from the logger.
func TestLoggerCritical(t *testing.T) {
	// Create a folder for the log file.
	testdir := build.TempDir(persistDir, t.Name())
	err := os.MkdirAll(testdir, 0700)
	if err != nil {
		t.Fatal(err)
	}

	// Create the logger.
	logFilename := filepath.Join(testdir, "test.log")
	fl, err := NewFileLogger(logFilename)
	if err != nil {
		t.Fatal(err)
	}

	// Write a catch for a panic that should trigger when logger.Critical is
	// called.
	defer func() {
		r := recover()
		if r == nil {
			t.Error("critical message was not thrown in a panic")
		}

		// Close the file logger to clean up the test.
		err = fl.Close()
		if err != nil {
			t.Fatal(err)
		}
	}()
	fl.Critical("a critical message")
}
<|endoftext|>


<commit_before>package hystrix

import (
	"fmt"
	"log"
	"sync"
	"time"
)

type runFunc func() error
type fallbackFunc func(error) error

// A CircuitError is an error which models various failure states of execution,
// such as the circuit being open or a timeout.
type CircuitError struct {
	Message string
}

func (e CircuitError) Error() string {
	return "hystrix: " + e.Message
}

// command models the state used for a single execution on a circuit. "hystrix command" is commonly
// used to describe the pairing of your run/fallback functions with a circuit.
type command struct {
	sync.Mutex

	ticket       *struct{}
	start        time.Time
	errChan      chan error
	finished     chan bool
	circuit      *CircuitBreaker
	run          runFunc
	fallback     fallbackFunc
	runDuration  time.Duration
	events       []string
}

var (
	// ErrMaxConcurrency occurs when too many of the same named command are executed at the same time.
	ErrMaxConcurrency = CircuitError{Message: "max concurrency"}
	// ErrCircuitOpen returns when an execution attempt "short circuits". This happens due to the circuit being measured as unhealthy.
	ErrCircuitOpen = CircuitError{Message: "circuit open"}
	// ErrTimeout occurs when the provided function takes too long to execute.
	ErrTimeout = CircuitError{Message: "timeout"}
)

// Go runs your function while tracking the health of previous calls to it.
// If your function begins slowing down or failing repeatedly, we will block
// new calls to it for you to give the dependent service time to repair.
//
// Define a fallback function if you want to define some code to execute during outages.
func Go(name string, run runFunc, fallback fallbackFunc) chan error {
	cmd := &command{
		run:          run,
		fallback:     fallback,
		start:        time.Now(),
		errChan:      make(chan error, 1),
		finished:     make(chan bool, 1),
	}

	// dont have methods with explicit params and returns
	// let data come in and out naturally, like with any closure
	// explicit error return to give place for us to kill switch the operation (fallback)

	circuit, _, err := GetCircuit(name)
	if err != nil {
		cmd.errChan <- err
		return cmd.errChan
	}
	cmd.circuit = circuit
	returnTicket := func() {
		cmd.Lock()
		cmd.circuit.executorPool.Return(cmd.ticket)
		cmd.ticket = nil
		cmd.Unlock()
	}
	// Shared by the following two goroutines. It ensures only the faster
	// goroutine runs errWithFallback() and reportAllEvent().
	runOnce := &sync.Once{}
	reportAllEvent := func() {
		err := cmd.circuit.ReportEvent(cmd.events, cmd.start, cmd.runDuration)
		if err != nil {
			log.Print(err)
		}
	}

	go func() {
		defer func() { cmd.finished <- true }()

		// Circuits get opened when recent executions have shown to have a high error rate.
		// Rejecting new executions allows backends to recover, and the circuit will allow
		// new traffic when it feels a healthly state has returned.
		if !cmd.circuit.AllowRequest() {
			runOnce.Do(func() {
				cmd.errorWithFallback(ErrCircuitOpen)
				reportAllEvent()
			})
			return
		}

		// As backends falter, requests take longer but don't always fail.
		//
		// When requests slow down but the incoming rate of requests stays the same, you have to
		// run more at a time to keep up. By controlling concurrency during these situations, you can
		// shed load which accumulates due to the increasing ratio of active commands to incoming requests.
		cmd.Lock()
		select {
		case cmd.ticket = <-circuit.executorPool.Tickets:
			cmd.Unlock()
			defer returnTicket()
		default:
			cmd.Unlock()
			runOnce.Do(func() {
				cmd.errorWithFallback(ErrMaxConcurrency)
				reportAllEvent()
			})
			return
		}

		runStart := time.Now()
		runErr := run()
		runOnce.Do(func() {
			defer reportAllEvent()
			cmd.runDuration = time.Since(runStart)
			if runErr != nil {
				cmd.errorWithFallback(runErr)
				return
			}
			cmd.reportEvent("success")
		})
	}()

	go func() {
		defer returnTicket()

		timer := time.NewTimer(getSettings(name).Timeout)
		defer timer.Stop()

		select {
		case <-cmd.finished:
			// runnOnce has been executed in another goroutine
		case <-timer.C:
			runOnce.Do(func() {
				cmd.errorWithFallback(ErrTimeout)
				reportAllEvent()
			})
			return
		}
	}()

	return cmd.errChan
}

// Do runs your function in a synchronous manner, blocking until either your function succeeds
// or an error is returned, including hystrix circuit errors
func Do(name string, run runFunc, fallback fallbackFunc) error {
	done := make(chan struct{}, 1)

	r := func() error {
		err := run()
		if err != nil {
			return err
		}

		done <- struct{}{}
		return nil
	}

	f := func(e error) error {
		err := fallback(e)
		if err != nil {
			return err
		}

		done <- struct{}{}
		return nil
	}

	var errChan chan error
	if fallback == nil {
		errChan = Go(name, r, nil)
	} else {
		errChan = Go(name, r, f)
	}

	select {
	case <-done:
		return nil
	case err := <-errChan:
		return err
	}
}

func (c *command) reportEvent(eventType string) {
	c.Lock()
	defer c.Unlock()

	c.events = append(c.events, eventType)
}

// errorWithFallback triggers the fallback while reporting the appropriate metric events.
func (c *command) errorWithFallback(err error) {
	eventType := "failure"
	if err == ErrCircuitOpen {
		eventType = "short-circuit"
	} else if err == ErrMaxConcurrency {
		eventType = "rejected"
	} else if err == ErrTimeout {
		eventType = "timeout"
	}

	c.reportEvent(eventType)
	fallbackErr := c.tryFallback(err)
	if fallbackErr != nil {
		c.errChan <- fallbackErr
	}
}

func (c *command) tryFallback(err error) error {
	if c.fallback == nil {
		// If we don't have a fallback return the original error.
		return err
	}

	fallbackErr := c.fallback(err)
	if fallbackErr != nil {
		c.reportEvent("fallback-failure")
		return fmt.Errorf("fallback failed with '%v'. run error was '%v'", fallbackErr, err)
	}

	c.reportEvent("fallback-success")

	return nil
}
<commit_msg>fix #662f1df which may failed TestReturnTicket()<commit_after>package hystrix

import (
	"fmt"
	"log"
	"sync"
	"time"
)

type runFunc func() error
type fallbackFunc func(error) error

// A CircuitError is an error which models various failure states of execution,
// such as the circuit being open or a timeout.
type CircuitError struct {
	Message string
}

func (e CircuitError) Error() string {
	return "hystrix: " + e.Message
}

// command models the state used for a single execution on a circuit. "hystrix command" is commonly
// used to describe the pairing of your run/fallback functions with a circuit.
type command struct {
	sync.Mutex

	ticket       *struct{}
	start        time.Time
	errChan      chan error
	finished     chan bool
	circuit      *CircuitBreaker
	run          runFunc
	fallback     fallbackFunc
	runDuration  time.Duration
	events       []string
}

var (
	// ErrMaxConcurrency occurs when too many of the same named command are executed at the same time.
	ErrMaxConcurrency = CircuitError{Message: "max concurrency"}
	// ErrCircuitOpen returns when an execution attempt "short circuits". This happens due to the circuit being measured as unhealthy.
	ErrCircuitOpen = CircuitError{Message: "circuit open"}
	// ErrTimeout occurs when the provided function takes too long to execute.
	ErrTimeout = CircuitError{Message: "timeout"}
)

// Go runs your function while tracking the health of previous calls to it.
// If your function begins slowing down or failing repeatedly, we will block
// new calls to it for you to give the dependent service time to repair.
//
// Define a fallback function if you want to define some code to execute during outages.
func Go(name string, run runFunc, fallback fallbackFunc) chan error {
	cmd := &command{
		run:          run,
		fallback:     fallback,
		start:        time.Now(),
		errChan:      make(chan error, 1),
		finished:     make(chan bool, 1),
	}

	// dont have methods with explicit params and returns
	// let data come in and out naturally, like with any closure
	// explicit error return to give place for us to kill switch the operation (fallback)

	circuit, _, err := GetCircuit(name)
	if err != nil {
		cmd.errChan <- err
		return cmd.errChan
	}
	cmd.circuit = circuit
	ticketCond := sync.NewCond(cmd)
	ticketChecked := false
	// Shared by the following two goroutines. It ensures only the faster
	// goroutine runs errWithFallback() and reportAllEvent().
	runOnce := &sync.Once{}
	reportAllEvent := func() {
		err := cmd.circuit.ReportEvent(cmd.events, cmd.start, cmd.runDuration)
		if err != nil {
			log.Print(err)
		}
	}

	go func() {
		defer func() { cmd.finished <- true }()

		// Circuits get opened when recent executions have shown to have a high error rate.
		// Rejecting new executions allows backends to recover, and the circuit will allow
		// new traffic when it feels a healthly state has returned.
		if !cmd.circuit.AllowRequest() {
			cmd.Lock()
			// It's safe for another goroutine to go ahead releasing a nil ticket.
			ticketChecked = true
			ticketCond.Signal()
			cmd.Unlock()
			runOnce.Do(func() {
				cmd.errorWithFallback(ErrCircuitOpen)
				reportAllEvent()
			})
			return
		}

		// As backends falter, requests take longer but don't always fail.
		//
		// When requests slow down but the incoming rate of requests stays the same, you have to
		// run more at a time to keep up. By controlling concurrency during these situations, you can
		// shed load which accumulates due to the increasing ratio of active commands to incoming requests.
		cmd.Lock()
		select {
		case cmd.ticket = <-circuit.executorPool.Tickets:
			ticketChecked = true
			ticketCond.Signal()
			cmd.Unlock()
		default:
			ticketChecked = true
			ticketCond.Signal()
			cmd.Unlock()
			runOnce.Do(func() {
				cmd.errorWithFallback(ErrMaxConcurrency)
				reportAllEvent()
			})
			return
		}

		runStart := time.Now()
		runErr := run()
		runOnce.Do(func() {
			defer reportAllEvent()
			cmd.runDuration = time.Since(runStart)
			if runErr != nil {
				cmd.errorWithFallback(runErr)
				return
			}
			cmd.reportEvent("success")
		})
	}()

	go func() {
		defer func() {
			cmd.Lock()
			// Avoid releasing before a ticket is acquired.
			for !ticketChecked {
				ticketCond.Wait()
			}
			cmd.circuit.executorPool.Return(cmd.ticket)
			cmd.Unlock()
		}()

		timer := time.NewTimer(getSettings(name).Timeout)
		defer timer.Stop()

		select {
		case <-cmd.finished:
			// runOnce has been executed in another goroutine
		case <-timer.C:
			runOnce.Do(func() {
				cmd.errorWithFallback(ErrTimeout)
				reportAllEvent()
			})
			return
		}
	}()

	return cmd.errChan
}

// Do runs your function in a synchronous manner, blocking until either your function succeeds
// or an error is returned, including hystrix circuit errors
func Do(name string, run runFunc, fallback fallbackFunc) error {
	done := make(chan struct{}, 1)

	r := func() error {
		err := run()
		if err != nil {
			return err
		}

		done <- struct{}{}
		return nil
	}

	f := func(e error) error {
		err := fallback(e)
		if err != nil {
			return err
		}

		done <- struct{}{}
		return nil
	}

	var errChan chan error
	if fallback == nil {
		errChan = Go(name, r, nil)
	} else {
		errChan = Go(name, r, f)
	}

	select {
	case <-done:
		return nil
	case err := <-errChan:
		return err
	}
}

func (c *command) reportEvent(eventType string) {
	c.Lock()
	defer c.Unlock()

	c.events = append(c.events, eventType)
}

// errorWithFallback triggers the fallback while reporting the appropriate metric events.
func (c *command) errorWithFallback(err error) {
	eventType := "failure"
	if err == ErrCircuitOpen {
		eventType = "short-circuit"
	} else if err == ErrMaxConcurrency {
		eventType = "rejected"
	} else if err == ErrTimeout {
		eventType = "timeout"
	}

	c.reportEvent(eventType)
	fallbackErr := c.tryFallback(err)
	if fallbackErr != nil {
		c.errChan <- fallbackErr
	}
}

func (c *command) tryFallback(err error) error {
	if c.fallback == nil {
		// If we don't have a fallback return the original error.
		return err
	}

	fallbackErr := c.fallback(err)
	if fallbackErr != nil {
		c.reportEvent("fallback-failure")
		return fmt.Errorf("fallback failed with '%v'. run error was '%v'", fallbackErr, err)
	}

	c.reportEvent("fallback-success")

	return nil
}
<|endoftext|>


<commit_before>package sentinel

import (
	"time"

	"github.com/mdevilliers/redishappy/configuration"
	"github.com/mdevilliers/redishappy/services/logger"
	"github.com/mdevilliers/redishappy/services/redis"
	"github.com/mdevilliers/redishappy/types"
	"github.com/mdevilliers/redishappy/util"
)

const (
	SentinelMarkedUp    = 1
	SentinelMarkedDown  = 2
	SentinelMarkedAlive = 3
)

const (
	SentinelReconnectionPeriod        = time.Second * 5
	SentinelTopologyExplorationPeriod = time.Second * 60
)

type Manager interface {
	Notify(event SentinelEvent)
}

type SentinelManager struct {
	eventsChannel          chan SentinelEvent
	topologyRequestChannel chan TopologyRequest
	switchmasterchannel    chan types.MasterSwitchedEvent
	redisConnection        redis.RedisConnection
	configurationManager   *configuration.ConfigurationManager
	throttle               *Throttle
}

var topologyState = SentinelTopology{Sentinels: map[string]*SentinelInfo{}}

func NewManager(switchmasterchannel chan types.MasterSwitchedEvent, cm *configuration.ConfigurationManager) *SentinelManager {

	events := make(chan SentinelEvent)
	requests := make(chan TopologyRequest)
	unthrottled := make(chan types.MasterSwitchedEvent)

	throttle := NewThrottle(unthrottled, switchmasterchannel)

	manager := &SentinelManager{eventsChannel: events,
		topologyRequestChannel: requests,
		switchmasterchannel:    unthrottled,
		redisConnection:        redis.RadixRedisConnection{},
		configurationManager:   cm,
		throttle:               throttle}

	go manager.loopEvents(events, requests)
	go manager.bootstrap()
	return manager
}

func (m *SentinelManager) Notify(event SentinelEvent) {
	m.eventsChannel <- event
}

func (m *SentinelManager) GetState(request TopologyRequest) {
	m.topologyRequestChannel <- request
}

func (m *SentinelManager) ClearState() {
	topologyState = SentinelTopology{Sentinels: map[string]*SentinelInfo{}}
}

func (m *SentinelManager) GetCurrentTopology() types.MasterDetailsCollection {
	stateChannel := make(chan types.MasterDetailsCollection)
	go m.getTopology(stateChannel)
	return <-stateChannel
}

func (m *SentinelManager) getTopology(stateChannel chan types.MasterDetailsCollection) {

	topology := types.NewMasterDetailsCollection()
	configuration := m.configurationManager.GetCurrentConfiguration()

	for _, sentinel := range configuration.Sentinels {
		client, err := NewSentinelClient(sentinel, m.redisConnection)

		if err != nil {
			logger.Info.Printf("Error starting sentinel (%s) client : %s", sentinel.GetLocation(), err.Error())
			continue
		}
		defer client.Close()

		for _, clusterDetails := range configuration.Clusters {

			details, err := client.DiscoverMasterForCluster(clusterDetails.Name)
			if err != nil {
				continue
			}

			details.ExternalPort = clusterDetails.ExternalPort
			// TODO : last one wins?
			topology.AddOrReplace(details)
		}
	}
	stateChannel <- topology
}

func (m *SentinelManager) bootstrap() {

	configuration := m.configurationManager.GetCurrentConfiguration()

	for _, sentinel := range configuration.Sentinels {
		m.Notify(&SentinelAdded{Sentinel: sentinel})
	}

	util.Schedule(func() { m.bootstrap() }, SentinelTopologyExplorationPeriod)
}

func (m *SentinelManager) exploreSentinelTopology(sentinel types.Sentinel) {

	client, err := NewSentinelClient(sentinel, m.redisConnection)

	if err != nil {
		logger.Info.Printf("Error starting sentinel (%s) client : %s", sentinel.GetLocation(), err.Error())
	}
	defer client.Close()

	knownClusters := client.FindKnownClusters()

	m.Notify(&SentinelClustersMonitoredUpdate{Sentinel: sentinel, Clusters: knownClusters})

	for _, clustername := range knownClusters {

		sentinels := client.FindConnectedSentinels(clustername)

		for _, connectedsentinel := range sentinels {
			m.Notify(&SentinelAdded{Sentinel: connectedsentinel})
		}
	}
}

func (m *SentinelManager) startNewMonitor(sentinel types.Sentinel) (*Monitor, error) {

	monitor, err := NewMonitor(sentinel, m, m.redisConnection)

	if err != nil {
		logger.Error.Printf("Error starting monitor %s : %s", sentinel.GetLocation(), err.Error())
		m.Notify(&SentinelLost{Sentinel: sentinel})
		return nil, err
	}

	go monitor.StartMonitoringMasterEvents(m.switchmasterchannel)

	return monitor, nil
}

func (m *SentinelManager) loopEvents(events chan SentinelEvent, topology chan TopologyRequest) {
	for {
		select {
		case event := <-events:
			m.updateState(event)
		case read := <-topology:
			read.ReplyChannel <- topologyState
		}
	}
}

func (m *SentinelManager) updateState(event interface{}) {

	switch e := event.(type) {
	case *SentinelAdded:

		sentinel := e.GetSentinel()
		uid := topologyState.createKey(sentinel)

		//if we don't know about the sentinel start monitoring it
		if _, exists := topologyState.Sentinels[uid]; !exists {

			info := &SentinelInfo{SentinelLocation: uid,
				LastUpdated: time.Now().UTC(),
				State:       SentinelMarkedUp}

			topologyState.Sentinels[uid] = info

			go m.startNewMonitor(sentinel)
			go m.exploreSentinelTopology(sentinel)

			logger.Trace.Printf("Sentinel added : %s", util.String(topologyState))
		}

	case *SentinelLost:

		sentinel := e.GetSentinel()
		uid := topologyState.createKey(sentinel)
		currentInfo, ok := topologyState.Sentinels[uid]

		if ok {
			currentInfo.State = SentinelMarkedDown
			currentInfo.LastUpdated = time.Now().UTC()
		}

		util.Schedule(func() { m.startNewMonitor(sentinel) }, SentinelReconnectionPeriod)

		logger.Trace.Printf("Sentinel lost : %s (scheduling new client and monitor).", util.String(topologyState))

	case *SentinelPing:
		sentinel := e.GetSentinel()
		uid := topologyState.createKey(sentinel)
		currentInfo, ok := topologyState.Sentinels[uid]

		if ok {
			currentInfo.State = SentinelMarkedAlive
			currentInfo.LastUpdated = time.Now().UTC()
		}

	case *SentinelClustersMonitoredUpdate:
		sentinel := e.GetSentinel()
		uid := topologyState.createKey(sentinel)

		if info, exists := topologyState.Sentinels[uid]; exists {

			info.Clusters = e.Clusters
		}

	default:
		logger.Error.Println("Unknown sentinel event : ", util.String(e))
	}
}
<commit_msg>tidy up if cannot connect to sentinel<commit_after>package sentinel

import (
	"time"

	"github.com/mdevilliers/redishappy/configuration"
	"github.com/mdevilliers/redishappy/services/logger"
	"github.com/mdevilliers/redishappy/services/redis"
	"github.com/mdevilliers/redishappy/types"
	"github.com/mdevilliers/redishappy/util"
)

const (
	SentinelMarkedUp    = 1
	SentinelMarkedDown  = 2
	SentinelMarkedAlive = 3
)

const (
	SentinelReconnectionPeriod        = time.Second * 5
	SentinelTopologyExplorationPeriod = time.Second * 60
)

type Manager interface {
	Notify(event SentinelEvent)
}

type SentinelManager struct {
	eventsChannel          chan SentinelEvent
	topologyRequestChannel chan TopologyRequest
	switchmasterchannel    chan types.MasterSwitchedEvent
	redisConnection        redis.RedisConnection
	configurationManager   *configuration.ConfigurationManager
	throttle               *Throttle
}

var topologyState = SentinelTopology{Sentinels: map[string]*SentinelInfo{}}

func NewManager(switchmasterchannel chan types.MasterSwitchedEvent, cm *configuration.ConfigurationManager) *SentinelManager {

	events := make(chan SentinelEvent)
	requests := make(chan TopologyRequest)
	unthrottled := make(chan types.MasterSwitchedEvent)

	throttle := NewThrottle(unthrottled, switchmasterchannel)

	manager := &SentinelManager{eventsChannel: events,
		topologyRequestChannel: requests,
		switchmasterchannel:    unthrottled,
		redisConnection:        redis.RadixRedisConnection{},
		configurationManager:   cm,
		throttle:               throttle}

	go manager.loopEvents(events, requests)
	go manager.bootstrap()
	return manager
}

func (m *SentinelManager) Notify(event SentinelEvent) {
	m.eventsChannel <- event
}

func (m *SentinelManager) GetState(request TopologyRequest) {
	m.topologyRequestChannel <- request
}

func (m *SentinelManager) ClearState() {
	topologyState = SentinelTopology{Sentinels: map[string]*SentinelInfo{}}
}

func (m *SentinelManager) GetCurrentTopology() types.MasterDetailsCollection {
	stateChannel := make(chan types.MasterDetailsCollection)
	go m.getTopology(stateChannel)
	return <-stateChannel
}

func (m *SentinelManager) getTopology(stateChannel chan types.MasterDetailsCollection) {

	topology := types.NewMasterDetailsCollection()
	configuration := m.configurationManager.GetCurrentConfiguration()

	for _, sentinel := range configuration.Sentinels {
		client, err := NewSentinelClient(sentinel, m.redisConnection)

		if err != nil {
			logger.Info.Printf("Error starting sentinel (%s) client : %s", sentinel.GetLocation(), err.Error())
			continue
		}
		defer client.Close()

		for _, clusterDetails := range configuration.Clusters {

			details, err := client.DiscoverMasterForCluster(clusterDetails.Name)
			if err != nil {
				continue
			}

			details.ExternalPort = clusterDetails.ExternalPort
			// TODO : last one wins?
			topology.AddOrReplace(details)
		}
	}
	stateChannel <- topology
}

func (m *SentinelManager) bootstrap() {

	configuration := m.configurationManager.GetCurrentConfiguration()

	for _, sentinel := range configuration.Sentinels {
		m.Notify(&SentinelAdded{Sentinel: sentinel})
	}

	util.Schedule(func() { m.bootstrap() }, SentinelTopologyExplorationPeriod)
}

func (m *SentinelManager) exploreSentinelTopology(sentinel types.Sentinel) {

	client, err := NewSentinelClient(sentinel, m.redisConnection)

	if err != nil {
		logger.Info.Printf("Error starting sentinel (%s) client : %s", sentinel.GetLocation(), err.Error())
		m.Notify(&SentinelLost{Sentinel: connectedsentinel})
		return
	}
	defer client.Close()

	knownClusters := client.FindKnownClusters()

	m.Notify(&SentinelClustersMonitoredUpdate{Sentinel: sentinel, Clusters: knownClusters})

	for _, clustername := range knownClusters {

		sentinels := client.FindConnectedSentinels(clustername)

		for _, connectedsentinel := range sentinels {
			m.Notify(&SentinelAdded{Sentinel: connectedsentinel})
		}
	}
}

func (m *SentinelManager) startNewMonitor(sentinel types.Sentinel) (*Monitor, error) {

	monitor, err := NewMonitor(sentinel, m, m.redisConnection)

	if err != nil {
		logger.Error.Printf("Error starting monitor %s : %s", sentinel.GetLocation(), err.Error())
		m.Notify(&SentinelLost{Sentinel: sentinel})
		return nil, err
	}

	go monitor.StartMonitoringMasterEvents(m.switchmasterchannel)

	return monitor, nil
}

func (m *SentinelManager) loopEvents(events chan SentinelEvent, topology chan TopologyRequest) {
	for {
		select {
		case event := <-events:
			m.updateState(event)
		case read := <-topology:
			read.ReplyChannel <- topologyState
		}
	}
}

func (m *SentinelManager) updateState(event interface{}) {

	switch e := event.(type) {
	case *SentinelAdded:

		sentinel := e.GetSentinel()
		uid := topologyState.createKey(sentinel)

		//if we don't know about the sentinel start monitoring it
		if _, exists := topologyState.Sentinels[uid]; !exists {

			info := &SentinelInfo{SentinelLocation: uid,
				LastUpdated: time.Now().UTC(),
				State:       SentinelMarkedUp}

			topologyState.Sentinels[uid] = info

			go m.startNewMonitor(sentinel)
			go m.exploreSentinelTopology(sentinel)

			logger.Trace.Printf("Sentinel added : %s", util.String(topologyState))
		}

	case *SentinelLost:

		sentinel := e.GetSentinel()
		uid := topologyState.createKey(sentinel)
		currentInfo, ok := topologyState.Sentinels[uid]

		if ok {
			currentInfo.State = SentinelMarkedDown
			currentInfo.LastUpdated = time.Now().UTC()
		}

		util.Schedule(func() { m.startNewMonitor(sentinel) }, SentinelReconnectionPeriod)

		logger.Trace.Printf("Sentinel lost : %s (scheduling new client and monitor).", util.String(topologyState))

	case *SentinelPing:
		sentinel := e.GetSentinel()
		uid := topologyState.createKey(sentinel)
		currentInfo, ok := topologyState.Sentinels[uid]

		if ok {
			currentInfo.State = SentinelMarkedAlive
			currentInfo.LastUpdated = time.Now().UTC()
		}

	case *SentinelClustersMonitoredUpdate:
		sentinel := e.GetSentinel()
		uid := topologyState.createKey(sentinel)

		if info, exists := topologyState.Sentinels[uid]; exists {

			info.Clusters = e.Clusters
		}

	default:
		logger.Error.Println("Unknown sentinel event : ", util.String(e))
	}
}
<|endoftext|>


<commit_before>package mem

import (
	"github.com/ossareh/gosysstat/core"
	"github.com/ossareh/gosysstat/core/reader"
	"github.com/ossareh/gosysstat/processor"
)

type MemStat struct {
	region string
	value  int
}

func (m *MemStat) Type() string {
	return m.region
}

func (m *MemStat) Values() []int {
	return []int{m.value}
}

type MemProcessor struct {
	rr *reader.ResettingReader
}

func processStatLine(data []string, memTotal, swapTotal int) *MemStat {
	switch data[0] {
	case "MemTotal:":
		return &MemStat{"total", processor.Stoi(data[1])}
	case "MemFree:":
		used := memTotal - processor.Stoi(data[1])
		return &MemStat{"used", used}
	case "Cached:":
		return &MemStat{"cached", processor.Stoi(data[1])}
	case "SwapTotal:":
		return &MemStat{"swap_total", processor.Stoi(data[1])}
	case "SwapFree:":
		used := swapTotal - processor.Stoi(data[1])
		return &MemStat{"swap_free", used}
	default:
		return nil
	}
}

func (mp *MemProcessor) Process() ([]core.Stat, error) {
	data, err := mp.rr.Read()
	if err != nil {
		return nil, err
	}
	result := []core.Stat{}
	var memTotal, swapTotal int
	for _, d := range data {
		if len(d) > 0 {
			r := processStatLine(d, memTotal, swapTotal)
			if r != nil {
				switch r.region {
				case "total":
					memTotal = r.value
				case "swap_total":
					swapTotal = r.value
				}
				result = append(result, r)
			}
		}
	}
	return result, nil
}

func NewProcessor(filename string) (processor.Processor, error) {
	rr, err := reader.Open(filename)
	if err != nil {
		return nil, err
	}
	return &MemProcessor{rr}, nil
}
<commit_msg>mem.Processor now a ResettingReader{}<commit_after>package mem

import (
	"os"

	"github.com/ossareh/gosysstat/core"
	"github.com/ossareh/gosysstat/core/reader"
	"github.com/ossareh/gosysstat/processor"
)

type MemStat struct {
	region string
	value  int
}

func (m *MemStat) Type() string {
	return m.region
}

func (m *MemStat) Values() []int {
	return []int{m.value}
}

type MemProcessor struct {
	rr *reader.ResettingReader
}

func processStatLine(data []string, memTotal, swapTotal int) *MemStat {
	switch data[0] {
	case "MemTotal:":
		return &MemStat{"total", processor.Stoi(data[1])}
	case "MemFree:":
		used := memTotal - processor.Stoi(data[1])
		return &MemStat{"used", used}
	case "Cached:":
		return &MemStat{"cached", processor.Stoi(data[1])}
	case "SwapTotal:":
		return &MemStat{"swap_total", processor.Stoi(data[1])}
	case "SwapFree:":
		used := swapTotal - processor.Stoi(data[1])
		return &MemStat{"swap_free", used}
	default:
		return nil
	}
}

func (mp *MemProcessor) Process() ([]core.Stat, error) {
	data, err := mp.rr.Read()
	if err != nil {
		return nil, err
	}
	result := []core.Stat{}
	var memTotal, swapTotal int
	for _, d := range data {
		if len(d) > 0 {
			r := processStatLine(d, memTotal, swapTotal)
			if r != nil {
				switch r.region {
				case "total":
					memTotal = r.value
				case "swap_total":
					swapTotal = r.value
				}
				result = append(result, r)
			}
		}
	}
	return result, nil
}

func NewProcessor(filename string) (processor.Processor, error) {
	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	return &MemProcessor{reader.NewResettingReader(file)}, nil
}
<|endoftext|>