<commit_before>//package syncmap is a map with sync.RWMutex.
package syncmap

import (
	"errors"
	. "github.com/yanjinzh6/flowkey/tools"
	"sync"
	"time"
)

var (
	NilKeyError   = errors.New("nil key error")
	TimeOutError  = errors.New("the entity is die")
	HasEntError   = errors.New("old data is erased")
	NotEntError   = errors.New("not entity remove")
	NotEqualError = errors.New("map[key] and value are not equal")
)

type syncMap struct {
	m      map[interface{}]interface{}
	rwlock sync.RWMutex
}

type SyncMap interface {
	Get(key interface{}) (val interface{}, err error)
	Put(key, value interface{}, d time.Duration) (val interface{}, err error)
	PutSimple(key, value interface{}) (val interface{}, err error)
	PutNormal(key, value interface{}) (val interface{}, err error)
	PutIfAbsent(key, value interface{}, d time.Duration) (b bool, err error)
	PutAll(child map[interface{}]interface{}, d time.Duration) (err error)
	Remove(key interface{}) (val interface{}, err error)
	RemoveEntry(key, value interface{}) (b bool, err error)
	Update(key, value interface{}) (b bool, err error)
	IsEmpty() (b bool)
	Clear() (err error)
	ClearUp() (err error)
	Size() (size int)
}

func NewSyncMap() SyncMap {
	return &syncMap{
		m: make(map[interface{}]interface{}),
	}
}

func (s *syncMap) Get(key interface{}) (val interface{}, err error) {
	if !ChKey(key) {
		return nil, NilKeyError
	}
	s.rwlock.RLock()
	val = s.m[key]
	/*if ok, ent := chTimeEntity(val); ok {
		val, err = ent.Value()
	}*/
	s.rwlock.RUnlock()
	return
}

func (s *syncMap) Put(key, value interface{}, d time.Duration) (val interface{}, err error) {
	if !ChKey(key) {
		return nil, NilKeyError
	}
	s.rwlock.Lock()
	val = s.m[key]
	/*if val == nil {
		ent := NewTimeEntity(value, d)
		s.m[key] = ent
	} else {
		if ok, ent := chTimeEntity(val); ok {
			val, err = ent.Value()
		} else {
			s.m[key] = value
		}
	}*/
	s.m[key] = value
	s.rwlock.Unlock()
	return
}

func (s *syncMap) PutSimple(key, value interface{}) (val interface{}, err error) {
	val, err = s.Put(key, value, DEFAULT_DURATION_TIME)
	return
}

func (s *syncMap) PutNormal(key, value interface{}) (val interface{}, err error) {
	val, err = s.Put(key, value, 0)
	return
}

func (s *syncMap) PutIfAbsent(key, value interface{}, d time.Duration) (b bool, err error) {
	if !ChKey(key) {
		return false, NilKeyError
	}
	s.rwlock.Lock()
	if s.m[key] == nil {
		b = true
		s.m[key] = value
	} else {
		b = false
	}
	s.rwlock.Unlock()
	return
}

func (s *syncMap) PutAll(child map[interface{}]interface{}, d time.Duration) (err error) {
	if child != nil {
		s.rwlock.Lock()
		for k, v := range child {
			s.m[k] = v
		}
		s.rwlock.Unlock()
	}
	return
}

func (s *syncMap) Remove(key interface{}) (val interface{}, err error) {
	if !ChKey(key) {
		return nil, NilKeyError
	}
	s.rwlock.Lock()
	val = s.m[key]
	if val != nil {
		delete(s.m, key)
	}
	s.rwlock.Unlock()
	return
}

func (s *syncMap) RemoveEntry(key, value interface{}) (b bool, err error) {
	if !ChKey(key) {
		return false, NilKeyError
	}
	s.rwlock.Lock()
	val := s.m[key]
	if val != nil && val == value {
		b = true
		delete(s.m, key)
	} else {
		b = false
	}
	s.rwlock.Unlock()
	return
}

func (s *syncMap) Update(key, value interface{}) (b bool, err error) {
	if !ChKey(key) {
		return false, NilKeyError
	}
	return
}

func (s *syncMap) IsEmpty() (b bool) {
	s.rwlock.RLock()
	if s.m == nil || len(s.m) == 0 {
		b = true
	} else {
		b = false
	}
	s.rwlock.RUnlock()
	return
}

func (s *syncMap) Clear() (err error) {
	s.rwlock.Lock()
	for k := range s.m {
		delete(s.m, k)
	}
	s.rwlock.Unlock()
	return
}

func (s *syncMap) ClearUp() (err error) {
	return
}

func (s *syncMap) Size() (size int) {
	s.rwlock.RLock()
	size = len(s.m)
	s.rwlock.RUnlock()
	return
}

func chTimeEntity(val interface{}) (ok bool, ent TimeEntity) {
	if val == nil {
		return false, nil
	}
	switch value := val.(type) {
	case TimeEntity:
		return true, value
	default:
		return false, nil
	}
}

type syncMapEnt struct {
	m      map[interface{}]TimeEntity
	rwlock sync.RWMutex
}

func NewSyncMapEnt() SyncMap {
	return &syncMapEnt{
		m: make(map[interface{}]TimeEntity),
	}
}

func (s *syncMapEnt) Get(key interface{}) (val interface{}, err error) {
	if !ChKey(key) {
		return nil, NilKeyError
	}
	if s.m[key].IsDie() {
		s.rwlock.Lock()
		s.m[key] = nil
		delete(s.m, key)
		val = nil
		err = TimeOutError
		s.rwlock.Unlock()
	} else {
		s.rwlock.RLock()
		val, err = s.m[key].Value()
		s.rwlock.RUnlock()
	}
	return
}

func (s *syncMapEnt) Put(key, value interface{}, d time.Duration) (val interface{}, err error) {
	if !ChKey(key) {
		return nil, NilKeyError
	}
	s.rwlock.Lock()
	oldEnt := s.m[key]
	if oldEnt == nil {
		ent := NewTimeEntity(value, d)
		s.m[key] = ent
	} else {
		/*if val, _ := oldEnt.Value(); val != value {
			s.m[key].Update(value)
		}
		if oldEnt.Dtime() != d {
			s.m[key].ChangeDur(d)
		}*/
		val, _ = s.m[key].Value()
		s.m[key] = nil
		ent := NewTimeEntity(value, d)
		s.m[key] = ent
		err = HasEntError
	}
	s.rwlock.Unlock()
	return
}

func (s *syncMapEnt) PutSimple(key, value interface{}) (val interface{}, err error) {
	val, err = s.Put(key, value, DEFAULT_DURATION_TIME)
	return
}

func (s *syncMapEnt) PutNormal(key, value interface{}) (val interface{}, err error) {
	val, err = s.Put(key, value, 0)
	return
}

func (s *syncMapEnt) PutIfAbsent(key, value interface{}, d time.Duration) (b bool, err error) {
	if !ChKey(key) {
		return false, NilKeyError
	}
	s.rwlock.Lock()
	if s.m[key] == nil {
		b = true
		ent := NewTimeEntity(value, d)
		s.m[key] = ent
	} else {
		b = false
	}
	s.rwlock.Unlock()
	return
}

func (s *syncMapEnt) PutAll(child map[interface{}]interface{}, d time.Duration) (err error) {
	if child != nil {
		s.rwlock.Lock()
		for k, v := range child {
			if s.m[k] != nil {
				s.m[k] = nil
			}
			ent := NewTimeEntity(v, d)
			s.m[k] = ent
		}
		s.rwlock.Unlock()
	}
	return
}

func (s *syncMapEnt) Remove(key interface{}) (val interface{}, err error) {
	if !ChKey(key) {
		return nil, NilKeyError
	}
	s.rwlock.Lock()
	ent := s.m[key]
	if ent != nil {
		val, err = ent.Value()
		s.m[key] = nil
		delete(s.m, key)
	} else {
		err = NotEntError
	}
	s.rwlock.Unlock()
	return
}

func (s *syncMapEnt) RemoveEntry(key, value interface{}) (b bool, err error) {
	if !ChKey(key) {
		return false, NilKeyError
	}
	s.rwlock.Lock()
	val := s.m[key]
	if val != nil {
		if v, err := val.Value(); v == value {
			b = true
			s.m[key] = nil
			delete(s.m, key)
		} else {
			err = NotEqualError
		}
	} else {
		err = NotEntError
		b = false
	}
	s.rwlock.Unlock()
	return
}

func (s *syncMapEnt) Update(key, value interface{}) (b bool, err error) {
	if !ChKey(key) {
		return false, NilKeyError
	}
	s.rwlock.Lock()
	val := s.m[key]
	if val != nil {
		b = true
		val.Update(value)
	} else {
		b = false
	}
	s.rwlock.Unlock()
	return
}

func (s *syncMapEnt) IsEmpty() (b bool) {
	s.rwlock.RLock()
	if s.m == nil || len(s.m) == 0 {
		b = true
	} else {
		b = false
	}
	s.rwlock.RUnlock()
	return
}

func (s *syncMapEnt) Clear() (err error) {
	s.rwlock.Lock()
	for k, v := range s.m {
		v = nil
		delete(s.m, k)
	}
	s.rwlock.Unlock()
	return
}

func (s *syncMapEnt) ClearUp() (err error) {
	s.rwlock.Lock()
	for k, v := range s.m {
		if v.IsDie() {
			v = nil
			delete(s.m, k)
		}
	}
	return
}

func (s *syncMapEnt) Size() (size int) {
	s.rwlock.RLock()
	size = len(s.m)
	s.rwlock.RUnlock()
	return
}
<commit_msg>syncMap.go fixed "declared and not used" error.<commit_after>//package syncmap is a map with sync.RWMutex.
package syncmap

import (
	"errors"
	. "github.com/yanjinzh6/flowkey/tools"
	"sync"
	"time"
)

var (
	NilKeyError   = errors.New("nil key error")
	TimeOutError  = errors.New("the entity is die")
	HasEntError   = errors.New("old data is erased")
	NotEntError   = errors.New("not entity remove")
	NotEqualError = errors.New("map[key] and value are not equal")
)

type syncMap struct {
	m      map[interface{}]interface{}
	rwlock sync.RWMutex
}

type SyncMap interface {
	Get(key interface{}) (val interface{}, err error)
	Put(key, value interface{}, d time.Duration) (val interface{}, err error)
	PutSimple(key, value interface{}) (val interface{}, err error)
	PutNormal(key, value interface{}) (val interface{}, err error)
	PutIfAbsent(key, value interface{}, d time.Duration) (b bool, err error)
	PutAll(child map[interface{}]interface{}, d time.Duration) (err error)
	Remove(key interface{}) (val interface{}, err error)
	RemoveEntry(key, value interface{}) (b bool, err error)
	Update(key, value interface{}) (b bool, err error)
	IsEmpty() (b bool)
	Clear() (err error)
	ClearUp() (err error)
	Size() (size int)
}

func NewSyncMap() SyncMap {
	return &syncMap{
		m: make(map[interface{}]interface{}),
	}
}

func (s *syncMap) Get(key interface{}) (val interface{}, err error) {
	if !ChKey(key) {
		return nil, NilKeyError
	}
	s.rwlock.RLock()
	val = s.m[key]
	/*if ok, ent := chTimeEntity(val); ok {
		val, err = ent.Value()
	}*/
	s.rwlock.RUnlock()
	return
}

func (s *syncMap) Put(key, value interface{}, d time.Duration) (val interface{}, err error) {
	if !ChKey(key) {
		return nil, NilKeyError
	}
	s.rwlock.Lock()
	val = s.m[key]
	/*if val == nil {
		ent := NewTimeEntity(value, d)
		s.m[key] = ent
	} else {
		if ok, ent := chTimeEntity(val); ok {
			val, err = ent.Value()
		} else {
			s.m[key] = value
		}
	}*/
	s.m[key] = value
	s.rwlock.Unlock()
	return
}

func (s *syncMap) PutSimple(key, value interface{}) (val interface{}, err error) {
	val, err = s.Put(key, value, DEFAULT_DURATION_TIME)
	return
}

func (s *syncMap) PutNormal(key, value interface{}) (val interface{}, err error) {
	val, err = s.Put(key, value, 0)
	return
}

func (s *syncMap) PutIfAbsent(key, value interface{}, d time.Duration) (b bool, err error) {
	if !ChKey(key) {
		return false, NilKeyError
	}
	s.rwlock.Lock()
	if s.m[key] == nil {
		b = true
		s.m[key] = value
	} else {
		b = false
	}
	s.rwlock.Unlock()
	return
}

func (s *syncMap) PutAll(child map[interface{}]interface{}, d time.Duration) (err error) {
	if child != nil {
		s.rwlock.Lock()
		for k, v := range child {
			s.m[k] = v
		}
		s.rwlock.Unlock()
	}
	return
}

func (s *syncMap) Remove(key interface{}) (val interface{}, err error) {
	if !ChKey(key) {
		return nil, NilKeyError
	}
	s.rwlock.Lock()
	val = s.m[key]
	if val != nil {
		delete(s.m, key)
	}
	s.rwlock.Unlock()
	return
}

func (s *syncMap) RemoveEntry(key, value interface{}) (b bool, err error) {
	if !ChKey(key) {
		return false, NilKeyError
	}
	s.rwlock.Lock()
	val := s.m[key]
	if val != nil && val == value {
		b = true
		delete(s.m, key)
	} else {
		b = false
	}
	s.rwlock.Unlock()
	return
}

func (s *syncMap) Update(key, value interface{}) (b bool, err error) {
	if !ChKey(key) {
		return false, NilKeyError
	}
	return
}

func (s *syncMap) IsEmpty() (b bool) {
	s.rwlock.RLock()
	if s.m == nil || len(s.m) == 0 {
		b = true
	} else {
		b = false
	}
	s.rwlock.RUnlock()
	return
}

func (s *syncMap) Clear() (err error) {
	s.rwlock.Lock()
	for k := range s.m {
		delete(s.m, k)
	}
	s.rwlock.Unlock()
	return
}

func (s *syncMap) ClearUp() (err error) {
	return
}

func (s *syncMap) Size() (size int) {
	s.rwlock.RLock()
	size = len(s.m)
	s.rwlock.RUnlock()
	return
}

func chTimeEntity(val interface{}) (ok bool, ent TimeEntity) {
	if val == nil {
		return false, nil
	}
	switch value := val.(type) {
	case TimeEntity:
		return true, value
	default:
		return false, nil
	}
}

type syncMapEnt struct {
	m      map[interface{}]TimeEntity
	rwlock sync.RWMutex
}

func NewSyncMapEnt() SyncMap {
	return &syncMapEnt{
		m: make(map[interface{}]TimeEntity),
	}
}

func (s *syncMapEnt) Get(key interface{}) (val interface{}, err error) {
	if !ChKey(key) {
		return nil, NilKeyError
	}
	if s.m[key].IsDie() {
		s.rwlock.Lock()
		s.m[key] = nil
		delete(s.m, key)
		val = nil
		err = TimeOutError
		s.rwlock.Unlock()
	} else {
		s.rwlock.RLock()
		val, err = s.m[key].Value()
		s.rwlock.RUnlock()
	}
	return
}

func (s *syncMapEnt) Put(key, value interface{}, d time.Duration) (val interface{}, err error) {
	if !ChKey(key) {
		return nil, NilKeyError
	}
	s.rwlock.Lock()
	oldEnt := s.m[key]
	if oldEnt == nil {
		ent := NewTimeEntity(value, d)
		s.m[key] = ent
	} else {
		/*if val, _ := oldEnt.Value(); val != value {
			s.m[key].Update(value)
		}
		if oldEnt.Dtime() != d {
			s.m[key].ChangeDur(d)
		}*/
		val, _ = s.m[key].Value()
		s.m[key] = nil
		ent := NewTimeEntity(value, d)
		s.m[key] = ent
		err = HasEntError
	}
	s.rwlock.Unlock()
	return
}

func (s *syncMapEnt) PutSimple(key, value interface{}) (val interface{}, err error) {
	val, err = s.Put(key, value, DEFAULT_DURATION_TIME)
	return
}

func (s *syncMapEnt) PutNormal(key, value interface{}) (val interface{}, err error) {
	val, err = s.Put(key, value, 0)
	return
}

func (s *syncMapEnt) PutIfAbsent(key, value interface{}, d time.Duration) (b bool, err error) {
	if !ChKey(key) {
		return false, NilKeyError
	}
	s.rwlock.Lock()
	if s.m[key] == nil {
		b = true
		ent := NewTimeEntity(value, d)
		s.m[key] = ent
	} else {
		b = false
	}
	s.rwlock.Unlock()
	return
}

func (s *syncMapEnt) PutAll(child map[interface{}]interface{}, d time.Duration) (err error) {
	if child != nil {
		s.rwlock.Lock()
		for k, v := range child {
			if s.m[k] != nil {
				s.m[k] = nil
			}
			ent := NewTimeEntity(v, d)
			s.m[k] = ent
		}
		s.rwlock.Unlock()
	}
	return
}

func (s *syncMapEnt) Remove(key interface{}) (val interface{}, err error) {
	if !ChKey(key) {
		return nil, NilKeyError
	}
	s.rwlock.Lock()
	ent := s.m[key]
	if ent != nil {
		val, err = ent.Value()
		s.m[key] = nil
		delete(s.m, key)
	} else {
		err = NotEntError
	}
	s.rwlock.Unlock()
	return
}

func (s *syncMapEnt) RemoveEntry(key, value interface{}) (b bool, err error) {
	if !ChKey(key) {
		return false, NilKeyError
	}
	s.rwlock.Lock()
	val := s.m[key]
	if val != nil {
		if v, _ := val.Value(); v == value {
			b = true
			s.m[key] = nil
			delete(s.m, key)
		} else {
			err = NotEqualError
		}
	} else {
		err = NotEntError
		b = false
	}
	s.rwlock.Unlock()
	return
}

func (s *syncMapEnt) Update(key, value interface{}) (b bool, err error) {
	if !ChKey(key) {
		return false, NilKeyError
	}
	s.rwlock.Lock()
	val := s.m[key]
	if val != nil {
		b = true
		val.Update(value)
	} else {
		b = false
	}
	s.rwlock.Unlock()
	return
}

func (s *syncMapEnt) IsEmpty() (b bool) {
	s.rwlock.RLock()
	if s.m == nil || len(s.m) == 0 {
		b = true
	} else {
		b = false
	}
	s.rwlock.RUnlock()
	return
}

func (s *syncMapEnt) Clear() (err error) {
	s.rwlock.Lock()
	for k, v := range s.m {
		if v != nil {
			v = nil
			delete(s.m, k)
		}
	}
	s.rwlock.Unlock()
	return
}

func (s *syncMapEnt) ClearUp() (err error) {
	s.rwlock.Lock()
	for k, v := range s.m {
		if v.IsDie() {
			v = nil
			delete(s.m, k)
		}
	}
	return
}

func (s *syncMapEnt) Size() (size int) {
	s.rwlock.RLock()
	size = len(s.m)
	s.rwlock.RUnlock()
	return
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"time"

	"github.com/luizbranco/eventsource"

	"golang.org/x/net/context"
	"golang.org/x/oauth2/google"
	"google.golang.org/api/cloudmonitoring/v2beta2"
)

type GoogleCloudMonitoring struct {
	base_url string
	remote   cloudmonitoring.TimeseriesService
}

type Timeseries struct {
	base_url    string
	metric_name string
	start       time.Time
	end         time.Time
	value       float64
}

func createTimeseries(args Timeseries) *cloudmonitoring.TimeseriesPoint {

	var end_string string
	var start_string string

	start_string = args.start.Format(time.RFC3339)

	if &args.end != nil {
		end_string = args.end.Format(time.RFC3339)
	} else {
		end_string = start_string
	}

	description := cloudmonitoring.TimeseriesDescriptor{
		Labels: map[string]string{
			args.base_url + "implementation": "golang",
		},
		Metric:  args.base_url + args.metric_name,
		Project: "replay-gaming",
	}

	point := cloudmonitoring.Point{
		Start:       start_string,
		End:         end_string,
		DoubleValue: &args.value,
	}

	timeseries := cloudmonitoring.TimeseriesPoint{
		Point:          &point,
		TimeseriesDesc: &description,
	}

	return &timeseries
}

func pushMetrics(points []*cloudmonitoring.TimeseriesPoint, remote cloudmonitoring.TimeseriesService) {
	request := cloudmonitoring.WriteTimeseriesRequest{
		CommonLabels: map[string]string{
			"container.googleapis.com/container_name": "eventsource",
		},
		Timeseries: points,
	}

	response, err := remote.Write("replay-poker", &request).Do()
	if err != nil {
		log.Fatal("pushMetrics - Unable to write timeseries: %v", err)
	}
	log.Printf("pushMetrics - Response: %s", response)
}

func NewMetrics(prefix string) (GoogleCloudMonitoring, error) {
	monitor := GoogleCloudMonitoring{base_url: "custom.cloudmonitoring.googleapis.com/" + prefix + "/"}

	client, err := google.DefaultClient(
		context.Background(),
		cloudmonitoring.MonitoringScope,
	)
	if err != nil {
		log.Fatal("Unable to get default client: %v", err)
	}

	cloudmonitoringService, err := cloudmonitoring.New(client)
	if err != nil {
		log.Fatal("Unable to create monitoring service: %v", err)
	}

	timeseriesService := cloudmonitoring.NewTimeseriesService(cloudmonitoringService)

	monitor.remote = *timeseriesService
	return monitor, nil
}

func (monitor GoogleCloudMonitoring) ClientCount(count int) {
	log.Printf("[METRIC] %sconnections: %d\n", monitor.base_url, count)

	timeseries := createTimeseries(Timeseries{
		base_url:    monitor.base_url,
		metric_name: "connections",
		start:       time.Now().UTC(),
		value:       float64(count),
	})

	points := []*cloudmonitoring.TimeseriesPoint{
		timeseries,
	}

	pushMetrics(points, monitor.remote)
}

func (monitor GoogleCloudMonitoring) EventDone(event eventsource.Event, duration time.Duration, eventdurations []time.Duration) {
	var sum int64
	var count int64
	var avg float64

	for _, d := range eventdurations {
		if d > 0 {
			sum += d.Nanoseconds()
		}
	}

	count = int64(len(eventdurations))

	if count > 0 {
		avg = float64(sum) / float64(count)
	}

	log.Printf("[METRIC] %s.event_distributed.clients: %d\n", monitor.base_url, count)
	log.Printf("[METRIC] %s.event_distributed.avg_time: %dns\n", monitor.base_url, avg)

	clients_timeseries := createTimeseries(Timeseries{
		base_url:    monitor.base_url,
		metric_name: "clients",
		start:       time.Now().UTC(),
		value:       float64(count),
	})

	avg_time_timeseries := createTimeseries(Timeseries{
		base_url:    monitor.base_url,
		metric_name: "avg_time",
		start:       time.Now().UTC(),
		value:       avg,
	})

	points := []*cloudmonitoring.TimeseriesPoint{
		clients_timeseries,
		avg_time_timeseries,
	}

	pushMetrics(points, monitor.remote)
}
<commit_msg>Adds metrics creation logic to initilization<commit_after>package main

import (
	"log"
	"time"

	"github.com/luizbranco/eventsource"

	"golang.org/x/net/context"
	"golang.org/x/oauth2/google"
	"google.golang.org/api/cloudmonitoring/v2beta2"
)

type GoogleCloudMonitoring struct {
	base_url string
	remote   cloudmonitoring.TimeseriesService
}

type Timeseries struct {
	base_url    string
	metric_name string
	start       time.Time
	end         time.Time
	value       float64
}

func createTimeseries(args Timeseries) *cloudmonitoring.TimeseriesPoint {

	var end_string string
	var start_string string

	start_string = args.start.Format(time.RFC3339)

	if &args.end != nil {
		end_string = args.end.Format(time.RFC3339)
	} else {
		end_string = start_string
	}

	description := cloudmonitoring.TimeseriesDescriptor{
		Labels: map[string]string{
			args.base_url + "implementation": "golang",
		},
		Metric:  args.base_url + args.metric_name,
		Project: "replay-gaming",
	}

	point := cloudmonitoring.Point{
		Start:       start_string,
		End:         end_string,
		DoubleValue: &args.value,
	}

	timeseries := cloudmonitoring.TimeseriesPoint{
		Point:          &point,
		TimeseriesDesc: &description,
	}

	return &timeseries
}

func pushMetrics(points []*cloudmonitoring.TimeseriesPoint, remote cloudmonitoring.TimeseriesService) {
	request := cloudmonitoring.WriteTimeseriesRequest{
		CommonLabels: map[string]string{
			"container.googleapis.com/container_name": "eventsource",
		},
		Timeseries: points,
	}

	response, err := remote.Write("replay-gaming", &request).Do()
	if err != nil {
		log.Fatal("pushMetrics - Unable to write timeseries: ", err)
	}
	log.Printf("pushMetrics - Response: %s", response)
}

func createMetricDescriptor(prefix string, name string, description string) *cloudmonitoring.MetricDescriptor {
	metric_type := cloudmonitoring.MetricDescriptorTypeDescriptor{
		MetricType: "gauge",
		ValueType:  "double",
	}

	label := cloudmonitoring.MetricDescriptorLabelDescriptor{
		Description: "Application",
		Key:         "eventsource",
	}

	return &cloudmonitoring.MetricDescriptor{
		Description: description,
		Labels: []*cloudmonitoring.MetricDescriptorLabelDescriptor{
			&label,
		},
		Name:           "custom.cloudmonitoring.googleapis.com/" + prefix + "/" + name,
		Project:        "replay-gaming",
		TypeDescriptor: &metric_type,
	}
}

func createMetric(prefix string, metricDescriptorsService *cloudmonitoring.MetricDescriptorsService, name string, description string) {
	metricDescriptor := createMetricDescriptor(prefix, name, description)
	metricDescriptor, err := metricDescriptorsService.Create("replay-gaming", metricDescriptor).Do()
	if err != nil {
		log.Fatal("Unable to create '"+name+"' metric: ", err)
	}
}

func createMetrics(prefix string, cloudmonitoringService *cloudmonitoring.Service) {
	metricDescriptorsService := cloudmonitoring.NewMetricDescriptorsService(cloudmonitoringService)

	createMetric(prefix, metricDescriptorsService, "clients", "Number of clients that the event was distributed to")
	createMetric(prefix, metricDescriptorsService, "avg_time", "Average time to send an event to all connected clients")
	createMetric(prefix, metricDescriptorsService, "connections", "Number of connected SSE clients (browser sessions)")
}

func NewMetrics(prefix string) (GoogleCloudMonitoring, error) {
	monitor := GoogleCloudMonitoring{base_url: "custom.cloudmonitoring.googleapis.com/" + prefix + "/"}

	client, err := google.DefaultClient(
		context.Background(),
		cloudmonitoring.MonitoringScope,
	)
	if err != nil {
		log.Fatal("Unable to get default client: ", err)
	}

	cloudmonitoringService, err := cloudmonitoring.New(client)
	if err != nil {
		log.Fatal("Unable to create monitoring service: ", err)
	}

	createMetrics(prefix, cloudmonitoringService)

	timeseriesService := cloudmonitoring.NewTimeseriesService(cloudmonitoringService)

	monitor.remote = *timeseriesService
	return monitor, nil
}

func (monitor GoogleCloudMonitoring) ClientCount(count int) {
	log.Printf("[METRIC] %sconnections: %d\n", monitor.base_url, count)

	timeseries := createTimeseries(Timeseries{
		base_url:    monitor.base_url,
		metric_name: "connections",
		start:       time.Now().UTC(),
		value:       float64(count),
	})

	points := []*cloudmonitoring.TimeseriesPoint{
		timeseries,
	}

	pushMetrics(points, monitor.remote)
}

func (monitor GoogleCloudMonitoring) EventDone(event eventsource.Event, duration time.Duration, eventdurations []time.Duration) {
	var sum int64
	var count int64
	var avg float64

	for _, d := range eventdurations {
		if d > 0 {
			sum += d.Nanoseconds()
		}
	}

	count = int64(len(eventdurations))

	if count > 0 {
		avg = float64(sum) / float64(count)
	}

	log.Printf("[METRIC] %s.event_distributed.clients: %d\n", monitor.base_url, count)
	log.Printf("[METRIC] %s.event_distributed.avg_time: %dns\n", monitor.base_url, avg)

	clients_timeseries := createTimeseries(Timeseries{
		base_url:    monitor.base_url,
		metric_name: "clients",
		start:       time.Now().UTC(),
		value:       float64(count),
	})

	avg_time_timeseries := createTimeseries(Timeseries{
		base_url:    monitor.base_url,
		metric_name: "avg_time",
		start:       time.Now().UTC(),
		value:       avg,
	})

	points := []*cloudmonitoring.TimeseriesPoint{
		clients_timeseries,
		avg_time_timeseries,
	}

	pushMetrics(points, monitor.remote)
}
<|endoftext|>


<commit_before><commit_msg>e2e: skip download of kubernikusctl if on darwin<commit_after><|endoftext|>


<commit_before>package themekit

const ThemeKitVersion string = "v0.2.5"

func LibraryInfo() []string {
	return []string{
		"ThemeKit - Shopify Theme Utilities",
		ThemeKitVersion,
		"Author: Chris Saunders",
	}
}
<commit_msg>bump to version 0.2.6<commit_after>package themekit

const ThemeKitVersion string = "v0.2.6"

func LibraryInfo() []string {
	return []string{
		"ThemeKit - Shopify Theme Utilities",
		ThemeKitVersion,
		"Author: Chris Saunders",
	}
}
<|endoftext|>


<commit_before>package atlas

const ourVersion = "0.2"

<commit_msg>Welcome v0.3 with semantic versioning & result fixes.<commit_after>package atlas

const ourVersion = "0.3"
<|endoftext|>


<commit_before>package test161

import (
	"fmt"
)

type ProgramVersion struct {
	Major    uint `yaml:"major"`
	Minor    uint `yaml:"minor"`
	Revision uint `yaml:"revision"`
}

var Version = ProgramVersion{
	Major:    1,
	Minor:    3,
	Revision: 0,
}

func (v ProgramVersion) String() string {
	return fmt.Sprintf("%v.%v.%v", v.Major, v.Minor, v.Revision)
}

// Returns 1 if this > other, 0 if this == other, and -1 if this < other
func (this ProgramVersion) CompareTo(other ProgramVersion) int {

	if this.Major > other.Major {
		return 1
	} else if this.Major < other.Major {
		return -1
	} else if this.Minor > other.Minor {
		return 1
	} else if this.Minor < other.Minor {
		return -1
	} else if this.Revision > other.Revision {
		return 1
	} else if this.Revision < other.Revision {
		return -1
	} else {
		return 0
	}

}
<commit_msg>Increment version to 1.3.1<commit_after>package test161

import (
	"fmt"
)

type ProgramVersion struct {
	Major    uint `yaml:"major"`
	Minor    uint `yaml:"minor"`
	Revision uint `yaml:"revision"`
}

var Version = ProgramVersion{
	Major:    1,
	Minor:    3,
	Revision: 1,
}

func (v ProgramVersion) String() string {
	return fmt.Sprintf("%v.%v.%v", v.Major, v.Minor, v.Revision)
}

// Returns 1 if this > other, 0 if this == other, and -1 if this < other
func (this ProgramVersion) CompareTo(other ProgramVersion) int {

	if this.Major > other.Major {
		return 1
	} else if this.Major < other.Major {
		return -1
	} else if this.Minor > other.Minor {
		return 1
	} else if this.Minor < other.Minor {
		return -1
	} else if this.Revision > other.Revision {
		return 1
	} else if this.Revision < other.Revision {
		return -1
	} else {
		return 0
	}

}
<|endoftext|>


<commit_before>package main

const VERSION = "0.2.0"
<commit_msg>bump version to v0.3.0<commit_after>package main

const VERSION = "0.3.0"
<|endoftext|>


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

package main

// Version is the release version of memsniff.
const Version = "1.3.1"

// GitRevision holds the HEAD revision when building memsniff.
var GitRevision = "autopopulated by build.sh"
<commit_msg>Advance version to 1.4.0<commit_after>// Copyright 2017 Box, Inc.  All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

// Version is the release version of memsniff.
const Version = "1.4.0"

// GitRevision holds the HEAD revision when building memsniff.
var GitRevision = "autopopulated by build.sh"
<|endoftext|>


<commit_before>package main

// The git commit that was compiled. This will be filled in by the compiler.
var GitCommit string

// The main version number that is being run at the moment.
const Version = "0.3.0"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
const VersionPrerelease = ""
<commit_msg>0.3.1<commit_after>package main

// The git commit that was compiled. This will be filled in by the compiler.
var GitCommit string

// The main version number that is being run at the moment.
const Version = "0.3.1"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
const VersionPrerelease = ""
<|endoftext|>


<commit_before>package main

const Version = "1.0.2"
<commit_msg>1.0.3<commit_after>package main

const Version = "1.0.3"
<|endoftext|>


<commit_before>package dns

import "fmt"

// Version is current version of this library.
var Version = v{1, 1, 42}

// v holds the version of this library.
type v struct {
	Major, Minor, Patch int
}

func (v v) String() string {
	return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch)
}
<commit_msg>Release 1.1.43<commit_after>package dns

import "fmt"

// Version is current version of this library.
var Version = v{1, 1, 43}

// v holds the version of this library.
type v struct {
	Major, Minor, Patch int
}

func (v v) String() string {
	return fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch)
}
<|endoftext|>


<commit_before>package main

// Version is the tagged version or "dev"
var Version = "v0.1.21"

// BuildDate is the date of the release build or ""
var BuildDate = ""
<commit_msg>version bump: v0.1.22<commit_after>package main

// Version is the tagged version or "dev"
var Version = "v0.1.22"

// BuildDate is the date of the release build or ""
var BuildDate = ""
<|endoftext|>


<commit_before>package egoscale

// Version of the library
const Version = "0.11.6"
<commit_msg>prepare release<commit_after>package egoscale

// Version of the library
const Version = "0.12.0"
<|endoftext|>


<commit_before>package main

const Version = "1.0.4"
<commit_msg>1.0.5<commit_after>package main

const Version = "1.0.5"
<|endoftext|>


<commit_before>package mtree

import "fmt"

const (
	// AppName is the name ... of this library/application
	AppName = "gomtree"
)

const (
	// VersionMajor is for an API incompatible changes
	VersionMajor = 0
	// VersionMinor is for functionality in a backwards-compatible manner
	VersionMinor = 5
	// VersionPatch is for backwards-compatible bug fixes
	VersionPatch = 0

	// VersionDev indicates development branch. Releases will be empty string.
	VersionDev = "-dev"
)

// Version is the specification version that the package types support.
var Version = fmt.Sprintf("%d.%d.%d%s", VersionMajor, VersionMinor, VersionPatch, VersionDev)
<commit_msg>version: release 0.4.2<commit_after>package mtree

import "fmt"

const (
	// AppName is the name ... of this library/application
	AppName = "gomtree"
)

const (
	// VersionMajor is for an API incompatible changes
	VersionMajor = 0
	// VersionMinor is for functionality in a backwards-compatible manner
	VersionMinor = 4
	// VersionPatch is for backwards-compatible bug fixes
	VersionPatch = 2

	// VersionDev indicates development branch. Releases will be empty string.
	VersionDev = ""
)

// Version is the specification version that the package types support.
var Version = fmt.Sprintf("%d.%d.%d%s", VersionMajor, VersionMinor, VersionPatch, VersionDev)
<|endoftext|>


<commit_before>package main

import "github.com/tsaikd/KDGoLib/version"

func init() {
	version.VERSION = "1.0.3"
}
<commit_msg>1.0.4<commit_after>package main

import "github.com/tsaikd/KDGoLib/version"

func init() {
	version.VERSION = "1.0.4"
}
<|endoftext|>


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

package grpc

// Version is the current grpc version.
const Version = "1.40.0-dev"
<commit_msg>Change version to 1.41.0-dev (#4625)<commit_after>/*
 *
 * Copyright 2018 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package grpc

// Version is the current grpc version.
const Version = "1.41.0-dev"
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/mktmpio/go-mktmpio/mktmpio"
	"os"
	"time"
)

func main() {
	app := cli.NewApp()
	app.Name = "mktmpio"
	app.Usage = "create, destroy, and manage mktmpio instances"
	app.Action = func(c *cli.Context) {
		if len(c.Args()) < 1 {
			cli.ShowAppHelp(c)
			return
		}
		client, err := mktmpio.NewClient()
		if err != nil {
			fmt.Printf("Error creating client: %s\n", err)
			return
		}
		instance, err := client.Create(c.Args()[0])
		if err != nil {
			fmt.Printf("Error creating instance: %s\n", err)
			return
		}
		defer func() {
			instance.Destroy()
			fmt.Printf("Instance %s terminated.\n", instance.ID)
		}()
		_ = instance.LoadEnv()
		cmd := instance.Cmd()
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		// MySQL is particularly slow to start up
		if instance.Type == "mysql" {
			time.Sleep(500 * time.Millisecond)
		} else {
			time.Sleep(100 * time.Millisecond)
		}
		err = cmd.Run()
	}

	app.Run(os.Args)
}
<commit_msg>use new canonical path for mktmpio package<commit_after>package main

import (
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/mktmpio/go-mktmpio"
	"os"
	"time"
)

func main() {
	app := cli.NewApp()
	app.Name = "mktmpio"
	app.Usage = "create, destroy, and manage mktmpio instances"
	app.Action = func(c *cli.Context) {
		if len(c.Args()) < 1 {
			cli.ShowAppHelp(c)
			return
		}
		client, err := mktmpio.NewClient()
		if err != nil {
			fmt.Printf("Error creating client: %s\n", err)
			return
		}
		instance, err := client.Create(c.Args()[0])
		if err != nil {
			fmt.Printf("Error creating instance: %s\n", err)
			return
		}
		defer func() {
			instance.Destroy()
			fmt.Printf("Instance %s terminated.\n", instance.ID)
		}()
		_ = instance.LoadEnv()
		cmd := instance.Cmd()
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		// MySQL is particularly slow to start up
		if instance.Type == "mysql" {
			time.Sleep(500 * time.Millisecond)
		} else {
			time.Sleep(100 * time.Millisecond)
		}
		err = cmd.Run()
	}

	app.Run(os.Args)
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Knative Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// spoof contains logic to make polling HTTP requests against an endpoint with optional host spoofing.

package spoof

import (
	"context"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"strings"
	"time"

	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/kubernetes"
	"knative.dev/pkg/test/ingress"
	"knative.dev/pkg/test/logging"
	"knative.dev/pkg/test/zipkin"
	"knative.dev/pkg/tracing/propagation/tracecontextb3"

	"go.opencensus.io/plugin/ochttp"
	"go.opencensus.io/trace"
)

const (
	// Name of the temporary HTTP header that is added to http.Request to indicate that
	// it is a SpoofClient.Poll request. This header is removed before making call to backend.
	pollReqHeader = "X-Kn-Poll-Request-Do-Not-Trace"
)

// Response is a stripped down subset of http.Response. The is primarily useful
// for ResponseCheckers to inspect the response body without consuming it.
// Notably, Body is a byte slice instead of an io.ReadCloser.
type Response struct {
	Status     string
	StatusCode int
	Header     http.Header
	Body       []byte
}

func (r *Response) String() string {
	return fmt.Sprintf("status: %d, body: %s, headers: %v", r.StatusCode, string(r.Body), r.Header)
}

// Interface defines the actions that can be performed by the spoofing client.
type Interface interface {
	Do(*http.Request, ...ErrorRetryChecker) (*Response, error)
	Poll(*http.Request, ResponseChecker, ...ErrorRetryChecker) (*Response, error)
}

// https://medium.com/stupid-gopher-tricks/ensuring-go-interface-satisfaction-at-compile-time-1ed158e8fa17
var (
	_           Interface = (*SpoofingClient)(nil)
	dialContext           = (&net.Dialer{}).DialContext
)

// ResponseChecker is used to determine when SpoofinClient.Poll is done polling.
// This allows you to predicate wait.PollImmediate on the request's http.Response.
//
// See the apimachinery wait package:
// https://github.com/kubernetes/apimachinery/blob/cf7ae2f57dabc02a3d215f15ca61ae1446f3be8f/pkg/util/wait/wait.go#L172
type ResponseChecker func(resp *Response) (done bool, err error)

// ErrorRetryChecker is used to determine if an error should be retried or not.
// If an error should be retried, it should return true and the wrapped error to explain why to retry.
type ErrorRetryChecker func(e error) (retry bool, err error)

// SpoofingClient is a minimal HTTP client wrapper that spoofs the domain of requests
// for non-resolvable domains.
type SpoofingClient struct {
	Client          *http.Client
	RequestInterval time.Duration
	RequestTimeout  time.Duration
	Logf            logging.FormatLogger
}

// TransportOption allows callers to customize the http.Transport used by a SpoofingClient
type TransportOption func(transport *http.Transport) *http.Transport

// New returns a SpoofingClient that rewrites requests if the target domain is not `resolvable`.
// It does this by looking up the ingress at construction time, so reusing a client will not
// follow the ingress if it moves (or if there are multiple ingresses).
//
// If that's a problem, see test/request.go#WaitForEndpointState for oneshot spoofing.
func New(
	ctx context.Context,
	kubeClientset *kubernetes.Clientset,
	logf logging.FormatLogger,
	domain string,
	resolvable bool,
	endpointOverride string,
	requestInterval time.Duration,
	requestTimeout time.Duration,
	opts ...TransportOption) (*SpoofingClient, error) {
	endpoint, err := ResolveEndpoint(ctx, kubeClientset, domain, resolvable, endpointOverride)
	if err != nil {
		return nil, fmt.Errorf("failed get the cluster endpoint: %w", err)
	}

	// Spoof the hostname at the resolver level
	logf("Spoofing %s -> %s", domain, endpoint)
	transport := &http.Transport{
		DialContext: func(ctx context.Context, network, addr string) (conn net.Conn, e error) {
			spoofed := addr
			if i := strings.LastIndex(addr, ":"); i != -1 && domain == addr[:i] {
				// The original hostname:port is spoofed by replacing the hostname by the value
				// returned by ResolveEndpoint.
				spoofed = endpoint + ":" + addr[i+1:]
			}
			return dialContext(ctx, network, spoofed)
		},
	}

	for _, opt := range opts {
		transport = opt(transport)
	}

	// Enable Zipkin tracing
	roundTripper := &ochttp.Transport{
		Base:        transport,
		Propagation: tracecontextb3.TraceContextB3Egress,
	}

	sc := SpoofingClient{
		Client:          &http.Client{Transport: roundTripper},
		RequestInterval: requestInterval,
		RequestTimeout:  requestTimeout,
		Logf:            logf,
	}
	return &sc, nil
}

// ResolveEndpoint resolves the endpoint address considering whether the domain is resolvable and taking into
// account whether the user overrode the endpoint address externally
func ResolveEndpoint(ctx context.Context, kubeClientset *kubernetes.Clientset, domain string, resolvable bool, endpointOverride string) (string, error) {
	// If the domain is resolvable, it can be used directly
	if resolvable {
		return domain, nil
	}
	// If an override is provided, use it
	if endpointOverride != "" {
		return endpointOverride, nil
	}
	// Otherwise, use the actual cluster endpoint
	return ingress.GetIngressEndpoint(ctx, kubeClientset)
}

// Do dispatches to the underlying http.Client.Do, spoofing domains as needed
// and transforming the http.Response into a spoof.Response.
// Each response is augmented with "ZipkinTraceID" header that identifies the zipkin trace corresponding to the request.
func (sc *SpoofingClient) Do(req *http.Request, errorRetryCheckers ...ErrorRetryChecker) (*Response, error) {
	return sc.Poll(req, func(*Response) (bool, error) { return true, nil }, errorRetryCheckers...)
}

// Poll executes an http request until it satisfies the inState condition or encounters an error.
func (sc *SpoofingClient) Poll(req *http.Request, inState ResponseChecker, errorRetryCheckers ...ErrorRetryChecker) (*Response, error) {
	if len(errorRetryCheckers) == 0 {
		errorRetryCheckers = []ErrorRetryChecker{DefaultErrorRetryChecker}
	}

	var resp *Response
	err := wait.PollImmediate(sc.RequestInterval, sc.RequestTimeout, func() (bool, error) {
		// Starting span to capture zipkin trace.
		traceContext, span := trace.StartSpan(req.Context(), "SpoofingClient-Trace")
		defer span.End()
		rawResp, err := sc.Client.Do(req.WithContext(traceContext))
		if err != nil {
			for _, checker := range errorRetryCheckers {
				retry, newErr := checker(err)
				if retry {
					sc.Logf("Retrying %s: %v", req.URL.String(), newErr)
					return false, nil
				}
			}
			sc.Logf("NOT Retrying %s: %v", req.URL.String(), err)
			return true, err
		}
		defer rawResp.Body.Close()

		body, err := ioutil.ReadAll(rawResp.Body)
		if err != nil {
			return true, err
		}
		rawResp.Header.Add(zipkin.ZipkinTraceIDHeader, span.SpanContext().TraceID.String())

		resp = &Response{
			Status:     rawResp.Status,
			StatusCode: rawResp.StatusCode,
			Header:     rawResp.Header,
			Body:       body,
		}
		return inState(resp)
	})

	if resp != nil {
		sc.logZipkinTrace(resp)
	}

	if err != nil {
		return resp, fmt.Errorf("response: %s did not pass checks: %w", resp, err)
	}
	return resp, nil
}

// DefaultErrorRetryChecker implements the defaults for retrying on error.
func DefaultErrorRetryChecker(err error) (bool, error) {
	if isTCPTimeout(err) {
		return true, fmt.Errorf("retrying for TCP timeout: %w", err)
	}
	// Retrying on DNS error, since we may be using xip.io or nip.io in tests.
	if isDNSError(err) {
		return true, fmt.Errorf("retrying for DNS error: %w", err)
	}
	// Repeat the poll on `connection refused` errors, which are usually transient Istio errors.
	if isConnectionRefused(err) {
		return true, fmt.Errorf("retrying for connection refused: %w", err)
	}
	if isConnectionReset(err) {
		return true, fmt.Errorf("retrying for connection reset: %w", err)
	}
	// Retry on connection/network errors.
	if errors.Is(err, io.EOF) {
		return true, fmt.Errorf("retrying for: %w", err)
	}
	return false, err
}

// logZipkinTrace provides support to log Zipkin Trace for param: spoofResponse
// We only log Zipkin trace for HTTP server errors i.e for HTTP status codes between 500 to 600
func (sc *SpoofingClient) logZipkinTrace(spoofResp *Response) {
	if !zipkin.ZipkinTracingEnabled || spoofResp.StatusCode < http.StatusInternalServerError || spoofResp.StatusCode >= 600 {
		return
	}

	traceID := spoofResp.Header.Get(zipkin.ZipkinTraceIDHeader)
	sc.Logf("Logging Zipkin Trace for: %s", traceID)

	json, err := zipkin.JSONTrace(traceID /* We don't know the expected number of spans */, -1, 5*time.Second)
	if err != nil {
		if _, ok := err.(*zipkin.TimeoutError); !ok {
			sc.Logf("Error getting zipkin trace: %v", err)
		}
	}

	sc.Logf("%s", json)
}
<commit_msg>Fix a nit in the spoof (#1705)<commit_after>/*
Copyright 2019 The Knative Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// spoof contains logic to make polling HTTP requests against an endpoint with optional host spoofing.

package spoof

import (
	"context"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"strings"
	"time"

	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/kubernetes"
	"knative.dev/pkg/test/ingress"
	"knative.dev/pkg/test/logging"
	"knative.dev/pkg/test/zipkin"
	"knative.dev/pkg/tracing/propagation/tracecontextb3"

	"go.opencensus.io/plugin/ochttp"
	"go.opencensus.io/trace"
)

const (
	// Name of the temporary HTTP header that is added to http.Request to indicate that
	// it is a SpoofClient.Poll request. This header is removed before making call to backend.
	pollReqHeader = "X-Kn-Poll-Request-Do-Not-Trace"
)

// Response is a stripped down subset of http.Response. The is primarily useful
// for ResponseCheckers to inspect the response body without consuming it.
// Notably, Body is a byte slice instead of an io.ReadCloser.
type Response struct {
	Status     string
	StatusCode int
	Header     http.Header
	Body       []byte
}

func (r *Response) String() string {
	return fmt.Sprintf("status: %d, body: %s, headers: %v", r.StatusCode, string(r.Body), r.Header)
}

// Interface defines the actions that can be performed by the spoofing client.
type Interface interface {
	Do(*http.Request, ...ErrorRetryChecker) (*Response, error)
	Poll(*http.Request, ResponseChecker, ...ErrorRetryChecker) (*Response, error)
}

// https://medium.com/stupid-gopher-tricks/ensuring-go-interface-satisfaction-at-compile-time-1ed158e8fa17
var (
	_           Interface = (*SpoofingClient)(nil)
	dialContext           = (&net.Dialer{}).DialContext
)

// ResponseChecker is used to determine when SpoofinClient.Poll is done polling.
// This allows you to predicate wait.PollImmediate on the request's http.Response.
//
// See the apimachinery wait package:
// https://github.com/kubernetes/apimachinery/blob/cf7ae2f57dabc02a3d215f15ca61ae1446f3be8f/pkg/util/wait/wait.go#L172
type ResponseChecker func(resp *Response) (done bool, err error)

// ErrorRetryChecker is used to determine if an error should be retried or not.
// If an error should be retried, it should return true and the wrapped error to explain why to retry.
type ErrorRetryChecker func(e error) (retry bool, err error)

// SpoofingClient is a minimal HTTP client wrapper that spoofs the domain of requests
// for non-resolvable domains.
type SpoofingClient struct {
	Client          *http.Client
	RequestInterval time.Duration
	RequestTimeout  time.Duration
	Logf            logging.FormatLogger
}

// TransportOption allows callers to customize the http.Transport used by a SpoofingClient
type TransportOption func(transport *http.Transport) *http.Transport

// New returns a SpoofingClient that rewrites requests if the target domain is not `resolvable`.
// It does this by looking up the ingress at construction time, so reusing a client will not
// follow the ingress if it moves (or if there are multiple ingresses).
//
// If that's a problem, see test/request.go#WaitForEndpointState for oneshot spoofing.
func New(
	ctx context.Context,
	kubeClientset *kubernetes.Clientset,
	logf logging.FormatLogger,
	domain string,
	resolvable bool,
	endpointOverride string,
	requestInterval time.Duration,
	requestTimeout time.Duration,
	opts ...TransportOption) (*SpoofingClient, error) {
	endpoint, err := ResolveEndpoint(ctx, kubeClientset, domain, resolvable, endpointOverride)
	if err != nil {
		return nil, fmt.Errorf("failed get the cluster endpoint: %w", err)
	}

	// Spoof the hostname at the resolver level
	logf("Spoofing %s -> %s", domain, endpoint)
	transport := &http.Transport{
		DialContext: func(ctx context.Context, network, addr string) (conn net.Conn, e error) {
			spoofed := addr
			if i := strings.LastIndex(addr, ":"); i != -1 && domain == addr[:i] {
				// The original hostname:port is spoofed by replacing the hostname by the value
				// returned by ResolveEndpoint.
				spoofed = endpoint + ":" + addr[i+1:]
			}
			return dialContext(ctx, network, spoofed)
		},
	}

	for _, opt := range opts {
		transport = opt(transport)
	}

	// Enable Zipkin tracing
	roundTripper := &ochttp.Transport{
		Base:        transport,
		Propagation: tracecontextb3.TraceContextB3Egress,
	}

	sc := SpoofingClient{
		Client:          &http.Client{Transport: roundTripper},
		RequestInterval: requestInterval,
		RequestTimeout:  requestTimeout,
		Logf:            logf,
	}
	return &sc, nil
}

// ResolveEndpoint resolves the endpoint address considering whether the domain is resolvable and taking into
// account whether the user overrode the endpoint address externally
func ResolveEndpoint(ctx context.Context, kubeClientset *kubernetes.Clientset, domain string, resolvable bool, endpointOverride string) (string, error) {
	// If the domain is resolvable, it can be used directly
	if resolvable {
		return domain, nil
	}
	// If an override is provided, use it
	if endpointOverride != "" {
		return endpointOverride, nil
	}
	// Otherwise, use the actual cluster endpoint
	return ingress.GetIngressEndpoint(ctx, kubeClientset)
}

// Do dispatches to the underlying http.Client.Do, spoofing domains as needed
// and transforming the http.Response into a spoof.Response.
// Each response is augmented with "ZipkinTraceID" header that identifies the zipkin trace corresponding to the request.
func (sc *SpoofingClient) Do(req *http.Request, errorRetryCheckers ...ErrorRetryChecker) (*Response, error) {
	return sc.Poll(req, func(*Response) (bool, error) { return true, nil }, errorRetryCheckers...)
}

// Poll executes an http request until it satisfies the inState condition or, if there's an error,
// none of the error retry checkers permit a retry.
// If no retry checkers are specified `DefaultErrorRetryChecker` will be used.
func (sc *SpoofingClient) Poll(req *http.Request, inState ResponseChecker, errorRetryCheckers ...ErrorRetryChecker) (*Response, error) {
	if len(errorRetryCheckers) == 0 {
		errorRetryCheckers = []ErrorRetryChecker{DefaultErrorRetryChecker}
	}

	var resp *Response
	err := wait.PollImmediate(sc.RequestInterval, sc.RequestTimeout, func() (bool, error) {
		// Starting span to capture zipkin trace.
		traceContext, span := trace.StartSpan(req.Context(), "SpoofingClient-Trace")
		defer span.End()
		rawResp, err := sc.Client.Do(req.WithContext(traceContext))
		if err != nil {
			for _, checker := range errorRetryCheckers {
				retry, newErr := checker(err)
				if retry {
					sc.Logf("Retrying %s: %v", req.URL.String(), newErr)
					return false, nil
				}
			}
			sc.Logf("NOT Retrying %s: %v", req.URL.String(), err)
			return true, err
		}
		defer rawResp.Body.Close()

		body, err := ioutil.ReadAll(rawResp.Body)
		if err != nil {
			return true, err
		}
		rawResp.Header.Add(zipkin.ZipkinTraceIDHeader, span.SpanContext().TraceID.String())

		resp = &Response{
			Status:     rawResp.Status,
			StatusCode: rawResp.StatusCode,
			Header:     rawResp.Header,
			Body:       body,
		}
		return inState(resp)
	})

	if resp != nil {
		sc.logZipkinTrace(resp)
	}

	if err != nil {
		return resp, fmt.Errorf("response: %s did not pass checks: %w", resp, err)
	}
	return resp, nil
}

// DefaultErrorRetryChecker implements the defaults for retrying on error.
func DefaultErrorRetryChecker(err error) (bool, error) {
	if isTCPTimeout(err) {
		return true, fmt.Errorf("retrying for TCP timeout: %w", err)
	}
	// Retrying on DNS error, since we may be using xip.io or nip.io in tests.
	if isDNSError(err) {
		return true, fmt.Errorf("retrying for DNS error: %w", err)
	}
	// Repeat the poll on `connection refused` errors, which are usually transient Istio errors.
	if isConnectionRefused(err) {
		return true, fmt.Errorf("retrying for connection refused: %w", err)
	}
	if isConnectionReset(err) {
		return true, fmt.Errorf("retrying for connection reset: %w", err)
	}
	// Retry on connection/network errors.
	if errors.Is(err, io.EOF) {
		return true, fmt.Errorf("retrying for: %w", err)
	}
	return false, err
}

// logZipkinTrace provides support to log Zipkin Trace for param: spoofResponse
// We only log Zipkin trace for HTTP server errors i.e for HTTP status codes between 500 to 600
func (sc *SpoofingClient) logZipkinTrace(spoofResp *Response) {
	if !zipkin.ZipkinTracingEnabled || spoofResp.StatusCode < http.StatusInternalServerError || spoofResp.StatusCode >= 600 {
		return
	}

	traceID := spoofResp.Header.Get(zipkin.ZipkinTraceIDHeader)
	sc.Logf("Logging Zipkin Trace for: %s", traceID)

	json, err := zipkin.JSONTrace(traceID /* We don't know the expected number of spans */, -1, 5*time.Second)
	if err != nil {
		if _, ok := err.(*zipkin.TimeoutError); !ok {
			sc.Logf("Error getting zipkin trace: %v", err)
		}
	}

	sc.Logf("%s", json)
}
<|endoftext|>


<commit_before>package lua

import (
	"fmt"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
)

func testString(t *testing.T, s string)  { testStringHelper(t, s, false) }
func traceString(t *testing.T, s string) { testStringHelper(t, s, true) }

func testStringHelper(t *testing.T, s string, trace bool) {
	l := NewState()
	OpenLibraries(l)
	LoadString(l, s)
	if trace {
		SetDebugHook(l, func(state *State, ar Debug) {
			ci := state.callInfo
			p := state.prototype(ci)
			println(stack(state.stack[ci.base():state.top]))
			println(ci.code[ci.savedPC].String(), p.source, p.lineInfo[ci.savedPC])
		}, MaskCount, 1)
	}
	l.Call(0, 0)
}

func TestProtectedCall(t *testing.T) {
	l := NewState()
	OpenLibraries(l)
	SetDebugHook(l, func(state *State, ar Debug) {
		ci := state.callInfo
		_ = stack(state.stack[ci.base():state.top])
		_ = ci.code[ci.savedPC].String()
	}, MaskCount, 1)
	LoadString(l, "assert(not pcall(bit32.band, {}))")
	l.Call(0, 0)
}

func TestLua(t *testing.T) {
	tests := []struct {
		name    string
		nonPort bool
	}{
		{name: "attrib", nonPort: true},
		// {name: "big"},
		{name: "bitwise"},
		// {name: "calls"},
		// {name: "checktable"},
		{name: "closure"},
		// {name: "code"},
		// {name: "constructs"},
		// {name: "db"},
		// {name: "errors"},
		{name: "events"},
		// {name: "files"},
		// {name: "gc"},
		{name: "goto"},
		// {name: "literals"},
		{name: "locals"},
		// {name: "main"},
		{name: "math"},
		// {name: "nextvar"},
		// {name: "pm"},
		{name: "sort", nonPort: true}, // sort.lua depends on os.clock(), which is not yet implemented on Windows.
		{name: "strings"},
		// {name: "vararg"},
		// {name: "verybig"},
	}
	for _, v := range tests {
		if v.nonPort && runtime.GOOS == "windows" {
			t.Skipf("'%s' skipped because it's non-portable & we're running Windows", v.name)
		}
		t.Log(v)
		l := NewState()
		OpenLibraries(l)
		for _, s := range []string{"_port", "_no32", "_noformatA"} {
			l.PushBoolean(true)
			l.SetGlobal(s)
		}
		if v.nonPort {
			l.PushBoolean(false)
			l.SetGlobal("_port")
		}
		// l.SetDebugHook(func(state *State, ar Debug) {
		// 	ci := state.callInfo.(*luaCallInfo)
		// 	p := state.prototype(ci)
		// 	println(stack(state.stack[ci.base():state.top]))
		// 	println(ci.code[ci.savedPC].String(), p.source, p.lineInfo[ci.savedPC])
		// }, MaskCount, 1)
		l.Global("debug")
		l.Field(-1, "traceback")
		traceback := l.Top()
		// t.Logf("%#v", l.ToValue(traceback))
		if err := LoadFile(l, filepath.Join("lua-tests", v.name+".lua"), "text"); err != nil {
			t.Errorf("'%s' failed: %s", v.name, err.Error())
		}
		// l.Call(0, 0)
		if err := l.ProtectedCall(0, 0, traceback); err != nil {
			t.Errorf("'%s' failed: %s", v.name, err.Error())
		}
	}
}

func benchmarkSort(b *testing.B, program string) {
	l := NewState()
	OpenLibraries(l)
	s := `a = {}
		for i=1,%d do
			a[i] = math.random()
		end`
	LoadString(l, fmt.Sprintf(s, b.N))
	if err := l.ProtectedCall(0, 0, 0); err != nil {
		b.Error(err.Error())
	}
	LoadString(l, program)
	b.ResetTimer()
	if err := l.ProtectedCall(0, 0, 0); err != nil {
		b.Error(err.Error())
	}
}

func BenchmarkSort(b *testing.B) { benchmarkSort(b, "table.sort(a)") }
func BenchmarkSort2(b *testing.B) {
	benchmarkSort(b, "i = 0; table.sort(a, function(x,y) i=i+1; return y<x end)")
}

func BenchmarkFibonnaci(b *testing.B) {
	l := NewState()
	s := `return function(n)
			if n == 0 then
				return 0
			elseif n == 1 then
				return 1
			end
			local n0, n1 = 0, 1
			for i = n, 2, -1 do
				local tmp = n0 + n1
				n0 = n1
				n1 = tmp
			end
			return n1
		end`
	LoadString(l, s)
	if err := l.ProtectedCall(0, 1, 0); err != nil {
		b.Error(err.Error())
	}
	l.PushInteger(b.N)
	b.ResetTimer()
	if err := l.ProtectedCall(1, 1, 0); err != nil {
		b.Error(err.Error())
	}
}

func TestVarArgMeta(t *testing.T) {
	s := `function f(t, ...) return t, {...} end
		local a = setmetatable({}, {__call = f})
		local x, y = a(table.unpack{"a", 1})
		assert(#x == 0)
		assert(#y == 2 and y[1] == "a" and y[2] == 1)`
	testString(t, s)
}

func TestCanRemoveNilObjectFromStack(t *testing.T) {
	defer func() {
		if r := recover(); r != nil {
			t.Fatalf("failed to remove `nil`, %v", r)
		}
	}()

	l := NewState()
	l.PushString("hello")
	l.Remove(-1)
	l.PushNil()
	l.Remove(-1)
}

func TestTableNext(t *testing.T) {
	l := NewState()
	OpenLibraries(l)
	l.CreateTable(10, 0)
	for i := 1; i <= 4; i++ {
		l.PushInteger(i)
		l.PushValue(-1)
		l.SetTable(-3)
	}
	if length := LengthEx(l, -1); length != 4 {
		t.Errorf("expected table length to be 4, but was %d", length)
	}
	count := 0
	for l.PushNil(); l.Next(-2); count++ {
		if k, v := CheckInteger(l, -2), CheckInteger(l, -1); k != v {
			t.Errorf("key %d != value %d", k, v)
		}
		l.Pop(1)
	}
	if count != 4 {
		t.Errorf("incorrect iteration count %d in Next()", count)
	}
}

func TestError(t *testing.T) {
	l := NewState()
	BaseOpen(l)
	errorHandled := false
	program := "error('error')"
	l.PushGoFunction(func(l *State) int {
		if l.Top() == 0 {
			t.Error("error handler received no arguments")
		} else if errorMessage, ok := l.ToString(-1); !ok {
			t.Errorf("error handler received %s instead of string", TypeNameOf(l, -1))
		} else if errorMessage != chunkID(program)+":1: error" {
			t.Errorf("error handler received '%s' instead of 'error'", errorMessage)
		}
		errorHandled = true
		return 1
	})
	LoadString(l, program)
	l.ProtectedCall(0, 0, -2)
	if !errorHandled {
		t.Error("error not handled")
	}
}

func TestErrorf(t *testing.T) {
	l := NewState()
	BaseOpen(l)
	program := "-- script that is bigger than the max ID size\nhelper()\n" + strings.Repeat("--", idSize)
	expectedErrorMessage := chunkID(program) + ":2: error"
	l.PushGoFunction(func(l *State) int {
		Errorf(l, "error")
		return 0
	})
	l.SetGlobal("helper")
	errorHandled := false
	l.PushGoFunction(func(l *State) int {
		if l.Top() == 0 {
			t.Error("error handler received no arguments")
		} else if errorMessage, ok := l.ToString(-1); !ok {
			t.Errorf("error handler received %s instead of string", TypeNameOf(l, -1))
		} else if errorMessage != expectedErrorMessage {
			t.Errorf("error handler received '%s' instead of '%s'", errorMessage, expectedErrorMessage)
		}
		errorHandled = true
		return 1
	})
	LoadString(l, program)
	l.ProtectedCall(0, 0, -2)
	if !errorHandled {
		t.Error("error not handled")
	}
}
<commit_msg>Add tests for userdata/table nil comparison<commit_after>package lua

import (
	"fmt"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
)

func testString(t *testing.T, s string)  { testStringHelper(t, s, false) }
func traceString(t *testing.T, s string) { testStringHelper(t, s, true) }

func testStringHelper(t *testing.T, s string, trace bool) {
	l := NewState()
	OpenLibraries(l)
	LoadString(l, s)
	if trace {
		SetDebugHook(l, func(state *State, ar Debug) {
			ci := state.callInfo
			p := state.prototype(ci)
			println(stack(state.stack[ci.base():state.top]))
			println(ci.code[ci.savedPC].String(), p.source, p.lineInfo[ci.savedPC])
		}, MaskCount, 1)
	}
	l.Call(0, 0)
}

func TestProtectedCall(t *testing.T) {
	l := NewState()
	OpenLibraries(l)
	SetDebugHook(l, func(state *State, ar Debug) {
		ci := state.callInfo
		_ = stack(state.stack[ci.base():state.top])
		_ = ci.code[ci.savedPC].String()
	}, MaskCount, 1)
	LoadString(l, "assert(not pcall(bit32.band, {}))")
	l.Call(0, 0)
}

func TestLua(t *testing.T) {
	tests := []struct {
		name    string
		nonPort bool
	}{
		{name: "attrib", nonPort: true},
		// {name: "big"},
		{name: "bitwise"},
		// {name: "calls"},
		// {name: "checktable"},
		{name: "closure"},
		// {name: "code"},
		// {name: "constructs"},
		// {name: "db"},
		// {name: "errors"},
		{name: "events"},
		// {name: "files"},
		// {name: "gc"},
		{name: "goto"},
		// {name: "literals"},
		{name: "locals"},
		// {name: "main"},
		{name: "math"},
		// {name: "nextvar"},
		// {name: "pm"},
		{name: "sort", nonPort: true}, // sort.lua depends on os.clock(), which is not yet implemented on Windows.
		{name: "strings"},
		// {name: "vararg"},
		// {name: "verybig"},
	}
	for _, v := range tests {
		if v.nonPort && runtime.GOOS == "windows" {
			t.Skipf("'%s' skipped because it's non-portable & we're running Windows", v.name)
		}
		t.Log(v)
		l := NewState()
		OpenLibraries(l)
		for _, s := range []string{"_port", "_no32", "_noformatA"} {
			l.PushBoolean(true)
			l.SetGlobal(s)
		}
		if v.nonPort {
			l.PushBoolean(false)
			l.SetGlobal("_port")
		}
		// l.SetDebugHook(func(state *State, ar Debug) {
		// 	ci := state.callInfo.(*luaCallInfo)
		// 	p := state.prototype(ci)
		// 	println(stack(state.stack[ci.base():state.top]))
		// 	println(ci.code[ci.savedPC].String(), p.source, p.lineInfo[ci.savedPC])
		// }, MaskCount, 1)
		l.Global("debug")
		l.Field(-1, "traceback")
		traceback := l.Top()
		// t.Logf("%#v", l.ToValue(traceback))
		if err := LoadFile(l, filepath.Join("lua-tests", v.name+".lua"), "text"); err != nil {
			t.Errorf("'%s' failed: %s", v.name, err.Error())
		}
		// l.Call(0, 0)
		if err := l.ProtectedCall(0, 0, traceback); err != nil {
			t.Errorf("'%s' failed: %s", v.name, err.Error())
		}
	}
}

func benchmarkSort(b *testing.B, program string) {
	l := NewState()
	OpenLibraries(l)
	s := `a = {}
		for i=1,%d do
			a[i] = math.random()
		end`
	LoadString(l, fmt.Sprintf(s, b.N))
	if err := l.ProtectedCall(0, 0, 0); err != nil {
		b.Error(err.Error())
	}
	LoadString(l, program)
	b.ResetTimer()
	if err := l.ProtectedCall(0, 0, 0); err != nil {
		b.Error(err.Error())
	}
}

func BenchmarkSort(b *testing.B) { benchmarkSort(b, "table.sort(a)") }
func BenchmarkSort2(b *testing.B) {
	benchmarkSort(b, "i = 0; table.sort(a, function(x,y) i=i+1; return y<x end)")
}

func BenchmarkFibonnaci(b *testing.B) {
	l := NewState()
	s := `return function(n)
			if n == 0 then
				return 0
			elseif n == 1 then
				return 1
			end
			local n0, n1 = 0, 1
			for i = n, 2, -1 do
				local tmp = n0 + n1
				n0 = n1
				n1 = tmp
			end
			return n1
		end`
	LoadString(l, s)
	if err := l.ProtectedCall(0, 1, 0); err != nil {
		b.Error(err.Error())
	}
	l.PushInteger(b.N)
	b.ResetTimer()
	if err := l.ProtectedCall(1, 1, 0); err != nil {
		b.Error(err.Error())
	}
}

func TestVarArgMeta(t *testing.T) {
	s := `function f(t, ...) return t, {...} end
		local a = setmetatable({}, {__call = f})
		local x, y = a(table.unpack{"a", 1})
		assert(#x == 0)
		assert(#y == 2 and y[1] == "a" and y[2] == 1)`
	testString(t, s)
}

func TestCanRemoveNilObjectFromStack(t *testing.T) {
	defer func() {
		if r := recover(); r != nil {
			t.Fatalf("failed to remove `nil`, %v", r)
		}
	}()

	l := NewState()
	l.PushString("hello")
	l.Remove(-1)
	l.PushNil()
	l.Remove(-1)
}

func TestTableUserdataEquality(t *testing.T) {
	const s = `return function(x)
		local b = x == {}
		assert(type(b) == "boolean")
		assert(b == false)
		-- reverse
		b = {} == x
		assert(type(b) == "boolean")
		assert(b == false)
	end`

	l := NewState()
	OpenLibraries(l)
	LoadString(l, s)
	if err := l.ProtectedCall(0, 1, 0); err != nil {
		t.Error(err.Error())
	}

	l.PushUserData(5)
	if err := l.ProtectedCall(1, 0, 0); err != nil {
		t.Error(err.Error())
	}
}

func TestUserDataEqualityNil(t *testing.T) {
	const s = `return function(x)
		local b = x == nil
		assert(type(b) == "boolean")
		assert(b == false)
	end`

	l := NewState()
	OpenLibraries(l)
	LoadString(l, s)
	if err := l.ProtectedCall(0, 1, 0); err != nil {
		t.Error(err.Error())
	}

	l.PushUserData(5)
	if err := l.ProtectedCall(1, 0, 0); err != nil {
		t.Error(err.Error())
	}
}

func TestTableEqualityNil(t *testing.T) {
	const s = `local b = {} == nil
	assert(type(b) == "boolean")
	assert(b == false)`

	testString(t, s)
}

func TestTableNext(t *testing.T) {
	l := NewState()
	OpenLibraries(l)
	l.CreateTable(10, 0)
	for i := 1; i <= 4; i++ {
		l.PushInteger(i)
		l.PushValue(-1)
		l.SetTable(-3)
	}
	if length := LengthEx(l, -1); length != 4 {
		t.Errorf("expected table length to be 4, but was %d", length)
	}
	count := 0
	for l.PushNil(); l.Next(-2); count++ {
		if k, v := CheckInteger(l, -2), CheckInteger(l, -1); k != v {
			t.Errorf("key %d != value %d", k, v)
		}
		l.Pop(1)
	}
	if count != 4 {
		t.Errorf("incorrect iteration count %d in Next()", count)
	}
}

func TestError(t *testing.T) {
	l := NewState()
	BaseOpen(l)
	errorHandled := false
	program := "error('error')"
	l.PushGoFunction(func(l *State) int {
		if l.Top() == 0 {
			t.Error("error handler received no arguments")
		} else if errorMessage, ok := l.ToString(-1); !ok {
			t.Errorf("error handler received %s instead of string", TypeNameOf(l, -1))
		} else if errorMessage != chunkID(program)+":1: error" {
			t.Errorf("error handler received '%s' instead of 'error'", errorMessage)
		}
		errorHandled = true
		return 1
	})
	LoadString(l, program)
	l.ProtectedCall(0, 0, -2)
	if !errorHandled {
		t.Error("error not handled")
	}
}

func TestErrorf(t *testing.T) {
	l := NewState()
	BaseOpen(l)
	program := "-- script that is bigger than the max ID size\nhelper()\n" + strings.Repeat("--", idSize)
	expectedErrorMessage := chunkID(program) + ":2: error"
	l.PushGoFunction(func(l *State) int {
		Errorf(l, "error")
		return 0
	})
	l.SetGlobal("helper")
	errorHandled := false
	l.PushGoFunction(func(l *State) int {
		if l.Top() == 0 {
			t.Error("error handler received no arguments")
		} else if errorMessage, ok := l.ToString(-1); !ok {
			t.Errorf("error handler received %s instead of string", TypeNameOf(l, -1))
		} else if errorMessage != expectedErrorMessage {
			t.Errorf("error handler received '%s' instead of '%s'", errorMessage, expectedErrorMessage)
		}
		errorHandled = true
		return 1
	})
	LoadString(l, program)
	l.ProtectedCall(0, 0, -2)
	if !errorHandled {
		t.Error("error not handled")
	}
}
<|endoftext|>


<commit_before>package presilo

import (
	"bytes"
	"fmt"
	"strings"
)

/*
  Generates valid Java code for a given schema.
*/
func GenerateJava(schema *ObjectSchema, module string) string {

	var ret bytes.Buffer

	ret.WriteString("package " + module + ";")
	ret.WriteString("\n")
	ret.WriteString(generateJavaImports(schema))
	ret.WriteString("\n")
	ret.WriteString(generateJavaTypeDeclaration(schema))
	ret.WriteString("\n")
	ret.WriteString(generateJavaConstructor(schema))
	ret.WriteString("\n")
	ret.WriteString(generateJavaFunctions(schema))
	ret.WriteString("\n}\n")

	return ret.String()
}

func generateJavaImports(schema *ObjectSchema) string {

  // import regex if we need it
  if(containsRegexpMatch(schema)) {
    return "import java.util.regex.*;\n\n"
  }
  return ""
}

func generateJavaTypeDeclaration(schema *ObjectSchema) string {

  var ret bytes.Buffer
  var subschema TypeSchema
  var propertyName string
  var toWrite string

  toWrite = fmt.Sprintf("public class %s\n{\n", ToCamelCase(schema.Title))
  ret.WriteString(toWrite)

  for propertyName, subschema = range schema.Properties {

    propertyName = ToJavaCase(propertyName)
    toWrite = fmt.Sprintf("\n\tprotected %s %s;", generateJavaTypeForSchema(subschema), propertyName)
    ret.WriteString(toWrite)
  }

  return ret.String()
}

func generateJavaConstructor(schema *ObjectSchema) string {

  var ret bytes.Buffer
  var subschema TypeSchema
  var declarations, setters []string
  var propertyName string
  var toWrite string
  var constrained bool

  toWrite = fmt.Sprintf("\n\tpublic %s(", ToCamelCase(schema.Title))
  ret.WriteString(toWrite)

  for _, propertyName = range schema.RequiredProperties {

    subschema = schema.Properties[propertyName]
    propertyName = ToJavaCase(propertyName)

    if(subschema.HasConstraints()) {
      constrained = true
    }

    toWrite = fmt.Sprintf("%s %s", generateJavaTypeForSchema(subschema), propertyName)
    declarations = append(declarations, toWrite)

    toWrite = fmt.Sprintf("\n\t\tset%s(%s);", ToCamelCase(propertyName), propertyName)
    setters = append(setters, toWrite)
  }

  toWrite = strings.Join(declarations, ",")
  ret.WriteString(toWrite)
  ret.WriteString(")")

  if(constrained) {
    ret.WriteString(" throws Exception")
  }

  ret.WriteString("\n\t{")

  for _, setter := range setters {
    ret.WriteString(setter)
  }

  ret.WriteString("\n\t}\n")
  return ret.String()
}

func generateJavaFunctions(schema *ObjectSchema) string {

  var ret bytes.Buffer
  var subschema TypeSchema
  var toWrite string
  var propertyName, properName, camelName, typeName string

  for propertyName, subschema = range schema.Properties {

    properName = ToJavaCase(propertyName)
    camelName = ToCamelCase(propertyName)
    typeName = generateJavaTypeForSchema(subschema)

    // getter
    toWrite = fmt.Sprintf("\n\tpublic %s get%s()\n\t{", typeName, camelName)
    ret.WriteString(toWrite)

    toWrite = fmt.Sprintf("\n\t\treturn this.%s;\n\t}", properName)
    ret.WriteString(toWrite)

    // setter
    toWrite = fmt.Sprintf("\n\tpublic void set%s(%s value)", camelName, typeName)
    ret.WriteString(toWrite)

    if(subschema.HasConstraints()) {
      ret.WriteString(" throws Exception")
    }

    ret.WriteString("\n\t{")

    switch subschema.GetSchemaType() {
    case SCHEMATYPE_BOOLEAN:
      toWrite = ""
    case SCHEMATYPE_STRING:
      toWrite = generateJavaStringSetter(subschema.(*StringSchema))
    case SCHEMATYPE_INTEGER: fallthrough
    case SCHEMATYPE_NUMBER:
      toWrite = generateJavaNumericSetter(subschema.(NumericSchemaType))
    case SCHEMATYPE_OBJECT:
      toWrite = generateJavaObjectSetter(subschema.(*ObjectSchema))
    case SCHEMATYPE_ARRAY:
      toWrite = generateJavaArraySetter(subschema.(*ArraySchema))
    }

    ret.WriteString(toWrite)

    toWrite = fmt.Sprintf("\n\t\t%s = value;", properName)
    ret.WriteString(toWrite)

    ret.WriteString("\n\t}\n")
  }

  return ret.String()
}

func generateJavaStringSetter(schema *StringSchema) string {

  var ret bytes.Buffer
  var toWrite string

  ret.WriteString(generateJavaNullCheck())

  if(schema.MinLength!= nil) {
    ret.WriteString(generateJavaRangeCheck(*schema.MinLength, "value.length()", "was shorter than allowable minimum", "%d", false, "<", ""))
  }

  if(schema.MaxLength != nil) {
    ret.WriteString(generateJavaRangeCheck(*schema.MaxLength, "value.length()", "was longer than allowable maximum", "%d", false, ">", ""))
  }

  if(schema.Pattern != nil) {

    toWrite = fmt.Sprintf("\n\t\tPattern regex = Pattern.compile(\"%s\");", sanitizeQuotedString(*schema.Pattern))
    ret.WriteString(toWrite)

    ret.WriteString("\n\t\tif(!regex.matcher(value).matches())\n\t\t{")

    toWrite = fmt.Sprintf("\n\t\t\tthrow new Exception(\"Value '\"+value+\"' did not match pattern '%s'\");", *schema.Pattern)
    ret.WriteString(toWrite)

    ret.WriteString("\n\t\t}")
  }
  return ret.String()
}

func generateJavaNumericSetter(schema NumericSchemaType) string {

  var ret bytes.Buffer
  var toWrite string

  if(schema.HasMinimum()) {
		ret.WriteString(generateJavaRangeCheck(schema.GetMinimum(), "value", "is under the allowable minimum", schema.GetConstraintFormat(), schema.IsExclusiveMinimum(), "<=", "<"))
	}

	if(schema.HasMaximum()) {
		ret.WriteString(generateJavaRangeCheck(schema.GetMaximum(), "value", "is over the allowable maximum", schema.GetConstraintFormat(), schema.IsExclusiveMaximum(), ">=", ">"))
	}

  if(schema.HasEnum()) {
		ret.WriteString(generateJavaEnumCheck(schema, schema.GetEnum(), "", ""))
	}

  if(schema.HasMultiple()) {

    toWrite = fmt.Sprintf("\n\tif(value %% %f != 0)\n\t{", schema.GetMultiple())
    ret.WriteString(toWrite)

    toWrite = fmt.Sprintf("\n\t\tthrow new Exception(\"Property '\"+value+\"' was not a multiple of %s\");", schema.GetMultiple())
    ret.WriteString(toWrite)

    ret.WriteString("\n\t}\n")
  }
  return ret.String()
}

func generateJavaObjectSetter(schema *ObjectSchema) string {

  var ret bytes.Buffer

  ret.WriteString(generateJavaNullCheck())
  return ret.String()
}

func generateJavaArraySetter(schema *ArraySchema) string {

  var ret bytes.Buffer

  ret.WriteString(generateJavaNullCheck())

  if(schema.MinItems != nil) {
    ret.WriteString(generateJavaRangeCheck(*schema.MinItems, "value.length", "does not have enough items", "%d", false, "<", ""))
  }

  if(schema.MaxItems != nil) {
    ret.WriteString(generateJavaRangeCheck(*schema.MaxItems, "value.length", "does not have enough items", "%d", false, ">", ""))
  }

  return ret.String()
}

func generateJavaNullCheck() string {

  var ret bytes.Buffer

  ret.WriteString("\n\t\tif(value == null)\n\t\t{")
  ret.WriteString("\n\t\t\tthrow new NullPointerException(\"Cannot set property to null value\");")
  ret.WriteString("\n\t\t}\n")

  return ret.String()
}

func generateJavaRangeCheck(value interface{}, reference, message, format string, exclusive bool, comparator, exclusiveComparator string) string {

	var ret bytes.Buffer
	var toWrite, compareString string

	if(exclusive) {
		compareString = exclusiveComparator
	} else {
		compareString = comparator
	}

	toWrite = "\n\t\tif("+ reference +" " + compareString + " " +format+ ")\n\t\t{"
	toWrite = fmt.Sprintf(toWrite, value)
	ret.WriteString(toWrite)

	toWrite = fmt.Sprintf("\n\t\t\tthrow new Exception(\"Property '\"+value+\"' %s.\");\n\t\t}\n", message)
	ret.WriteString(toWrite)

	return ret.String()
}

/*
	Generates code which throws an error if the given [parameter]'s value is not contained in the given [validValues].
*/
func generateJavaEnumCheck(schema TypeSchema, enumValues []interface{}, prefix string, postfix string) string {

	var ret bytes.Buffer
	var constraint, typeName string
	var length int

	length = len(enumValues)

	if(length <= 0) {
		return ""
	}

	// write array of valid values
  typeName = generateJavaTypeForSchema(schema)
	constraint = fmt.Sprintf("\t%s[] validValues = new %s[]{%s%v%s", typeName, typeName, prefix, enumValues[0], postfix)
	ret.WriteString(constraint)

	for _, enumValue := range enumValues[1:length] {

		constraint = fmt.Sprintf(",%s%v%s", prefix, enumValue, postfix)
		ret.WriteString(constraint)
	}
	ret.WriteString("};\n")

	// compare
	ret.WriteString("\tboolean isValid = false;\n")
	ret.WriteString("\tfor(int i = 0; i < validValues.length; i++) \n\t{\n")
	ret.WriteString("\t\tif(validValues[i] == value)\n\t\t{\n\t\t\tisValid = true;")
	ret.WriteString("\n\t\t\tbreak;\n\t\t}\n\t}")

	ret.WriteString("\n\tif(!isValid)\n\t{")
	ret.WriteString("\n\t\tthrow new Error(\"Given value '\"+value+\"' was not found in list of acceptable values\");\n")
	ret.WriteString("\t}\n")

	return ret.String()
}

func generateJavaTypeForSchema(subschema TypeSchema) string {

  switch(subschema.GetSchemaType()) {
  case SCHEMATYPE_NUMBER: return "double"
  case SCHEMATYPE_INTEGER: return "int"
  case SCHEMATYPE_ARRAY: return ToCamelCase(subschema.(*ArraySchema).Items.GetTitle()) + "[]"
  case SCHEMATYPE_OBJECT: return ToCamelCase(subschema.GetTitle())
  case SCHEMATYPE_STRING: return "String"
  case SCHEMATYPE_BOOLEAN: return "boolean"
  }

  return "Object"
}
<commit_msg>Fixed accidental use of 'error' instead of 'exception' in java gen<commit_after>package presilo

import (
	"bytes"
	"fmt"
	"strings"
)

/*
  Generates valid Java code for a given schema.
*/
func GenerateJava(schema *ObjectSchema, module string) string {

	var ret bytes.Buffer

	ret.WriteString("package " + module + ";")
	ret.WriteString("\n")
	ret.WriteString(generateJavaImports(schema))
	ret.WriteString("\n")
	ret.WriteString(generateJavaTypeDeclaration(schema))
	ret.WriteString("\n")
	ret.WriteString(generateJavaConstructor(schema))
	ret.WriteString("\n")
	ret.WriteString(generateJavaFunctions(schema))
	ret.WriteString("\n}\n")

	return ret.String()
}

func generateJavaImports(schema *ObjectSchema) string {

  // import regex if we need it
  if(containsRegexpMatch(schema)) {
    return "import java.util.regex.*;\n\n"
  }
  return ""
}

func generateJavaTypeDeclaration(schema *ObjectSchema) string {

  var ret bytes.Buffer
  var subschema TypeSchema
  var propertyName string
  var toWrite string

  toWrite = fmt.Sprintf("public class %s\n{\n", ToCamelCase(schema.Title))
  ret.WriteString(toWrite)

  for propertyName, subschema = range schema.Properties {

    propertyName = ToJavaCase(propertyName)
    toWrite = fmt.Sprintf("\n\tprotected %s %s;", generateJavaTypeForSchema(subschema), propertyName)
    ret.WriteString(toWrite)
  }

  return ret.String()
}

func generateJavaConstructor(schema *ObjectSchema) string {

  var ret bytes.Buffer
  var subschema TypeSchema
  var declarations, setters []string
  var propertyName string
  var toWrite string
  var constrained bool

  toWrite = fmt.Sprintf("\n\tpublic %s(", ToCamelCase(schema.Title))
  ret.WriteString(toWrite)

  for _, propertyName = range schema.RequiredProperties {

    subschema = schema.Properties[propertyName]
    propertyName = ToJavaCase(propertyName)

    if(subschema.HasConstraints()) {
      constrained = true
    }

    toWrite = fmt.Sprintf("%s %s", generateJavaTypeForSchema(subschema), propertyName)
    declarations = append(declarations, toWrite)

    toWrite = fmt.Sprintf("\n\t\tset%s(%s);", ToCamelCase(propertyName), propertyName)
    setters = append(setters, toWrite)
  }

  toWrite = strings.Join(declarations, ",")
  ret.WriteString(toWrite)
  ret.WriteString(")")

  if(constrained) {
    ret.WriteString(" throws Exception")
  }

  ret.WriteString("\n\t{")

  for _, setter := range setters {
    ret.WriteString(setter)
  }

  ret.WriteString("\n\t}\n")
  return ret.String()
}

func generateJavaFunctions(schema *ObjectSchema) string {

  var ret bytes.Buffer
  var subschema TypeSchema
  var toWrite string
  var propertyName, properName, camelName, typeName string

  for propertyName, subschema = range schema.Properties {

    properName = ToJavaCase(propertyName)
    camelName = ToCamelCase(propertyName)
    typeName = generateJavaTypeForSchema(subschema)

    // getter
    toWrite = fmt.Sprintf("\n\tpublic %s get%s()\n\t{", typeName, camelName)
    ret.WriteString(toWrite)

    toWrite = fmt.Sprintf("\n\t\treturn this.%s;\n\t}", properName)
    ret.WriteString(toWrite)

    // setter
    toWrite = fmt.Sprintf("\n\tpublic void set%s(%s value)", camelName, typeName)
    ret.WriteString(toWrite)

    if(subschema.HasConstraints()) {
      ret.WriteString(" throws Exception")
    }

    ret.WriteString("\n\t{")

    switch subschema.GetSchemaType() {
    case SCHEMATYPE_BOOLEAN:
      toWrite = ""
    case SCHEMATYPE_STRING:
      toWrite = generateJavaStringSetter(subschema.(*StringSchema))
    case SCHEMATYPE_INTEGER: fallthrough
    case SCHEMATYPE_NUMBER:
      toWrite = generateJavaNumericSetter(subschema.(NumericSchemaType))
    case SCHEMATYPE_OBJECT:
      toWrite = generateJavaObjectSetter(subschema.(*ObjectSchema))
    case SCHEMATYPE_ARRAY:
      toWrite = generateJavaArraySetter(subschema.(*ArraySchema))
    }

    ret.WriteString(toWrite)

    toWrite = fmt.Sprintf("\n\t\t%s = value;", properName)
    ret.WriteString(toWrite)

    ret.WriteString("\n\t}\n")
  }

  return ret.String()
}

func generateJavaStringSetter(schema *StringSchema) string {

  var ret bytes.Buffer
  var toWrite string

  ret.WriteString(generateJavaNullCheck())

  if(schema.MinLength!= nil) {
    ret.WriteString(generateJavaRangeCheck(*schema.MinLength, "value.length()", "was shorter than allowable minimum", "%d", false, "<", ""))
  }

  if(schema.MaxLength != nil) {
    ret.WriteString(generateJavaRangeCheck(*schema.MaxLength, "value.length()", "was longer than allowable maximum", "%d", false, ">", ""))
  }

  if(schema.Pattern != nil) {

    toWrite = fmt.Sprintf("\n\t\tPattern regex = Pattern.compile(\"%s\");", sanitizeQuotedString(*schema.Pattern))
    ret.WriteString(toWrite)

    ret.WriteString("\n\t\tif(!regex.matcher(value).matches())\n\t\t{")

    toWrite = fmt.Sprintf("\n\t\t\tthrow new Exception(\"Value '\"+value+\"' did not match pattern '%s'\");", *schema.Pattern)
    ret.WriteString(toWrite)

    ret.WriteString("\n\t\t}")
  }
  return ret.String()
}

func generateJavaNumericSetter(schema NumericSchemaType) string {

  var ret bytes.Buffer
  var toWrite string

  if(schema.HasMinimum()) {
		ret.WriteString(generateJavaRangeCheck(schema.GetMinimum(), "value", "is under the allowable minimum", schema.GetConstraintFormat(), schema.IsExclusiveMinimum(), "<=", "<"))
	}

	if(schema.HasMaximum()) {
		ret.WriteString(generateJavaRangeCheck(schema.GetMaximum(), "value", "is over the allowable maximum", schema.GetConstraintFormat(), schema.IsExclusiveMaximum(), ">=", ">"))
	}

  if(schema.HasEnum()) {
		ret.WriteString(generateJavaEnumCheck(schema, schema.GetEnum(), "", ""))
	}

  if(schema.HasMultiple()) {

    toWrite = fmt.Sprintf("\n\tif(value %% %f != 0)\n\t{", schema.GetMultiple())
    ret.WriteString(toWrite)

    toWrite = fmt.Sprintf("\n\t\tthrow new Exception(\"Property '\"+value+\"' was not a multiple of %s\");", schema.GetMultiple())
    ret.WriteString(toWrite)

    ret.WriteString("\n\t}\n")
  }
  return ret.String()
}

func generateJavaObjectSetter(schema *ObjectSchema) string {

  var ret bytes.Buffer

  ret.WriteString(generateJavaNullCheck())
  return ret.String()
}

func generateJavaArraySetter(schema *ArraySchema) string {

  var ret bytes.Buffer

  ret.WriteString(generateJavaNullCheck())

  if(schema.MinItems != nil) {
    ret.WriteString(generateJavaRangeCheck(*schema.MinItems, "value.length", "does not have enough items", "%d", false, "<", ""))
  }

  if(schema.MaxItems != nil) {
    ret.WriteString(generateJavaRangeCheck(*schema.MaxItems, "value.length", "does not have enough items", "%d", false, ">", ""))
  }

  return ret.String()
}

func generateJavaNullCheck() string {

  var ret bytes.Buffer

  ret.WriteString("\n\t\tif(value == null)\n\t\t{")
  ret.WriteString("\n\t\t\tthrow new NullPointerException(\"Cannot set property to null value\");")
  ret.WriteString("\n\t\t}\n")

  return ret.String()
}

func generateJavaRangeCheck(value interface{}, reference, message, format string, exclusive bool, comparator, exclusiveComparator string) string {

	var ret bytes.Buffer
	var toWrite, compareString string

	if(exclusive) {
		compareString = exclusiveComparator
	} else {
		compareString = comparator
	}

	toWrite = "\n\t\tif("+ reference +" " + compareString + " " +format+ ")\n\t\t{"
	toWrite = fmt.Sprintf(toWrite, value)
	ret.WriteString(toWrite)

	toWrite = fmt.Sprintf("\n\t\t\tthrow new Exception(\"Property '\"+value+\"' %s.\");\n\t\t}\n", message)
	ret.WriteString(toWrite)

	return ret.String()
}

/*
	Generates code which throws an error if the given [parameter]'s value is not contained in the given [validValues].
*/
func generateJavaEnumCheck(schema TypeSchema, enumValues []interface{}, prefix string, postfix string) string {

	var ret bytes.Buffer
	var constraint, typeName string
	var length int

	length = len(enumValues)

	if(length <= 0) {
		return ""
	}

	// write array of valid values
  typeName = generateJavaTypeForSchema(schema)
	constraint = fmt.Sprintf("\t%s[] validValues = new %s[]{%s%v%s", typeName, typeName, prefix, enumValues[0], postfix)
	ret.WriteString(constraint)

	for _, enumValue := range enumValues[1:length] {

		constraint = fmt.Sprintf(",%s%v%s", prefix, enumValue, postfix)
		ret.WriteString(constraint)
	}
	ret.WriteString("};\n")

	// compare
	ret.WriteString("\tboolean isValid = false;\n")
	ret.WriteString("\tfor(int i = 0; i < validValues.length; i++) \n\t{\n")
	ret.WriteString("\t\tif(validValues[i] == value)\n\t\t{\n\t\t\tisValid = true;")
	ret.WriteString("\n\t\t\tbreak;\n\t\t}\n\t}")

	ret.WriteString("\n\tif(!isValid)\n\t{")
	ret.WriteString("\n\t\tthrow new Exception(\"Given value '\"+value+\"' was not found in list of acceptable values\");\n")
	ret.WriteString("\t}\n")

	return ret.String()
}

func generateJavaTypeForSchema(subschema TypeSchema) string {

  switch(subschema.GetSchemaType()) {
  case SCHEMATYPE_NUMBER: return "double"
  case SCHEMATYPE_INTEGER: return "int"
  case SCHEMATYPE_ARRAY: return ToCamelCase(subschema.(*ArraySchema).Items.GetTitle()) + "[]"
  case SCHEMATYPE_OBJECT: return ToCamelCase(subschema.GetTitle())
  case SCHEMATYPE_STRING: return "String"
  case SCHEMATYPE_BOOLEAN: return "boolean"
  }

  return "Object"
}
<|endoftext|>