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

package renderer

import (
	"encoding/json"
	"io"
	"net/http"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"time"

	"github.com/yahoo/gryffin"
	_ "github.com/yahoo/gryffin/renderer/resource"
)

/* all of these are the JSON struct for phantomjs render.js */

type PhantomJSRenderer struct {
	BaseRenderer
	Timeout int
	process *os.Process
}

type input struct {
	Method         string       `json:"method"`
	AllowedDomains []string     `json:"allowed_domains,omitempty"`
	Headers        inputHeaders `json:"headers"`
}

type inputHeaders struct {
	AcceptEncoding string `json:"Accept-Encoding"`
	AcceptLanguage string `json:"Accept-Language"`
	Cookie         string
	UserAgent      string `json:"User-Agent"`
}

type details struct {
	Links []link
	Forms []string
}

type link struct {
	Text string
	Url  string
}

type response struct {
	Headers     map[string][]string
	Body        string
	ContentType string
	Status      int
	Url         string
	Details     details
}

type responseMessage struct {
	Response response
	Elapsed  int
	Ok       int
}

type domMessage struct {
	Action   string
	Events   []string
	KeyChain []string
	Links    []link
	JSError  []string
}

type message struct {
	*responseMessage
	*domMessage
	Signature string
	MsgType   string
}

type noCloseReader struct {
	io.Reader
}

func (r noCloseReader) Close() error {
	return nil
}

func (m *response) fill(s *gryffin.Scan) {

	/*
	   {"response":{"headers":{"Date":["Thu, 30 Jul 2015 00:13:43 GMT"],"Set-Cookie":["B=82j3nrdarir1n&b=3&s=23; expires=Sun, 30-Jul-2017 00:13:43 GMT; path=/; domain=.yahoo.com"]

	*/
	resp := &http.Response{
		Request:    s.Request,
		StatusCode: m.Status,
		Status:     strconv.FormatInt(int64(m.Status), 10),
		Proto:      "HTTP/1.1",
		ProtoMajor: 1,
		ProtoMinor: 1,
		Header:     m.Headers,
		Body:       noCloseReader{strings.NewReader(m.Body)},
	}

	s.Response = resp
	s.ReadResponseBody()

}

func (l *link) toScan(parent *gryffin.Scan) *gryffin.Scan {
	if req, err := http.NewRequest("GET", l.Url, nil); err == nil {
		s := parent.Spawn()
		s.MergeRequest(req)
		return s
	}
	// invalid url
	return nil
}

func (r *PhantomJSRenderer) extract(stdout io.ReadCloser, s *gryffin.Scan) {

	defer close(r.done)

	dec := json.NewDecoder(stdout)
	for {
		var m message
		err := dec.Decode(&m)
		if err == io.EOF {
			return
		} else {
			if m.responseMessage != nil {
				m.Response.fill(s)
				if s.IsDuplicatedPage() {
					return
				}

				r.chanResponse <- s
				for _, link := range m.Response.Details.Links {
					if newScan := link.toScan(s); newScan != nil && newScan.IsScanAllowed() {
						r.chanLinks <- newScan
					}
				}
			} else if m.domMessage != nil {
				for _, link := range m.domMessage.Links {
					if newScan := link.toScan(s); newScan != nil && newScan.IsScanAllowed() {
						r.chanLinks <- newScan
					}
				}
			}
		}
	}

}

func (r *PhantomJSRenderer) kill(reason string, s *gryffin.Scan) {
	if err := r.process.Kill(); err == nil {
		s.Logmf("PhantomjsRenderer.Do", "[%s] Terminating the crawl process.", reason)
	}
}

func (r *PhantomJSRenderer) wait(s *gryffin.Scan) {

	select {
	case <-r.done:
		r.kill("Cleanup", s)
	case <-time.After(time.Duration(r.Timeout) * time.Second):
		r.kill("Timeout", s)
	}
	close(r.chanResponse)
	close(r.chanLinks)
}

func (r *PhantomJSRenderer) Do(s *gryffin.Scan) {

	r.chanResponse = make(chan *gryffin.Scan, 10)
	r.chanLinks = make(chan *gryffin.Scan, 10)
	r.done = make(chan string)

	// Construct the command.
	// render.js http(s)://<host>[:port][/path] [{"method":"post", "data":"a=1&b=2"}]
	url := s.Request.URL.String()
	cookies := make([]string, 0)
	// ua := s.Request.UserAgent()
	ua := "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36"

	for _, c := range s.Cookies {
		cookies = append(cookies, c.String())
	}

	arg := input{
		Method: s.Request.Method,
		Headers: inputHeaders{
			UserAgent: ua,
			Cookie:    strings.Join(cookies, ";"),
		},
	}

	opt, err := json.Marshal(arg)
	if err != nil {
		s.Error("PhantomjsRenderer.Do", err)
		return
	}

	// s.Logmf("PhantomjsRenderer.Do", "Running: render.js %s '%s'", url, string(opt))
	s.Logmf("PhantomjsRenderer.Do", "Running: render.js")

	cmd := exec.Command(
		os.Getenv("GOPATH")+"/src/github.com/yahoo/gryffin/renderer/resource/render.js",
		url,
		string(opt))

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		s.Error("PhantomjsRenderer.Do", err)
		return
	}

	if err := cmd.Start(); err != nil {
		s.Error("PhantomjsRenderer.Do", err)
		return
	}

	r.process = cmd.Process

	// wait until done or timeout.
	go r.extract(stdout, s)
	go r.wait(s)

	cmd.Wait()

}
<commit_msg>call phantomjs directly, no pound sign<commit_after>// Copyright 2015, Yahoo Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package renderer

import (
	"encoding/json"
	"io"
	"net/http"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"time"

	"github.com/yahoo/gryffin"
	_ "github.com/yahoo/gryffin/renderer/resource"
)

/* all of these are the JSON struct for phantomjs render.js */

type PhantomJSRenderer struct {
	BaseRenderer
	Timeout int
	process *os.Process
}

type input struct {
	Method         string       `json:"method"`
	AllowedDomains []string     `json:"allowed_domains,omitempty"`
	Headers        inputHeaders `json:"headers"`
}

type inputHeaders struct {
	AcceptEncoding string `json:"Accept-Encoding"`
	AcceptLanguage string `json:"Accept-Language"`
	Cookie         string
	UserAgent      string `json:"User-Agent"`
}

type details struct {
	Links []link
	Forms []string
}

type link struct {
	Text string
	Url  string
}

type response struct {
	Headers     map[string][]string
	Body        string
	ContentType string
	Status      int
	Url         string
	Details     details
}

type responseMessage struct {
	Response response
	Elapsed  int
	Ok       int
}

type domMessage struct {
	Action   string
	Events   []string
	KeyChain []string
	Links    []link
	JSError  []string
}

type message struct {
	*responseMessage
	*domMessage
	Signature string
	MsgType   string
}

type noCloseReader struct {
	io.Reader
}

func (r noCloseReader) Close() error {
	return nil
}

func (m *response) fill(s *gryffin.Scan) {

	/*
	   {"response":{"headers":{"Date":["Thu, 30 Jul 2015 00:13:43 GMT"],"Set-Cookie":["B=82j3nrdarir1n&b=3&s=23; expires=Sun, 30-Jul-2017 00:13:43 GMT; path=/; domain=.yahoo.com"]

	*/
	resp := &http.Response{
		Request:    s.Request,
		StatusCode: m.Status,
		Status:     strconv.FormatInt(int64(m.Status), 10),
		Proto:      "HTTP/1.1",
		ProtoMajor: 1,
		ProtoMinor: 1,
		Header:     m.Headers,
		Body:       noCloseReader{strings.NewReader(m.Body)},
	}

	s.Response = resp
	s.ReadResponseBody()

}

func (l *link) toScan(parent *gryffin.Scan) *gryffin.Scan {
	if req, err := http.NewRequest("GET", l.Url, nil); err == nil {
		s := parent.Spawn()
		s.MergeRequest(req)
		return s
	}
	// invalid url
	return nil
}

func (r *PhantomJSRenderer) extract(stdout io.ReadCloser, s *gryffin.Scan) {

	defer close(r.done)

	dec := json.NewDecoder(stdout)
	for {
		var m message
		err := dec.Decode(&m)
		if err == io.EOF {
			return
		} else {
			if m.responseMessage != nil {
				m.Response.fill(s)
				if s.IsDuplicatedPage() {
					return
				}

				r.chanResponse <- s
				for _, link := range m.Response.Details.Links {
					if newScan := link.toScan(s); newScan != nil && newScan.IsScanAllowed() {
						r.chanLinks <- newScan
					}
				}
			} else if m.domMessage != nil {
				for _, link := range m.domMessage.Links {
					if newScan := link.toScan(s); newScan != nil && newScan.IsScanAllowed() {
						r.chanLinks <- newScan
					}
				}
			}
		}
	}

}

func (r *PhantomJSRenderer) kill(reason string, s *gryffin.Scan) {
	if err := r.process.Kill(); err == nil {
		s.Logmf("PhantomjsRenderer.Do", "[%s] Terminating the crawl process.", reason)
	}
}

func (r *PhantomJSRenderer) wait(s *gryffin.Scan) {

	select {
	case <-r.done:
		r.kill("Cleanup", s)
	case <-time.After(time.Duration(r.Timeout) * time.Second):
		r.kill("Timeout", s)
	}
	close(r.chanResponse)
	close(r.chanLinks)
}

func (r *PhantomJSRenderer) Do(s *gryffin.Scan) {

	r.chanResponse = make(chan *gryffin.Scan, 10)
	r.chanLinks = make(chan *gryffin.Scan, 10)
	r.done = make(chan string)

	// Construct the command.
	// render.js http(s)://<host>[:port][/path] [{"method":"post", "data":"a=1&b=2"}]
	url := s.Request.URL.String()
	cookies := make([]string, 0)
	// ua := s.Request.UserAgent()
	ua := "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36"

	for _, c := range s.Cookies {
		cookies = append(cookies, c.String())
	}

	arg := input{
		Method: s.Request.Method,
		Headers: inputHeaders{
			UserAgent: ua,
			Cookie:    strings.Join(cookies, ";"),
		},
	}

	opt, err := json.Marshal(arg)
	if err != nil {
		s.Error("PhantomjsRenderer.Do", err)
		return
	}

	// s.Logmf("PhantomjsRenderer.Do", "Running: render.js %s '%s'", url, string(opt))
	s.Logmf("PhantomjsRenderer.Do", "Running: render.js")

	cmd := exec.Command(
		"phantomjs",
		"--ssl-protocol=any",
		"--ignore-ssl-errors=true",
		os.Getenv("GOPATH")+"/src/github.com/yahoo/gryffin/renderer/resource/render.js",
		url,
		string(opt))

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		s.Error("PhantomjsRenderer.Do", err)
		return
	}

	if err := cmd.Start(); err != nil {
		s.Error("PhantomjsRenderer.Do", err)
		return
	}

	r.process = cmd.Process

	// wait until done or timeout.
	go r.extract(stdout, s)
	go r.wait(s)

	cmd.Wait()

}
<|endoftext|>


<commit_before>package thrift_test

// Copyright (c) 2015 Uber Technologies, Inc.

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import (
	"errors"
	"net"
	"testing"
	"time"

	// Test is in a separate package to avoid circular dependencies.

	"github.com/uber/tchannel/golang/testutils"
	. "github.com/uber/tchannel/golang/thrift"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	"github.com/stretchr/testify/require"
	tchannel "github.com/uber/tchannel/golang"
	gen "github.com/uber/tchannel/golang/thrift/gen-go/test"
	"github.com/uber/tchannel/golang/thrift/mocks"
)

// Generate the service mocks using go generate.
//go:generate mockery -name TChanSimpleService
//go:generate mockery -name TChanSecondService

type testArgs struct {
	s1 *mocks.TChanSimpleService
	s2 *mocks.TChanSecondService
	c1 gen.TChanSimpleService
	c2 gen.TChanSecondService
}

func ctxArg() mock.AnythingOfTypeArgument {
	return mock.AnythingOfType("*thrift.thriftCtx")
}

func TestThriftArgs(t *testing.T) {
	withSetup(t, func(ctx Context, args testArgs) {
		arg := &gen.Data{
			B1: true,
			S2: "str",
			I3: 102,
		}
		ret := &gen.Data{
			B1: false,
			S2: "return-str",
			I3: 105,
		}

		args.s1.On("Call", ctxArg(), arg).Return(ret, nil)
		got, err := args.c1.Call(ctx, arg)
		require.NoError(t, err)
		assert.Equal(t, ret, got)
	})
}

func TestRequest(t *testing.T) {
	withSetup(t, func(ctx Context, args testArgs) {
		args.s1.On("Simple", ctxArg()).Return(nil)
		require.NoError(t, args.c1.Simple(ctx))
	})
}

func TestThriftError(t *testing.T) {
	thriftErr := &gen.SimpleErr{
		Message: "this is the error",
	}
	withSetup(t, func(ctx Context, args testArgs) {
		args.s1.On("Simple", ctxArg()).Return(thriftErr)
		got := args.c1.Simple(ctx)
		require.Error(t, got)
		require.Equal(t, thriftErr, got)
	})
}

func TestUnknownError(t *testing.T) {
	withSetup(t, func(ctx Context, args testArgs) {
		args.s1.On("Simple", ctxArg()).Return(errors.New("unexpected err"))
		got := args.c1.Simple(ctx)
		require.Error(t, got)
		require.Equal(t, tchannel.NewSystemError(tchannel.ErrCodeUnexpected, "unexpected err"), got)
	})
}

func TestMultiple(t *testing.T) {
	withSetup(t, func(ctx Context, args testArgs) {
		args.s1.On("Simple", ctxArg()).Return(nil)
		args.s2.On("Echo", ctxArg(), "test1").Return("test2", nil)

		require.NoError(t, args.c1.Simple(ctx))
		res, err := args.c2.Echo(ctx, "test1")
		require.NoError(t, err)
		require.Equal(t, "test2", res)
	})
}

func TestHeaders(t *testing.T) {
	reqHeaders := map[string]string{"header1": "value1", "header2": "value2"}
	respHeaders := map[string]string{"resp1": "value1-resp", "resp2": "value2-resp"}

	withSetup(t, func(ctx Context, args testArgs) {
		args.s1.On("Simple", ctxArg()).Return(nil).Run(func(args mock.Arguments) {
			ctx := args.Get(0).(Context)
			assert.Equal(t, reqHeaders, ctx.Headers(), "request headers mismatch")
			ctx.SetResponseHeaders(respHeaders)
		})

		ctx = WithHeaders(ctx, reqHeaders)
		require.NoError(t, args.c1.Simple(ctx))
		assert.Equal(t, respHeaders, ctx.ResponseHeaders(), "response headers mismatch")
	})
}

func TestClientHostPort(t *testing.T) {
	ctx, cancel := NewContext(time.Second * 10)
	defer cancel()

	s1ch, err := testutils.NewServer(nil)
	require.NoError(t, err, "testutils.NewServer failed")
	s2ch, err := testutils.NewServer(nil)
	require.NoError(t, err, "testutils.NewServer failed")
	defer s1ch.Close()
	defer s2ch.Close()

	s1ch.Peers().Add(s2ch.PeerInfo().HostPort)
	s2ch.Peers().Add(s1ch.PeerInfo().HostPort)

	mock1, mock2 := new(mocks.TChanSecondService), new(mocks.TChanSecondService)
	NewServer(s1ch).Register(gen.NewTChanSecondServiceServer(mock1))
	NewServer(s2ch).Register(gen.NewTChanSecondServiceServer(mock2))

	// When we call using a normal client, it can only call the other server (only peer).
	c1 := gen.NewTChanSecondServiceClient(NewClient(s1ch, s2ch.PeerInfo().ServiceName, nil))
	mock2.On("Echo", ctxArg(), "call1").Return("call1", nil)
	res, err := c1.Echo(ctx, "call1")
	assert.NoError(t, err, "call1 failed")
	assert.Equal(t, "call1", res)

	// When we call using a client that specifies host:port, it should call that server.
	c2 := gen.NewTChanSecondServiceClient(NewClient(s1ch, s1ch.PeerInfo().ServiceName, &ClientOptions{
		HostPort: s1ch.PeerInfo().HostPort,
	}))
	mock1.On("Echo", ctxArg(), "call2").Return("call2", nil)
	res, err = c2.Echo(ctx, "call2")
	assert.NoError(t, err, "call2 failed")
	assert.Equal(t, "call2", res)
}

func withSetup(t *testing.T, f func(ctx Context, args testArgs)) {
	args := testArgs{
		s1: new(mocks.TChanSimpleService),
		s2: new(mocks.TChanSecondService),
	}

	ctx, cancel := NewContext(time.Second * 10)
	defer cancel()

	// Start server
	tchan, listener, err := setupServer(args.s1, args.s2)
	require.NoError(t, err)
	defer tchan.Close()

	// Get client1
	args.c1, args.c2, err = getClients(listener.Addr().String())
	require.NoError(t, err)

	f(ctx, args)

	args.s1.AssertExpectations(t)
	args.s2.AssertExpectations(t)
}

func setupServer(h *mocks.TChanSimpleService, sh *mocks.TChanSecondService) (*tchannel.Channel, net.Listener, error) {
	tchan, err := tchannel.NewChannel("service", nil)
	if err != nil {
		return nil, nil, err
	}

	listener, err := net.Listen("tcp", ":0")
	if err != nil {
		return nil, nil, err
	}

	server := NewServer(tchan)
	server.Register(gen.NewTChanSimpleServiceServer(h))
	server.Register(gen.NewTChanSecondServiceServer(sh))

	tchan.Serve(listener)
	return tchan, listener, nil
}

func getClients(dst string) (gen.TChanSimpleService, gen.TChanSecondService, error) {
	tchan, err := tchannel.NewChannel("client", &tchannel.ChannelOptions{
		Logger: tchannel.SimpleLogger,
	})
	if err != nil {
		return nil, nil, err
	}

	tchan.Peers().Add(dst)
	client := NewClient(tchan, "service", nil)

	simpleClient := gen.NewTChanSimpleServiceClient(client)
	secondClient := gen.NewTChanSecondServiceClient(client)
	return simpleClient, secondClient, nil
}
<commit_msg>Add test to verify SubChannel Thrift registration<commit_after>package thrift_test

// Copyright (c) 2015 Uber Technologies, Inc.

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import (
	"errors"
	"net"
	"testing"
	"time"

	// Test is in a separate package to avoid circular dependencies.

	"github.com/uber/tchannel/golang/testutils"
	. "github.com/uber/tchannel/golang/thrift"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	"github.com/stretchr/testify/require"
	tchannel "github.com/uber/tchannel/golang"
	gen "github.com/uber/tchannel/golang/thrift/gen-go/test"
	"github.com/uber/tchannel/golang/thrift/mocks"
)

// Generate the service mocks using go generate.
//go:generate mockery -name TChanSimpleService
//go:generate mockery -name TChanSecondService

type testArgs struct {
	s1 *mocks.TChanSimpleService
	s2 *mocks.TChanSecondService
	c1 gen.TChanSimpleService
	c2 gen.TChanSecondService
}

func ctxArg() mock.AnythingOfTypeArgument {
	return mock.AnythingOfType("*thrift.thriftCtx")
}

func TestThriftArgs(t *testing.T) {
	withSetup(t, func(ctx Context, args testArgs) {
		arg := &gen.Data{
			B1: true,
			S2: "str",
			I3: 102,
		}
		ret := &gen.Data{
			B1: false,
			S2: "return-str",
			I3: 105,
		}

		args.s1.On("Call", ctxArg(), arg).Return(ret, nil)
		got, err := args.c1.Call(ctx, arg)
		require.NoError(t, err)
		assert.Equal(t, ret, got)
	})
}

func TestRequest(t *testing.T) {
	withSetup(t, func(ctx Context, args testArgs) {
		args.s1.On("Simple", ctxArg()).Return(nil)
		require.NoError(t, args.c1.Simple(ctx))
	})
}

func TestRequestSubChannel(t *testing.T) {
	ctx, cancel := NewContext(time.Second)
	defer cancel()

	tchan, err := tchannel.NewChannel("svc1", nil)
	require.NoError(t, err, "server NewChannel failed")
	require.NoError(t, tchan.ListenAndServe(":0"), "Listen failed")
	defer tchan.Close()

	clientCh, err := tchannel.NewChannel("client", nil)
	require.NoError(t, err, "client NewChannel failed")
	defer clientCh.Close()
	clientCh.Peers().Add(tchan.PeerInfo().HostPort)

	tests := []tchannel.Registrar{tchan, tchan.GetSubChannel("svc2"), tchan.GetSubChannel("svc3")}
	for _, ch := range tests {
		mockHandler := new(mocks.TChanSecondService)
		server := NewServer(ch)
		server.Register(gen.NewTChanSecondServiceServer(mockHandler))

		client := NewClient(clientCh, ch.ServiceName(), nil)
		secondClient := gen.NewTChanSecondServiceClient(client)

		echoArg := ch.ServiceName()
		echoRes := echoArg + "-echo"
		mockHandler.On("Echo", ctxArg(), echoArg).Return(echoRes, nil)
		res, err := secondClient.Echo(ctx, echoArg)
		assert.NoError(t, err, "Echo failed")
		assert.Equal(t, echoRes, res)
	}
}

func TestThriftError(t *testing.T) {
	thriftErr := &gen.SimpleErr{
		Message: "this is the error",
	}
	withSetup(t, func(ctx Context, args testArgs) {
		args.s1.On("Simple", ctxArg()).Return(thriftErr)
		got := args.c1.Simple(ctx)
		require.Error(t, got)
		require.Equal(t, thriftErr, got)
	})
}

func TestUnknownError(t *testing.T) {
	withSetup(t, func(ctx Context, args testArgs) {
		args.s1.On("Simple", ctxArg()).Return(errors.New("unexpected err"))
		got := args.c1.Simple(ctx)
		require.Error(t, got)
		require.Equal(t, tchannel.NewSystemError(tchannel.ErrCodeUnexpected, "unexpected err"), got)
	})
}

func TestMultiple(t *testing.T) {
	withSetup(t, func(ctx Context, args testArgs) {
		args.s1.On("Simple", ctxArg()).Return(nil)
		args.s2.On("Echo", ctxArg(), "test1").Return("test2", nil)

		require.NoError(t, args.c1.Simple(ctx))
		res, err := args.c2.Echo(ctx, "test1")
		require.NoError(t, err)
		require.Equal(t, "test2", res)
	})
}

func TestHeaders(t *testing.T) {
	reqHeaders := map[string]string{"header1": "value1", "header2": "value2"}
	respHeaders := map[string]string{"resp1": "value1-resp", "resp2": "value2-resp"}

	withSetup(t, func(ctx Context, args testArgs) {
		args.s1.On("Simple", ctxArg()).Return(nil).Run(func(args mock.Arguments) {
			ctx := args.Get(0).(Context)
			assert.Equal(t, reqHeaders, ctx.Headers(), "request headers mismatch")
			ctx.SetResponseHeaders(respHeaders)
		})

		ctx = WithHeaders(ctx, reqHeaders)
		require.NoError(t, args.c1.Simple(ctx))
		assert.Equal(t, respHeaders, ctx.ResponseHeaders(), "response headers mismatch")
	})
}

func TestClientHostPort(t *testing.T) {
	ctx, cancel := NewContext(time.Second * 10)
	defer cancel()

	s1ch, err := testutils.NewServer(nil)
	require.NoError(t, err, "testutils.NewServer failed")
	s2ch, err := testutils.NewServer(nil)
	require.NoError(t, err, "testutils.NewServer failed")
	defer s1ch.Close()
	defer s2ch.Close()

	s1ch.Peers().Add(s2ch.PeerInfo().HostPort)
	s2ch.Peers().Add(s1ch.PeerInfo().HostPort)

	mock1, mock2 := new(mocks.TChanSecondService), new(mocks.TChanSecondService)
	NewServer(s1ch).Register(gen.NewTChanSecondServiceServer(mock1))
	NewServer(s2ch).Register(gen.NewTChanSecondServiceServer(mock2))

	// When we call using a normal client, it can only call the other server (only peer).
	c1 := gen.NewTChanSecondServiceClient(NewClient(s1ch, s2ch.PeerInfo().ServiceName, nil))
	mock2.On("Echo", ctxArg(), "call1").Return("call1", nil)
	res, err := c1.Echo(ctx, "call1")
	assert.NoError(t, err, "call1 failed")
	assert.Equal(t, "call1", res)

	// When we call using a client that specifies host:port, it should call that server.
	c2 := gen.NewTChanSecondServiceClient(NewClient(s1ch, s1ch.PeerInfo().ServiceName, &ClientOptions{
		HostPort: s1ch.PeerInfo().HostPort,
	}))
	mock1.On("Echo", ctxArg(), "call2").Return("call2", nil)
	res, err = c2.Echo(ctx, "call2")
	assert.NoError(t, err, "call2 failed")
	assert.Equal(t, "call2", res)
}

func withSetup(t *testing.T, f func(ctx Context, args testArgs)) {
	args := testArgs{
		s1: new(mocks.TChanSimpleService),
		s2: new(mocks.TChanSecondService),
	}

	ctx, cancel := NewContext(time.Second * 10)
	defer cancel()

	// Start server
	tchan, listener, err := setupServer(args.s1, args.s2)
	require.NoError(t, err)
	defer tchan.Close()

	// Get client1
	args.c1, args.c2, err = getClients(listener.Addr().String())
	require.NoError(t, err)

	f(ctx, args)

	args.s1.AssertExpectations(t)
	args.s2.AssertExpectations(t)
}

func setupServer(h *mocks.TChanSimpleService, sh *mocks.TChanSecondService) (*tchannel.Channel, net.Listener, error) {
	tchan, err := tchannel.NewChannel("service", nil)
	if err != nil {
		return nil, nil, err
	}

	listener, err := net.Listen("tcp", ":0")
	if err != nil {
		return nil, nil, err
	}

	server := NewServer(tchan)
	server.Register(gen.NewTChanSimpleServiceServer(h))
	server.Register(gen.NewTChanSecondServiceServer(sh))

	tchan.Serve(listener)
	return tchan, listener, nil
}

func getClients(dst string) (gen.TChanSimpleService, gen.TChanSecondService, error) {
	tchan, err := tchannel.NewChannel("client", &tchannel.ChannelOptions{
		Logger: tchannel.SimpleLogger,
	})
	if err != nil {
		return nil, nil, err
	}

	tchan.Peers().Add(dst)
	client := NewClient(tchan, "service", nil)

	simpleClient := gen.NewTChanSimpleServiceClient(client)
	secondClient := gen.NewTChanSecondServiceClient(client)
	return simpleClient, secondClient, nil
}
<|endoftext|>


<commit_before>package quicktemplate

import (
	"io"
	"sync"
)

func acquireHTMLEscapeWriter(w io.Writer) io.Writer {
	v := htmlEscapeWriterPool.Get()
	if v == nil {
		v = &htmlEscapeWriter{}
	}
	hw := v.(*htmlEscapeWriter)
	hw.w = w
	return hw
}

func releaseHTMLEscapeWriter(w io.Writer) {
	hw := w.(*htmlEscapeWriter)
	hw.w = nil
	htmlEscapeWriterPool.Put(hw)
}

var htmlEscapeWriterPool sync.Pool

type htmlEscapeWriter struct {
	w io.Writer
}

func (w *htmlEscapeWriter) Write(p []byte) (int, error) {
	i := 0
	ww := w.w
	var (
		b   []byte
		err error
		n   int
	)
	for j, c := range p {
		b = nil
		switch c {
		case '<':
			b = strLT
		case '>':
			b = strGT
		case '"':
			b = strQuot
		case '\'':
			b = strApos
		case '&':
			b = strAmp
		}
		if b != nil {
			if n, err = ww.Write(p[i:j]); err != nil {
				return i + n, err
			}
			if n, err = ww.Write(b); err != nil {
				return j, err
			}
			i = j + 1
		}
	}
	if n, err = ww.Write(p[i:]); err != nil {
		return i + n, err
	}
	return len(p), nil
}

var (
	strLT   = []byte("<")
	strGT   = []byte(">")
	strQuot = []byte(""")
	strApos = []byte("'")
	strAmp  = []byte("&")
)
<commit_msg>Improved html encoding performance for strings without special chars<commit_after>package quicktemplate

import (
	"io"
	"sync"
)

func acquireHTMLEscapeWriter(w io.Writer) io.Writer {
	v := htmlEscapeWriterPool.Get()
	if v == nil {
		v = &htmlEscapeWriter{}
	}
	hw := v.(*htmlEscapeWriter)
	hw.w = w
	return hw
}

func releaseHTMLEscapeWriter(w io.Writer) {
	hw := w.(*htmlEscapeWriter)
	hw.w = nil
	htmlEscapeWriterPool.Put(hw)
}

var htmlEscapeWriterPool sync.Pool

type htmlEscapeWriter struct {
	w io.Writer
}

func (w *htmlEscapeWriter) Write(b []byte) (int, error) {
	write := w.w.Write
	j := 0
	for i, c := range b {
		switch c {
		case '<':
			write(b[j:i])
			write(strLT)
			j = i + 1
		case '>':
			write(b[j:i])
			write(strGT)
			j = i + 1
		case '"':
			write(b[j:i])
			write(strQuot)
			j = i + 1
		case '\'':
			write(b[j:i])
			write(strApos)
			j = i + 1
		case '&':
			write(b[j:i])
			write(strAmp)
			j = i + 1
		}
	}
	if n, err := write(b[j:]); err != nil {
		return j + n, err
	}
	return len(b), nil
}

var (
	strLT   = []byte("<")
	strGT   = []byte(">")
	strQuot = []byte(""")
	strApos = []byte("'")
	strAmp  = []byte("&")
)
<|endoftext|>


<commit_before>// myLG is command line looking glass that written with Go language
// it tries from its own icmp and external looking glasses tools
package main

import (
	"errors"
	"fmt"
	"github.com/briandowns/spinner"
	"os/exec"
	"regexp"
	"runtime"
	"strings"
	"time"

	"github.com/mehrdadrad/mylg/cli"
	"github.com/mehrdadrad/mylg/http/ping"
	"github.com/mehrdadrad/mylg/icmp"
	"github.com/mehrdadrad/mylg/lg"
	"github.com/mehrdadrad/mylg/ns"
	"github.com/mehrdadrad/mylg/packet"
	"github.com/mehrdadrad/mylg/peeringdb"
	"github.com/mehrdadrad/mylg/scan"
	"github.com/mehrdadrad/mylg/services/httpd"
	"github.com/mehrdadrad/mylg/whois"
)

// Provider represents looking glass
type Provider interface {
	Set(host, version string)
	GetDefaultNode() string
	GetNodes() []string
	ChangeNode(node string) bool
	Ping() (string, error)
	Trace() chan string
	BGP() chan string
}

const (
	version = "0.1.9"
)

var (
	// register looking glass hosts
	providers = map[string]Provider{"telia": new(lg.Telia), "level3": new(lg.Level3), "cogent": new(lg.Cogent)}
	pNames    = providerNames()
	req       = make(chan string, 1)
	nxt       = make(chan struct{}, 1)
	spin      = spinner.New(spinner.CharSets[26], 220*time.Millisecond)
	args      string
	prompt    string
	cPName    string
	nsr       *ns.Request
	c         *cli.Readline
)

// providerName
func providerNames() []string {
	pNames := []string{}
	for p := range providers {
		pNames = append(pNames, p)
	}
	return pNames
}

// validateProvider
func validateProvider(p string) (string, error) {
	pNames := []string{}
	match, _ := regexp.MatchString("("+strings.Join(pNames, "|")+")", p)
	p = strings.ToLower(p)
	if match {
		return p, nil
	}
	return "", errors.New("provider not support")

}
func init() {
	// initialize cli
	c = cli.Init("local", version)
	go c.Run(req, nxt)
	// initialize name server
	nsr = ns.NewRequest()
	go nsr.Init()
	// start web server
	go httpd.Run()
	// set default provider
	cPName = "local"
}

func main() {
	var (
		request string
		loop    = true
	)

	for loop {
		select {
		case request, loop = <-req:
			if !loop {
				break
			}
			if len(request) < 1 {
				c.Next()
				continue
			}
			subReq := cli.CMDReg.FindStringSubmatch(request)
			if len(subReq) == 0 {
				println("syntax error")
				c.Next()
				continue
			}
			prompt = c.GetPrompt()
			args = strings.TrimSpace(subReq[2])
			cmd := strings.TrimSpace(subReq[1])
			switch {
			case cmd == "hping" && cPName == "local":
				hping()
			case cmd == "ping" && cPName == "local":
				pingLocal()
			case cmd == "ping":
				pingLG()
			case cmd == "trace":
				trace()
			case cmd == "bgp":
				BGP()
			case cmd == "dig":
				nsr.Dig(args)
			case cmd == "node":
				node()
			case cmd == "local":
				nsr.Local()
				cPName = "local"
				c.SetPrompt(cPName)
			case cmd == "connect":
				connect()
			case cmd == "lg":
				c.SetPrompt("lg")
				c.UpdateCompleter("connect", pNames)
			case cmd == "ns":
				c.UpdateCompleter("connect", nsr.CountryList())
				c.UpdateCompleter("node", []string{})
				c.SetPrompt("ns")
			case cmd == "whois":
				whois.Lookup(args)
			case cmd == "peering":
				peeringdb.Search(args)
			case cmd == "scan":
				scanPorts()
			case cmd == "mode":
				mode()
			case cmd == "web":
				web()
			case cmd == "dump":
				dump()
			case cmd == "help":
				c.Help()
			case cmd == "exit", cmd == "quit":
				c.Close(nxt)
				close(req)
			}
			// next line
			c.Next()
		}
	}
}

// node handles node cmd
func node() {
	switch {
	case strings.HasPrefix(prompt, "lg"):
		if _, ok := providers[cPName]; ok {
			if providers[cPName].ChangeNode(args) {
				c.UpdatePromptN(args, 3)
				return
			}
		}
		println("the specified node doesn't support")
	case strings.HasPrefix(prompt, "ns"):
		if !nsr.ChkNode(args) {
			println("error: argument is not valid")
		} else {
			c.UpdatePromptN(args, 3)
		}
	default:
		if cPName == "local" {
			println("local doesn't support node")
		}

	}
}

// web tries to open web interface at default web browser
func web() {
	var openCmd = "open"
	println("opening default web broswer ...")
	if runtime.GOOS != "darwin" {
		openCmd = "xdg-open"
	}
	cmd := exec.Command(openCmd, "http://localhost:8080")
	err := cmd.Start()
	if err != nil {
		println("error opening default browser")
	}

}

// dump provides decoding packets
func dump() {
	p, err := packet.NewPacket(args)
	if err != nil {
		return
	}
	for l := range p.Open() {
		l.PrintPretty()
	}
}

// connect handles connect cmd
func connect() {
	var (
		pName string
		err   error
	)
	switch {
	case strings.HasPrefix(prompt, "lg"):
		if pName, err = validateProvider(args); err != nil {
			println("provider not available")
			c.Next()
			return
		}
		cPName = pName
		if _, ok := providers[cPName]; ok {
			c.UpdatePromptN(cPName+"/"+providers[cPName].GetDefaultNode(), 2)
			go func() {
				c.UpdateCompleter("node", providers[cPName].GetNodes())
			}()
		} else {
			println("it doesn't support")
		}
	case strings.HasPrefix(prompt, "ns"):
		if !nsr.ChkCountry(args) {
			println("error: argument is not valid")
		} else {
			c.SetPrompt("ns/" + args)
			c.UpdateCompleter("node", nsr.NodeList())
		}
	}
}

// mode set editor mode
func mode() {
	if args == "vim" {
		c.SetVim()
	} else if args == "emacs" {
		c.SetEmacs()
	} else {
		println("the request mode doesn't support")
	}
}

// trace tries to trace from local and lg
func trace() {
	switch {
	case strings.HasPrefix(prompt, "local"):
		trace := icmp.Trace{}
		trace.Run(args)
	case strings.HasPrefix(prompt, "lg"):
		providers[cPName].Set(args, "ipv4")
		for l := range providers[cPName].Trace() {
			println(l)
		}
	}
}

// hping tries to ping a web server by http
func hping() {
	p, err := ping.NewPing(args)
	if err != nil {
		println(err.Error())
	} else {
		p.Run()
	}
}

// pingLG tries to ping through a looking glass
func pingLG() {
	spin.Prefix = "please wait "
	spin.Start()
	providers[cPName].Set(args, "ipv4")
	m, err := providers[cPName].Ping()
	spin.Stop()
	if err != nil {
		println(err.Error())
	} else {
		println(m)
	}
}

// pingLocal tries to ping from local source ip
func pingLocal() {
	pFmt := "%d bytes from %s icmp_seq=%d time=%f ms"
	p, err := icmp.NewPing(args)
	if err != nil {
		return
	}
	resp := p.Run()
	for r := range resp {
		if r.Error != nil {
			println(r.Error.Error())
			continue
		}
		msg := fmt.Sprintf(pFmt, r.Size, r.Addr, r.Sequence, r.RTT)
		println(msg)
	}
}

// scanPorts tries to scan tcp/ip ports
func scanPorts() {
	scan, err := scan.NewScan(args)
	if err != nil {
		println(err.Error())
	} else {
		scan.Run()
	}
}

// BGP tries to get BGP lookup from a LG
func BGP() {
	if cPName == "local" {
		println("no provider selected")
		return
	}
	providers[cPName].Set(args, "ipv4")
	for l := range providers[cPName].BGP() {
		println(l)
	}
}
<commit_msg>bump version : 0.2.0 after 299 commits<commit_after>// myLG is command line looking glass that written with Go language
// it tries from its own icmp and external looking glasses tools
package main

import (
	"errors"
	"fmt"
	"github.com/briandowns/spinner"
	"os/exec"
	"regexp"
	"runtime"
	"strings"
	"time"

	"github.com/mehrdadrad/mylg/cli"
	"github.com/mehrdadrad/mylg/http/ping"
	"github.com/mehrdadrad/mylg/icmp"
	"github.com/mehrdadrad/mylg/lg"
	"github.com/mehrdadrad/mylg/ns"
	"github.com/mehrdadrad/mylg/packet"
	"github.com/mehrdadrad/mylg/peeringdb"
	"github.com/mehrdadrad/mylg/scan"
	"github.com/mehrdadrad/mylg/services/httpd"
	"github.com/mehrdadrad/mylg/whois"
)

// Provider represents looking glass
type Provider interface {
	Set(host, version string)
	GetDefaultNode() string
	GetNodes() []string
	ChangeNode(node string) bool
	Ping() (string, error)
	Trace() chan string
	BGP() chan string
}

const (
	version = "0.2.0"
)

var (
	// register looking glass hosts
	providers = map[string]Provider{"telia": new(lg.Telia), "level3": new(lg.Level3), "cogent": new(lg.Cogent)}
	pNames    = providerNames()
	req       = make(chan string, 1)
	nxt       = make(chan struct{}, 1)
	spin      = spinner.New(spinner.CharSets[26], 220*time.Millisecond)
	args      string
	prompt    string
	cPName    string
	nsr       *ns.Request
	c         *cli.Readline
)

// providerName
func providerNames() []string {
	pNames := []string{}
	for p := range providers {
		pNames = append(pNames, p)
	}
	return pNames
}

// validateProvider
func validateProvider(p string) (string, error) {
	pNames := []string{}
	match, _ := regexp.MatchString("("+strings.Join(pNames, "|")+")", p)
	p = strings.ToLower(p)
	if match {
		return p, nil
	}
	return "", errors.New("provider not support")

}
func init() {
	// initialize cli
	c = cli.Init("local", version)
	go c.Run(req, nxt)
	// initialize name server
	nsr = ns.NewRequest()
	go nsr.Init()
	// start web server
	go httpd.Run()
	// set default provider
	cPName = "local"
}

func main() {
	var (
		request string
		loop    = true
	)

	for loop {
		select {
		case request, loop = <-req:
			if !loop {
				break
			}
			if len(request) < 1 {
				c.Next()
				continue
			}
			subReq := cli.CMDReg.FindStringSubmatch(request)
			if len(subReq) == 0 {
				println("syntax error")
				c.Next()
				continue
			}
			prompt = c.GetPrompt()
			args = strings.TrimSpace(subReq[2])
			cmd := strings.TrimSpace(subReq[1])
			switch {
			case cmd == "hping" && cPName == "local":
				hping()
			case cmd == "ping" && cPName == "local":
				pingLocal()
			case cmd == "ping":
				pingLG()
			case cmd == "trace":
				trace()
			case cmd == "bgp":
				BGP()
			case cmd == "dig":
				nsr.Dig(args)
			case cmd == "node":
				node()
			case cmd == "local":
				nsr.Local()
				cPName = "local"
				c.SetPrompt(cPName)
			case cmd == "connect":
				connect()
			case cmd == "lg":
				c.SetPrompt("lg")
				c.UpdateCompleter("connect", pNames)
			case cmd == "ns":
				c.UpdateCompleter("connect", nsr.CountryList())
				c.UpdateCompleter("node", []string{})
				c.SetPrompt("ns")
			case cmd == "whois":
				whois.Lookup(args)
			case cmd == "peering":
				peeringdb.Search(args)
			case cmd == "scan":
				scanPorts()
			case cmd == "mode":
				mode()
			case cmd == "web":
				web()
			case cmd == "dump":
				dump()
			case cmd == "help":
				c.Help()
			case cmd == "exit", cmd == "quit":
				c.Close(nxt)
				close(req)
			}
			// next line
			c.Next()
		}
	}
}

// node handles node cmd
func node() {
	switch {
	case strings.HasPrefix(prompt, "lg"):
		if _, ok := providers[cPName]; ok {
			if providers[cPName].ChangeNode(args) {
				c.UpdatePromptN(args, 3)
				return
			}
		}
		println("the specified node doesn't support")
	case strings.HasPrefix(prompt, "ns"):
		if !nsr.ChkNode(args) {
			println("error: argument is not valid")
		} else {
			c.UpdatePromptN(args, 3)
		}
	default:
		if cPName == "local" {
			println("local doesn't support node")
		}

	}
}

// web tries to open web interface at default web browser
func web() {
	var openCmd = "open"
	println("opening default web broswer ...")
	if runtime.GOOS != "darwin" {
		openCmd = "xdg-open"
	}
	cmd := exec.Command(openCmd, "http://localhost:8080")
	err := cmd.Start()
	if err != nil {
		println("error opening default browser")
	}

}

// dump provides decoding packets
func dump() {
	p, err := packet.NewPacket(args)
	if err != nil {
		return
	}
	for l := range p.Open() {
		l.PrintPretty()
	}
}

// connect handles connect cmd
func connect() {
	var (
		pName string
		err   error
	)
	switch {
	case strings.HasPrefix(prompt, "lg"):
		if pName, err = validateProvider(args); err != nil {
			println("provider not available")
			c.Next()
			return
		}
		cPName = pName
		if _, ok := providers[cPName]; ok {
			c.UpdatePromptN(cPName+"/"+providers[cPName].GetDefaultNode(), 2)
			go func() {
				c.UpdateCompleter("node", providers[cPName].GetNodes())
			}()
		} else {
			println("it doesn't support")
		}
	case strings.HasPrefix(prompt, "ns"):
		if !nsr.ChkCountry(args) {
			println("error: argument is not valid")
		} else {
			c.SetPrompt("ns/" + args)
			c.UpdateCompleter("node", nsr.NodeList())
		}
	}
}

// mode set editor mode
func mode() {
	if args == "vim" {
		c.SetVim()
	} else if args == "emacs" {
		c.SetEmacs()
	} else {
		println("the request mode doesn't support")
	}
}

// trace tries to trace from local and lg
func trace() {
	switch {
	case strings.HasPrefix(prompt, "local"):
		trace := icmp.Trace{}
		trace.Run(args)
	case strings.HasPrefix(prompt, "lg"):
		providers[cPName].Set(args, "ipv4")
		for l := range providers[cPName].Trace() {
			println(l)
		}
	}
}

// hping tries to ping a web server by http
func hping() {
	p, err := ping.NewPing(args)
	if err != nil {
		println(err.Error())
	} else {
		p.Run()
	}
}

// pingLG tries to ping through a looking glass
func pingLG() {
	spin.Prefix = "please wait "
	spin.Start()
	providers[cPName].Set(args, "ipv4")
	m, err := providers[cPName].Ping()
	spin.Stop()
	if err != nil {
		println(err.Error())
	} else {
		println(m)
	}
}

// pingLocal tries to ping from local source ip
func pingLocal() {
	pFmt := "%d bytes from %s icmp_seq=%d time=%f ms"
	p, err := icmp.NewPing(args)
	if err != nil {
		return
	}
	resp := p.Run()
	for r := range resp {
		if r.Error != nil {
			println(r.Error.Error())
			continue
		}
		msg := fmt.Sprintf(pFmt, r.Size, r.Addr, r.Sequence, r.RTT)
		println(msg)
	}
}

// scanPorts tries to scan tcp/ip ports
func scanPorts() {
	scan, err := scan.NewScan(args)
	if err != nil {
		println(err.Error())
	} else {
		scan.Run()
	}
}

// BGP tries to get BGP lookup from a LG
func BGP() {
	if cPName == "local" {
		println("no provider selected")
		return
	}
	providers[cPName].Set(args, "ipv4")
	for l := range providers[cPName].BGP() {
		println(l)
	}
}
<|endoftext|>


<commit_before>// myLG is command line looking glass that written with Go language
// it tries from its own icmp and external looking glasses tools
package main

import (
	"errors"
	"github.com/briandowns/spinner"
	"regexp"
	"strings"
	"time"

	"github.com/mehrdadrad/mylg/cli"
	"github.com/mehrdadrad/mylg/http/ping"
	"github.com/mehrdadrad/mylg/http/server"
	"github.com/mehrdadrad/mylg/icmp"
	"github.com/mehrdadrad/mylg/lg"
	"github.com/mehrdadrad/mylg/ns"
	"github.com/mehrdadrad/mylg/peeringdb"
	"github.com/mehrdadrad/mylg/scan"
	"github.com/mehrdadrad/mylg/whois"
)

// Provider represents looking glass
type Provider interface {
	Set(host, version string)
	GetDefaultNode() string
	GetNodes() []string
	ChangeNode(node string) bool
	Ping() (string, error)
	Trace() chan string
	BGP() chan string
}

const (
	version = "0.1.8"
)

var (
	// register looking glass hosts
	providers = map[string]Provider{"telia": new(lg.Telia), "level3": new(lg.Level3), "cogent": new(lg.Cogent)}
	pNames    = providerNames()
	req       = make(chan string, 1)
	nxt       = make(chan struct{}, 1)
	spin      = spinner.New(spinner.CharSets[26], 220*time.Millisecond)
	args      string
	prompt    string
	cPName    string
	nsr       *ns.Request
	c         *cli.Readline
)

// providerName
func providerNames() []string {
	pNames := []string{}
	for p := range providers {
		pNames = append(pNames, p)
	}
	return pNames
}

// validateProvider
func validateProvider(p string) (string, error) {
	pNames := []string{}
	match, _ := regexp.MatchString("("+strings.Join(pNames, "|")+")", p)
	p = strings.ToLower(p)
	if match {
		return p, nil
	}
	return "", errors.New("provider not support")

}
func init() {
	// initialize cli
	c = cli.Init("local", version)
	go c.Run(req, nxt)
	// initialize name server
	nsr = ns.NewRequest()
	go nsr.Init()
	// start web server
	go server.Run()
	// set default provider
	cPName = "local"
}

func main() {
	var (
		request string
		loop    = true
	)

	r, _ := regexp.Compile(`(ping|trace|bgp|lg|ns|dig|whois|peering|scan|hping|connect|node|local|mode|help|exit|quit)\s{0,1}(.*)`)

	for loop {
		select {
		case request, loop = <-req:
			if !loop {
				break
			}
			if len(request) < 1 {
				c.Next()
				continue
			}
			subReq := r.FindStringSubmatch(request)
			if len(subReq) == 0 {
				println("syntax error")
				c.Next()
				continue
			}
			prompt = c.GetPrompt()
			args = strings.TrimSpace(subReq[2])
			cmd := strings.TrimSpace(subReq[1])
			switch {
			case cmd == "hping" && cPName == "local":
				hping()
			case cmd == "ping" && cPName == "local":
				pingLocal()
			case cmd == "ping":
				pingLG()
			case cmd == "trace":
				trace()
			case cmd == "bgp":
				BGP()
			case cmd == "dig":
				nsr.Dig(args)
			case cmd == "node":
				node()
			case cmd == "local":
				nsr.Local()
				cPName = "local"
				c.SetPrompt(cPName)
			case cmd == "connect":
				connect()
			case cmd == "lg":
				c.SetPrompt("lg")
				c.UpdateCompleter("connect", pNames)
			case cmd == "ns":
				c.UpdateCompleter("connect", nsr.CountryList())
				c.UpdateCompleter("node", []string{})
				c.SetPrompt("ns")
			case cmd == "whois":
				whois.Lookup(args)
			case cmd == "peering":
				peeringdb.Search(args)
			case cmd == "scan":
				scanPorts()
			case cmd == "mode":
				mode()
			case cmd == "help":
				c.Help()
			case cmd == "exit", cmd == "quit":
				c.Close(nxt)
				close(req)
			}
			// next line
			c.Next()
		}
	}
}

// node handles node cmd
func node() {
	switch {
	case strings.HasPrefix(prompt, "lg"):
		if _, ok := providers[cPName]; ok {
			if providers[cPName].ChangeNode(args) {
				c.UpdatePromptN(args, 3)
				return
			}
		}
		println("the specified node doesn't support")
	case strings.HasPrefix(prompt, "ns"):
		if !nsr.ChkNode(args) {
			println("error: argument is not valid")
		} else {
			c.UpdatePromptN(args, 3)
		}
	default:
		if cPName == "local" {
			println("local doesn't support node")
		}

	}
}

// connect handles connect cmd
func connect() {
	var (
		pName string
		err   error
	)
	switch {
	case strings.HasPrefix(prompt, "lg"):
		if pName, err = validateProvider(args); err != nil {
			println("provider not available")
			c.Next()
			return
		}
		cPName = pName
		if _, ok := providers[cPName]; ok {
			c.UpdatePromptN(cPName+"/"+providers[cPName].GetDefaultNode(), 2)
			go func() {
				c.UpdateCompleter("node", providers[cPName].GetNodes())
			}()
		} else {
			println("it doesn't support")
		}
	case strings.HasPrefix(prompt, "ns"):
		if !nsr.ChkCountry(args) {
			println("error: argument is not valid")
		} else {
			c.SetPrompt("ns/" + args)
			c.UpdateCompleter("node", nsr.NodeList())
		}
	}
}

// mode set editor mode
func mode() {
	if args == "vim" {
		c.SetVim()
	} else if args == "emacs" {
		c.SetEmacs()
	} else {
		println("the request mode doesn't support")
	}
}

// trace tries to trace from local and lg
func trace() {
	switch {
	case strings.HasPrefix(prompt, "local"):
		trace := icmp.Trace{}
		trace.Run(args)
	case strings.HasPrefix(prompt, "lg"):
		providers[cPName].Set(args, "ipv4")
		for l := range providers[cPName].Trace() {
			println(l)
		}
	}
}

// hping tries to ping a web server by http
func hping() {
	p, err := ping.NewPing(args)
	if err != nil {
		println(err.Error())
	} else {
		p.Run()
	}
}

// pingLG tries to ping through a looking glass
func pingLG() {
	spin.Prefix = "please wait "
	spin.Start()
	providers[cPName].Set(args, "ipv4")
	m, err := providers[cPName].Ping()
	spin.Stop()
	if err != nil {
		println(err.Error())
	} else {
		println(m)
	}
}

// pingLocal tries to ping from local source ip
func pingLocal() {
	p, err := icmp.NewPing(args)
	if err != nil {
		return
	}
	p.Run()
}

// scanPorts tries to scan tcp/ip ports
func scanPorts() {
	scan, err := scan.NewScan(args)
	if err != nil {
		println(err.Error())
	} else {
		scan.Run()
	}
}

// BGP tries to get BGP lookup from a LG
func BGP() {
	if cPName == "local" {
		println("no provider selected")
		return
	}
	providers[cPName].Set(args, "ipv4")
	for l := range providers[cPName].BGP() {
		println(l)
	}
}
<commit_msg>changed mylg to work w/ new icmp.run<commit_after>// myLG is command line looking glass that written with Go language
// it tries from its own icmp and external looking glasses tools
package main

import (
	"errors"
	"fmt"
	"github.com/briandowns/spinner"
	"regexp"
	"strings"
	"time"

	"github.com/mehrdadrad/mylg/cli"
	"github.com/mehrdadrad/mylg/http/ping"
	"github.com/mehrdadrad/mylg/http/server"
	"github.com/mehrdadrad/mylg/icmp"
	"github.com/mehrdadrad/mylg/lg"
	"github.com/mehrdadrad/mylg/ns"
	"github.com/mehrdadrad/mylg/peeringdb"
	"github.com/mehrdadrad/mylg/scan"
	"github.com/mehrdadrad/mylg/whois"
)

// Provider represents looking glass
type Provider interface {
	Set(host, version string)
	GetDefaultNode() string
	GetNodes() []string
	ChangeNode(node string) bool
	Ping() (string, error)
	Trace() chan string
	BGP() chan string
}

const (
	version = "0.1.8"
)

var (
	// register looking glass hosts
	providers = map[string]Provider{"telia": new(lg.Telia), "level3": new(lg.Level3), "cogent": new(lg.Cogent)}
	pNames    = providerNames()
	req       = make(chan string, 1)
	nxt       = make(chan struct{}, 1)
	spin      = spinner.New(spinner.CharSets[26], 220*time.Millisecond)
	args      string
	prompt    string
	cPName    string
	nsr       *ns.Request
	c         *cli.Readline
)

// providerName
func providerNames() []string {
	pNames := []string{}
	for p := range providers {
		pNames = append(pNames, p)
	}
	return pNames
}

// validateProvider
func validateProvider(p string) (string, error) {
	pNames := []string{}
	match, _ := regexp.MatchString("("+strings.Join(pNames, "|")+")", p)
	p = strings.ToLower(p)
	if match {
		return p, nil
	}
	return "", errors.New("provider not support")

}
func init() {
	// initialize cli
	c = cli.Init("local", version)
	go c.Run(req, nxt)
	// initialize name server
	nsr = ns.NewRequest()
	go nsr.Init()
	// start web server
	go server.Run()
	// set default provider
	cPName = "local"
}

func main() {
	var (
		request string
		loop    = true
	)

	r, _ := regexp.Compile(`(ping|trace|bgp|lg|ns|dig|whois|peering|scan|hping|connect|node|local|mode|help|exit|quit)\s{0,1}(.*)`)

	for loop {
		select {
		case request, loop = <-req:
			if !loop {
				break
			}
			if len(request) < 1 {
				c.Next()
				continue
			}
			subReq := r.FindStringSubmatch(request)
			if len(subReq) == 0 {
				println("syntax error")
				c.Next()
				continue
			}
			prompt = c.GetPrompt()
			args = strings.TrimSpace(subReq[2])
			cmd := strings.TrimSpace(subReq[1])
			switch {
			case cmd == "hping" && cPName == "local":
				hping()
			case cmd == "ping" && cPName == "local":
				pingLocal()
			case cmd == "ping":
				pingLG()
			case cmd == "trace":
				trace()
			case cmd == "bgp":
				BGP()
			case cmd == "dig":
				nsr.Dig(args)
			case cmd == "node":
				node()
			case cmd == "local":
				nsr.Local()
				cPName = "local"
				c.SetPrompt(cPName)
			case cmd == "connect":
				connect()
			case cmd == "lg":
				c.SetPrompt("lg")
				c.UpdateCompleter("connect", pNames)
			case cmd == "ns":
				c.UpdateCompleter("connect", nsr.CountryList())
				c.UpdateCompleter("node", []string{})
				c.SetPrompt("ns")
			case cmd == "whois":
				whois.Lookup(args)
			case cmd == "peering":
				peeringdb.Search(args)
			case cmd == "scan":
				scanPorts()
			case cmd == "mode":
				mode()
			case cmd == "help":
				c.Help()
			case cmd == "exit", cmd == "quit":
				c.Close(nxt)
				close(req)
			}
			// next line
			c.Next()
		}
	}
}

// node handles node cmd
func node() {
	switch {
	case strings.HasPrefix(prompt, "lg"):
		if _, ok := providers[cPName]; ok {
			if providers[cPName].ChangeNode(args) {
				c.UpdatePromptN(args, 3)
				return
			}
		}
		println("the specified node doesn't support")
	case strings.HasPrefix(prompt, "ns"):
		if !nsr.ChkNode(args) {
			println("error: argument is not valid")
		} else {
			c.UpdatePromptN(args, 3)
		}
	default:
		if cPName == "local" {
			println("local doesn't support node")
		}

	}
}

// connect handles connect cmd
func connect() {
	var (
		pName string
		err   error
	)
	switch {
	case strings.HasPrefix(prompt, "lg"):
		if pName, err = validateProvider(args); err != nil {
			println("provider not available")
			c.Next()
			return
		}
		cPName = pName
		if _, ok := providers[cPName]; ok {
			c.UpdatePromptN(cPName+"/"+providers[cPName].GetDefaultNode(), 2)
			go func() {
				c.UpdateCompleter("node", providers[cPName].GetNodes())
			}()
		} else {
			println("it doesn't support")
		}
	case strings.HasPrefix(prompt, "ns"):
		if !nsr.ChkCountry(args) {
			println("error: argument is not valid")
		} else {
			c.SetPrompt("ns/" + args)
			c.UpdateCompleter("node", nsr.NodeList())
		}
	}
}

// mode set editor mode
func mode() {
	if args == "vim" {
		c.SetVim()
	} else if args == "emacs" {
		c.SetEmacs()
	} else {
		println("the request mode doesn't support")
	}
}

// trace tries to trace from local and lg
func trace() {
	switch {
	case strings.HasPrefix(prompt, "local"):
		trace := icmp.Trace{}
		trace.Run(args)
	case strings.HasPrefix(prompt, "lg"):
		providers[cPName].Set(args, "ipv4")
		for l := range providers[cPName].Trace() {
			println(l)
		}
	}
}

// hping tries to ping a web server by http
func hping() {
	p, err := ping.NewPing(args)
	if err != nil {
		println(err.Error())
	} else {
		p.Run()
	}
}

// pingLG tries to ping through a looking glass
func pingLG() {
	spin.Prefix = "please wait "
	spin.Start()
	providers[cPName].Set(args, "ipv4")
	m, err := providers[cPName].Ping()
	spin.Stop()
	if err != nil {
		println(err.Error())
	} else {
		println(m)
	}
}

// pingLocal tries to ping from local source ip
func pingLocal() {
	pFmt := "%d bytes from %s icmp_seq=%d time=%f ms"
	p, err := icmp.NewPing(args)
	if err != nil {
		return
	}
	resp := p.Run()
	for r := range resp {
		if r.Error != nil {
			println(r.Error.Error())
			continue
		}
		msg := fmt.Sprintf(pFmt, r.Size, r.Addr, r.Sequence, r.RTT)
		println(msg)
	}
}

// scanPorts tries to scan tcp/ip ports
func scanPorts() {
	scan, err := scan.NewScan(args)
	if err != nil {
		println(err.Error())
	} else {
		scan.Run()
	}
}

// BGP tries to get BGP lookup from a LG
func BGP() {
	if cPName == "local" {
		println("no provider selected")
		return
	}
	providers[cPName].Set(args, "ipv4")
	for l := range providers[cPName].BGP() {
		println(l)
	}
}
<|endoftext|>


<commit_before>package handler

import (
	"fmt"
	"net/http"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/sprungknoedl/reputile/lib"
	"github.com/sprungknoedl/reputile/lists"
	"github.com/sprungknoedl/reputile/model"
	"golang.org/x/net/context"
)

func UpdateDatabase(w http.ResponseWriter, r *http.Request) {
	// create new background context & copy db handle
	db := lib.NewContext(r).Value(lib.DatabaseKey).(*model.Datastore)
	ctx := context.WithValue(context.Background(), lib.DatabaseKey, db)

	go func() {
		count := 0
		start := time.Now()

		// "convert" List to Iterator
		its := make([]lists.Iterator, len(lists.Lists))
		for i, list := range lists.Lists {
			its[i] = list
		}

		ch := lists.Combine(its...).Run(ctx)

		for entry := range ch {
			if entry.Err != nil {
				logrus.Errorf("(%s) failed to fetch entry: %v", entry.Source, entry.Err)
				return
			}

			count++
			err := model.Store(ctx, entry)
			if err != nil {
				logrus.Errorf("(%s) failed to store entry: %v", entry.Source, err)
				return
			}
		}

		model.Prune(ctx)
		logrus.Printf("added %d entries in %v", count, time.Since(start))
	}()

	fmt.Fprintf(w, "dispatched update job")
}
<commit_msg>simple authorization for update handler<commit_after>package handler

import (
	"fmt"
	"net/http"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/spf13/viper"
	"github.com/sprungknoedl/reputile/lib"
	"github.com/sprungknoedl/reputile/lists"
	"github.com/sprungknoedl/reputile/model"
	"golang.org/x/net/context"
)

func UpdateDatabase(w http.ResponseWriter, r *http.Request) {
	// check if update request is authorized
	token := viper.GetString("update_token")
	if r.Header.Get("authorization") != "Token "+token {
		w.WriteHeader(http.StatusForbidden)
		w.Write([]byte("update token required\n"))
		return
	}

	// create new background context & copy db handle
	db := lib.NewContext(r).Value(lib.DatabaseKey).(*model.Datastore)
	ctx := context.WithValue(context.Background(), lib.DatabaseKey, db)

	go func() {
		count := 0
		start := time.Now()

		// "convert" List to Iterator
		its := make([]lists.Iterator, len(lists.Lists))
		for i, list := range lists.Lists {
			its[i] = list
		}

		ch := lists.Combine(its...).Run(ctx)

		for entry := range ch {
			if entry.Err != nil {
				logrus.Errorf("(%s) failed to fetch entry: %v", entry.Source, entry.Err)
				return
			}

			count++
			err := model.Store(ctx, entry)
			if err != nil {
				logrus.Errorf("(%s) failed to store entry: %v", entry.Source, err)
				return
			}
		}

		model.Prune(ctx)
		logrus.Printf("added %d entries in %v", count, time.Since(start))
	}()

	fmt.Fprintf(w, "dispatched update job")
}
<|endoftext|>


<commit_before>package watcher

import (
	. "github.com/smartystreets/goconvey/convey"
	"github.com/smartystreets/goconvey/web/server/contract"
	"github.com/smartystreets/goconvey/web/server/system"
	"testing"
	"time"
)

func TestScanner(t *testing.T) {
	var fixture *scannerFixture
	var changed bool

	Convey("To begin with, the scanner is provided a contrived file system environment", t, func() {
		fixture = newScannerFixture()

		Convey("When we call Scan() for the first time", func() {
			changed = fixture.scan()

			Convey("The scanner should report a change in state", func() {
				So(changed, ShouldBeTrue)
			})
		})

		Convey("Then, on subsequent calls to Scan()", func() {
			changed = fixture.scan()

			Convey("When the file system has not changed in any way", func() {

				Convey("The scanner should NOT report any change in state", func() {
					So(fixture.scan(), ShouldBeFalse)
				})
			})

			Convey("When a new go file is created within a watched folder", func() {
				fixture.fs.Create("/root/new_stuff.go", 42, time.Now())

				Convey("The scanner should report a change in state", func() {
					So(fixture.scan(), ShouldBeTrue)
				})
			})

			Convey("When an existing go file within a watched folder has been modified", func() {
				fixture.fs.Modify("/root/sub/file.go")

				Convey("The scanner should report a change in state", func() {
					So(fixture.scan(), ShouldBeTrue)
				})
			})

			Convey("When an existing go file within a watched folder has been renamed", func() {
				fixture.fs.Rename("/root/sub/file.go", "/root/sub/asdf.go")

				Convey("The scanner should report a change in state", func() {
					So(fixture.scan(), ShouldBeTrue)
				})
			})

			Convey("When an existing go file within a watched folder has been deleted", func() {
				fixture.fs.Delete("/root/sub/file.go")

				Convey("The scanner should report a change in state", func() {
					So(fixture.scan(), ShouldBeTrue)
				})
			})

			Convey("When a go file is created outside any watched folders", func() {
				fixture.fs.Create("/outside/new_stuff.go", 42, time.Now())

				Convey("The scanner should NOT report a change in state", func() {
					So(fixture.scan(), ShouldBeFalse)
				})
			})

			Convey("When a go file is modified outside any watched folders", func() {
				fixture.fs.Create("/outside/new_stuff.go", 42, time.Now())
				fixture.scan() // reset

				Convey("The scanner should NOT report a change in state", func() {
					So(fixture.scan(), ShouldBeFalse)
				})
			})

			Convey("When a go file is renamed outside any watched folders", func() {
				fixture.fs.Create("/outside/new_stuff.go", 42, time.Now())
				fixture.scan() // reset
				fixture.fs.Rename("/outside/new_stuff.go", "/outside/newer_stoff.go")

				Convey("The scanner should NOT report a change in state", func() {
					So(fixture.scan(), ShouldBeFalse)
				})
			})

			Convey("When a go file is deleted outside any watched folders", func() {
				fixture.fs.Create("/outside/new_stuff.go", 42, time.Now())
				fixture.scan() // reset
				fixture.fs.Delete("/outside/new_stuff.go")

				Convey("The scanner should NOT report a change in state", func() {
					So(fixture.scan(), ShouldBeFalse)
				})
			})

			Convey("When a miscellaneous file is created", func() {
				fixture.fs.Create("/root/new_stuff.MISC", 42, time.Now())

				Convey("The scanner should NOT report a change in state", func() {
					So(fixture.scan(), ShouldBeFalse)
				})
			})

			Convey("When a miscellaneous file is modified", func() {
				fixture.fs.Create("/root/new_stuff.MISC", 42, time.Now())
				fixture.scan() // reset

				Convey("The scanner should NOT report a change in state", func() {
					So(fixture.scan(), ShouldBeFalse)
				})
			})

			Convey("When a miscellaneous file is renamed", func() {
				fixture.fs.Create("/root/new_stuff.MISC", 42, time.Now())
				fixture.scan() // reset
				fixture.fs.Rename("/root/new_stuff.MISC", "/root/newer_stoff.MISC")

				Convey("The scanner should NOT report a change in state", func() {
					So(fixture.scan(), ShouldBeFalse)
				})
			})

			Convey("When a miscellaneous file is deleted", func() {
				fixture.fs.Create("/root/new_stuff.MISC", 42, time.Now())
				fixture.scan() // reset
				fixture.fs.Delete("/root/new_stuff.MISC")

				Convey("The scanner should NOT report a change in state", func() {
					So(fixture.scan(), ShouldBeFalse)
				})
			})

			Convey("When a new folder is created inside a watched folder", func() {
				fixture.fs.Create("/root/new", 41, time.Now())
				changed := fixture.scan()

				Convey("The scanner should report the change", func() {
					So(changed, ShouldBeTrue)
				})

				Convey("The scanner should notify the watcher of the creation", func() {
					So(fixture.wasCreated("/root/new"), ShouldBeTrue)
				})
			})

			Convey("When an empty watched folder is deleted", func() {
				fixture.fs.Delete("/root/sub/empty")
				changed := fixture.scan()

				Convey("The scanner should report the change", func() {
					So(changed, ShouldBeTrue)
				})

				Convey("The scanner should notify the watcher of the deletion", func() {
					So(fixture.wasDeleted("/root/sub/empty"), ShouldBeTrue)
				})
			})

			Convey("When a folder is created outside any watched folders", func() {
				fixture.fs.Create("/outside/asdf", 41, time.Now())
				changed := fixture.scan()

				Convey("The scanner should NOT report the change", func() {
					So(changed, ShouldBeFalse)
				})

				Convey("The scanner should NOT notify the watcher of the change", func() {
					So(fixture.wasCreated("/outside/asdf"), ShouldBeFalse)
				})
			})

			Convey("When an ignored folder is deleted", func() {
				fixture.watcher.Ignore("/root/sub/empty")
				fixture.fs.Delete("/root/sub/empty")
				changed := fixture.scan()

				Convey("The scanner should report the change", func() {
					So(changed, ShouldBeTrue)
				})

				Convey("The scanner should notify the watcher of the change", func() {
					So(fixture.wasDeleted("/root/sub/empty"), ShouldBeTrue)
				})
			})

			// Once upon a time the scanner didn't keep track of the root internally.
			// This meant that when the scanner was instructed to scan a new root location
			// it appeared to the scanner that many of the internally stored folders had
			// been deleted becuase they were not part of the new root directory structure
			// and they were reported as deletions to the watcher, which was incorrect behavior.
			Convey("When the watcher has adjusted the root", func() {
				fixture.fs.Create("/somewhere", 3, time.Now())
				fixture.fs.Create("/somewhere/else", 3, time.Now())
				fixture.watcher.Adjust("/somewhere")

				Convey("And the scanner scans", func() {
					changed := fixture.scan()

					Convey("The scanner should report the change", func() {
						So(changed, ShouldBeTrue)
					})

					Convey("The scanner should NOT notify the watcher of incorrect folder deletions", func() {
						So(len(fixture.watcher.deleted), ShouldEqual, 0)
					})

					Convey("The scanner should NOT notify the watcher of incorrect folder creations", func() {
						So(len(fixture.watcher.created), ShouldEqual, 0)
					})
				})
			})

		})
	})
}

type scannerFixture struct {
	scanner *Scanner
	fs      *system.FakeFileSystem
	watcher *WatcherWrapper
}

func (self *scannerFixture) scan() bool {
	return self.scanner.Scan()
}
func (self *scannerFixture) wasDeleted(folder string) bool {
	return !self.wasCreated(folder)
}
func (self *scannerFixture) wasCreated(folder string) bool {
	for _, w := range self.watcher.WatchedFolders() {
		if w.Path == folder {
			return true
		}
	}
	return false
}

func newScannerFixture() *scannerFixture {
	self := &scannerFixture{}
	self.fs = system.NewFakeFileSystem()
	self.fs.Create("/root", 0, time.Now())
	self.fs.Create("/root/file.go", 1, time.Now())
	self.fs.Create("/root/sub", 0, time.Now())
	self.fs.Create("/root/sub/file.go", 2, time.Now())
	self.fs.Create("/root/sub/empty", 0, time.Now())
	self.watcher = newWatcherWrapper(NewWatcher(self.fs, system.NewFakeShell()))
	self.watcher.Adjust("/root")
	self.scanner = NewScanner(self.fs, self.watcher)
	return self
}

/******** WatcherWrapper ********/

type WatcherWrapper struct {
	inner   *Watcher
	created []string
	deleted []string
}

func (self *WatcherWrapper) WatchedFolders() []*contract.Package {
	return self.inner.WatchedFolders()
}

func (self *WatcherWrapper) Root() string {
	return self.inner.Root()
}

func (self *WatcherWrapper) Adjust(root string) error {
	return self.inner.Adjust(root)
}

func (self *WatcherWrapper) Deletion(folder string) {
	self.deleted = append(self.deleted, folder)
	self.inner.Deletion(folder)
}

func (self *WatcherWrapper) Creation(folder string) {
	self.created = append(self.created, folder)
	self.inner.Creation(folder)
}

func (self *WatcherWrapper) Ignore(folder string) {
	self.inner.Ignore(folder)
}

func (self *WatcherWrapper) Reinstate(folder string) {
	self.inner.Reinstate(folder)
}

func (self *WatcherWrapper) IsWatched(folder string) bool {
	return self.inner.IsWatched(folder)
}

func (self *WatcherWrapper) IsIgnored(folder string) bool {
	return self.inner.IsIgnored(folder)
}

func newWatcherWrapper(inner *Watcher) *WatcherWrapper {
	self := &WatcherWrapper{}
	self.inner = inner
	self.created = []string{}
	self.deleted = []string{}
	return self
}
<commit_msg>adding test case for ignoring .files<commit_after>package watcher

import (
	. "github.com/smartystreets/goconvey/convey"
	"github.com/smartystreets/goconvey/web/server/contract"
	"github.com/smartystreets/goconvey/web/server/system"
	"testing"
	"time"
)

func TestScanner(t *testing.T) {
	var fixture *scannerFixture
	var changed bool

	Convey("To begin with, the scanner is provided a contrived file system environment", t, func() {
		fixture = newScannerFixture()

		Convey("When we call Scan() for the first time", func() {
			changed = fixture.scan()

			Convey("The scanner should report a change in state", func() {
				So(changed, ShouldBeTrue)
			})
		})

		Convey("Then, on subsequent calls to Scan()", func() {
			changed = fixture.scan()

			Convey("When the file system has not changed in any way", func() {

				Convey("The scanner should NOT report any change in state", func() {
					So(fixture.scan(), ShouldBeFalse)
				})
			})

			Convey("When a new go file is created within a watched folder", func() {
				fixture.fs.Create("/root/new_stuff.go", 42, time.Now())

				Convey("The scanner should report a change in state", func() {
					So(fixture.scan(), ShouldBeTrue)
				})
			})

			Convey("When a file that starts with . is created within a watched folder", func() {
				fixture.fs.Create("/root/.new_stuff.go", 42, time.Now())

				Convey("The scanner should not report a change in state", func() {
					So(fixture.scan(), ShouldBeFalse)
				})
			})

			Convey("When an existing go file within a watched folder has been modified", func() {
				fixture.fs.Modify("/root/sub/file.go")

				Convey("The scanner should report a change in state", func() {
					So(fixture.scan(), ShouldBeTrue)
				})
			})

			Convey("When an existing go file within a watched folder has been renamed", func() {
				fixture.fs.Rename("/root/sub/file.go", "/root/sub/asdf.go")

				Convey("The scanner should report a change in state", func() {
					So(fixture.scan(), ShouldBeTrue)
				})
			})

			Convey("When an existing go file within a watched folder has been deleted", func() {
				fixture.fs.Delete("/root/sub/file.go")

				Convey("The scanner should report a change in state", func() {
					So(fixture.scan(), ShouldBeTrue)
				})
			})

			Convey("When a go file is created outside any watched folders", func() {
				fixture.fs.Create("/outside/new_stuff.go", 42, time.Now())

				Convey("The scanner should NOT report a change in state", func() {
					So(fixture.scan(), ShouldBeFalse)
				})
			})

			Convey("When a go file is modified outside any watched folders", func() {
				fixture.fs.Create("/outside/new_stuff.go", 42, time.Now())
				fixture.scan() // reset

				Convey("The scanner should NOT report a change in state", func() {
					So(fixture.scan(), ShouldBeFalse)
				})
			})

			Convey("When a go file is renamed outside any watched folders", func() {
				fixture.fs.Create("/outside/new_stuff.go", 42, time.Now())
				fixture.scan() // reset
				fixture.fs.Rename("/outside/new_stuff.go", "/outside/newer_stoff.go")

				Convey("The scanner should NOT report a change in state", func() {
					So(fixture.scan(), ShouldBeFalse)
				})
			})

			Convey("When a go file is deleted outside any watched folders", func() {
				fixture.fs.Create("/outside/new_stuff.go", 42, time.Now())
				fixture.scan() // reset
				fixture.fs.Delete("/outside/new_stuff.go")

				Convey("The scanner should NOT report a change in state", func() {
					So(fixture.scan(), ShouldBeFalse)
				})
			})

			Convey("When a miscellaneous file is created", func() {
				fixture.fs.Create("/root/new_stuff.MISC", 42, time.Now())

				Convey("The scanner should NOT report a change in state", func() {
					So(fixture.scan(), ShouldBeFalse)
				})
			})

			Convey("When a miscellaneous file is modified", func() {
				fixture.fs.Create("/root/new_stuff.MISC", 42, time.Now())
				fixture.scan() // reset

				Convey("The scanner should NOT report a change in state", func() {
					So(fixture.scan(), ShouldBeFalse)
				})
			})

			Convey("When a miscellaneous file is renamed", func() {
				fixture.fs.Create("/root/new_stuff.MISC", 42, time.Now())
				fixture.scan() // reset
				fixture.fs.Rename("/root/new_stuff.MISC", "/root/newer_stoff.MISC")

				Convey("The scanner should NOT report a change in state", func() {
					So(fixture.scan(), ShouldBeFalse)
				})
			})

			Convey("When a miscellaneous file is deleted", func() {
				fixture.fs.Create("/root/new_stuff.MISC", 42, time.Now())
				fixture.scan() // reset
				fixture.fs.Delete("/root/new_stuff.MISC")

				Convey("The scanner should NOT report a change in state", func() {
					So(fixture.scan(), ShouldBeFalse)
				})
			})

			Convey("When a new folder is created inside a watched folder", func() {
				fixture.fs.Create("/root/new", 41, time.Now())
				changed := fixture.scan()

				Convey("The scanner should report the change", func() {
					So(changed, ShouldBeTrue)
				})

				Convey("The scanner should notify the watcher of the creation", func() {
					So(fixture.wasCreated("/root/new"), ShouldBeTrue)
				})
			})

			Convey("When an empty watched folder is deleted", func() {
				fixture.fs.Delete("/root/sub/empty")
				changed := fixture.scan()

				Convey("The scanner should report the change", func() {
					So(changed, ShouldBeTrue)
				})

				Convey("The scanner should notify the watcher of the deletion", func() {
					So(fixture.wasDeleted("/root/sub/empty"), ShouldBeTrue)
				})
			})

			Convey("When a folder is created outside any watched folders", func() {
				fixture.fs.Create("/outside/asdf", 41, time.Now())
				changed := fixture.scan()

				Convey("The scanner should NOT report the change", func() {
					So(changed, ShouldBeFalse)
				})

				Convey("The scanner should NOT notify the watcher of the change", func() {
					So(fixture.wasCreated("/outside/asdf"), ShouldBeFalse)
				})
			})

			Convey("When an ignored folder is deleted", func() {
				fixture.watcher.Ignore("/root/sub/empty")
				fixture.fs.Delete("/root/sub/empty")
				changed := fixture.scan()

				Convey("The scanner should report the change", func() {
					So(changed, ShouldBeTrue)
				})

				Convey("The scanner should notify the watcher of the change", func() {
					So(fixture.wasDeleted("/root/sub/empty"), ShouldBeTrue)
				})
			})

			// Once upon a time the scanner didn't keep track of the root internally.
			// This meant that when the scanner was instructed to scan a new root location
			// it appeared to the scanner that many of the internally stored folders had
			// been deleted becuase they were not part of the new root directory structure
			// and they were reported as deletions to the watcher, which was incorrect behavior.
			Convey("When the watcher has adjusted the root", func() {
				fixture.fs.Create("/somewhere", 3, time.Now())
				fixture.fs.Create("/somewhere/else", 3, time.Now())
				fixture.watcher.Adjust("/somewhere")

				Convey("And the scanner scans", func() {
					changed := fixture.scan()

					Convey("The scanner should report the change", func() {
						So(changed, ShouldBeTrue)
					})

					Convey("The scanner should NOT notify the watcher of incorrect folder deletions", func() {
						So(len(fixture.watcher.deleted), ShouldEqual, 0)
					})

					Convey("The scanner should NOT notify the watcher of incorrect folder creations", func() {
						So(len(fixture.watcher.created), ShouldEqual, 0)
					})
				})
			})

		})
	})
}

type scannerFixture struct {
	scanner *Scanner
	fs      *system.FakeFileSystem
	watcher *WatcherWrapper
}

func (self *scannerFixture) scan() bool {
	return self.scanner.Scan()
}
func (self *scannerFixture) wasDeleted(folder string) bool {
	return !self.wasCreated(folder)
}
func (self *scannerFixture) wasCreated(folder string) bool {
	for _, w := range self.watcher.WatchedFolders() {
		if w.Path == folder {
			return true
		}
	}
	return false
}

func newScannerFixture() *scannerFixture {
	self := &scannerFixture{}
	self.fs = system.NewFakeFileSystem()
	self.fs.Create("/root", 0, time.Now())
	self.fs.Create("/root/file.go", 1, time.Now())
	self.fs.Create("/root/sub", 0, time.Now())
	self.fs.Create("/root/sub/file.go", 2, time.Now())
	self.fs.Create("/root/sub/empty", 0, time.Now())
	self.watcher = newWatcherWrapper(NewWatcher(self.fs, system.NewFakeShell()))
	self.watcher.Adjust("/root")
	self.scanner = NewScanner(self.fs, self.watcher)
	return self
}

/******** WatcherWrapper ********/

type WatcherWrapper struct {
	inner   *Watcher
	created []string
	deleted []string
}

func (self *WatcherWrapper) WatchedFolders() []*contract.Package {
	return self.inner.WatchedFolders()
}

func (self *WatcherWrapper) Root() string {
	return self.inner.Root()
}

func (self *WatcherWrapper) Adjust(root string) error {
	return self.inner.Adjust(root)
}

func (self *WatcherWrapper) Deletion(folder string) {
	self.deleted = append(self.deleted, folder)
	self.inner.Deletion(folder)
}

func (self *WatcherWrapper) Creation(folder string) {
	self.created = append(self.created, folder)
	self.inner.Creation(folder)
}

func (self *WatcherWrapper) Ignore(folder string) {
	self.inner.Ignore(folder)
}

func (self *WatcherWrapper) Reinstate(folder string) {
	self.inner.Reinstate(folder)
}

func (self *WatcherWrapper) IsWatched(folder string) bool {
	return self.inner.IsWatched(folder)
}

func (self *WatcherWrapper) IsIgnored(folder string) bool {
	return self.inner.IsIgnored(folder)
}

func newWatcherWrapper(inner *Watcher) *WatcherWrapper {
	self := &WatcherWrapper{}
	self.inner = inner
	self.created = []string{}
	self.deleted = []string{}
	return self
}
<|endoftext|>


<commit_before>package nsone

import (
	"fmt"
	"testing"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccZone_basic(t *testing.T) {
	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccZone_basic,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckZoneState("zone", "example.com"),
				),
			},
		},
	})
}

func testAccCheckZoneState(key, value string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources["nsone_zone.foobar"]
		if !ok {
			return fmt.Errorf("Not found: %s", "nsone_zone.foobar")
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No ID is set")
		}

		p := rs.Primary
		if p.Attributes[key] != value {
			return fmt.Errorf(
				"%s != %s (actual: %s)", key, value, p.Attributes[key])
		}

		return nil
	}
}

const testAccZone_basic = `
resource "nsone_zone" "foobar" {
	zone = "example.com"
	hostmaster = "example.com"
}`
<commit_msg>Make acceptance tests pass<commit_after>package nsone

import (
	"fmt"
	"testing"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccZone_basic(t *testing.T) {
	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccZone_basic,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckZoneState("zone", "terraform.io"),
				),
			},
		},
	})
}

func testAccCheckZoneState(key, value string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources["nsone_zone.foobar"]
		if !ok {
			return fmt.Errorf("Not found: %s", "nsone_zone.foobar")
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No ID is set")
		}

		p := rs.Primary
		if p.Attributes[key] != value {
			return fmt.Errorf(
				"%s != %s (actual: %s)", key, value, p.Attributes[key])
		}

		return nil
	}
}

const testAccZone_basic = `
resource "nsone_zone" "foobar" {
	zone = "terraform.io"
	hostmaster = "hostmaster@nsone.net"
}`
<|endoftext|>


<commit_before>package lisp

import (
	"strings"
	"testing"
)

type readTokenTest struct{
	str string
	tok token
}

var readTokenTests = []readTokenTest{
	{"1\n", "1"},
	{"32.5a\n", "32.5a"},
	{"32.5 a\n", "32.5"},
	{"\"b\"\n", "\"b\""},
	{" \t5;6", "5"},
	{"(", "("},
	{")(", ")"},
}

func TestReadToken(t *testing.T) {
	for _, test := range readTokenTests {
		r := strings.NewReader(test.str)
		tok, err := readToken(r)
		if err != nil {
			t.Errorf("ERROR: %s", err.Error())
		}
		if tok != test.tok {
			t.Errorf("%s != %s", tok, test.tok)
		}
	}
}
<commit_msg>tests for parse()<commit_after>package lisp

import (
	"strings"
	"testing"
)

type readTokenTest struct{
	str string
	tok token
}

var readTokenTests = []readTokenTest{
	{"1\n", "1"},
	{"32.5a\n", "32.5a"},
	{"32.5 a\n", "32.5"},
	{"\"b\"\n", "\"b\""},
	{" \t5;6", "5"},
	{"(", "("},
	{")(", ")"},
}

func TestReadToken(t *testing.T) {
	for _, test := range readTokenTests {
		r := strings.NewReader(test.str)
		tok, err := readToken(r)
		if err != nil {
			t.Errorf("ERROR: %s", err.Error())
		}
		if tok != test.tok {
			t.Errorf("%s != %s", tok, test.tok)
		}
	}
}

type parseTest struct{
	str string
	res sexpr
}

var parseTests = []parseTest{
	{"1\n", 1.0},
	{"5.5\n", 5.5},
	{"5e-9\n", 5e-9},
	{"x\n", sym("x")},
	{"5%x\n", sym("5%x")},
	{"\"a\"", "a"},

	{"()", Nil},
	{"(())", cons{nil, nil}},
	{"(1)", cons{1.0, nil}},
	{"(1 (2 3) ())",
		cons{1.0, cons{cons{2.0, cons{3.0, nil}}, cons{nil, nil}}}},
}

func eqS(a sexpr, b sexpr) bool {
	ac, ok := a.(cons)
	if ok {
		bc, ok := b.(cons)
		if !ok {
			return false
		}
		return eqS(ac.car, bc.car) && eqS(ac.cdr, bc.cdr)
	}
	return a == b
}

func TestParse(t *testing.T) {
	for _, test := range parseTests {
		r := strings.NewReader(test.str)
		res, err := parse(r)
		if err != nil {
			t.Errorf("ERROR: %s", err.Error())
		}
		if !eqS(res, test.res) {
			t.Errorf("%s != %s", res, test.res)
		}
	}
}
<|endoftext|>