<commit_before>package libreofficekit

/*
#cgo CFLAGS: -I ./ -D LOK_USE_UNSTABLE_API
#cgo LDFLAGS: -ldl
#include <lokbridge.h>
*/
import "C"
import (
	"fmt"
	"sync"
	"unsafe"
)

type Office struct {
	handle *C.struct__LibreOfficeKit
	mutex  *sync.Mutex
}

func NewOffice(path string) (*Office, error) {
	office := new(Office)

	c_path := C.CString(path)
	defer C.free(unsafe.Pointer(c_path))

	lokit := C.lok_init(c_path)
	if lokit == nil {
		return nil, fmt.Errorf("Failed to initialize LibreOfficeKit with path: '%s'", path)
	}

	office.handle = lokit
	office.mutex = &sync.Mutex{}

	return office, nil

}

func (self *Office) Close() {
	C.destroy_office(self.handle)
}

func (self *Office) GetError() string {
	message := C.get_error(self.handle)
	return C.GoString(message)
}

func (self *Office) LoadDocument(path string) (*Document, error) {
	document := new(Document)
	c_path := C.CString(path)
	defer C.free(unsafe.Pointer(c_path))
	handle := C.document_load(self.handle, c_path)
	if handle == nil {
		return nil, fmt.Errorf("Failed to load document")
	}
	document.handle = handle
	return document, nil
}

const (
	TextDocument = iota
	SpreadsheetDocument
	PresentationDocument
	DrawingDocument
	OtherDocument
)

type Document struct {
	handle *C.struct__LibreOfficeKitDocument
}

func (self *Document) Close() {
	C.destroy_document(self.handle)
}

func (self *Document) GetType() int {
	return int(C.get_document_type(self.handle))
}

func (self *Document) GetParts() int {
	return int(C.get_document_parts(self.handle))
}

func (self *Document) GetPart() int {
	return int(C.get_document_part(self.handle))
}

func (self *Document) SetPart(part int) {
	C.set_document_part(self.handle, C.int(part))
}

func (self *Document) GetPartName(part int) string {
	c_part := C.int(part)
	c_part_name := C.get_document_part_name(self.handle, c_part)
	defer C.free(unsafe.Pointer(c_part_name))
	return C.GoString(c_part_name)
}

func (self *Document) GetSize() (int, int) {
	width := C.long(0)
	heigth := C.long(0)
	C.get_document_size(self.handle, &width, &heigth)
	return int(width), int(heigth)
}

func (self *Document) InitializeForRendering(arguments string) {
	c_arguments := C.CString(arguments)
	defer C.free(unsafe.Pointer(c_arguments))
	C.initialize_for_rendering(self.handle, c_arguments)
}

func (self *Document) SaveAs(path string, format string, filter string) error {
	c_path := C.CString(path)
	defer C.free(unsafe.Pointer(c_path))
	c_format := C.CString(format)
	defer C.free(unsafe.Pointer(c_format))
	c_filter := C.CString(filter)
	defer C.free(unsafe.Pointer(c_filter))
	status := C.document_save(self.handle, c_path, c_format, c_filter)
	if status != 0 {
		return fmt.Errorf("Failed to save document")
	} else {
		return nil
	}
}
<commit_msg>Add documentation to Document struct and its methods<commit_after>package libreofficekit

/*
#cgo CFLAGS: -I ./ -D LOK_USE_UNSTABLE_API
#cgo LDFLAGS: -ldl
#include <lokbridge.h>
*/
import "C"
import (
	"fmt"
	"sync"
	"unsafe"
)

type Office struct {
	handle *C.struct__LibreOfficeKit
	mutex  *sync.Mutex
}

func NewOffice(path string) (*Office, error) {
	office := new(Office)

	c_path := C.CString(path)
	defer C.free(unsafe.Pointer(c_path))

	lokit := C.lok_init(c_path)
	if lokit == nil {
		return nil, fmt.Errorf("Failed to initialize LibreOfficeKit with path: '%s'", path)
	}

	office.handle = lokit
	office.mutex = &sync.Mutex{}

	return office, nil

}

func (self *Office) Close() {
	C.destroy_office(self.handle)
}

func (self *Office) GetError() string {
	message := C.get_error(self.handle)
	return C.GoString(message)
}

func (self *Office) LoadDocument(path string) (*Document, error) {
	document := new(Document)
	c_path := C.CString(path)
	defer C.free(unsafe.Pointer(c_path))
	handle := C.document_load(self.handle, c_path)
	if handle == nil {
		return nil, fmt.Errorf("Failed to load document")
	}
	document.handle = handle
	return document, nil
}

// Types of documents returned by Document.GetType function
const (
	TextDocument = iota
	SpreadsheetDocument
	PresentationDocument
	DrawingDocument
	OtherDocument
)

type Document struct {
	handle *C.struct__LibreOfficeKitDocument
}

func (self *Document) Close() {
	C.destroy_document(self.handle)
}

// Returns type of loaded document
func (self *Document) GetType() int {
	return int(C.get_document_type(self.handle))
}

// Returns count of slides (for presentations) or pages (for text documents)
func (self *Document) GetParts() int {
	return int(C.get_document_parts(self.handle))
}

// GetPart returns current part of document, e.g.
// if document was just loaded it's current part will be 0
func (self *Document) GetPart() int {
	return int(C.get_document_part(self.handle))
}

// SetPart updates current part of document
func (self *Document) SetPart(part int) {
	C.set_document_part(self.handle, C.int(part))
}

// Returns current slide title (for presentations) or page title (for text documents)
func (self *Document) GetPartName(part int) string {
	c_part := C.int(part)
	c_part_name := C.get_document_part_name(self.handle, c_part)
	defer C.free(unsafe.Pointer(c_part_name))
	return C.GoString(c_part_name)
}

// GetSize returns width and height of document in twips (1 Twip = 1/1440th of an inch)
// You can convert twips to pixels by this formula: (width or height) * (1.0 / 1440.0) * DPI
func (self *Document) GetSize() (int, int) {
	width := C.long(0)
	heigth := C.long(0)
	C.get_document_size(self.handle, &width, &heigth)
	return int(width), int(heigth)
}

// Must be called before performing any rendering-related actions
func (self *Document) InitializeForRendering(arguments string) {
	c_arguments := C.CString(arguments)
	defer C.free(unsafe.Pointer(c_arguments))
	C.initialize_for_rendering(self.handle, c_arguments)
}

// Saves document at desired path in desired format with applied filter rules
// Actual (from libreoffice) error message can be read with Office.GetError
func (self *Document) SaveAs(path string, format string, filter string) error {
	c_path := C.CString(path)
	defer C.free(unsafe.Pointer(c_path))
	c_format := C.CString(format)
	defer C.free(unsafe.Pointer(c_format))
	c_filter := C.CString(filter)
	defer C.free(unsafe.Pointer(c_filter))
	status := C.document_save(self.handle, c_path, c_format, c_filter)
	if status != 0 {
		return fmt.Errorf("Failed to save document")
	} else {
		return nil
	}
}
<|endoftext|>


<commit_before>package errors

import (
	"testing"
)

func TestTrimSourcePath(t *testing.T) {
	cases := []struct {
		name, path, trim string
	}{
		{"main.main", "D:/Gowork/src/github.com/y4v8/test/main.go", "main.go"},
		{"main.main", "D:/main.go", "main.go"},
		{"github.com/y4v8/test/m.MyFunc", "D:/Gowork/src/github.com/y4v8/test/m/tst.go", "github.com/y4v8/test/m/tst.go"},
	}

	for _, c := range cases {
		trim := trimSourcePath(c.name, c.path)
		if trim != c.trim {
			t.Errorf(`[%v] for path=[%v], want [%v]`, trim, c.path, c.trim)
		}
	}
}
<commit_msg>added tests<commit_after>package errors

import (
	"testing"
	"strings"
	e "errors"
)

func TestTrimSourcePath(t *testing.T) {
	cases := []struct {
		name, path, result string
	}{
		{
			name:   "main.main",
			path:   "D:/Gowork/src/github.com/y4v8/test/main.go",
			result: "main.go",
		}, {
			name:   "main.main",
			path:   "D:/main.go",
			result: "main.go",
		}, {
			name:   "github.com/y4v8/test/m.MyFunc",
			path:   "D:/Gowork/src/github.com/y4v8/test/m/tst.go",
			result: "github.com/y4v8/test/m/tst.go",
		},
	}

	for _, c := range cases {
		result := trimSourcePath(c.name, c.path)

		if result != c.result {
			t.Errorf(`("%v", "%v") = "%v", expected "%v"`, c.name, c.path, result, c.result)
		}
	}
}

func TestNew(t *testing.T) {
	cases := []struct {
		text   string
		params []interface{}
		suffix string
	}{
		{
			text:   "test error %v %v %v",
			params: []interface{}{1, 2, 3},
			suffix: "test error 1 2 3",
		}, {
			text:   "test error",
			params: nil,
			suffix: "test error",
		},
	}

	for _, c := range cases {
		err := New(c.text, c.params...)

		result := err.Error()
		if !strings.HasSuffix(result, c.suffix) {
			t.Errorf(`("%v", %v) = "%v", expected "%v"`, c.text, c.params, result, c.suffix)
		}
	}
}

func TestAppend(t *testing.T) {
	cases := []struct {
		err      error
		text     string
		params   []interface{}
		suffixes []string
	}{
		{
			err:      e.New("test error"),
			text:     "append error %v %v %v",
			params:   []interface{}{1, 2, 3},
			suffixes: []string{"test error", "append error 1 2 3"},
		}, {
			err:      nil,
			text:     "append error %v %v %v",
			params:   []interface{}{1, 2, 3},
			suffixes: []string{"append error 1 2 3"},
		},
	}

	for _, c := range cases {
		err := Append(c.err, c.text, c.params...)

		result := errorSuffixes(err, c.suffixes)

		resultString := strings.Join(result, ",")
		testString := strings.Join(c.suffixes, ",")
		if resultString != testString {
			t.Errorf(`("%v", "%v", %v) = %v, expected %v`, c.err, c.text, c.params, result, c.suffixes)
		}
	}
}

func TestWrap(t *testing.T) {
	cases := []struct {
		errors   []error
		suffixes []string
	}{
		{
			errors:   []error{e.New("error1"), e.New("error2"), e.New("error3")},
			suffixes: []string{"error1", "error2", "error3"},
		}, {
			errors:   []error{nil},
			suffixes: []string{""},
		}, {
			errors:   []error{nil, e.New("error2"), e.New("error3")},
			suffixes: []string{"error2", "error3"},
		}, {
			errors:   []error{e.New("error1"), nil, e.New("error3")},
			suffixes: []string{"error1", "error3"},
		},
	}

	for _, c := range cases {
		err := Wrap(c.errors...)

		result := errorSuffixes(err, c.suffixes)

		resultString := strings.Join(result, ",")
		testString := strings.Join(c.suffixes, ",")
		if resultString != testString {
			t.Errorf(`(%v) = %v, expected %v`, c.errors, result, c.suffixes)
		}
	}
}

func errorSuffixes(err error, suffixes []string) []string {
	if err == nil {
		return []string{}
	}
	split := strings.Split(err.Error(), "\n")
	result := make([]string, len(split))
	for i := range split {
		if len(split[i]) > len(suffixes[i]) {
			result[i] = split[i][len(split[i])-len(suffixes[i]):]
		} else {
			result[i] = split[i]
		}
	}
	return result
}
<|endoftext|>


<commit_before>package dsl_test

import (
	"testing"

	"reflect"

	"github.com/goadesign/goa/design"
	. "github.com/goadesign/goa/design/dsl"
	"github.com/goadesign/goa/eval"
)

func TestEndpoint(t *testing.T) {
	cases := map[string]struct {
		Expr   eval.Expression
		DSL    func()
		Assert map[string]func(t *testing.T, s *design.EndpointExpr)
	}{
		"basic": {
			&design.ServiceExpr{},
			func() {
				Endpoint("basic", func() {
					Description("Optional description")
					// Docs allows linking to external documentation.
					Docs(func() {
						Description("Optional description")
						URL("https://goa.design")
					})
					Request(func() {
						Description("Optional description")
						Attribute("required", design.String)
						Required("required")
					})
					Response(func() {
						Description("Optional description")
						Attribute("required", design.String)
						Required("required")
					})
					Error("basic_error")
					Error("basic_media_error", design.ErrorMedia)
					Metadata("name", "some value", "some other value")
				})
				Endpoint("another", func() {
					// Docs allows linking to external documentation.
					Docs(func() {
						Description("Optional description")
						URL("https://goa.design")
					})
					Request(design.String)
					Response(design.String)
					Error("basic_media_error", design.ErrorMedia)
				})
			},
			map[string]func(t *testing.T, s *design.EndpointExpr){
				"basic": func(t *testing.T, e *design.EndpointExpr) {
					assertEndpointDescription(t, "Optional description", e.Description)
					assertEndpointDocs(t, e.Docs, "https://goa.design", "Optional description")
					if len(e.Errors) != 2 {
						t.Errorf("expected %d error definitions but got %d ", 2, len(e.Errors))
					}
					assertEndpointError(t, e.Errors[0], "basic_error", design.ErrorMedia)
					assertEndpointError(t, e.Errors[1], "basic_media_error", design.ErrorMedia)
					expectedMeta := design.MetadataExpr{
						"name": []string{"some value", "some other value"},
					}
					assertEndpointMetaData(t, e.Metadata, expectedMeta)
					expectedReq := &design.UserTypeExpr{
						TypeName:      "BasicRequest",
						AttributeExpr: &design.AttributeExpr{Description: "Optional description", Type: &design.Object{}}}
					assertEndpointRequestResponse(t, "Request", e.Request, expectedReq)
					expectedRes := &design.UserTypeExpr{
						TypeName:      "BasicResponse",
						AttributeExpr: &design.AttributeExpr{Description: "Optional description", Type: &design.Object{}}}
					assertEndpointRequestResponse(t, "Response", e.Response, expectedRes)
				},
				"another": func(t *testing.T, e *design.EndpointExpr) {
					assertEndpointDocs(t, e.Docs, "https://goa.design", "Optional description")
					if len(e.Errors) != 1 {
						t.Errorf("expected %d error definitions but got %d ", 1, len(e.Errors))
					}
					assertEndpointError(t, e.Errors[0], "basic_media_error", design.ErrorMedia)
					expectedReq := &design.UserTypeExpr{TypeName: "AnotherRequest", AttributeExpr: &design.AttributeExpr{Type: design.String}}
					assertEndpointRequestResponse(t, "Request", e.Request, expectedReq)
					expectedRes := &design.UserTypeExpr{TypeName: "AnotherResponse", AttributeExpr: &design.AttributeExpr{Type: design.String}}
					assertEndpointRequestResponse(t, "Response", e.Response, expectedRes)
				},
			},
		},
	}
	//Run our tests
	for k, tc := range cases {
		t.Run(k, func(t *testing.T) {
			eval.Context = &eval.DSLContext{}
			eval.Execute(tc.DSL, tc.Expr)
			evalService := tc.Expr.(*design.ServiceExpr)
			if eval.Context.Errors != nil {
				t.Errorf("%s: Endpoint failed unexpectedly with %s", k, eval.Context.Errors)
			}
			for _, e := range evalService.Endpoints {
				if _, ok := tc.Assert[e.Name]; !ok {
					t.Errorf("no assert found for endpoint %s ", e.Name)
					break
				}
				tc.Assert[e.Name](t, e)
			}
		})
	}
}

//helper funcs
func assertEndpointDocs(t *testing.T, doc *design.DocsExpr, url, desc string) {
	if doc.Description != desc {
		t.Errorf("expected docs description '%s' to match '%s' ", desc, doc.Description)
	}
	if doc.URL != url {
		t.Errorf("expected docs url '%s' to match '%s' ", url, doc.URL)
	}
}

func assertEndpointDescription(t *testing.T, expectedDesc, actualDesc string) {
	if expectedDesc != actualDesc {
		t.Errorf("expected description '%s' to match '%s' ", actualDesc, expectedDesc)
	}
}

func assertEndpointError(t *testing.T, actual *design.ErrorExpr, name string, dt design.DataType) {
	if actual.Name != name {
		t.Errorf("expected error to have name %s but got %s ", name, actual.Name)
	}

	if actual.AttributeExpr.Type != dt {
		t.Errorf("expected the error DataType to be %v but got %v ", dt, actual.AttributeExpr.Type)
	}
}

func assertEndpointMetaData(t *testing.T, actual design.MetadataExpr, expected design.MetadataExpr) {
	for key, val := range actual {
		vals, ok := expected[key]
		if !ok {
			t.Errorf("metaData was missing expected key %s ", key)
			continue
		}
		for _, metaVal := range val {
			if !hasValue(vals, metaVal) {
				t.Errorf("metaData was missing expected value %s ", metaVal)
			}
		}

	}
}

func assertEndpointRequestResponse(t *testing.T, assertType string, actual design.DataType, expected *design.UserTypeExpr) {
	ut, ok := actual.(*design.UserTypeExpr)
	if !ok {
		t.Errorf("expected endpoint %s to be a *UserTypeExpr but got %v", assertType, reflect.TypeOf(ut))
	}
	if ut.Name() != expected.Name() {
		t.Errorf("expected endpoint %s name %s to match %s", assertType, ut.Name(), expected.Name())
	}
	if ut.AttributeExpr.Type.Name() != expected.Type.Name() {
		t.Errorf("expected endpoint %s TypeName %s to match %s ", assertType, ut.Type.Name(), expected.Type.Name())
	}
	if ut.Description != expected.Description {
		t.Errorf("expected endpoint %s description %s to match %s", assertType, ut.Description, expected.Description)
	}

}
<commit_msg>add nil check<commit_after>package dsl_test

import (
	"testing"

	"reflect"

	"github.com/goadesign/goa/design"
	. "github.com/goadesign/goa/design/dsl"
	"github.com/goadesign/goa/eval"
)

func TestEndpoint(t *testing.T) {
	cases := map[string]struct {
		Expr   eval.Expression
		DSL    func()
		Assert map[string]func(t *testing.T, s *design.EndpointExpr)
	}{
		"basic": {
			&design.ServiceExpr{},
			func() {
				Endpoint("basic", func() {
					Description("Optional description")
					// Docs allows linking to external documentation.
					Docs(func() {
						Description("Optional description")
						URL("https://goa.design")
					})
					Request(func() {
						Description("Optional description")
						Attribute("required", design.String)
						Required("required")
					})
					Response(func() {
						Description("Optional description")
						Attribute("required", design.String)
						Required("required")
					})
					Error("basic_error")
					Error("basic_media_error", design.ErrorMedia)
					Metadata("name", "some value", "some other value")
				})
				Endpoint("another", func() {
					// Docs allows linking to external documentation.
					Docs(func() {
						Description("Optional description")
						URL("https://goa.design")
					})
					Request(design.String)
					Response(design.String)
					Error("basic_media_error", design.ErrorMedia)
				})
			},
			map[string]func(t *testing.T, s *design.EndpointExpr){
				"basic": func(t *testing.T, e *design.EndpointExpr) {
					assertEndpointDescription(t, "Optional description", e.Description)
					assertEndpointDocs(t, e.Docs, "https://goa.design", "Optional description")
					if len(e.Errors) != 2 {
						t.Errorf("expected %d error definitions but got %d ", 2, len(e.Errors))
					}
					assertEndpointError(t, e.Errors[0], "basic_error", design.ErrorMedia)
					assertEndpointError(t, e.Errors[1], "basic_media_error", design.ErrorMedia)
					expectedMeta := design.MetadataExpr{
						"name": []string{"some value", "some other value"},
					}
					assertEndpointMetaData(t, e.Metadata, expectedMeta)
					expectedReq := &design.UserTypeExpr{
						TypeName:      "BasicRequest",
						AttributeExpr: &design.AttributeExpr{Description: "Optional description", Type: &design.Object{}}}
					assertEndpointRequestResponse(t, "Request", e.Request, expectedReq)
					expectedRes := &design.UserTypeExpr{
						TypeName:      "BasicResponse",
						AttributeExpr: &design.AttributeExpr{Description: "Optional description", Type: &design.Object{}}}
					assertEndpointRequestResponse(t, "Response", e.Response, expectedRes)
				},
				"another": func(t *testing.T, e *design.EndpointExpr) {
					assertEndpointDocs(t, e.Docs, "https://goa.design", "Optional description")
					if len(e.Errors) != 1 {
						t.Errorf("expected %d error definitions but got %d ", 1, len(e.Errors))
					}
					assertEndpointError(t, e.Errors[0], "basic_media_error", design.ErrorMedia)
					expectedReq := &design.UserTypeExpr{TypeName: "AnotherRequest", AttributeExpr: &design.AttributeExpr{Type: design.String}}
					assertEndpointRequestResponse(t, "Request", e.Request, expectedReq)
					expectedRes := &design.UserTypeExpr{TypeName: "AnotherResponse", AttributeExpr: &design.AttributeExpr{Type: design.String}}
					assertEndpointRequestResponse(t, "Response", e.Response, expectedRes)
				},
			},
		},
	}
	//Run our tests
	for k, tc := range cases {
		t.Run(k, func(t *testing.T) {
			eval.Context = &eval.DSLContext{}
			eval.Execute(tc.DSL, tc.Expr)
			evalService := tc.Expr.(*design.ServiceExpr)
			if eval.Context.Errors != nil {
				t.Errorf("%s: Endpoint failed unexpectedly with %s", k, eval.Context.Errors)
			}
			for _, e := range evalService.Endpoints {
				if _, ok := tc.Assert[e.Name]; !ok {
					t.Errorf("no assert found for endpoint %s ", e.Name)
					break
				}
				tc.Assert[e.Name](t, e)
			}
		})
	}
}

//helper funcs
func assertEndpointDocs(t *testing.T, doc *design.DocsExpr, url, desc string) {
	if doc.Description != desc {
		t.Errorf("expected docs description '%s' to match '%s' ", desc, doc.Description)
	}
	if doc.URL != url {
		t.Errorf("expected docs url '%s' to match '%s' ", url, doc.URL)
	}
}

func assertEndpointDescription(t *testing.T, expectedDesc, actualDesc string) {
	if expectedDesc != actualDesc {
		t.Errorf("expected description '%s' to match '%s' ", actualDesc, expectedDesc)
	}
}

func assertEndpointError(t *testing.T, actual *design.ErrorExpr, name string, dt design.DataType) {
	if actual.Name != name {
		t.Errorf("expected error to have name %s but got %s ", name, actual.Name)
	}

	if actual.AttributeExpr.Type != dt {
		t.Errorf("expected the error DataType to be %v but got %v ", dt, actual.AttributeExpr.Type)
	}
}

func assertEndpointMetaData(t *testing.T, actual design.MetadataExpr, expected design.MetadataExpr) {
	for key, val := range actual {
		vals, ok := expected[key]
		if !ok {
			t.Errorf("metaData was missing expected key %s ", key)
			continue
		}
		for _, metaVal := range val {
			if !hasValue(vals, metaVal) {
				t.Errorf("metaData was missing expected value %s ", metaVal)
			}
		}

	}
}

func assertEndpointRequestResponse(t *testing.T, assertType string, actual design.DataType, expected *design.UserTypeExpr) {
	ut, ok := actual.(*design.UserTypeExpr)
	if !ok || ut == nil {
		t.Errorf("expected endpoint %s to be a *UserTypeExpr but got %v", assertType, reflect.TypeOf(ut))
	}
	if ut.Name() != expected.Name() {
		t.Errorf("expected endpoint %s name %s to match %s", assertType, ut.Name(), expected.Name())
	}
	if ut.AttributeExpr.Type.Name() != expected.Type.Name() {
		t.Errorf("expected endpoint %s TypeName %s to match %s ", assertType, ut.Type.Name(), expected.Type.Name())
	}
	if ut.Description != expected.Description {
		t.Errorf("expected endpoint %s description %s to match %s", assertType, ut.Description, expected.Description)
	}

}
<|endoftext|>


<commit_before>package controllers

import (
	"fmt"
	"github.com/shopspring/decimal"
	log "github.com/sirupsen/logrus"

	common "github.com/nttdots/go-dots/dots_common"
	"github.com/nttdots/go-dots/dots_common/messages"
	"github.com/nttdots/go-dots/dots_server/models"
	dots_config "github.com/nttdots/go-dots/dots_server/config"
)

/*
 * Controller for the session_configuration API.
 */
type SessionConfiguration struct {
	Controller
}

func (m *SessionConfiguration) HandleGet(request Request, customer *models.Customer) (res Response, err error) {
        log.Debugf("[GET]SessionConfig customer.Id=%+v", customer.Id)
	signalSessionConfiguration, err := models.GetCurrentSignalSessionConfiguration(customer.Id)
	if err != nil {
		res = Response{
			Type: common.NonConfirmable,
			Code: common.BadRequest,
			Body: nil,
		}
		return
	}

	// TODO: check found or not

	config := dots_config.GetServerSystemConfig().SignalConfigurationParameter

	resp := messages.ConfigurationResponse{}
	resp.SignalConfigs = messages.ConfigurationResponseConfigs{}
	resp.SignalConfigs.MitigationConfig = messages.ConfigurationResponseConfig{}
	resp.SignalConfigs.MitigationConfig.HeartbeatInterval.SetMinMax(config.HeartbeatInterval)
	resp.SignalConfigs.MitigationConfig.MissingHbAllowed.SetMinMax(config.MissingHbAllowed)
	resp.SignalConfigs.MitigationConfig.MaxRetransmit.SetMinMax(config.MaxRetransmit)
	resp.SignalConfigs.MitigationConfig.AckTimeout.SetMinMax(config.AckTimeout)
	resp.SignalConfigs.MitigationConfig.AckRandomFactor.SetMinMax(config.AckRandomFactor)

	resp.SignalConfigs.MitigationConfig.HeartbeatInterval.CurrentValue = signalSessionConfiguration.HeartbeatInterval
	resp.SignalConfigs.MitigationConfig.MissingHbAllowed.CurrentValue  = signalSessionConfiguration.MissingHbAllowed
	resp.SignalConfigs.MitigationConfig.MaxRetransmit.CurrentValue     = signalSessionConfiguration.MaxRetransmit
	resp.SignalConfigs.MitigationConfig.AckTimeout.CurrentValue        = signalSessionConfiguration.AckTimeout
	resp.SignalConfigs.MitigationConfig.AckRandomFactor.CurrentValue   = decimal.NewFromFloat(signalSessionConfiguration.AckRandomFactor)
	resp.SignalConfigs.MitigationConfig.TriggerMitigation              = signalSessionConfiguration.TriggerMitigation

	// TODO: support Idle-Config
	res = Response{
			Type: common.NonConfirmable,
			Code: common.Content,
			Body: resp,
	}

	return
}

/*
 * Handles session_configuration PUT requests and start the mitigation.
 *  1. Validate the received session configuration requests.
 *  2. return the validation results.
 *
 * parameter:
 *  request request message
 *  customer request source Customer
 * return:
 *  res response message
 *  err error
 */
func (m *SessionConfiguration) HandlePut(newRequest Request, customer *models.Customer) (res Response, err error) {

	request := newRequest.Body

	if request == nil {
		res = Response{
			Type: common.NonConfirmable,
			Code: common.BadRequest,
			Body: nil,
		}
		return
	}

	payload := &request.(*messages.SignalConfigRequest).SignalConfigs.MitigationConfig
	sessionConfigurationPayloadDisplay(payload)
	// TODO: support IdleConfig, draft-17+

	ackRandomFactor, _ := payload.AckRandomFactor.CurrentValue.Float64()
	// validate
	signalSessionConfiguration := models.NewSignalSessionConfiguration(
		payload.SessionId,
		payload.HeartbeatInterval.CurrentValue,
		payload.MissingHbAllowed.CurrentValue,
		payload.MaxRetransmit.CurrentValue,
		payload.AckTimeout.CurrentValue,
		ackRandomFactor,
		payload.TriggerMitigation,
	)
	v := models.SignalConfigurationValidator{}
	validateResult := v.Validate(signalSessionConfiguration, *customer)
	if !validateResult {
		goto ResponseNG
	} else {
		// Register SignalConfigurationParameter
		_, err = models.CreateSignalSessionConfiguration(*signalSessionConfiguration, *customer)
		if err != nil {
			goto ResponseNG
		}

		goto ResponseOK
	}

ResponseNG:
// on validation error
	res = Response{
		Type: common.NonConfirmable,
		Code: common.BadRequest,
		Body: nil,
	}
	return
ResponseOK:
// on validation success
	res = Response{
		Type: common.NonConfirmable,
		Code: common.Created,
		Body: nil,
	}
	return
}

func (m *SessionConfiguration) HandleDelete(newRequest Request, customer *models.Customer) (res Response, err error) {
	err = models.DeleteSignalSessionConfigurationByCustomerId(customer.Id)
	if err != nil {
		res = Response{
			Type: common.NonConfirmable,
			Code: common.InternalServerError,
			Body: nil,
		}
		return
	}

	res = Response{
		Type: common.NonConfirmable,
		Code: common.Deleted,
		Body: nil,
	}
	return
}

/*
 * Parse the request body and display the contents of the messages to stdout.
*/
func sessionConfigurationPayloadDisplay(data *messages.SignalConfig) {

	var result string = "\n"
	result += fmt.Sprintf("   \"%s\": %d\n", "session-id", data.SessionId)
	result += fmt.Sprintf("   \"%s\": %d\n", "heartbeat-interval", data.HeartbeatInterval)
	result += fmt.Sprintf("   \"%s\": %d\n", "missing-hb-allowed", data.MissingHbAllowed)
	result += fmt.Sprintf("   \"%s\": %d\n", "max-retransmit", data.MaxRetransmit)
	result += fmt.Sprintf("   \"%s\": %d\n", "ack-timeout", data.AckTimeout)
	result += fmt.Sprintf("   \"%s\": %f\n", "ack-random-factor", data.AckRandomFactor)
	result += fmt.Sprintf("   \"%s\": %f\n", "trigger-mitigation", data.TriggerMitigation)
	log.Infoln(result)
}
<commit_msg>Fix response of SessionConfig to CoAP Ack<commit_after>package controllers

import (
	"fmt"
	"github.com/shopspring/decimal"
	log "github.com/sirupsen/logrus"

	common "github.com/nttdots/go-dots/dots_common"
	"github.com/nttdots/go-dots/dots_common/messages"
	"github.com/nttdots/go-dots/dots_server/models"
	dots_config "github.com/nttdots/go-dots/dots_server/config"
)

/*
 * Controller for the session_configuration API.
 */
type SessionConfiguration struct {
	Controller
}

func (m *SessionConfiguration) HandleGet(request Request, customer *models.Customer) (res Response, err error) {
        log.Debugf("[GET]SessionConfig customer.Id=%+v", customer.Id)
	signalSessionConfiguration, err := models.GetCurrentSignalSessionConfiguration(customer.Id)
	if err != nil {
		res = Response{
			Type: common.Acknowledgement,
			Code: common.BadRequest,
			Body: nil,
		}
		return
	}

	// TODO: check found or not

	config := dots_config.GetServerSystemConfig().SignalConfigurationParameter

	resp := messages.ConfigurationResponse{}
	resp.SignalConfigs = messages.ConfigurationResponseConfigs{}
	resp.SignalConfigs.MitigationConfig = messages.ConfigurationResponseConfig{}
	resp.SignalConfigs.MitigationConfig.HeartbeatInterval.SetMinMax(config.HeartbeatInterval)
	resp.SignalConfigs.MitigationConfig.MissingHbAllowed.SetMinMax(config.MissingHbAllowed)
	resp.SignalConfigs.MitigationConfig.MaxRetransmit.SetMinMax(config.MaxRetransmit)
	resp.SignalConfigs.MitigationConfig.AckTimeout.SetMinMax(config.AckTimeout)
	resp.SignalConfigs.MitigationConfig.AckRandomFactor.SetMinMax(config.AckRandomFactor)

	resp.SignalConfigs.MitigationConfig.HeartbeatInterval.CurrentValue = signalSessionConfiguration.HeartbeatInterval
	resp.SignalConfigs.MitigationConfig.MissingHbAllowed.CurrentValue  = signalSessionConfiguration.MissingHbAllowed
	resp.SignalConfigs.MitigationConfig.MaxRetransmit.CurrentValue     = signalSessionConfiguration.MaxRetransmit
	resp.SignalConfigs.MitigationConfig.AckTimeout.CurrentValue        = signalSessionConfiguration.AckTimeout
	resp.SignalConfigs.MitigationConfig.AckRandomFactor.CurrentValue   = decimal.NewFromFloat(signalSessionConfiguration.AckRandomFactor)
	resp.SignalConfigs.MitigationConfig.TriggerMitigation              = signalSessionConfiguration.TriggerMitigation

	// TODO: support Idle-Config
	res = Response{
			Type: common.Acknowledgement,
			Code: common.Content,
			Body: resp,
	}

	return
}

/*
 * Handles session_configuration PUT requests and start the mitigation.
 *  1. Validate the received session configuration requests.
 *  2. return the validation results.
 *
 * parameter:
 *  request request message
 *  customer request source Customer
 * return:
 *  res response message
 *  err error
 */
func (m *SessionConfiguration) HandlePut(newRequest Request, customer *models.Customer) (res Response, err error) {

	request := newRequest.Body

	if request == nil {
		res = Response{
			Type: common.Acknowledgement,
			Code: common.BadRequest,
			Body: nil,
		}
		return
	}

	payload := &request.(*messages.SignalConfigRequest).SignalConfigs.MitigationConfig
	sessionConfigurationPayloadDisplay(payload)
	// TODO: support IdleConfig, draft-17+

	ackRandomFactor, _ := payload.AckRandomFactor.CurrentValue.Float64()
	// validate
	signalSessionConfiguration := models.NewSignalSessionConfiguration(
		payload.SessionId,
		payload.HeartbeatInterval.CurrentValue,
		payload.MissingHbAllowed.CurrentValue,
		payload.MaxRetransmit.CurrentValue,
		payload.AckTimeout.CurrentValue,
		ackRandomFactor,
		payload.TriggerMitigation,
	)
	v := models.SignalConfigurationValidator{}
	validateResult := v.Validate(signalSessionConfiguration, *customer)
	if !validateResult {
		goto ResponseNG
	} else {
		// Register SignalConfigurationParameter
		_, err = models.CreateSignalSessionConfiguration(*signalSessionConfiguration, *customer)
		if err != nil {
			goto ResponseNG
		}

		goto ResponseOK
	}

ResponseNG:
// on validation error
	res = Response{
		Type: common.Acknowledgement,
		Code: common.BadRequest,
		Body: nil,
	}
	return
ResponseOK:
// on validation success
	res = Response{
		Type: common.Acknowledgement,
		Code: common.Created,
		Body: nil,
	}
	return
}

func (m *SessionConfiguration) HandleDelete(newRequest Request, customer *models.Customer) (res Response, err error) {
	err = models.DeleteSignalSessionConfigurationByCustomerId(customer.Id)
	if err != nil {
		res = Response{
			Type: common.Acknowledgement,
			Code: common.InternalServerError,
			Body: nil,
		}
		return
	}

	res = Response{
		Type: common.Acknowledgement,
		Code: common.Deleted,
		Body: nil,
	}
	return
}

/*
 * Parse the request body and display the contents of the messages to stdout.
*/
func sessionConfigurationPayloadDisplay(data *messages.SignalConfig) {

	var result string = "\n"
	result += fmt.Sprintf("   \"%s\": %d\n", "session-id", data.SessionId)
	result += fmt.Sprintf("   \"%s\": %d\n", "heartbeat-interval", data.HeartbeatInterval)
	result += fmt.Sprintf("   \"%s\": %d\n", "missing-hb-allowed", data.MissingHbAllowed)
	result += fmt.Sprintf("   \"%s\": %d\n", "max-retransmit", data.MaxRetransmit)
	result += fmt.Sprintf("   \"%s\": %d\n", "ack-timeout", data.AckTimeout)
	result += fmt.Sprintf("   \"%s\": %f\n", "ack-random-factor", data.AckRandomFactor)
	result += fmt.Sprintf("   \"%s\": %f\n", "trigger-mitigation", data.TriggerMitigation)
	log.Infoln(result)
}
<|endoftext|>


<commit_before>package dsl_test

import (
	"testing"

	"fmt"

	"github.com/goadesign/goa/design"
	. "github.com/goadesign/goa/design/dsl"
	"github.com/goadesign/goa/eval"
)

func TestEndpoint(t *testing.T) {
	cases := map[string]struct {
		Expr   eval.Expression
		DSL    func()
		Assert func(testName string, t *testing.T, s *design.ServiceExpr)
	}{
		"basic endpoint": {
			&design.ServiceExpr{},
			func() {
				Endpoint("basic", func() {
					Description("basic endpoint")
				})
			},
			func(testName string, t *testing.T, s *design.ServiceExpr) {
				if len(s.Endpoints) != 1 {
					t.Errorf("%s: expected %d endpoints but got %d", testName, 1, len(s.Endpoints))
				}
				for _, e := range s.Endpoints {
					if err := assertEndpointDescription("basic endpoint", e.Description); err != nil {
						t.Errorf("%s assert failed %s ", testName, err.Error())
					}
				}
			},
		},
		"basic endpoint with docs": {
			&design.ServiceExpr{},
			func() {
				Endpoint("basic", func() {
					Description("basic endpoint")
					Docs(func() {
						URL("http://example.com")
						Description("some docs")
					})
				})
			},
			func(testName string, t *testing.T, s *design.ServiceExpr) {
				if len(s.Endpoints) != 1 {
					t.Errorf("%s: expected %d endpoints but got %d", testName, 1, len(s.Endpoints))
				}
				for _, e := range s.Endpoints {
					if err := assertEndpointDescription("basic endpoint", e.Description); err != nil {
						t.Errorf("%s assert failed %s ", testName, err.Error())
					}
					if err := assertEndpointDocs(e.Docs, "http://example.com", "some docs"); err != nil {
						t.Errorf("%s assert failed %s ", testName, err.Error())
					}
				}
			},
		},
	}

	for k, tc := range cases {
		t.Run(k, func(t *testing.T) {
			eval.Context = &eval.DSLContext{}
			eval.Execute(tc.DSL, tc.Expr)
			//After evaling the service our endpoints are present but need to also need to have thier DSL func exectuted.
			evalService := tc.Expr.(*design.ServiceExpr)
			for _, endpointExp := range evalService.Endpoints {
				eval.Execute(endpointExp.DSLFunc, endpointExp)
			}
			if eval.Context.Errors != nil {
				t.Errorf("%s: Endpoint failed unexpectedly with %s", k, eval.Context.Errors)
			}
			if tc.Assert != nil {
				tc.Assert(k, t, evalService)
			}
		})
	}
}

//helper funcs
func assertEndpointDocs(doc *design.DocsExpr, url, desc string) error {
	if doc.Description != desc {
		return fmt.Errorf("expected docs description '%s' to match '%s' ", desc, doc.Description)
	}
	if doc.URL != url {
		return fmt.Errorf("expected docs url '%s' to match '%s' ", url, doc.URL)
	}
	return nil
}

func assertEndpointDescription(expectedDesc, actualDesc string) error {
	if expectedDesc != actualDesc {
		return fmt.Errorf("expected description '%s' to match '%s' ", actualDesc, expectedDesc)
	}
	return nil
}
<commit_msg>another pass at better structure for tests<commit_after>package dsl_test

import (
	"testing"

	"reflect"

	"github.com/goadesign/goa/design"
	. "github.com/goadesign/goa/design/dsl"
	"github.com/goadesign/goa/eval"
)

func TestEndpoint(t *testing.T) {
	cases := map[string]struct {
		Expr   eval.Expression
		DSL    func()
		Assert map[string]func(t *testing.T, s *design.EndpointExpr)
	}{
		"basic": {
			&design.ServiceExpr{},
			func() {
				Endpoint("basic", func() {
					Description("Optional description")

					// Docs allows linking to external documentation.
					Docs(func() {
						Description("Optional description")
						URL("https://goa.design")
					})
					Request(func() {
						Description("Optional description")
						Attribute("required", design.String)
						Required("required")
					})
					Response(func() {
						Description("Optional description")
						Attribute("required", design.String)
						Required("required")
					})
					Error("basic_error")
					Error("basic_media_error", design.ErrorMedia)
					Metadata("name", "some value", "some other value")
				})
			},
			map[string]func(t *testing.T, s *design.EndpointExpr){
				"basic": func(t *testing.T, e *design.EndpointExpr) {
					assertEndpointDescription(t, "Optional description", e.Description)
					assertEndpointDocs(t, e.Docs, "https://goa.design", "Optional description")
					if len(e.Errors) != 2 {
						t.Errorf("expected %d error definitions but got %d ", 1, len(e.Errors))
					}
					assertEndpointError(t, e.Errors[0], "basic_error", design.ErrorMedia)
					assertEndpointError(t, e.Errors[1], "basic_media_error", design.ErrorMedia)
					expectedMeta := design.MetadataExpr{
						"name": []string{"some value", "some other value"},
					}
					assertEndpointMetaData(t, e.Metadata, expectedMeta)
					expectedReq := &design.UserTypeExpr{TypeName: "BasicRequest"}
					assertEndpointRequestResponse(t, "Request", e.Request, expectedReq)
					expectedRes := &design.UserTypeExpr{TypeName: "BasicResponse"}
					assertEndpointRequestResponse(t, "Response", e.Response, expectedRes)
				},
			},
		},
	}
	//Run our tests
	for k, tc := range cases {
		t.Run(k, func(t *testing.T) {
			eval.Context = &eval.DSLContext{}
			eval.Execute(tc.DSL, tc.Expr)
			evalService := tc.Expr.(*design.ServiceExpr)
			if eval.Context.Errors != nil {
				t.Errorf("%s: Endpoint failed unexpectedly with %s", k, eval.Context.Errors)
			}
			for _, e := range evalService.Endpoints {
				if _, ok := tc.Assert[e.Name]; !ok {
					t.Errorf("no assert found for endpoint %s ", e.Name)
				}
				tc.Assert[e.Name](t, e)
			}
		})
	}
}

//helper funcs
func assertEndpointDocs(t *testing.T, doc *design.DocsExpr, url, desc string) {
	if doc.Description != desc {
		t.Errorf("expected docs description '%s' to match '%s' ", desc, doc.Description)
	}
	if doc.URL != url {
		t.Errorf("expected docs url '%s' to match '%s' ", url, doc.URL)
	}
}

func assertEndpointDescription(t *testing.T, expectedDesc, actualDesc string) {
	if expectedDesc != actualDesc {
		t.Errorf("expected description '%s' to match '%s' ", actualDesc, expectedDesc)
	}
}

func assertEndpointError(t *testing.T, actual *design.ErrorExpr, name string, dt design.DataType) {
	if actual.Name != name {
		t.Errorf("expected error to have name %s but got %s ", name, actual.Name)
	}

	if actual.AttributeExpr.Type != dt {
		t.Errorf("expected the error DataType to be %v but got %v ", dt, actual.AttributeExpr.Type)
	}
}

func assertEndpointMetaData(t *testing.T, actual design.MetadataExpr, expected design.MetadataExpr) {
	for key, val := range actual {
		vals, ok := expected[key]
		if !ok {
			t.Errorf("metaData was missing expected key %s ", key)
			continue
		}
		for _, metaVal := range val {
			if !hasValue(vals, metaVal) {
				t.Errorf("metaData was missing expected value %s ", metaVal)
			}
		}

	}
}

func assertEndpointRequestResponse(t *testing.T, assertType string, actual design.DataType, expected *design.UserTypeExpr) {
	ut, ok := actual.(*design.UserTypeExpr)
	if !ok {
		t.Errorf("expected endpoint %s to be a *UserTypeExpr but got %v", assertType, reflect.TypeOf(ut))
	}
	if ut.Name() != expected.Name() {
		t.Errorf("expected endpoint %s name %s to match %s", assertType, ut.Name(), expected.Name())
	}

}
<|endoftext|>


<commit_before>// +build !appengine

package aetest

import (
	"bufio"
	"crypto/rand"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"time"

	"golang.org/x/net/context"
	"google.golang.org/appengine/internal"
)

// NewInstance launches a running instance of api_server.py which can be used
// for multiple test Contexts that delegate all App Engine API calls to that
// instance.
// If opts is nil the default values are used.
func NewInstance(opts *Options) (Instance, error) {
	i := &instance{
		opts:           opts,
		appID:          "testapp",
		startupTimeout: 15 * time.Second,
	}
	if opts != nil {
		if opts.AppID != "" {
			i.appID = opts.AppID
		}
		if opts.StartupTimeout > 0 {
			i.startupTimeout = opts.StartupTimeout
		}
	}
	if err := i.startChild(); err != nil {
		return nil, err
	}
	return i, nil
}

func newSessionID() string {
	var buf [16]byte
	io.ReadFull(rand.Reader, buf[:])
	return fmt.Sprintf("%x", buf[:])
}

// instance implements the Instance interface.
type instance struct {
	opts           *Options
	child          *exec.Cmd
	apiURL         *url.URL // base URL of API HTTP server
	adminURL       string   // base URL of admin HTTP server
	appDir         string
	appID          string
	startupTimeout time.Duration
	relFuncs       []func() // funcs to release any associated contexts
}

// NewRequest returns an *http.Request associated with this instance.
func (i *instance) NewRequest(method, urlStr string, body io.Reader) (*http.Request, error) {
	req, err := http.NewRequest(method, urlStr, body)
	if err != nil {
		return nil, err
	}

	// Associate this request.
	req, release := internal.RegisterTestRequest(req, i.apiURL, func(ctx context.Context) context.Context {
		ctx = internal.WithAppIDOverride(ctx, "dev~"+i.appID)
		return ctx
	})
	i.relFuncs = append(i.relFuncs, release)

	return req, nil
}

// Close kills the child api_server.py process, releasing its resources.
func (i *instance) Close() (err error) {
	for _, rel := range i.relFuncs {
		rel()
	}
	i.relFuncs = nil
	child := i.child
	if child == nil {
		return nil
	}
	defer func() {
		i.child = nil
		err1 := os.RemoveAll(i.appDir)
		if err == nil {
			err = err1
		}
	}()

	if p := child.Process; p != nil {
		errc := make(chan error, 1)
		go func() {
			errc <- child.Wait()
		}()

		// Call the quit handler on the admin server.
		res, err := http.Get(i.adminURL + "/quit")
		if err != nil {
			p.Kill()
			return fmt.Errorf("unable to call /quit handler: %v", err)
		}
		res.Body.Close()
		select {
		case <-time.After(15 * time.Second):
			p.Kill()
			return errors.New("timeout killing child process")
		case err = <-errc:
			// Do nothing.
		}
	}
	return
}

func fileExists(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}

func findPython() (path string, err error) {
	for _, name := range []string{"python2.7", "python"} {
		path, err = exec.LookPath(name)
		if err == nil {
			return
		}
	}
	return
}

func findDevAppserver() (string, error) {
	if p := os.Getenv("APPENGINE_DEV_APPSERVER"); p != "" {
		if fileExists(p) {
			return p, nil
		}
		return "", fmt.Errorf("invalid APPENGINE_DEV_APPSERVER environment variable; path %q doesn't exist", p)
	}
	return exec.LookPath("dev_appserver.py")
}

var apiServerAddrRE = regexp.MustCompile(`Starting API server at: (\S+)`)
var adminServerAddrRE = regexp.MustCompile(`Starting admin server at: (\S+)`)

func (i *instance) startChild() (err error) {
	if PrepareDevAppserver != nil {
		if err := PrepareDevAppserver(); err != nil {
			return err
		}
	}
	python, err := findPython()
	if err != nil {
		return fmt.Errorf("Could not find python interpreter: %v", err)
	}
	devAppserver, err := findDevAppserver()
	if err != nil {
		return fmt.Errorf("Could not find dev_appserver.py: %v", err)
	}

	i.appDir, err = ioutil.TempDir("", "appengine-aetest")
	if err != nil {
		return err
	}
	defer func() {
		if err != nil {
			os.RemoveAll(i.appDir)
		}
	}()
	err = os.Mkdir(filepath.Join(i.appDir, "app"), 0755)
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(filepath.Join(i.appDir, "app", "app.yaml"), []byte(i.appYAML()), 0644)
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(filepath.Join(i.appDir, "app", "stubapp.go"), []byte(appSource), 0644)
	if err != nil {
		return err
	}

	appserverArgs := []string{
		devAppserver,
		"--port=0",
		"--api_port=0",
		"--admin_port=0",
		"--automatic_restart=false",
		"--skip_sdk_update_check=true",
		"--clear_datastore=true",
		"--clear_search_indexes=true",
		"--datastore_path", filepath.Join(i.appDir, "datastore"),
	}
	if i.opts != nil && i.opts.StronglyConsistentDatastore {
		appserverArgs = append(appserverArgs, "--datastore_consistency_policy=consistent")
	}
	if i.opts != nil && i.opts.SupportDatastoreEmulator != nil {
		appserverArgs = append(appserverArgs, fmt.Sprintf("--support_datastore_emulator=%t", *i.opts.SupportDatastoreEmulator))
	}
	appserverArgs = append(appserverArgs, filepath.Join(i.appDir, "app"))

	i.child = exec.Command(python,
		appserverArgs...,
	)
	i.child.Stdout = os.Stdout
	var stderr io.Reader
	stderr, err = i.child.StderrPipe()
	if err != nil {
		return err
	}

	if err = i.child.Start(); err != nil {
		return err
	}

	// Read stderr until we have read the URLs of the API server and admin interface.
	errc := make(chan error, 1)
	go func() {
		s := bufio.NewScanner(stderr)
		for s.Scan() {
			// Pass stderr along as we go so the user can see it.
			if !(i.opts != nil && i.opts.SuppressDevAppServerLog) {
				fmt.Fprintln(os.Stderr, s.Text())
			}
			if match := apiServerAddrRE.FindStringSubmatch(s.Text()); match != nil {
				u, err := url.Parse(match[1])
				if err != nil {
					errc <- fmt.Errorf("failed to parse API URL %q: %v", match[1], err)
					return
				}
				i.apiURL = u
			}
			if match := adminServerAddrRE.FindStringSubmatch(s.Text()); match != nil {
				i.adminURL = match[1]
			}
			if i.adminURL != "" && i.apiURL != nil {
				// Pass along stderr to the user after we're done with it.
				if !(i.opts != nil && i.opts.SuppressDevAppServerLog) {
					go io.Copy(os.Stderr, stderr)
				}
				break
			}
		}
		errc <- s.Err()
	}()

	select {
	case <-time.After(i.startupTimeout):
		if p := i.child.Process; p != nil {
			p.Kill()
		}
		return errors.New("timeout starting child process")
	case err := <-errc:
		if err != nil {
			return fmt.Errorf("error reading child process stderr: %v", err)
		}
	}
	if i.adminURL == "" {
		return errors.New("unable to find admin server URL")
	}
	if i.apiURL == nil {
		return errors.New("unable to find API server URL")
	}
	return nil
}

func (i *instance) appYAML() string {
	return fmt.Sprintf(appYAMLTemplate, i.appID)
}

const appYAMLTemplate = `
application: %s
version: 1
runtime: go
api_version: go1

handlers:
- url: /.*
  script: _go_app
`

const appSource = `
package main
import "google.golang.org/appengine"
func main() { appengine.Main() }
`
<commit_msg>aetest: appYAMLTemplate should use runtime: go111 (#214)<commit_after>// +build !appengine

package aetest

import (
	"bufio"
	"crypto/rand"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"time"

	"golang.org/x/net/context"
	"google.golang.org/appengine/internal"
)

// NewInstance launches a running instance of api_server.py which can be used
// for multiple test Contexts that delegate all App Engine API calls to that
// instance.
// If opts is nil the default values are used.
func NewInstance(opts *Options) (Instance, error) {
	i := &instance{
		opts:           opts,
		appID:          "testapp",
		startupTimeout: 15 * time.Second,
	}
	if opts != nil {
		if opts.AppID != "" {
			i.appID = opts.AppID
		}
		if opts.StartupTimeout > 0 {
			i.startupTimeout = opts.StartupTimeout
		}
	}
	if err := i.startChild(); err != nil {
		return nil, err
	}
	return i, nil
}

func newSessionID() string {
	var buf [16]byte
	io.ReadFull(rand.Reader, buf[:])
	return fmt.Sprintf("%x", buf[:])
}

// instance implements the Instance interface.
type instance struct {
	opts           *Options
	child          *exec.Cmd
	apiURL         *url.URL // base URL of API HTTP server
	adminURL       string   // base URL of admin HTTP server
	appDir         string
	appID          string
	startupTimeout time.Duration
	relFuncs       []func() // funcs to release any associated contexts
}

// NewRequest returns an *http.Request associated with this instance.
func (i *instance) NewRequest(method, urlStr string, body io.Reader) (*http.Request, error) {
	req, err := http.NewRequest(method, urlStr, body)
	if err != nil {
		return nil, err
	}

	// Associate this request.
	req, release := internal.RegisterTestRequest(req, i.apiURL, func(ctx context.Context) context.Context {
		ctx = internal.WithAppIDOverride(ctx, "dev~"+i.appID)
		return ctx
	})
	i.relFuncs = append(i.relFuncs, release)

	return req, nil
}

// Close kills the child api_server.py process, releasing its resources.
func (i *instance) Close() (err error) {
	for _, rel := range i.relFuncs {
		rel()
	}
	i.relFuncs = nil
	child := i.child
	if child == nil {
		return nil
	}
	defer func() {
		i.child = nil
		err1 := os.RemoveAll(i.appDir)
		if err == nil {
			err = err1
		}
	}()

	if p := child.Process; p != nil {
		errc := make(chan error, 1)
		go func() {
			errc <- child.Wait()
		}()

		// Call the quit handler on the admin server.
		res, err := http.Get(i.adminURL + "/quit")
		if err != nil {
			p.Kill()
			return fmt.Errorf("unable to call /quit handler: %v", err)
		}
		res.Body.Close()
		select {
		case <-time.After(15 * time.Second):
			p.Kill()
			return errors.New("timeout killing child process")
		case err = <-errc:
			// Do nothing.
		}
	}
	return
}

func fileExists(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}

func findPython() (path string, err error) {
	for _, name := range []string{"python2.7", "python"} {
		path, err = exec.LookPath(name)
		if err == nil {
			return
		}
	}
	return
}

func findDevAppserver() (string, error) {
	if p := os.Getenv("APPENGINE_DEV_APPSERVER"); p != "" {
		if fileExists(p) {
			return p, nil
		}
		return "", fmt.Errorf("invalid APPENGINE_DEV_APPSERVER environment variable; path %q doesn't exist", p)
	}
	return exec.LookPath("dev_appserver.py")
}

var apiServerAddrRE = regexp.MustCompile(`Starting API server at: (\S+)`)
var adminServerAddrRE = regexp.MustCompile(`Starting admin server at: (\S+)`)

func (i *instance) startChild() (err error) {
	if PrepareDevAppserver != nil {
		if err := PrepareDevAppserver(); err != nil {
			return err
		}
	}
	python, err := findPython()
	if err != nil {
		return fmt.Errorf("Could not find python interpreter: %v", err)
	}
	devAppserver, err := findDevAppserver()
	if err != nil {
		return fmt.Errorf("Could not find dev_appserver.py: %v", err)
	}

	i.appDir, err = ioutil.TempDir("", "appengine-aetest")
	if err != nil {
		return err
	}
	defer func() {
		if err != nil {
			os.RemoveAll(i.appDir)
		}
	}()
	err = os.Mkdir(filepath.Join(i.appDir, "app"), 0755)
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(filepath.Join(i.appDir, "app", "app.yaml"), []byte(i.appYAML()), 0644)
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(filepath.Join(i.appDir, "app", "stubapp.go"), []byte(appSource), 0644)
	if err != nil {
		return err
	}

	appserverArgs := []string{
		devAppserver,
		"--port=0",
		"--api_port=0",
		"--admin_port=0",
		"--automatic_restart=false",
		"--skip_sdk_update_check=true",
		"--clear_datastore=true",
		"--clear_search_indexes=true",
		"--datastore_path", filepath.Join(i.appDir, "datastore"),
	}
	if i.opts != nil && i.opts.StronglyConsistentDatastore {
		appserverArgs = append(appserverArgs, "--datastore_consistency_policy=consistent")
	}
	if i.opts != nil && i.opts.SupportDatastoreEmulator != nil {
		appserverArgs = append(appserverArgs, fmt.Sprintf("--support_datastore_emulator=%t", *i.opts.SupportDatastoreEmulator))
	}
	appserverArgs = append(appserverArgs, filepath.Join(i.appDir, "app"))

	i.child = exec.Command(python,
		appserverArgs...,
	)
	i.child.Stdout = os.Stdout
	var stderr io.Reader
	stderr, err = i.child.StderrPipe()
	if err != nil {
		return err
	}

	if err = i.child.Start(); err != nil {
		return err
	}

	// Read stderr until we have read the URLs of the API server and admin interface.
	errc := make(chan error, 1)
	go func() {
		s := bufio.NewScanner(stderr)
		for s.Scan() {
			// Pass stderr along as we go so the user can see it.
			if !(i.opts != nil && i.opts.SuppressDevAppServerLog) {
				fmt.Fprintln(os.Stderr, s.Text())
			}
			if match := apiServerAddrRE.FindStringSubmatch(s.Text()); match != nil {
				u, err := url.Parse(match[1])
				if err != nil {
					errc <- fmt.Errorf("failed to parse API URL %q: %v", match[1], err)
					return
				}
				i.apiURL = u
			}
			if match := adminServerAddrRE.FindStringSubmatch(s.Text()); match != nil {
				i.adminURL = match[1]
			}
			if i.adminURL != "" && i.apiURL != nil {
				// Pass along stderr to the user after we're done with it.
				if !(i.opts != nil && i.opts.SuppressDevAppServerLog) {
					go io.Copy(os.Stderr, stderr)
				}
				break
			}
		}
		errc <- s.Err()
	}()

	select {
	case <-time.After(i.startupTimeout):
		if p := i.child.Process; p != nil {
			p.Kill()
		}
		return errors.New("timeout starting child process")
	case err := <-errc:
		if err != nil {
			return fmt.Errorf("error reading child process stderr: %v", err)
		}
	}
	if i.adminURL == "" {
		return errors.New("unable to find admin server URL")
	}
	if i.apiURL == nil {
		return errors.New("unable to find API server URL")
	}
	return nil
}

func (i *instance) appYAML() string {
	return fmt.Sprintf(appYAMLTemplate, i.appID)
}

const appYAMLTemplate = `
application: %s
version: 1
runtime: go111

handlers:
- url: /.*
  script: _go_app
`

const appSource = `
package main
import "google.golang.org/appengine"
func main() { appengine.Main() }
`
<|endoftext|>


<commit_before>/*
 * Copyright 2018 The Kythe Authors. 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 span implements utilities to resolve byte offsets within a file to
// line and column numbers.
package span // import "kythe.io/kythe/go/util/span"

import (
	"bytes"
	"errors"
	"fmt"
	"log"
	"sort"

	"github.com/sergi/go-diff/diffmatchpatch"

	cpb "kythe.io/kythe/proto/common_go_proto"
	xpb "kythe.io/kythe/proto/xref_go_proto"
)

// InBounds reports whether [start,end) is bounded by the specified [startBoundary,endBoundary) span.
func InBounds(kind xpb.DecorationsRequest_SpanKind, start, end, startBoundary, endBoundary int32) bool {
	switch kind {
	case xpb.DecorationsRequest_WITHIN_SPAN:
		return start >= startBoundary && end <= endBoundary
	case xpb.DecorationsRequest_AROUND_SPAN:
		return start <= startBoundary && end >= endBoundary
	default:
		log.Printf("WARNING: unknown DecorationsRequest_SpanKind: %v", kind)
	}
	return false
}

// Patcher uses a computed diff between two texts to map spans from the original
// text to the new text.
type Patcher struct {
	dmp  *diffmatchpatch.DiffMatchPatch
	diff []diffmatchpatch.Diff
}

// NewPatcher returns a Patcher based on the diff between oldText and newText.
func NewPatcher(oldText, newText []byte) (p *Patcher, err error) {
	defer func() {
		// dmp may panic on some large requests; catch it and return an error instead
		if r := recover(); r != nil {
			err = fmt.Errorf("diffmatchpatch panic: %v", r)
		}
	}()
	dmp := diffmatchpatch.New()
	return &Patcher{dmp, dmp.DiffCleanupEfficiency(dmp.DiffMain(string(oldText), string(newText), true))}, nil
}

// Patch returns the resulting span of mapping the given span from the Patcher's
// constructed oldText to its newText.  If the span no longer exists in newText
// or is invalid, the returned bool will be false.  As a convenience, if p==nil,
// the original span will be returned.
func (p *Patcher) Patch(spanStart, spanEnd int32) (newStart, newEnd int32, exists bool) {
	if spanStart > spanEnd {
		return 0, 0, false
	} else if p == nil {
		return spanStart, spanEnd, true
	}

	var old, new int32
	for _, d := range p.diff {
		l := int32(len(d.Text))
		if old > spanStart {
			return 0, 0, false
		}
		switch d.Type {
		case diffmatchpatch.DiffEqual:
			if old <= spanStart && spanEnd <= old+l {
				newStart = new + (spanStart - old)
				newEnd = new + (spanEnd - old)
				exists = true
				return
			}
			old += l
			new += l
		case diffmatchpatch.DiffDelete:
			old += l
		case diffmatchpatch.DiffInsert:
			new += l
		}
	}

	return 0, 0, false
}

// PatchSpan returns the given Span's byte offsets mapped from the Patcher's
// oldText to its newText using Patcher.Patch.
func (p *Patcher) PatchSpan(span *cpb.Span) (newStart, newEnd int32, exists bool) {
	return p.Patch(span.GetStart().GetByteOffset(), span.GetEnd().GetByteOffset())
}

// Normalizer fixes xref.Locations within a given source text so that each point
// has consistent byte_offset, line_number, and column_offset fields within the
// range of text's length and its line lengths.
type Normalizer struct {
	textLen   int32
	lineLen   []int32
	prefixLen []int32
}

// NewNormalizer returns a Normalizer for Locations within text.
func NewNormalizer(text []byte) *Normalizer {
	lines := bytes.Split(text, lineEnd)
	lineLen := make([]int32, len(lines))
	prefixLen := make([]int32, len(lines))
	for i := 1; i < len(lines); i++ {
		lineLen[i-1] = int32(len(lines[i-1]) + len(lineEnd))
		prefixLen[i] = prefixLen[i-1] + lineLen[i-1]
	}
	lineLen[len(lines)-1] = int32(len(lines[len(lines)-1]) + len(lineEnd))
	return &Normalizer{int32(len(text)), lineLen, prefixLen}
}

// Location returns a normalized location within the Normalizer's text.
// Normalized FILE locations have no start/end points.  Normalized SPAN
// locations have fully populated start/end points clamped in the range [0,
// len(text)).
func (n *Normalizer) Location(loc *xpb.Location) (*xpb.Location, error) {
	nl := &xpb.Location{}
	if loc == nil {
		return nl, nil
	}
	nl.Ticket = loc.Ticket
	nl.Kind = loc.Kind
	if loc.Kind == xpb.Location_FILE {
		return nl, nil
	}

	if loc.Span == nil {
		return nil, errors.New("invalid SPAN: missing span")
	} else if loc.Span.Start == nil {
		return nil, errors.New("invalid SPAN: missing span start point")
	} else if loc.Span.End == nil {
		return nil, errors.New("invalid SPAN: missing span end point")
	}

	nl.Span = n.Span(loc.Span)

	start, end := nl.Span.Start.ByteOffset, nl.Span.End.ByteOffset
	if start > end {
		return nil, fmt.Errorf("invalid SPAN: start (%d) is after end (%d)", start, end)
	}
	return nl, nil
}

// Span returns a Span with its start and end normalized.
func (n *Normalizer) Span(s *cpb.Span) *cpb.Span {
	if s == nil {
		return nil
	}
	return &cpb.Span{
		Start: n.Point(s.Start),
		End:   n.Point(s.End),
	}
}

// SpanOffsets returns a Span based on normalized start and end byte offsets.
func (n *Normalizer) SpanOffsets(start, end int32) *cpb.Span {
	return &cpb.Span{
		Start: n.ByteOffset(start),
		End:   n.ByteOffset(end),
	}
}

var lineEnd = []byte("\n")

// Point returns a normalized point within the Normalizer's text.  A normalized
// point has all of its fields set consistently and clamped within the range
// [0,len(text)).
func (n *Normalizer) Point(p *cpb.Point) *cpb.Point {
	if p == nil {
		return nil
	}

	if p.ByteOffset > 0 {
		return n.ByteOffset(p.ByteOffset)
	} else if p.LineNumber > 0 {
		np := &cpb.Point{
			LineNumber:   p.LineNumber,
			ColumnOffset: p.ColumnOffset,
		}

		if totalLines := int32(len(n.lineLen)); p.LineNumber > totalLines {
			np.LineNumber = totalLines
			np.ColumnOffset = n.lineLen[np.LineNumber-1] - 1
		}
		if np.ColumnOffset < 0 {
			np.ColumnOffset = 0
		} else if np.ColumnOffset > 0 {
			if lineLen := n.lineLen[np.LineNumber-1] - 1; p.ColumnOffset > lineLen {
				np.ColumnOffset = lineLen
			}
		}

		np.ByteOffset = n.prefixLen[np.LineNumber-1] + np.ColumnOffset

		return np
	}

	return &cpb.Point{LineNumber: 1}
}

// ByteOffset returns a normalized point based on the given offset within the
// Normalizer's text.  A normalized point has all of its fields set consistently
// and clamped within the range [0,len(text)).
func (n *Normalizer) ByteOffset(offset int32) *cpb.Point {
	np := &cpb.Point{ByteOffset: offset}
	if np.ByteOffset > n.textLen {
		np.ByteOffset = n.textLen
	}

	np.LineNumber = int32(sort.Search(len(n.lineLen), func(i int) bool {
		return n.prefixLen[i] > np.ByteOffset
	}))
	np.ColumnOffset = np.ByteOffset - n.prefixLen[np.LineNumber-1]

	return np
}
<commit_msg>perf(serving): reduce patching to offsets; drop text (#5422)<commit_after>/*
 * Copyright 2018 The Kythe Authors. 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 span implements utilities to resolve byte offsets within a file to
// line and column numbers.
package span // import "kythe.io/kythe/go/util/span"

import (
	"bytes"
	"errors"
	"fmt"
	"log"
	"sort"

	"github.com/sergi/go-diff/diffmatchpatch"

	cpb "kythe.io/kythe/proto/common_go_proto"
	xpb "kythe.io/kythe/proto/xref_go_proto"
)

// InBounds reports whether [start,end) is bounded by the specified [startBoundary,endBoundary) span.
func InBounds(kind xpb.DecorationsRequest_SpanKind, start, end, startBoundary, endBoundary int32) bool {
	switch kind {
	case xpb.DecorationsRequest_WITHIN_SPAN:
		return start >= startBoundary && end <= endBoundary
	case xpb.DecorationsRequest_AROUND_SPAN:
		return start <= startBoundary && end >= endBoundary
	default:
		log.Printf("WARNING: unknown DecorationsRequest_SpanKind: %v", kind)
	}
	return false
}

// Patcher uses a computed diff between two texts to map spans from the original
// text to the new text.
type Patcher struct {
	spans []diff
}

// NewPatcher returns a Patcher based on the diff between oldText and newText.
func NewPatcher(oldText, newText []byte) (p *Patcher, err error) {
	defer func() {
		// dmp may panic on some large requests; catch it and return an error instead
		if r := recover(); r != nil {
			err = fmt.Errorf("diffmatchpatch panic: %v", r)
		}
	}()
	dmp := diffmatchpatch.New()
	diff := dmp.DiffCleanupEfficiency(dmp.DiffMain(string(oldText), string(newText), true))
	return &Patcher{mapToOffsets(diff)}, nil
}

type diff struct {
	Length int32
	Type   diffmatchpatch.Operation
}

func mapToOffsets(ds []diffmatchpatch.Diff) []diff {
	res := make([]diff, len(ds))
	for i, d := range ds {
		res[i] = diff{Length: int32(len(d.Text)), Type: d.Type}
	}
	return res
}

// Patch returns the resulting span of mapping the given span from the Patcher's
// constructed oldText to its newText.  If the span no longer exists in newText
// or is invalid, the returned bool will be false.  As a convenience, if p==nil,
// the original span will be returned.
func (p *Patcher) Patch(spanStart, spanEnd int32) (newStart, newEnd int32, exists bool) {
	if spanStart > spanEnd {
		return 0, 0, false
	} else if p == nil {
		return spanStart, spanEnd, true
	}

	var old, new int32
	for _, d := range p.spans {
		l := d.Length
		if old > spanStart {
			return 0, 0, false
		}
		switch d.Type {
		case diffmatchpatch.DiffEqual:
			if old <= spanStart && spanEnd <= old+l {
				newStart = new + (spanStart - old)
				newEnd = new + (spanEnd - old)
				exists = true
				return
			}
			old += l
			new += l
		case diffmatchpatch.DiffDelete:
			old += l
		case diffmatchpatch.DiffInsert:
			new += l
		}
	}

	return 0, 0, false
}

// PatchSpan returns the given Span's byte offsets mapped from the Patcher's
// oldText to its newText using Patcher.Patch.
func (p *Patcher) PatchSpan(span *cpb.Span) (newStart, newEnd int32, exists bool) {
	return p.Patch(span.GetStart().GetByteOffset(), span.GetEnd().GetByteOffset())
}

// Normalizer fixes xref.Locations within a given source text so that each point
// has consistent byte_offset, line_number, and column_offset fields within the
// range of text's length and its line lengths.
type Normalizer struct {
	textLen   int32
	lineLen   []int32
	prefixLen []int32
}

// NewNormalizer returns a Normalizer for Locations within text.
func NewNormalizer(text []byte) *Normalizer {
	lines := bytes.Split(text, lineEnd)
	lineLen := make([]int32, len(lines))
	prefixLen := make([]int32, len(lines))
	for i := 1; i < len(lines); i++ {
		lineLen[i-1] = int32(len(lines[i-1]) + len(lineEnd))
		prefixLen[i] = prefixLen[i-1] + lineLen[i-1]
	}
	lineLen[len(lines)-1] = int32(len(lines[len(lines)-1]) + len(lineEnd))
	return &Normalizer{int32(len(text)), lineLen, prefixLen}
}

// Location returns a normalized location within the Normalizer's text.
// Normalized FILE locations have no start/end points.  Normalized SPAN
// locations have fully populated start/end points clamped in the range [0,
// len(text)).
func (n *Normalizer) Location(loc *xpb.Location) (*xpb.Location, error) {
	nl := &xpb.Location{}
	if loc == nil {
		return nl, nil
	}
	nl.Ticket = loc.Ticket
	nl.Kind = loc.Kind
	if loc.Kind == xpb.Location_FILE {
		return nl, nil
	}

	if loc.Span == nil {
		return nil, errors.New("invalid SPAN: missing span")
	} else if loc.Span.Start == nil {
		return nil, errors.New("invalid SPAN: missing span start point")
	} else if loc.Span.End == nil {
		return nil, errors.New("invalid SPAN: missing span end point")
	}

	nl.Span = n.Span(loc.Span)

	start, end := nl.Span.Start.ByteOffset, nl.Span.End.ByteOffset
	if start > end {
		return nil, fmt.Errorf("invalid SPAN: start (%d) is after end (%d)", start, end)
	}
	return nl, nil
}

// Span returns a Span with its start and end normalized.
func (n *Normalizer) Span(s *cpb.Span) *cpb.Span {
	if s == nil {
		return nil
	}
	return &cpb.Span{
		Start: n.Point(s.Start),
		End:   n.Point(s.End),
	}
}

// SpanOffsets returns a Span based on normalized start and end byte offsets.
func (n *Normalizer) SpanOffsets(start, end int32) *cpb.Span {
	return &cpb.Span{
		Start: n.ByteOffset(start),
		End:   n.ByteOffset(end),
	}
}

var lineEnd = []byte("\n")

// Point returns a normalized point within the Normalizer's text.  A normalized
// point has all of its fields set consistently and clamped within the range
// [0,len(text)).
func (n *Normalizer) Point(p *cpb.Point) *cpb.Point {
	if p == nil {
		return nil
	}

	if p.ByteOffset > 0 {
		return n.ByteOffset(p.ByteOffset)
	} else if p.LineNumber > 0 {
		np := &cpb.Point{
			LineNumber:   p.LineNumber,
			ColumnOffset: p.ColumnOffset,
		}

		if totalLines := int32(len(n.lineLen)); p.LineNumber > totalLines {
			np.LineNumber = totalLines
			np.ColumnOffset = n.lineLen[np.LineNumber-1] - 1
		}
		if np.ColumnOffset < 0 {
			np.ColumnOffset = 0
		} else if np.ColumnOffset > 0 {
			if lineLen := n.lineLen[np.LineNumber-1] - 1; p.ColumnOffset > lineLen {
				np.ColumnOffset = lineLen
			}
		}

		np.ByteOffset = n.prefixLen[np.LineNumber-1] + np.ColumnOffset

		return np
	}

	return &cpb.Point{LineNumber: 1}
}

// ByteOffset returns a normalized point based on the given offset within the
// Normalizer's text.  A normalized point has all of its fields set consistently
// and clamped within the range [0,len(text)).
func (n *Normalizer) ByteOffset(offset int32) *cpb.Point {
	np := &cpb.Point{ByteOffset: offset}
	if np.ByteOffset > n.textLen {
		np.ByteOffset = n.textLen
	}

	np.LineNumber = int32(sort.Search(len(n.lineLen), func(i int) bool {
		return n.prefixLen[i] > np.ByteOffset
	}))
	np.ColumnOffset = np.ByteOffset - n.prefixLen[np.LineNumber-1]

	return np
}
<|endoftext|>


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

package profilescmdline_test

import (
	"bufio"
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"runtime"
	"strings"
	"sync"
	"testing"

	"v.io/jiri/jiri"
	"v.io/jiri/jiritest"
	"v.io/jiri/profiles"
	"v.io/jiri/profiles/profilescmdline"
	"v.io/jiri/profiles/profilesreader"
	"v.io/x/lib/envvar"
	"v.io/x/lib/gosh"
)

func TestManagerArgs(t *testing.T) {
	profilescmdline.Reset()
	p := parent
	profilescmdline.RegisterManagementCommands(&p, false, "", "", jiri.ProfilesRootDir)
	if got, want := len(p.Children), 5; got != want {
		t.Errorf("got %v, want %v", got, want)
	}
	type cl struct {
		args string
		n    int
	}
	cls := map[string]cl{
		"install":   cl{"--profiles-db=db --profiles-dir=root --target=arch-os --env=a=b,c=d --force=false", 5},
		"uninstall": cl{"--profiles-db=db --profiles-dir=root --target=arch-os --all-targets --v", 5},
		"cleanup":   cl{"--profiles-db=db --profiles-dir=root --gc --rm-all --v", 5},
		"update":    cl{"--profiles-db=db --profiles-dir=root -v", 3},
		"available": cl{"-v", 1},
	}
	for _, c := range p.Children {
		args := cls[c.Name].args
		if err := c.Flags.Parse(strings.Split(args, " ")); err != nil {
			t.Errorf("failed to parse for %s: %s: %v", c.Name, args, err)
			continue
		}
		if got, want := c.Flags.NFlag(), cls[c.Name].n; got != want {
			t.Errorf("%s: got %v, want %v", c.Name, got, want)
		}
	}
}

var (
	buildInstallersOnce, buildJiriOnce     sync.Once
	buildInstallersBinDir, buildJiriBinDir = "", ""
)

// TODO(sadovsky): This code leaves a lot of temp dirs behind. It would be nice
// to restructure things so that all temporary artifacts get cleaned up.
func buildInstallers(t *testing.T) string {
	buildInstallersOnce.Do(func() {
		binDir, err := ioutil.TempDir("", "")
		if err != nil {
			t.Fatal(err)
		}
		sh := gosh.NewShell(t)
		defer sh.Cleanup()
		prefix := "v.io/jiri/profiles/profilescmdline/internal/"
		gosh.BuildGoPkg(sh, binDir, "v.io/jiri/cmd/jiri", "-o", "jiri")
		gosh.BuildGoPkg(sh, binDir, prefix+"i1", "-o", "jiri-profile-i1")
		gosh.BuildGoPkg(sh, binDir, prefix+"i2", "-o", "jiri-profile-i2")
		buildInstallersBinDir = binDir
	})
	return buildInstallersBinDir
}

func buildJiri(t *testing.T) string {
	buildJiriOnce.Do(func() {
		binDir, err := ioutil.TempDir("", "")
		if err != nil {
			t.Fatal(err)
		}
		sh := gosh.NewShell(t)
		defer sh.Cleanup()
		gosh.BuildGoPkg(sh, binDir, "v.io/jiri/cmd/jiri", "-o", "jiri")
		buildJiriBinDir = binDir
	})
	return buildJiriBinDir
}

func run(sh *gosh.Shell, dir, bin string, args ...string) string {
	cmd := sh.Cmd(filepath.Join(dir, bin), args...)
	if testing.Verbose() {
		cmd.PropagateOutput = true
	}
	return cmd.Stdout()
}

func TestManagerAvailable(t *testing.T) {
	fake, cleanup := jiritest.NewFakeJiriRoot(t)
	defer cleanup()
	dir, sh := buildInstallers(t), gosh.NewShell(t)
	sh.Vars["JIRI_ROOT"] = fake.X.Root
	sh.Vars["PATH"] = envvar.PrependUsingSeparator(dir, os.Getenv("PATH"), ":")
	stdout := run(sh, dir, "jiri", "profile", "available", "-v")
	for _, installer := range []string{"i1", "i2"} {
		re := regexp.MustCompile("Available Subcommands:.*profile-" + installer + ".*\n")
		if got := stdout; !re.MatchString(got) {
			t.Errorf("%v does not match %v\n", got, re.String())
		}
		if got, want := stdout, installer+":eg"; !strings.Contains(got, want) {
			t.Errorf("%v does not contain %v\n", got, want)
		}
	}
	os.RemoveAll(filepath.Join(fake.X.Root, jiri.ProfilesDBDir))
	stdout = run(sh, dir, "jiri", "profile", "available", "-v")
	if got, want := strings.TrimSpace(stdout), "Available Subcommands:"; got != want {
		t.Errorf("got %v, want %v", got, want)
	}
}

func loc() string {
	_, file, line, _ := runtime.Caller(2)
	return fmt.Sprintf("%s:%d", filepath.Base(file), line)
}
func exists(filename string) bool {
	_, err := os.Stat(filename)
	return err == nil
}

func contains(t *testing.T, filename, want string) {
	o, err := ioutil.ReadFile(filename)
	if err != nil {
		t.Fatal(err)
	}
	if got := string(o); got != want {
		t.Errorf("%s: %s: got %v, want %v", loc(), filename, got, want)
	}
}

func cat(msg, filename string) {
	o, err := ioutil.ReadFile(filename)
	if err != nil {
		panic(err)
	}
	fmt.Fprintln(os.Stderr, msg)
	fmt.Fprintln(os.Stderr, string(o))
	fmt.Fprintln(os.Stderr, msg)
}

func removeDate(s string) string {
	var result bytes.Buffer
	scanner := bufio.NewScanner(bytes.NewBufferString(s))
	re := regexp.MustCompile("(.*) date=\".*\"")
	for scanner.Scan() {
		result.WriteString(re.ReplaceAllString(scanner.Text(), "$1"))
		result.WriteString("\n")
	}
	return strings.TrimSpace(result.String())
}

func cmpFiles(t *testing.T, gotFilename, wantFilename string) {
	g, err := ioutil.ReadFile(gotFilename)
	if err != nil {
		t.Fatal(err)
	}
	w, err := ioutil.ReadFile(wantFilename)
	if err != nil {
		t.Fatal(err)
	}
	if got, want := removeDate(strings.TrimSpace(string(g))), removeDate(strings.TrimSpace(string(w))); got != want {
		t.Errorf("%s: got %v, want %v from %q and %q", loc(), got, want, gotFilename, wantFilename)
	}
}

func TestManagerInstallUninstall(t *testing.T) {
	fake, cleanup := jiritest.NewFakeJiriRoot(t)
	defer cleanup()
	dir, sh := buildInstallers(t), gosh.NewShell(t)
	sh.Vars["JIRI_ROOT"] = fake.X.Root
	sh.Vars["PATH"] = envvar.PrependUsingSeparator(dir, os.Getenv("PATH"), ":")

	run(sh, dir, "jiri", "profile", "list", "-v")

	i1 := filepath.Join(fake.X.Root, ".jiri_root/profile_db/i1")
	i2 := filepath.Join(fake.X.Root, ".jiri_root/profile_db/i2")

	run(sh, dir, "jiri", "profile", "install", "--target=arch-os", "i1:eg", "i2:eg")
	for _, installer := range []string{"i1", "i2"} {
		tdir := filepath.Join(fake.X.Root, jiri.ProfilesRootDir, installer, "eg", "arch_os")
		contains(t, filepath.Join(tdir, "version"), "3")
		contains(t, filepath.Join(tdir, "3"), "3")
	}
	cmpFiles(t, i1, filepath.Join("testdata", "i1a.xml"))
	cmpFiles(t, i2, filepath.Join("testdata", "i2a.xml"))

	run(sh, dir, "jiri", "profile", "install", "--target=arch-os@2", "i1:eg", "i2:eg")
	// Installs are idempotent.
	run(sh, dir, "jiri", "profile", "install", "--target=arch-os@2", "i1:eg", "i2:eg")
	for _, installer := range []string{"i1", "i2"} {
		tdir := filepath.Join(fake.X.Root, jiri.ProfilesRootDir, installer, "eg", "arch_os")
		contains(t, filepath.Join(tdir, "version"), "2")
		contains(t, filepath.Join(tdir, "3"), "3")
		contains(t, filepath.Join(tdir, "2"), "2")
	}

	cmpFiles(t, i1, filepath.Join("testdata", "i1b.xml"))
	cmpFiles(t, i2, filepath.Join("testdata", "i2b.xml"))

	run(sh, dir, "jiri", "profile", "uninstall", "--target=arch-os@2", "i1:eg", "i2:eg")
	for _, installer := range []string{"i1", "i2"} {
		tdir := filepath.Join(fake.X.Root, jiri.ProfilesRootDir, installer, "eg", "arch_os")
		contains(t, filepath.Join(tdir, "version"), "2")
		contains(t, filepath.Join(tdir, "3"), "3")
		if got, want := exists(filepath.Join(tdir, "2")), false; got != want {
			t.Errorf("%s: got %v, want %v", filepath.Join(tdir, "2"), got, want)
		}
	}
	cmpFiles(t, i1, filepath.Join("testdata", "i1c.xml"))
	cmpFiles(t, i2, filepath.Join("testdata", "i2c.xml"))

	// Put v2 back.
	run(sh, dir, "jiri", "profile", "list", "-v")
	run(sh, dir, "jiri", "profile", "install", "--target=arch-os@2", "i1:eg", "i2:eg")
	cmpFiles(t, i1, filepath.Join("testdata", "i1b.xml"))
	cmpFiles(t, i2, filepath.Join("testdata", "i2b.xml"))
}

func TestManagerUpdate(t *testing.T) {
	fake, cleanup := jiritest.NewFakeJiriRoot(t)
	defer cleanup()
	dir, sh := buildInstallers(t), gosh.NewShell(t)
	sh.Vars["JIRI_ROOT"] = fake.X.Root
	sh.Vars["PATH"] = dir

	i1 := filepath.Join(fake.X.Root, ".jiri_root/profile_db/i1")
	i2 := filepath.Join(fake.X.Root, ".jiri_root/profile_db/i2")

	run(sh, dir, "jiri", "profile", "install", "--target=arch-os@2", "i1:eg", "i2:eg")
	cmpFiles(t, i1, filepath.Join("testdata", "i1d.xml"))
	cmpFiles(t, i2, filepath.Join("testdata", "i2d.xml"))

	run(sh, dir, "jiri", "profile", "update")
	cmpFiles(t, i1, filepath.Join("testdata", "i1e.xml"))
	cmpFiles(t, i2, filepath.Join("testdata", "i2e.xml"))

	run(sh, dir, "jiri", "profile", "cleanup", "-gc")
	cmpFiles(t, i1, filepath.Join("testdata", "i1f.xml"))
	cmpFiles(t, i2, filepath.Join("testdata", "i2f.xml"))
}

// Test using a fake jiri root.
func TestJiriFakeRoot(t *testing.T) {
	fake, cleanup := jiritest.NewFakeJiriRoot(t)
	defer cleanup()
	profilesDBDir := filepath.Join(fake.X.Root, jiri.ProfilesDBDir)
	_ = cleanup
	pdb := profiles.NewDB()
	t1, err := profiles.NewTarget("cpu1-os1@1", "A=B,C=D")
	if err != nil {
		t.Fatal(err)
	}
	pdb.InstallProfile("test", "b", "")
	if err := pdb.AddProfileTarget("test", "b", t1); err != nil {
		t.Fatal(err)
	}
	if err := pdb.Write(fake.X, "test", profilesDBDir); err != nil {
		t.Fatal(err)
	}

	rd, err := profilesreader.NewReader(fake.X, profilesreader.UseProfiles, profilesDBDir)
	if err != nil {
		t.Fatal(err)
	}

	if got, want := rd.ProfileNames(), []string{"test:b"}; !reflect.DeepEqual(got, want) {
		t.Errorf("got %v, want %v", got, want)
	}

	dir, sh := buildJiri(t), gosh.NewShell(t)
	sh.Vars["JIRI_ROOT"] = fake.X.Root
	sh.Vars["PATH"] = envvar.PrependUsingSeparator(dir, os.Getenv("PATH"), ":")
	run(sh, dir, "jiri", "profile", "list", "-v")
}
<commit_msg>jiri: Add helper functions to envvar to manipulate token lists.<commit_after>// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package profilescmdline_test

import (
	"bufio"
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"runtime"
	"strings"
	"sync"
	"testing"

	"v.io/jiri/jiri"
	"v.io/jiri/jiritest"
	"v.io/jiri/profiles"
	"v.io/jiri/profiles/profilescmdline"
	"v.io/jiri/profiles/profilesreader"
	"v.io/x/lib/envvar"
	"v.io/x/lib/gosh"
)

func TestManagerArgs(t *testing.T) {
	profilescmdline.Reset()
	p := parent
	profilescmdline.RegisterManagementCommands(&p, false, "", "", jiri.ProfilesRootDir)
	if got, want := len(p.Children), 5; got != want {
		t.Errorf("got %v, want %v", got, want)
	}
	type cl struct {
		args string
		n    int
	}
	cls := map[string]cl{
		"install":   cl{"--profiles-db=db --profiles-dir=root --target=arch-os --env=a=b,c=d --force=false", 5},
		"uninstall": cl{"--profiles-db=db --profiles-dir=root --target=arch-os --all-targets --v", 5},
		"cleanup":   cl{"--profiles-db=db --profiles-dir=root --gc --rm-all --v", 5},
		"update":    cl{"--profiles-db=db --profiles-dir=root -v", 3},
		"available": cl{"-v", 1},
	}
	for _, c := range p.Children {
		args := cls[c.Name].args
		if err := c.Flags.Parse(strings.Split(args, " ")); err != nil {
			t.Errorf("failed to parse for %s: %s: %v", c.Name, args, err)
			continue
		}
		if got, want := c.Flags.NFlag(), cls[c.Name].n; got != want {
			t.Errorf("%s: got %v, want %v", c.Name, got, want)
		}
	}
}

var (
	buildInstallersOnce, buildJiriOnce     sync.Once
	buildInstallersBinDir, buildJiriBinDir = "", ""
)

// TODO(sadovsky): This code leaves a lot of temp dirs behind. It would be nice
// to restructure things so that all temporary artifacts get cleaned up.
func buildInstallers(t *testing.T) string {
	buildInstallersOnce.Do(func() {
		binDir, err := ioutil.TempDir("", "")
		if err != nil {
			t.Fatal(err)
		}
		sh := gosh.NewShell(t)
		defer sh.Cleanup()
		prefix := "v.io/jiri/profiles/profilescmdline/internal/"
		gosh.BuildGoPkg(sh, binDir, "v.io/jiri/cmd/jiri", "-o", "jiri")
		gosh.BuildGoPkg(sh, binDir, prefix+"i1", "-o", "jiri-profile-i1")
		gosh.BuildGoPkg(sh, binDir, prefix+"i2", "-o", "jiri-profile-i2")
		buildInstallersBinDir = binDir
	})
	return buildInstallersBinDir
}

func buildJiri(t *testing.T) string {
	buildJiriOnce.Do(func() {
		binDir, err := ioutil.TempDir("", "")
		if err != nil {
			t.Fatal(err)
		}
		sh := gosh.NewShell(t)
		defer sh.Cleanup()
		gosh.BuildGoPkg(sh, binDir, "v.io/jiri/cmd/jiri", "-o", "jiri")
		buildJiriBinDir = binDir
	})
	return buildJiriBinDir
}

func run(sh *gosh.Shell, dir, bin string, args ...string) string {
	cmd := sh.Cmd(filepath.Join(dir, bin), args...)
	if testing.Verbose() {
		cmd.PropagateOutput = true
	}
	return cmd.Stdout()
}

func TestManagerAvailable(t *testing.T) {
	fake, cleanup := jiritest.NewFakeJiriRoot(t)
	defer cleanup()
	dir, sh := buildInstallers(t), gosh.NewShell(t)
	sh.Vars["JIRI_ROOT"] = fake.X.Root
	sh.Vars["PATH"] = envvar.PrependUniqueToken(sh.Vars["PATH"], ":", dir)
	stdout := run(sh, dir, "jiri", "profile", "available", "-v")
	for _, installer := range []string{"i1", "i2"} {
		re := regexp.MustCompile("Available Subcommands:.*profile-" + installer + ".*\n")
		if got := stdout; !re.MatchString(got) {
			t.Errorf("%v does not match %v\n", got, re.String())
		}
		if got, want := stdout, installer+":eg"; !strings.Contains(got, want) {
			t.Errorf("%v does not contain %v\n", got, want)
		}
	}
	os.RemoveAll(filepath.Join(fake.X.Root, jiri.ProfilesDBDir))
	stdout = run(sh, dir, "jiri", "profile", "available", "-v")
	if got, want := strings.TrimSpace(stdout), "Available Subcommands:"; got != want {
		t.Errorf("got %v, want %v", got, want)
	}
}

func loc() string {
	_, file, line, _ := runtime.Caller(2)
	return fmt.Sprintf("%s:%d", filepath.Base(file), line)
}
func exists(filename string) bool {
	_, err := os.Stat(filename)
	return err == nil
}

func contains(t *testing.T, filename, want string) {
	o, err := ioutil.ReadFile(filename)
	if err != nil {
		t.Fatal(err)
	}
	if got := string(o); got != want {
		t.Errorf("%s: %s: got %v, want %v", loc(), filename, got, want)
	}
}

func cat(msg, filename string) {
	o, err := ioutil.ReadFile(filename)
	if err != nil {
		panic(err)
	}
	fmt.Fprintln(os.Stderr, msg)
	fmt.Fprintln(os.Stderr, string(o))
	fmt.Fprintln(os.Stderr, msg)
}

func removeDate(s string) string {
	var result bytes.Buffer
	scanner := bufio.NewScanner(bytes.NewBufferString(s))
	re := regexp.MustCompile("(.*) date=\".*\"")
	for scanner.Scan() {
		result.WriteString(re.ReplaceAllString(scanner.Text(), "$1"))
		result.WriteString("\n")
	}
	return strings.TrimSpace(result.String())
}

func cmpFiles(t *testing.T, gotFilename, wantFilename string) {
	g, err := ioutil.ReadFile(gotFilename)
	if err != nil {
		t.Fatal(err)
	}
	w, err := ioutil.ReadFile(wantFilename)
	if err != nil {
		t.Fatal(err)
	}
	if got, want := removeDate(strings.TrimSpace(string(g))), removeDate(strings.TrimSpace(string(w))); got != want {
		t.Errorf("%s: got %v, want %v from %q and %q", loc(), got, want, gotFilename, wantFilename)
	}
}

func TestManagerInstallUninstall(t *testing.T) {
	fake, cleanup := jiritest.NewFakeJiriRoot(t)
	defer cleanup()
	dir, sh := buildInstallers(t), gosh.NewShell(t)
	sh.Vars["JIRI_ROOT"] = fake.X.Root
	sh.Vars["PATH"] = envvar.PrependUniqueToken(sh.Vars["PATH"], ":", dir)

	run(sh, dir, "jiri", "profile", "list", "-v")

	i1 := filepath.Join(fake.X.Root, ".jiri_root/profile_db/i1")
	i2 := filepath.Join(fake.X.Root, ".jiri_root/profile_db/i2")

	run(sh, dir, "jiri", "profile", "install", "--target=arch-os", "i1:eg", "i2:eg")
	for _, installer := range []string{"i1", "i2"} {
		tdir := filepath.Join(fake.X.Root, jiri.ProfilesRootDir, installer, "eg", "arch_os")
		contains(t, filepath.Join(tdir, "version"), "3")
		contains(t, filepath.Join(tdir, "3"), "3")
	}
	cmpFiles(t, i1, filepath.Join("testdata", "i1a.xml"))
	cmpFiles(t, i2, filepath.Join("testdata", "i2a.xml"))

	run(sh, dir, "jiri", "profile", "install", "--target=arch-os@2", "i1:eg", "i2:eg")
	// Installs are idempotent.
	run(sh, dir, "jiri", "profile", "install", "--target=arch-os@2", "i1:eg", "i2:eg")
	for _, installer := range []string{"i1", "i2"} {
		tdir := filepath.Join(fake.X.Root, jiri.ProfilesRootDir, installer, "eg", "arch_os")
		contains(t, filepath.Join(tdir, "version"), "2")
		contains(t, filepath.Join(tdir, "3"), "3")
		contains(t, filepath.Join(tdir, "2"), "2")
	}

	cmpFiles(t, i1, filepath.Join("testdata", "i1b.xml"))
	cmpFiles(t, i2, filepath.Join("testdata", "i2b.xml"))

	run(sh, dir, "jiri", "profile", "uninstall", "--target=arch-os@2", "i1:eg", "i2:eg")
	for _, installer := range []string{"i1", "i2"} {
		tdir := filepath.Join(fake.X.Root, jiri.ProfilesRootDir, installer, "eg", "arch_os")
		contains(t, filepath.Join(tdir, "version"), "2")
		contains(t, filepath.Join(tdir, "3"), "3")
		if got, want := exists(filepath.Join(tdir, "2")), false; got != want {
			t.Errorf("%s: got %v, want %v", filepath.Join(tdir, "2"), got, want)
		}
	}
	cmpFiles(t, i1, filepath.Join("testdata", "i1c.xml"))
	cmpFiles(t, i2, filepath.Join("testdata", "i2c.xml"))

	// Put v2 back.
	run(sh, dir, "jiri", "profile", "list", "-v")
	run(sh, dir, "jiri", "profile", "install", "--target=arch-os@2", "i1:eg", "i2:eg")
	cmpFiles(t, i1, filepath.Join("testdata", "i1b.xml"))
	cmpFiles(t, i2, filepath.Join("testdata", "i2b.xml"))
}

func TestManagerUpdate(t *testing.T) {
	fake, cleanup := jiritest.NewFakeJiriRoot(t)
	defer cleanup()
	dir, sh := buildInstallers(t), gosh.NewShell(t)
	sh.Vars["JIRI_ROOT"] = fake.X.Root
	sh.Vars["PATH"] = dir

	i1 := filepath.Join(fake.X.Root, ".jiri_root/profile_db/i1")
	i2 := filepath.Join(fake.X.Root, ".jiri_root/profile_db/i2")

	run(sh, dir, "jiri", "profile", "install", "--target=arch-os@2", "i1:eg", "i2:eg")
	cmpFiles(t, i1, filepath.Join("testdata", "i1d.xml"))
	cmpFiles(t, i2, filepath.Join("testdata", "i2d.xml"))

	run(sh, dir, "jiri", "profile", "update")
	cmpFiles(t, i1, filepath.Join("testdata", "i1e.xml"))
	cmpFiles(t, i2, filepath.Join("testdata", "i2e.xml"))

	run(sh, dir, "jiri", "profile", "cleanup", "-gc")
	cmpFiles(t, i1, filepath.Join("testdata", "i1f.xml"))
	cmpFiles(t, i2, filepath.Join("testdata", "i2f.xml"))
}

// Test using a fake jiri root.
func TestJiriFakeRoot(t *testing.T) {
	fake, cleanup := jiritest.NewFakeJiriRoot(t)
	defer cleanup()
	profilesDBDir := filepath.Join(fake.X.Root, jiri.ProfilesDBDir)
	_ = cleanup
	pdb := profiles.NewDB()
	t1, err := profiles.NewTarget("cpu1-os1@1", "A=B,C=D")
	if err != nil {
		t.Fatal(err)
	}
	pdb.InstallProfile("test", "b", "")
	if err := pdb.AddProfileTarget("test", "b", t1); err != nil {
		t.Fatal(err)
	}
	if err := pdb.Write(fake.X, "test", profilesDBDir); err != nil {
		t.Fatal(err)
	}

	rd, err := profilesreader.NewReader(fake.X, profilesreader.UseProfiles, profilesDBDir)
	if err != nil {
		t.Fatal(err)
	}

	if got, want := rd.ProfileNames(), []string{"test:b"}; !reflect.DeepEqual(got, want) {
		t.Errorf("got %v, want %v", got, want)
	}

	dir, sh := buildJiri(t), gosh.NewShell(t)
	sh.Vars["JIRI_ROOT"] = fake.X.Root
	sh.Vars["PATH"] = envvar.PrependUniqueToken(sh.Vars["PATH"], ":", dir)
	run(sh, dir, "jiri", "profile", "list", "-v")
}
<|endoftext|>


<commit_before>package github

import (
	"fmt"
	"net/url"
	"strings"

	"github.com/google/go-github/github"
	"github.com/hashicorp/vault/helper/policyutil"
	"github.com/hashicorp/vault/logical"
	"github.com/hashicorp/vault/logical/framework"
)

func pathLogin(b *backend) *framework.Path {
	return &framework.Path{
		Pattern: "login",
		Fields: map[string]*framework.FieldSchema{
			"token": &framework.FieldSchema{
				Type:        framework.TypeString,
				Description: "GitHub personal API token",
			},
		},

		Callbacks: map[logical.Operation]framework.OperationFunc{
			logical.UpdateOperation: b.pathLogin,
		},
	}
}

func (b *backend) pathLogin(
	req *logical.Request, data *framework.FieldData) (*logical.Response, error) {

	token := data.Get("token").(string)

	var verifyResp *verifyCredentialsResp
	if verifyResponse, resp, err := b.verifyCredentials(req, token); err != nil {
		return nil, err
	} else if resp != nil {
		return resp, nil
	} else {
		verifyResp = verifyResponse
	}

	config, err := b.Config(req.Storage)
	if err != nil {
		return nil, err
	}

	ttl, _, err := b.SanitizeTTLStr(config.TTL.String(), config.MaxTTL.String())
	if err != nil {
		return logical.ErrorResponse(fmt.Sprintf("[ERR]:%s", err)), nil
	}

	return &logical.Response{
		Auth: &logical.Auth{
			InternalData: map[string]interface{}{
				"token": token,
			},
			Policies: verifyResp.Policies,
			Metadata: map[string]string{
				"username": *verifyResp.User.Login,
				"org":      *verifyResp.Org.Login,
			},
			DisplayName: *verifyResp.User.Login,
			LeaseOptions: logical.LeaseOptions{
				TTL:       ttl,
				Renewable: true,
			},
		},
	}, nil
}

func (b *backend) pathLoginRenew(
	req *logical.Request, d *framework.FieldData) (*logical.Response, error) {

	token := req.Auth.InternalData["token"].(string)

	var verifyResp *verifyCredentialsResp
	if verifyResponse, resp, err := b.verifyCredentials(req, token); err != nil {
		return nil, err
	} else if resp != nil {
		return resp, nil
	} else {
		verifyResp = verifyResponse
	}
	if !policyutil.EquivalentPolicies(verifyResp.Policies, req.Auth.Policies) {
		return nil, fmt.Errorf("policies do not match")
	}

	config, err := b.Config(req.Storage)
	if err != nil {
		return nil, err
	}
	return framework.LeaseExtend(config.TTL, config.MaxTTL, b.System())(req, d)
}

func (b *backend) verifyCredentials(req *logical.Request, token string) (*verifyCredentialsResp, *logical.Response, error) {
	config, err := b.Config(req.Storage)
	if err != nil {
		return nil, nil, err
	}
	if config.Org == "" {
		return nil, logical.ErrorResponse(
			"configure the github credential backend first"), nil
	}

	client, err := b.Client(token)
	if err != nil {
		return nil, nil, err
	}

	if config.BaseURL != "" {
		parsedURL, err := url.Parse(config.BaseURL)
		if err != nil {
			return nil, nil, fmt.Errorf("Successfully parsed base_url when set but failing to parse now: %s", err)
		}
		client.BaseURL = parsedURL
	}

	// Get the user
	user, _, err := client.Users.Get("")
	if err != nil {
		return nil, nil, err
	}

	// Verify that the user is part of the organization
	var org *github.Organization

	orgOpt := &github.ListOptions{
		PerPage: 100,
	}

	var allOrgs []github.Organization
	for {
		orgs, resp, err := client.Organizations.List("", orgOpt)
		if err != nil {
			return nil, nil, err
		}
		allOrgs = append(allOrgs, orgs...)
		if resp.NextPage == 0 {
			break
		}
		orgOpt.Page = resp.NextPage
	}

	for _, o := range allOrgs {
		if strings.ToLower(*o.Login) == strings.ToLower(config.Org) {
			org = &o
			break
		}
	}
	if org == nil {
		return nil, logical.ErrorResponse("user is not part of required org"), nil
	}

	// Get the teams that this user is part of to determine the policies
	var teamNames []string

	teamOpt := &github.ListOptions{
		PerPage: 100,
	}

	var allTeams []github.Team
	for {
		teams, resp, err := client.Organizations.ListUserTeams(teamOpt)
		if err != nil {
			return nil, nil, err
		}
		allTeams = append(allTeams, teams...)
		if resp.NextPage == 0 {
			break
		}
		teamOpt.Page = resp.NextPage
	}

	for _, t := range allTeams {
		// We only care about teams that are part of the organization we use
		if *t.Organization.ID != *org.ID {
			continue
		}

		// Append the names so we can get the policies
		teamNames = append(teamNames, *t.Name)
		if *t.Name != *t.Slug {
			teamNames = append(teamNames, *t.Slug)
		}
	}

	policiesList, err := b.Map.Policies(req.Storage, teamNames...)
	if err != nil {
		return nil, nil, err
	}
	return &verifyCredentialsResp{
		User:     user,
		Org:      org,
		Policies: policiesList,
	}, nil, nil
}

type verifyCredentialsResp struct {
	User     *github.User
	Org      *github.Organization
	Policies []string
}
<commit_msg>Fix panic when renewing a github token from a previous version of Vault<commit_after>package github

import (
	"fmt"
	"net/url"
	"strings"

	"github.com/google/go-github/github"
	"github.com/hashicorp/vault/helper/policyutil"
	"github.com/hashicorp/vault/logical"
	"github.com/hashicorp/vault/logical/framework"
)

func pathLogin(b *backend) *framework.Path {
	return &framework.Path{
		Pattern: "login",
		Fields: map[string]*framework.FieldSchema{
			"token": &framework.FieldSchema{
				Type:        framework.TypeString,
				Description: "GitHub personal API token",
			},
		},

		Callbacks: map[logical.Operation]framework.OperationFunc{
			logical.UpdateOperation: b.pathLogin,
		},
	}
}

func (b *backend) pathLogin(
	req *logical.Request, data *framework.FieldData) (*logical.Response, error) {

	token := data.Get("token").(string)

	var verifyResp *verifyCredentialsResp
	if verifyResponse, resp, err := b.verifyCredentials(req, token); err != nil {
		return nil, err
	} else if resp != nil {
		return resp, nil
	} else {
		verifyResp = verifyResponse
	}

	config, err := b.Config(req.Storage)
	if err != nil {
		return nil, err
	}

	ttl, _, err := b.SanitizeTTLStr(config.TTL.String(), config.MaxTTL.String())
	if err != nil {
		return logical.ErrorResponse(fmt.Sprintf("[ERR]:%s", err)), nil
	}

	return &logical.Response{
		Auth: &logical.Auth{
			InternalData: map[string]interface{}{
				"token": token,
			},
			Policies: verifyResp.Policies,
			Metadata: map[string]string{
				"username": *verifyResp.User.Login,
				"org":      *verifyResp.Org.Login,
			},
			DisplayName: *verifyResp.User.Login,
			LeaseOptions: logical.LeaseOptions{
				TTL:       ttl,
				Renewable: true,
			},
		},
	}, nil
}

func (b *backend) pathLoginRenew(
	req *logical.Request, d *framework.FieldData) (*logical.Response, error) {

	if req.Auth == nil {
		return nil, fmt.Errorf("request auth was nil")
	}

	tokenInt, ok := req.Auth.InternalData["token"]
	if !ok {
		return nil, fmt.Errorf("token created in previous version of Vault cannot be validated properly at renewal time")
	}
	token := tokenInt.(string)

	var verifyResp *verifyCredentialsResp
	if verifyResponse, resp, err := b.verifyCredentials(req, token); err != nil {
		return nil, err
	} else if resp != nil {
		return resp, nil
	} else {
		verifyResp = verifyResponse
	}
	if !policyutil.EquivalentPolicies(verifyResp.Policies, req.Auth.Policies) {
		return nil, fmt.Errorf("policies do not match")
	}

	config, err := b.Config(req.Storage)
	if err != nil {
		return nil, err
	}
	return framework.LeaseExtend(config.TTL, config.MaxTTL, b.System())(req, d)
}

func (b *backend) verifyCredentials(req *logical.Request, token string) (*verifyCredentialsResp, *logical.Response, error) {
	config, err := b.Config(req.Storage)
	if err != nil {
		return nil, nil, err
	}
	if config.Org == "" {
		return nil, logical.ErrorResponse(
			"configure the github credential backend first"), nil
	}

	client, err := b.Client(token)
	if err != nil {
		return nil, nil, err
	}

	if config.BaseURL != "" {
		parsedURL, err := url.Parse(config.BaseURL)
		if err != nil {
			return nil, nil, fmt.Errorf("Successfully parsed base_url when set but failing to parse now: %s", err)
		}
		client.BaseURL = parsedURL
	}

	// Get the user
	user, _, err := client.Users.Get("")
	if err != nil {
		return nil, nil, err
	}

	// Verify that the user is part of the organization
	var org *github.Organization

	orgOpt := &github.ListOptions{
		PerPage: 100,
	}

	var allOrgs []github.Organization
	for {
		orgs, resp, err := client.Organizations.List("", orgOpt)
		if err != nil {
			return nil, nil, err
		}
		allOrgs = append(allOrgs, orgs...)
		if resp.NextPage == 0 {
			break
		}
		orgOpt.Page = resp.NextPage
	}

	for _, o := range allOrgs {
		if strings.ToLower(*o.Login) == strings.ToLower(config.Org) {
			org = &o
			break
		}
	}
	if org == nil {
		return nil, logical.ErrorResponse("user is not part of required org"), nil
	}

	// Get the teams that this user is part of to determine the policies
	var teamNames []string

	teamOpt := &github.ListOptions{
		PerPage: 100,
	}

	var allTeams []github.Team
	for {
		teams, resp, err := client.Organizations.ListUserTeams(teamOpt)
		if err != nil {
			return nil, nil, err
		}
		allTeams = append(allTeams, teams...)
		if resp.NextPage == 0 {
			break
		}
		teamOpt.Page = resp.NextPage
	}

	for _, t := range allTeams {
		// We only care about teams that are part of the organization we use
		if *t.Organization.ID != *org.ID {
			continue
		}

		// Append the names so we can get the policies
		teamNames = append(teamNames, *t.Name)
		if *t.Name != *t.Slug {
			teamNames = append(teamNames, *t.Slug)
		}
	}

	policiesList, err := b.Map.Policies(req.Storage, teamNames...)
	if err != nil {
		return nil, nil, err
	}
	return &verifyCredentialsResp{
		User:     user,
		Org:      org,
		Policies: policiesList,
	}, nil, nil
}

type verifyCredentialsResp struct {
	User     *github.User
	Org      *github.Organization
	Policies []string
}
<|endoftext|>