<commit_before>package ranchr

import (
	_ "fmt"

	"os"
	"testing"

	. "github.com/smartystreets/goconvey/convey"
)

func init() {
	setCommonTestData()
}

func TestBuilderStuff(t *testing.T) {
	Convey("Given a builder, or two", t, func() {
		b := builder{}
		b.Settings = []string{"key1=value1", "key2=value2", "key3=value3"}
		newSettings := []string{"key4=value4", "key2=value22"}

		Convey("Given two settings slices", func() {
			b.mergeSettings(newSettings)
			Convey("Merging them should result in", func() {
				So(b.Settings, ShouldContain, "key1=value1")
				So(b.Settings, ShouldContain, "key2=value22")
				So(b.Settings, ShouldContain, "key3=value3")
				So(b.Settings, ShouldContain, "key4=value4")
				So(b.Settings, ShouldNotContain, "key2=value2")
			})
		})

		Convey("Given two vm settings slices", func() {
			b.Arrays = map[string]interface{}{}
			b.Arrays[VMSettings] = []string{"VMkey1=VMvalue1", "VMkey2=VMvalue2"}
			newVMSettings := []string{"VMkey1=VMvalue11", "VMkey3=VMvalue3"}
			mergedVMSettings := []string{"VMkey1=VMvalue11", "VMkey2=VMvalue2", "VMkey3=VMvalue3"}
			merged := b.mergeVMSettings(newVMSettings)
			Convey("They should be merged", func() {
				So(merged, ShouldResemble, mergedVMSettings)
				So(merged, ShouldContain, "VMkey1=VMvalue11")
				So(merged, ShouldContain, "VMkey2=VMvalue2")
				So(merged, ShouldNotContain, "VMkey1=VMvalue1")
			})
		})

		Convey("Given a builder settings", func() {
			rawTpl := &rawTemplate{}
			res := b.settingsToMap(rawTpl)
			Convey("They should be turned into a map[string]interface", func() {
				So(res, ShouldResemble, map[string]interface{}{"key1": "value1", "key2": "value2", "key3": "value3"})
			})
		})
	})
}

func TestPostProcessorStuff(t *testing.T) {
	Convey("Given a postProcessor or two", t, func() {
		pp := postProcessor{}
		pp.Settings = []string{"key1=value1", "key2=value2"}
		newSettings := []string{"key1=value1", "key2=value22", "key3=value3"}
		Convey("Given two settings slices", func() {
			pp.mergeSettings(newSettings)
			Convey("Merging them should result in", func() {
				So(pp.Settings, ShouldContain, "key1=value1")
				So(pp.Settings, ShouldContain, "key2=value22")
				So(pp.Settings, ShouldContain, "key3=value3")
				So(pp.Settings, ShouldNotContain, "key2=value2")
			})
		})
	})
}

func TestProvisionerStuff(t *testing.T) {
	Convey("Given a provisioner or two", t, func() {
		p := provisioner{}
		p.Settings = []string{"key1=value1", "key2=value2"}
		newSettings := []string{"key1=value1", "key2=value22", "key3=value3"}
		Convey("Given two settings slices", func() {
			p.mergeSettings(newSettings)
			Convey("Merging them should result in", func() {
				So(p.Settings, ShouldContain, "key1=value1")
				So(p.Settings, ShouldContain, "key2=value22")
				So(p.Settings, ShouldContain, "key3=value3")
				So(p.Settings, ShouldNotContain, "key2=value2")
			})
		})

	})

}

func TestDefaults(t *testing.T) {
	tmpEnvDefaultsFile := os.Getenv(EnvDefaultsFile)
	Convey("Given a defaults struct", t, func() {
		Convey("Given an empty default file environment setting", func() {
			d := defaults{}
			os.Setenv(EnvDefaultsFile, "")
			Convey("A load should result in an error", func() {
				err := d.LoadOnce()
				So(err.Error(), ShouldEqual, "could not retrieve the default Settings because the RANCHER_DEFAULTS_FILE environment variable was not set. Either set it or check your rancher.cfg setting")
				So(d.MinPackerVersion, ShouldEqual, "")
			})
		})
		Convey("Given a valid defaults configuration file", func() {
			os.Setenv(EnvDefaultsFile, "../test_files/conf/defaults_test.toml")
			os.Setenv(EnvRancherFile, "../test_files/rancher.cfg")
			d := defaults{}
			err := d.LoadOnce()
			Convey("A load should not error and result in data loaded", func() {
				So(err, ShouldBeNil)
				So(d.IODirInf, ShouldResemble, testDefaults.IODirInf)
				So(d.PackerInf, ShouldResemble, testDefaults.PackerInf)
				So(d.BuildInf, ShouldResemble, testDefaults.BuildInf)
				So(d.build.BuilderTypes, ShouldResemble, testDefaults.build.BuilderTypes)
				So(MarshalJSONToString.Get(d.build.Builders[BuilderVirtualBoxISO]), ShouldEqual, MarshalJSONToString.Get(testDefaults.build.Builders[BuilderVirtualBoxISO]))
				So(d.build.PostProcessorTypes, ShouldResemble, testDefaults.build.PostProcessorTypes)
				So(MarshalJSONToString.Get(d.build.PostProcessors[PostProcessorVagrant]), ShouldEqual, MarshalJSONToString.Get(testDefaults.build.PostProcessors[PostProcessorVagrant]))
				So(MarshalJSONToString.Get(d.build.PostProcessors[PostProcessorVagrantCloud]), ShouldEqual, MarshalJSONToString.Get(testDefaults.build.PostProcessors[PostProcessorVagrantCloud]))
				So(d.build.ProvisionerTypes, ShouldResemble, testDefaults.build.ProvisionerTypes)
				So(MarshalJSONToString.Get(d.build.Provisioners[ProvisionerShell]), ShouldEqual, MarshalJSONToString.Get(testDefaults.build.Provisioners[ProvisionerShell]))
			})
		})
	})
	_ = os.Setenv(EnvDefaultsFile, tmpEnvDefaultsFile)
}

func TestSupported(t *testing.T) {
	tmpEnv := os.Getenv(EnvSupportedFile)

	Convey("Given a Supported struct", t, func() {
		Convey("Given an empty supported file environment setting", func() {
			s := supported{}
			os.Setenv(EnvSupportedFile, "")
			Convey("A load should result in an error", func() {
				err := s.LoadOnce()
				So(s.loaded, ShouldEqual, false)
				So(err.Error(), ShouldEqual, "could not retrieve the Supported information because the RANCHER_SUPPORTED_FILE environment variable was not set. Either set it or check your rancher.cfg setting")
			})
		})
		Convey("Given a valid defaults configuration file", func() {
			s := supported{}
			os.Setenv(EnvSupportedFile, "../test_files/conf/supported_test.toml")
			Convey("A load should not error and result in data loaded", func() {
				err := s.LoadOnce()
				So(err, ShouldBeNil)
				So(s.loaded, ShouldEqual, true)
				// Set this because, for some reason it isn't set in testing >.>
				testSupported.Distro["ubuntu"].BaseURL = "http://releases.ubuntu.com/"
				So(MarshalJSONToString.GetIndented(s.Distro["ubuntu"]), ShouldEqual, MarshalJSONToString.GetIndented(testSupported.Distro["ubuntu"]))
				So(MarshalJSONToString.GetIndented(s.Distro["centos"]), ShouldEqual, MarshalJSONToString.GetIndented(testSupported.Distro["centos"]))
			})
		})
	})

	_ = os.Setenv(EnvSupportedFile, tmpEnv)
}

func TestBuildsStuff(t *testing.T) {
	Convey("Given a Builds struct", t, func() {
		b := builds{}
		tmpEnv := os.Getenv(EnvBuildsFile)
		Convey("Given a filename that doesn't exist", func() {
			os.Setenv(EnvBuildsFile, "../test_files/notthere.toml")
			b.LoadOnce()
			Convey("A load should result in a log entry and the builds not being loaded", func() {
				So(b.loaded, ShouldEqual, false)
			})
		})
		Convey("Given a build filename", func() {
			Convey("A load should result in", func() {
				os.Setenv(EnvBuildsFile, "../test_files/conf/builds_test.toml")
				b.LoadOnce()
				So(b.loaded, ShouldEqual, true)
				So(MarshalJSONToString.GetIndented(b.Build["test1"]), ShouldEqual, MarshalJSONToString.GetIndented(testBuilds.Build["test1"]))
				So(MarshalJSONToString.GetIndented(b.Build["test2"]), ShouldEqual, MarshalJSONToString.GetIndented(testBuilds.Build["test2"]))
			})
		})
		Convey("Given an empty build filename", func() {
			Convey("A load should result in", func() {
				os.Setenv(EnvBuildsFile, "")
				b.LoadOnce()
				So(b.loaded, ShouldEqual, false)
			})
		})
		os.Setenv(EnvBuildsFile, tmpEnv)
	})
}

func TestBuildListsStuff(t *testing.T) {
	Convey("Given a buildLists struct", t, func() {
		b := buildLists{}
		tmpEnv := os.Getenv(EnvBuildListsFile)
		Convey("Given a filename that doesn't exist", func() {
			os.Setenv(EnvBuildListsFile, "../test_files/notthere.toml")
			err := b.Load()
			Convey("A load should result in an error", func() {
				So(err.Error(), ShouldEqual, "open ../test_files/notthere.toml: no such file or directory")
			})
		})
		Convey("Given a BuildLists name", func() {
			os.Setenv(EnvBuildListsFile, "../test_files/conf/build_lists_test.toml")
			err := b.Load()
			Convey("A load should successfully load the file", func() {
				So(err, ShouldBeNil)
				So(b, ShouldResemble, buildLists{map[string]list{"testlist-1": {Builds: []string{"test1", "test2"}}, "testlist-2": {Builds: []string{"test1", "test2", "test3", "test4"}}}})
			})
		})
		Convey("Given an empty filename", func() {
			os.Setenv(EnvBuildListsFile, "")
			err := b.Load()
			Convey("A load should result in an error", func() {
				So(err.Error(), ShouldEqual, "could not retrieve the BuildLists file because the "+EnvBuildListsFile+" environment variable was not set. Either set it or check your rancher.cfg setting")
			})
		})

		os.Setenv(EnvBuildListsFile, tmpEnv)
	})
}
<commit_msg>cleaned up and added some tests for cfg<commit_after>package ranchr

import (
	_ "fmt"

	"os"
	"testing"

	. "github.com/smartystreets/goconvey/convey"
)

func init() {
	setCommonTestData()
}

func TesMergeSettings(t *testing.T) {
	Convey("Given a builder with settings", t, func() {
		b := builder{}
		b.Settings = []string{"key1=value1", "key2=value2", "key3=value3"}
		Convey("Merging a nil slice", func() {
			b.mergeSettings(nil)
			Convey("Should result in no changes", func() {
				So(b.Settings, ShouldContain, "key1=value1")
				So(b.Settings, ShouldContain, "key2=value2")
				So(b.Settings, ShouldContain, "key3=value3")
			})
		})

		Convey("Given a slice of new settings", func() {
			newSettings := []string{"key4=value4", "key2=value22"}
			Convey("Merging the new slice", func() {
				b.mergeSettings(newSettings)
				Convey("Should result in", func() {
					So(b.Settings, ShouldContain, "key1=value1")
					So(b.Settings, ShouldContain, "key2=value22")
					So(b.Settings, ShouldContain, "key3=value3")
					So(b.Settings, ShouldContain, "key4=value4")
					So(b.Settings, ShouldNotContain, "key2=value2")
				})
			})
		})
	})
}

func TestMergeVMSettings(t *testing.T) {
	Convey("Given a builder with vm settings", t, func() {
		b := builder{}
		b.Arrays = map[string]interface{}{}
		b.Arrays[VMSettings] = []string{"VMkey1=VMvalue1", "VMkey2=VMvalue2"}
		Convey("merging the slice with a nil slice", func() {
			merged := b.mergeVMSettings(nil)
			Convey("should result in nil", func() {
				So(merged, ShouldBeNil)
			})
		})

		Convey("merging the slice with a populated slice", func() {
			newVMSettings := []string{"VMkey1=VMvalue11", "VMkey3=VMvalue3"}
			mergedVMSettings := []string{"VMkey1=VMvalue11", "VMkey2=VMvalue2", "VMkey3=VMvalue3"}
			merged := b.mergeVMSettings(newVMSettings)
			Convey("Shouuld result in a merged slice", func() {
				So(merged, ShouldResemble, mergedVMSettings)
				So(merged, ShouldContain, "VMkey1=VMvalue11")
				So(merged, ShouldContain, "VMkey2=VMvalue2")
				So(merged, ShouldNotContain, "VMkey1=VMvalue1")
			})
		})

		Convey("Given a builder settings", func() {
			b.Settings = []string{"key1=value1", "key2=value2", "key3=value3"}
			rawTpl := &rawTemplate{}
			Convey("Making a map from the settings", func() {
				res := b.settingsToMap(rawTpl)
				Convey("Should result in a map[string]interface of the values", func() {
					So(res, ShouldResemble, map[string]interface{}{"key1": "value1", "key2": "value2", "key3": "value3"})
				})
			})
		})
	})
}

func TestPostProcessorStuff(t *testing.T) {
	Convey("Given a postProcessor or two", t, func() {
		pp := postProcessor{}
		pp.Settings = []string{"key1=value1", "key2=value2"}
		newSettings := []string{"key1=value1", "key2=value22", "key3=value3"}
		Convey("Given two settings slices", func() {
			pp.mergeSettings(newSettings)
			Convey("Merging them should result in", func() {
				So(pp.Settings, ShouldContain, "key1=value1")
				So(pp.Settings, ShouldContain, "key2=value22")
				So(pp.Settings, ShouldContain, "key3=value3")
				So(pp.Settings, ShouldNotContain, "key2=value2")
			})
		})
	})
}

func TestProvisionerStuff(t *testing.T) {
	Convey("Given a provisioner or two", t, func() {
		p := provisioner{}
		p.Settings = []string{"key1=value1", "key2=value2"}
		newSettings := []string{"key1=value1", "key2=value22", "key3=value3"}
		Convey("Given two settings slices", func() {
			p.mergeSettings(newSettings)
			Convey("Merging them should result in", func() {
				So(p.Settings, ShouldContain, "key1=value1")
				So(p.Settings, ShouldContain, "key2=value22")
				So(p.Settings, ShouldContain, "key3=value3")
				So(p.Settings, ShouldNotContain, "key2=value2")
			})
		})

	})

}

func TestDefaults(t *testing.T) {
	tmpEnvDefaultsFile := os.Getenv(EnvDefaultsFile)
	Convey("Given a defaults struct", t, func() {
		Convey("Given an empty default file environment setting", func() {
			d := defaults{}
			os.Setenv(EnvDefaultsFile, "")
			Convey("A load should result in an error", func() {
				err := d.LoadOnce()
				So(err.Error(), ShouldEqual, "could not retrieve the default Settings because the RANCHER_DEFAULTS_FILE environment variable was not set. Either set it or check your rancher.cfg setting")
				So(d.MinPackerVersion, ShouldEqual, "")
			})
		})
		Convey("Given a valid defaults configuration file", func() {
			os.Setenv(EnvDefaultsFile, "../test_files/conf/defaults_test.toml")
			os.Setenv(EnvRancherFile, "../test_files/rancher.cfg")
			d := defaults{}
			err := d.LoadOnce()
			Convey("A load should not error and result in data loaded", func() {
				So(err, ShouldBeNil)
				So(d.IODirInf, ShouldResemble, testDefaults.IODirInf)
				So(d.PackerInf, ShouldResemble, testDefaults.PackerInf)
				So(d.BuildInf, ShouldResemble, testDefaults.BuildInf)
				So(d.build.BuilderTypes, ShouldResemble, testDefaults.build.BuilderTypes)
				So(MarshalJSONToString.Get(d.build.Builders[BuilderVirtualBoxISO]), ShouldEqual, MarshalJSONToString.Get(testDefaults.build.Builders[BuilderVirtualBoxISO]))
				So(d.build.PostProcessorTypes, ShouldResemble, testDefaults.build.PostProcessorTypes)
				So(MarshalJSONToString.Get(d.build.PostProcessors[PostProcessorVagrant]), ShouldEqual, MarshalJSONToString.Get(testDefaults.build.PostProcessors[PostProcessorVagrant]))
				So(MarshalJSONToString.Get(d.build.PostProcessors[PostProcessorVagrantCloud]), ShouldEqual, MarshalJSONToString.Get(testDefaults.build.PostProcessors[PostProcessorVagrantCloud]))
				So(d.build.ProvisionerTypes, ShouldResemble, testDefaults.build.ProvisionerTypes)
				So(MarshalJSONToString.Get(d.build.Provisioners[ProvisionerShell]), ShouldEqual, MarshalJSONToString.Get(testDefaults.build.Provisioners[ProvisionerShell]))
			})
		})
	})
	_ = os.Setenv(EnvDefaultsFile, tmpEnvDefaultsFile)
}

func TestSupported(t *testing.T) {
	tmpEnv := os.Getenv(EnvSupportedFile)

	Convey("Given a Supported struct", t, func() {
		Convey("Given an empty supported file environment setting", func() {
			s := supported{}
			os.Setenv(EnvSupportedFile, "")
			Convey("A load should result in an error", func() {
				err := s.LoadOnce()
				So(s.loaded, ShouldEqual, false)
				So(err.Error(), ShouldEqual, "could not retrieve the Supported information because the RANCHER_SUPPORTED_FILE environment variable was not set. Either set it or check your rancher.cfg setting")
			})
		})
		Convey("Given a valid defaults configuration file", func() {
			s := supported{}
			os.Setenv(EnvSupportedFile, "../test_files/conf/supported_test.toml")
			Convey("A load should not error and result in data loaded", func() {
				err := s.LoadOnce()
				So(err, ShouldBeNil)
				So(s.loaded, ShouldEqual, true)
				// Set this because, for some reason it isn't set in testing >.>
				testSupported.Distro["ubuntu"].BaseURL = "http://releases.ubuntu.com/"
				So(MarshalJSONToString.GetIndented(s.Distro["ubuntu"]), ShouldEqual, MarshalJSONToString.GetIndented(testSupported.Distro["ubuntu"]))
				So(MarshalJSONToString.GetIndented(s.Distro["centos"]), ShouldEqual, MarshalJSONToString.GetIndented(testSupported.Distro["centos"]))
			})
		})
	})

	_ = os.Setenv(EnvSupportedFile, tmpEnv)
}

func TestBuildsStuff(t *testing.T) {
	Convey("Given a Builds struct", t, func() {
		b := builds{}
		tmpEnv := os.Getenv(EnvBuildsFile)
		Convey("Given a filename that doesn't exist", func() {
			os.Setenv(EnvBuildsFile, "../test_files/notthere.toml")
			b.LoadOnce()
			Convey("A load should result in a log entry and the builds not being loaded", func() {
				So(b.loaded, ShouldEqual, false)
			})
		})
		Convey("Given a build filename", func() {
			Convey("A load should result in", func() {
				os.Setenv(EnvBuildsFile, "../test_files/conf/builds_test.toml")
				b.LoadOnce()
				So(b.loaded, ShouldEqual, true)
				So(MarshalJSONToString.GetIndented(b.Build["test1"]), ShouldEqual, MarshalJSONToString.GetIndented(testBuilds.Build["test1"]))
				So(MarshalJSONToString.GetIndented(b.Build["test2"]), ShouldEqual, MarshalJSONToString.GetIndented(testBuilds.Build["test2"]))
			})
		})
		Convey("Given an empty build filename", func() {
			Convey("A load should result in", func() {
				os.Setenv(EnvBuildsFile, "")
				b.LoadOnce()
				So(b.loaded, ShouldEqual, false)
			})
		})
		os.Setenv(EnvBuildsFile, tmpEnv)
	})
}

func TestBuildListsStuff(t *testing.T) {
	Convey("Given a buildLists struct", t, func() {
		b := buildLists{}
		tmpEnv := os.Getenv(EnvBuildListsFile)
		Convey("Given a filename that doesn't exist", func() {
			os.Setenv(EnvBuildListsFile, "../test_files/notthere.toml")
			err := b.Load()
			Convey("A load should result in an error", func() {
				So(err.Error(), ShouldEqual, "open ../test_files/notthere.toml: no such file or directory")
			})
		})
		Convey("Given a BuildLists name", func() {
			os.Setenv(EnvBuildListsFile, "../test_files/conf/build_lists_test.toml")
			err := b.Load()
			Convey("A load should successfully load the file", func() {
				So(err, ShouldBeNil)
				So(b, ShouldResemble, buildLists{map[string]list{"testlist-1": {Builds: []string{"test1", "test2"}}, "testlist-2": {Builds: []string{"test1", "test2", "test3", "test4"}}}})
			})
		})
		Convey("Given an empty filename", func() {
			os.Setenv(EnvBuildListsFile, "")
			err := b.Load()
			Convey("A load should result in an error", func() {
				So(err.Error(), ShouldEqual, "could not retrieve the BuildLists file because the "+EnvBuildListsFile+" environment variable was not set. Either set it or check your rancher.cfg setting")
			})
		})

		os.Setenv(EnvBuildListsFile, tmpEnv)
	})
}
<|endoftext|>


<commit_before>package redirect

import (
	"fmt"

	"github.com/fabioberger/airtable-go"
)

const (
	// DefaultRedirectKey should be fetched if the requested key is not found.
	DefaultRedirectKey = "default"
	redirectTableName  = "Redirects"
)

// Client uses Airtable to implement Redirector.
type Client struct {
	airtableGo *airtable.Client
}

type airtableRecord struct {
	AirtableID string
	Fields     Redirect
}

// NewClient builds an instance of client.
func NewClient(apiKey string, baseID string) (*Client, error) {
	airtableGo, err := airtable.New(apiKey, baseID)
	return &Client{airtableGo: airtableGo}, err
}

// Get implements Redirector.
func (c *Client) Get(key string) (*Redirect, error) {
	var records []airtableRecord

	err := c.airtableGo.ListRecords(
		redirectTableName,
		&records,
		airtable.ListParameters{FilterByFormula: fmt.Sprintf("{Key} = '%s'", key), MaxRecords: 1},
	)

	if err != nil {
		return nil, err
	}

	if len(records) == 0 {
		return nil, fmt.Errorf("redirect %s not found", key)
	}

	return &records[0].Fields, nil
}
<commit_msg>[Client] Use %q to generate quoted values in airtable query<commit_after>package redirect

import (
	"fmt"

	"github.com/fabioberger/airtable-go"
)

const (
	// DefaultRedirectKey should be fetched if the requested key is not found.
	DefaultRedirectKey = "default"
	redirectTableName  = "Redirects"
)

// Client uses Airtable to implement Redirector.
type Client struct {
	airtableGo *airtable.Client
}

type airtableRecord struct {
	AirtableID string
	Fields     Redirect
}

// NewClient builds an instance of client.
func NewClient(apiKey string, baseID string) (*Client, error) {
	airtableGo, err := airtable.New(apiKey, baseID)
	return &Client{airtableGo: airtableGo}, err
}

// Get implements Redirector.
func (c *Client) Get(key string) (*Redirect, error) {
	var records []airtableRecord

	err := c.airtableGo.ListRecords(
		redirectTableName,
		&records,
		airtable.ListParameters{FilterByFormula: fmt.Sprintf("{Key} = %q", key), MaxRecords: 1},
	)

	if err != nil {
		return nil, err
	}

	if len(records) == 0 {
		return nil, fmt.Errorf("redirect %q not found", key)
	}

	return &records[0].Fields, nil
}
<|endoftext|>


<commit_before>// Copyright © 2016 Zenly <hello@zen.ly>.
//
// 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 protein

import (
	"context"
	"reflect"
	"testing"
	"time"

	"github.com/garyburd/redigo/redis"
	"github.com/gocql/gocql"
	"github.com/rainycape/memcache"
	"github.com/stretchr/testify/assert"
	"github.com/znly/protein/protoscan"
)

// -----------------------------------------------------------------------------

func TestTranscoder_Helpers_Memcached(t *testing.T) {
	c, err := memcache.New("localhost:11211")
	assert.Nil(t, err)
	assert.NotNil(t, c)
	defer c.Close()

	// clear the store, just in case
	assert.Nil(t, c.Flush(-1))

	// create `Transcoder` and push all local schemas via user-defined setter
	trc, err := NewTranscoder(context.Background(),
		protoscan.SHA256, "PROT-",
		TranscoderOptGetter(NewTranscoderGetterMemcached(c)),
		TranscoderOptSetter(NewTranscoderSetterMemcached(c)),
	)
	assert.Nil(t, err)
	assert.NotNil(t, trc)

	testTranscoder_Helpers_common(t, trc)
}

func TestTranscoder_Helpers_Redis(t *testing.T) {
	p := &redis.Pool{
		Dial: func() (redis.Conn, error) {
			c, err := redis.DialURL("redis://localhost:6379/0")
			assert.NotNil(t, c)
			assert.Nil(t, err)
			return c, nil
		},
	}
	defer p.Close()

	// clear the store, just in case
	c := p.Get()
	_, err := c.Do("FLUSHALL")
	c.Close()
	assert.Nil(t, err)

	// create `Transcoder` and push all local schemas via user-defined setter
	trc, err := NewTranscoder(context.Background(),
		protoscan.SHA256, "PROT-",
		TranscoderOptGetter(NewTranscoderGetterRedis(p)),
		TranscoderOptSetter(NewTranscoderSetterRedis(p)),
	)
	assert.Nil(t, err)
	assert.NotNil(t, trc)

	testTranscoder_Helpers_common(t, trc)
}

func TestTranscoder_Helpers_Cassandra(t *testing.T) {
	cluster := gocql.NewCluster("localhost:9042")
	cluster.Consistency = gocql.LocalQuorum
	cluster.NumConns = 1
	gocql.TimeoutLimit = 10
	cluster.ConnectTimeout = time.Second * 10
	cluster.Timeout = time.Second * 10

	// HACK: Connect to standard 'system' keyspace in order to create
	//       the keyspace reserved for tuyauDB's blobs.
	cluster.Keyspace = "system"
	tmp, err := cluster.CreateSession()
	assert.Nil(t, err)
	assert.NotNil(t, tmp)

	// clear the store, just in case
	keyspaceDropQuery := `DROP KEYSPACE IF EXISTS blobs;`
	assert.Nil(t, tmp.Query(keyspaceDropQuery).Exec())

	keyspaceCreateQuery := `
	CREATE KEYSPACE IF NOT EXISTS blobs
	WITH REPLICATION = {
		'class':       'NetworkTopologyStrategy',
		'datacenter1': 1
	};`
	err = tmp.Query(keyspaceCreateQuery).Exec()
	tmp.Close()
	assert.Nil(t, err)

	cluster.Keyspace = "blobs"
	s, err := cluster.CreateSession()
	assert.Nil(t, err)
	assert.NotNil(t, s)
	defer s.Close()

	tableCreateQuery := `
	CREATE TABLE IF NOT EXISTS blobs (key ascii, data blob, PRIMARY KEY (key));`
	assert.Nil(t, s.Query(tableCreateQuery).Exec())

	// create `Transcoder` and push all local schemas via user-defined setter
	trc, err := NewTranscoder(context.Background(),
		protoscan.SHA256, "PROT-",
		TranscoderOptGetter(NewTranscoderGetterCassandra(s, "blobs", "key", "data")),
		TranscoderOptSetter(NewTranscoderSetterCassandra(s, "blobs", "key", "data")),
	)
	assert.Nil(t, err)
	assert.NotNil(t, trc)

	testTranscoder_Helpers_common(t, trc)
}

// -----------------------------------------------------------------------------

func testTranscoder_Helpers_common(t *testing.T, trc *Transcoder) {
	payload, err := trc.Encode(_transcoderTestSchemaXXX)
	assert.Nil(t, err)
	assert.NotNil(t, payload)

	// empty local `SchemaMap` struct-types cache
	trc.sm = NewSchemaMap()
	trc.typeCache = map[string]reflect.Type{}

	// decoding will have to use the user-specified getter to find the schemas
	v, err := trc.Decode(context.Background(), payload)
	assert.Nil(t, err)
	assertFieldValues(t, reflect.ValueOf(_transcoderTestSchemaXXX), v)
}
<commit_msg>transcoder:helpers > tests now wait for dockerized datastores to be up & running<commit_after>// Copyright © 2016 Zenly <hello@zen.ly>.
//
// 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 protein

import (
	"context"
	"reflect"
	"testing"
	"time"

	"github.com/garyburd/redigo/redis"
	"github.com/gocql/gocql"
	"github.com/rainycape/memcache"
	"github.com/stretchr/testify/assert"
	"github.com/znly/protein/protoscan"
)

// -----------------------------------------------------------------------------

const _transcoderHelpersTestNbTries = 40 // 2 minutes

func TestTranscoder_Helpers_Memcached(t *testing.T) {
	c, err := memcache.New("localhost:11211")
	assert.Nil(t, err)
	assert.NotNil(t, c)
	defer c.Close()

	// clear the store, just in case
	err = c.Flush(-1)
	tries := 0
	for err != nil {
		time.Sleep(time.Second * 3)
		tries++
		if tries > _transcoderHelpersTestNbTries {
			assert.Nil(t, err)
			return
		}
		err = c.Flush(-1)
	}
	assert.Nil(t, err)

	// create `Transcoder` and push all local schemas via user-defined setter
	trc, err := NewTranscoder(context.Background(),
		protoscan.SHA256, "PROT-",
		TranscoderOptGetter(NewTranscoderGetterMemcached(c)),
		TranscoderOptSetter(NewTranscoderSetterMemcached(c)),
	)
	assert.Nil(t, err)
	assert.NotNil(t, trc)

	testTranscoder_Helpers_common(t, trc)
}

func TestTranscoder_Helpers_Redis(t *testing.T) {
	p := &redis.Pool{
		Dial: func() (redis.Conn, error) {
			c, err := redis.DialURL("redis://localhost:6379/0")
			tries := 0
			for err != nil {
				time.Sleep(time.Second * 3)
				tries++
				if tries > _transcoderHelpersTestNbTries {
					assert.Nil(t, err)
					return nil, err
				}
				c, err = redis.DialURL("redis://localhost:6379/0")
			}
			assert.NotNil(t, c)
			assert.Nil(t, err)
			return c, nil
		},
	}
	defer p.Close()

	// clear the store, just in case
	c := p.Get()
	_, err := c.Do("FLUSHALL")
	c.Close()
	assert.Nil(t, err)

	// create `Transcoder` and push all local schemas via user-defined setter
	trc, err := NewTranscoder(context.Background(),
		protoscan.SHA256, "PROT-",
		TranscoderOptGetter(NewTranscoderGetterRedis(p)),
		TranscoderOptSetter(NewTranscoderSetterRedis(p)),
	)
	assert.Nil(t, err)
	assert.NotNil(t, trc)

	testTranscoder_Helpers_common(t, trc)
}

func TestTranscoder_Helpers_Cassandra(t *testing.T) {
	cluster := gocql.NewCluster("localhost:9042")
	cluster.Consistency = gocql.LocalQuorum
	cluster.NumConns = 1
	gocql.TimeoutLimit = 10
	cluster.ConnectTimeout = time.Second * 10
	cluster.Timeout = time.Second * 10

	// HACK: Connect to standard 'system' keyspace in order to create
	//       the keyspace reserved for tuyauDB's blobs.
	cluster.Keyspace = "system"
	tmp, err := cluster.CreateSession()
	tries := 0
	for err != nil {
		time.Sleep(time.Second * 3)
		tries++
		if tries > _transcoderHelpersTestNbTries {
			assert.Nil(t, err)
			return
		}
		tmp, err = cluster.CreateSession()
	}
	assert.Nil(t, err)
	assert.NotNil(t, tmp)

	// clear the store, just in case
	keyspaceDropQuery := `DROP KEYSPACE IF EXISTS blobs;`
	assert.Nil(t, tmp.Query(keyspaceDropQuery).Exec())

	keyspaceCreateQuery := `
	CREATE KEYSPACE IF NOT EXISTS blobs
	WITH REPLICATION = {
		'class':       'NetworkTopologyStrategy',
		'datacenter1': 1
	};`
	err = tmp.Query(keyspaceCreateQuery).Exec()
	tmp.Close()
	assert.Nil(t, err)

	cluster.Keyspace = "blobs"
	s, err := cluster.CreateSession()
	assert.Nil(t, err)
	assert.NotNil(t, s)
	defer s.Close()

	tableCreateQuery := `
	CREATE TABLE IF NOT EXISTS blobs (key ascii, data blob, PRIMARY KEY (key));`
	assert.Nil(t, s.Query(tableCreateQuery).Exec())

	// create `Transcoder` and push all local schemas via user-defined setter
	trc, err := NewTranscoder(context.Background(),
		protoscan.SHA256, "PROT-",
		TranscoderOptGetter(NewTranscoderGetterCassandra(s, "blobs", "key", "data")),
		TranscoderOptSetter(NewTranscoderSetterCassandra(s, "blobs", "key", "data")),
	)
	assert.Nil(t, err)
	assert.NotNil(t, trc)

	testTranscoder_Helpers_common(t, trc)
}

// -----------------------------------------------------------------------------

func testTranscoder_Helpers_common(t *testing.T, trc *Transcoder) {
	payload, err := trc.Encode(_transcoderTestSchemaXXX)
	assert.Nil(t, err)
	assert.NotNil(t, payload)

	// empty local `SchemaMap` struct-types cache
	trc.sm = NewSchemaMap()
	trc.typeCache = map[string]reflect.Type{}

	// decoding will have to use the user-specified getter to find the schemas
	v, err := trc.Decode(context.Background(), payload)
	assert.Nil(t, err)
	assertFieldValues(t, reflect.ValueOf(_transcoderTestSchemaXXX), v)
}
<|endoftext|>


<commit_before>package main
import (
	"net/http"
  "log"
  "io"
  "net/http/cookiejar"
)

func proxy(responseWriter http.ResponseWriter, request *http.Request) {
    client := &http.Client{}
    request.RequestURI = ""
    request.Host = "self.pharocloud.com"
    request.URL.Scheme="http"
    request.URL.Host=request.Host

    client.Jar, _ = cookiejar.New(nil)
    client.Jar.SetCookies(request.URL, request.Cookies())

    response, err := client.Do(request)
    if (err != nil) {
      http.Error(responseWriter, err.Error(), 500)
      return
    }

    for key, valueArray := range response.Header {
      for _, value := range valueArray {
        responseWriter.Header().Add(key, value)
      }
    }
    responseWriter.WriteHeader(response.StatusCode)

    io.Copy(responseWriter, response.Body)
    response.Body.Close()
}

func main() {
    http.HandleFunc("/", proxy)

    err := http.ListenAndServe(":8080", nil);
    if (err != nil){
        log.Fatal(err);
    }
}
<commit_msg>auto start, wait for connection, ephemeric cache, token config<commit_after>package main
import (
  "net/http"
  "log"
  "io"
  "io/ioutil"
  "net/http/cookiejar"
  "fmt"
  "encoding/json"
  "time"
  "flag"
  "strings"
)

type Ephemeric struct {
    Host string
    Port int
    RetrieveTime time.Time
}

var ephemericCache map[string] *Ephemeric;
var authToken string;

func getEphemeric(ephemericHost string) (ephemeric *Ephemeric, err error) {
    ephemeric, ok := ephemericCache[ephemericHost];

    if( ok && time.Since( ephemeric.RetrieveTime ) > 10 * time.Minute) {
	delete(ephemericCache, ephemericHost)
	ok = false;
    }

    if( !ok ) {
        //log.Println("Cache miss for", request.Host)

        apiresp, err := http.Get(fmt.Sprintf("http://ephemeric-api.pharocloud.com/system/ephemerics/%s/start?auth=%s", ephemericHost, authToken))
        if (err != nil) {
          return ephemeric, err
        }

        body, err := ioutil.ReadAll(apiresp.Body)
        if (err != nil) {
          return ephemeric, err
        }

        ephemeric = new(Ephemeric)
        err = json.Unmarshal(body, ephemeric)
        if (err != nil) {
          return ephemeric, err
        }

        ephemeric.RetrieveTime = time.Now()
        ephemericCache[ephemericHost] = ephemeric;
    }
    return ephemeric, err
}

func invalidateEphemeric(ephemericHost string) (ephemeric *Ephemeric, err error) {
	delete(ephemericCache, ephemericHost)
	return getEphemeric(ephemericHost)
}

func proxy(responseWriter http.ResponseWriter, request *http.Request) {
    var ephemeric *Ephemeric;
    var err error;
    ephemericHost := request.Host;
    ephemeric, err = getEphemeric(ephemericHost)

    if (err != nil) {
      http.Error(responseWriter, "Error reading from api server", 500)
      return
    }

    client := &http.Client{}
    request.RequestURI = ""
    request.Host = ephemeric.Host
    request.URL.Scheme="http"
    request.URL.Host=fmt.Sprintf("%s:%d", ephemeric.Host, ephemeric.Port)

    client.Jar, _ = cookiejar.New(nil)
    client.Jar.SetCookies(request.URL, request.Cookies())
    request.Header.Del("Connection")

    //for key, valueArray := range request.Header {
    //  for _, value := range valueArray {
    //    log.Println(key, value)
    //  }
    //}
    //log.Println("--------")

    response, err := client.Do(request)
    for i:=0; err != nil && i < 60; i++ {
        time.Sleep(1000 * time.Millisecond)
	if (i == 10) {
	    ephemeric, err := invalidateEphemeric(ephemericHost)
            if (err != nil) {
	      http.Error(responseWriter, "Error reading from api server", 500)
    	      return
            }

	    request.RequestURI = ""
	    request.Host = ephemeric.Host
	    request.URL.Scheme="http"
	    request.URL.Host=fmt.Sprintf("%s:%d", ephemeric.Host, ephemeric.Port)
	}
	response, err = client.Do(request)
    }

    if (err != nil) {
      http.Error(responseWriter, "Connection to 8080 port of ephemeric failed", 500)
      return
    }

    for key, valueArray := range response.Header {
      for _, value := range valueArray {
        responseWriter.Header().Add(key, value)
      }
    }
    responseWriter.WriteHeader(response.StatusCode)

    io.Copy(responseWriter, response.Body)
    response.Body.Close()
}

func main() {
    var err error
    ephemericCache = make(map[string]*Ephemeric)
    authFile := flag.String("authfile", "authToken", "File which contains system auth token");
    authTokenBytes, err := ioutil.ReadFile(*authFile);

    if (err != nil){
        log.Fatal(err);
    }
    authToken = strings.TrimSpace(string(authTokenBytes)) //todo: rebuild to closure ?

    http.HandleFunc("/", proxy)

    err = http.ListenAndServe(":80", nil);
    if (err != nil){
        log.Fatal(err);
    }
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strconv"

	"github.com/dotcloud/docker/pkg/libcontainer"
	"github.com/dotcloud/docker/pkg/libcontainer/nsinit"

	"github.com/dotcloud/docker/pkg/libcontainer"
	"github.com/dotcloud/docker/pkg/libcontainer/nsinit"
)

var (
	root, console, logs string
	pipeFd              int
)

func registerFlags() {
	flag.StringVar(&console, "console", "", "console (pty slave) path")
	flag.IntVar(&pipeFd, "pipe", 0, "sync pipe fd")
	flag.StringVar(&root, "root", ".", "root for storing configuration data")
	flag.StringVar(&logs, "log", "none", "set stderr or a filepath to enable logging")

	flag.Parse()
}

func main() {
	registerFlags()

	if flag.NArg() < 1 {
		log.Fatalf("wrong number of argments %d", flag.NArg())
	}
	container, err := loadContainer()
	if err != nil {
		log.Fatalf("Unable to load container: %s", err)
	}
	l, err := getLogger("[exec] ")
	if err != nil {
		log.Fatal(err)
	}

	ns, err := newNsInit(l)
	if err != nil {
		log.Fatalf("Unable to initialize nsinit: %s", err)
	}

	switch flag.Arg(0) {
	case "exec": // this is executed outside of the namespace in the cwd
		var exitCode int
		nspid, err := readPid()
		if err != nil {
			if !os.IsNotExist(err) {
				l.Fatalf("Unable to read pid: %s", err)
			}
		}
		if nspid > 0 {
			exitCode, err = ns.ExecIn(container, nspid, flag.Args()[1:])
		} else {
			term := nsinit.NewTerminal(os.Stdin, os.Stdout, os.Stderr, container.Tty)
			exitCode, err = ns.Exec(container, term, flag.Args()[1:])
		}
		if err != nil {
			l.Fatalf("Failed to exec: %s", err)
		}
		os.Exit(exitCode)
	case "init": // this is executed inside of the namespace to setup the container
		cwd, err := os.Getwd()
		if err != nil {
			l.Fatal(err)
		}
		if flag.NArg() < 2 {
			l.Fatalf("wrong number of argments %d", flag.NArg())
		}
		syncPipe, err := nsinit.NewSyncPipeFromFd(0, uintptr(pipeFd))
		if err != nil {
			l.Fatalf("Unable to create sync pipe: %s", err)
		}
		if err := ns.Init(container, cwd, console, syncPipe, flag.Args()[1:]); err != nil {
			l.Fatalf("Unable to initialize for container: %s", err)
		}
	default:
		l.Fatalf("command not supported for nsinit %s", flag.Arg(0))
	}
}

func loadContainer() (*libcontainer.Container, error) {
	f, err := os.Open(filepath.Join(root, "container.json"))
	if err != nil {
		return nil, err
	}
	defer f.Close()

	var container *libcontainer.Container
	if err := json.NewDecoder(f).Decode(&container); err != nil {
		return nil, err
	}
	return container, nil
}

func readPid() (int, error) {
	data, err := ioutil.ReadFile(filepath.Join(root, "pid"))
	if err != nil {
		return -1, err
	}
	pid, err := strconv.Atoi(string(data))
	if err != nil {
		return -1, err
	}
	return pid, nil
}

func newNsInit(l *log.Logger) (nsinit.NsInit, error) {
	return nsinit.NewNsInit(&nsinit.DefaultCommandFactory{root}, &nsinit.DefaultStateWriter{root}, l), nil
}

func getLogger(prefix string) (*log.Logger, error) {
	var w io.Writer
	switch logs {
	case "", "none":
		w = ioutil.Discard
	case "stderr":
		w = os.Stderr
	default: // we have a filepath
		f, err := os.OpenFile(logs, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0755)
		if err != nil {
			return nil, err
		}
		w = f
	}
	return log.New(w, prefix, log.LstdFlags), nil
}
<commit_msg>libcontainer: remove duplicate imports<commit_after>package main

import (
	"encoding/json"
	"flag"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strconv"

	"github.com/dotcloud/docker/pkg/libcontainer"
	"github.com/dotcloud/docker/pkg/libcontainer/nsinit"
)

var (
	root, console, logs string
	pipeFd              int
)

func registerFlags() {
	flag.StringVar(&console, "console", "", "console (pty slave) path")
	flag.IntVar(&pipeFd, "pipe", 0, "sync pipe fd")
	flag.StringVar(&root, "root", ".", "root for storing configuration data")
	flag.StringVar(&logs, "log", "none", "set stderr or a filepath to enable logging")

	flag.Parse()
}

func main() {
	registerFlags()

	if flag.NArg() < 1 {
		log.Fatalf("wrong number of argments %d", flag.NArg())
	}
	container, err := loadContainer()
	if err != nil {
		log.Fatalf("Unable to load container: %s", err)
	}
	l, err := getLogger("[exec] ")
	if err != nil {
		log.Fatal(err)
	}

	ns, err := newNsInit(l)
	if err != nil {
		log.Fatalf("Unable to initialize nsinit: %s", err)
	}

	switch flag.Arg(0) {
	case "exec": // this is executed outside of the namespace in the cwd
		var exitCode int
		nspid, err := readPid()
		if err != nil {
			if !os.IsNotExist(err) {
				l.Fatalf("Unable to read pid: %s", err)
			}
		}
		if nspid > 0 {
			exitCode, err = ns.ExecIn(container, nspid, flag.Args()[1:])
		} else {
			term := nsinit.NewTerminal(os.Stdin, os.Stdout, os.Stderr, container.Tty)
			exitCode, err = ns.Exec(container, term, flag.Args()[1:])
		}
		if err != nil {
			l.Fatalf("Failed to exec: %s", err)
		}
		os.Exit(exitCode)
	case "init": // this is executed inside of the namespace to setup the container
		cwd, err := os.Getwd()
		if err != nil {
			l.Fatal(err)
		}
		if flag.NArg() < 2 {
			l.Fatalf("wrong number of argments %d", flag.NArg())
		}
		syncPipe, err := nsinit.NewSyncPipeFromFd(0, uintptr(pipeFd))
		if err != nil {
			l.Fatalf("Unable to create sync pipe: %s", err)
		}
		if err := ns.Init(container, cwd, console, syncPipe, flag.Args()[1:]); err != nil {
			l.Fatalf("Unable to initialize for container: %s", err)
		}
	default:
		l.Fatalf("command not supported for nsinit %s", flag.Arg(0))
	}
}

func loadContainer() (*libcontainer.Container, error) {
	f, err := os.Open(filepath.Join(root, "container.json"))
	if err != nil {
		return nil, err
	}
	defer f.Close()

	var container *libcontainer.Container
	if err := json.NewDecoder(f).Decode(&container); err != nil {
		return nil, err
	}
	return container, nil
}

func readPid() (int, error) {
	data, err := ioutil.ReadFile(filepath.Join(root, "pid"))
	if err != nil {
		return -1, err
	}
	pid, err := strconv.Atoi(string(data))
	if err != nil {
		return -1, err
	}
	return pid, nil
}

func newNsInit(l *log.Logger) (nsinit.NsInit, error) {
	return nsinit.NewNsInit(&nsinit.DefaultCommandFactory{root}, &nsinit.DefaultStateWriter{root}, l), nil
}

func getLogger(prefix string) (*log.Logger, error) {
	var w io.Writer
	switch logs {
	case "", "none":
		w = ioutil.Discard
	case "stderr":
		w = os.Stderr
	default: // we have a filepath
		f, err := os.OpenFile(logs, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0755)
		if err != nil {
			return nil, err
		}
		w = f
	}
	return log.New(w, prefix, log.LstdFlags), nil
}
<|endoftext|>


<commit_before>// pastebin is a simple modern and powerful pastebin service
package pastebin

import (
	"crypto/sha1"
	"database/sql"
	"encoding/base64"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"html"
	"html/template"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"time"

	// uniuri is used for easy random string generation
	"github.com/dchest/uniuri"
	// pygments is used for syntax highlighting
	"github.com/ewhal/pygments"
	// mysql driver
	_ "github.com/go-sql-driver/mysql"
	// mux is used for url routing
	"github.com/gorilla/mux"
)

const (
	// ADDRESS that pastebin will return links for
	ADDRESS = "http://localhost:9900"
	// LENGTH of paste id
	LENGTH = 6
	// PORT that pastebin will listen on
	PORT = ":9900"
	//  USERNAME for databse
	USERNAME = ""
	// PASS database password
	PASS = ""
	// NAME database name
	NAME = ""
	// DATABASE connection String
	DATABASE = USERNAME + ":" + PASS + "@/" + NAME + "?charset=utf8"
)

// Response API struct
type Response struct {
	ID     string `json:"id"`
	TITLE  string `json:"title"`
	HASH   string `json:"hash"`
	URL    string `json:"url"`
	SIZE   int    `json:"size"`
	DELKEY string `json:"delkey"`
}

// Page generation struct
type Page struct {
	Title    string
	Body     []byte
	Raw      string
	Home     string
	Download string
	Clone    string
}

// check error handling function
func check(err error) {
	if err != nil {
		fmt.Println(err)
	}
}

// generateName uses uniuri to generate a random string that isn't in the
// database
func generateName() string {
	id := uniuri.NewLen(LENGTH)
	db, err := sql.Open("mysql", DATABASE)
	check(err)
	defer db.Close()

	query, err := db.Query("select id from pastebin where id=?", id)
	if err != sql.ErrNoRows {
		for query.Next() {
			generateName()
		}
	}

	return id

}

// hash hashes paste into a sha1 hash
func hash(paste string) string {
	hasher := sha1.New()

	hasher.Write([]byte(paste))
	sha := base64.URLEncoding.EncodeToString(hasher.Sum(nil))
	return sha
}

// durationFromExpiry takes the expiry in string format and returns the duration
// that the paste will exist for
func durationFromExpiry(expiry string) time.Duration {
	switch expiry {
	case "5 minutes":
		return time.Minute * 5
	case "1 hour":
		return time.Hour + 1 // XXX: did you mean '*'?
	case "1 day":
		return time.Hour * 24
	case "1 week":
		return time.Hour * 24 * 7
	case "1 month":
		return time.Hour * 24 * 30
	case "1 year":
		return time.Hour * 24 * 365
	case "forever":
		return time.Hour * 24 * (365 * 20)
	}
	return time.Hour * 24 * (365 * 20)
}

// save function handles the saving of each paste.
// raw string is the raw paste input
// lang string is the user specified language for syntax highlighting
// title string user customized title
// expiry string duration that the paste will exist for
// Returns Response struct
func save(raw string, lang string, title string, expiry string) Response {

	db, err := sql.Open("mysql", DATABASE)
	check(err)
	defer db.Close()

	sha := hash(raw)
	query, err := db.Query("select id, title, hash, data, delkey from pastebin where hash=?", sha)
	if err != sql.ErrNoRows {
		for query.Next() {
			var id, title, hash, paste, delkey string
			err := query.Scan(&id, &title, &hash, &paste, &delkey)
			check(err)
			url := ADDRESS + "/p/" + id
			return Response{id, title, hash, url, len(paste), delkey}
		}
	}
	id := generateName()
	url := ADDRESS + "/p/" + id
	if lang != "" {
		url += "/" + lang
	}

	const timeFormat = "2006-01-02 15:04:05"
	expiryTime := time.Now().Add(durationFromExpiry(expiry)).Format(timeFormat)

	delKey := uniuri.NewLen(40)
	dataEscaped := html.EscapeString(raw)

	stmt, err := db.Prepare("INSERT INTO pastebin(id, title, hash, data, delkey, expiry) values(?,?,?,?,?,?)")
	check(err)
	if title == "" {
		_, err = stmt.Exec(id, id, sha, dataEscaped, delKey, expiryTime)
		check(err)
	} else {
		_, err = stmt.Exec(id, html.EscapeString(title), sha, dataEscaped, delKey, expiryTime)
		check(err)
	}
	return Response{id, title, sha, url, len(dataEscaped), delKey}
}

// delHandler checks to see if delkey and pasteid exist in the database.
// if both exist and are correct the paste will be removed.
func delHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id := vars["pasteId"]
	delkey := vars["delKey"]

	db, err := sql.Open("mysql", DATABASE)
	check(err)
	defer db.Close()

	stmt, err := db.Prepare("delete from pastebin where delkey=? and id=?")
	check(err)

	res, err := stmt.Exec(html.EscapeString(delkey), html.EscapeString(id))
	check(err)

	_, err = res.RowsAffected()
	if err == sql.ErrNoRows {
		io.WriteString(w, "Error invalid paste")
	} else {
		io.WriteString(w, id+" deleted")
	}

}

// saveHandler
func saveHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	output := vars["output"]
	switch r.Method {
	case "POST":
		paste := r.FormValue("p")
		lang := r.FormValue("lang")
		title := r.FormValue("title")
		expiry := r.FormValue("expiry")
		if paste == "" {
			http.Error(w, "Empty paste", 500)
			return
		}
		b := save(paste, lang, title, expiry)

		switch output {
		case "json":

			w.Header().Set("Content-Type", "application/json")
			err := json.NewEncoder(w).Encode(b)

			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
		case "xml":
			x, err := xml.MarshalIndent(b, "", "  ")
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

			w.Header().Set("Content-Type", "application/xml")
			w.Write(x)

		case "html":
			w.Header().Set("Content-Type", "text/html")
			io.WriteString(w, "
URL: "+b.URL+"
")
			io.WriteString(w, "
Delete Key: "+b.DELKEY+"
")

		case "redirect":
			http.Redirect(w, r, b.URL, 301)

		default:
			w.Header().Set("Content-Type", "plain/text")
			io.WriteString(w, b.URL+"\n")
			io.WriteString(w, "delete key: "+b.DELKEY+"\n")
		}
	}

}

// highlight uses user specified input to call pygments library to highlight the
// paste
func highlight(s string, lang string) (string, error) {

	highlight, err := pygments.Highlight(html.UnescapeString(s), html.EscapeString(lang), "html", "style=autumn,linenos=True, lineanchors=True,anchorlinenos=True,noclasses=True,", "utf-8")
	if err != nil {
		return "", err
	}
	return highlight, nil

}

// getPaste
func getPaste(paste string, lang string) (string, string) {
	param1 := html.EscapeString(paste)
	db, err := sql.Open("mysql", DATABASE)
	check(err)
	defer db.Close()
	var title, s string
	var expiry string
	err = db.QueryRow("select title, data, expiry from pastebin where id=?", param1).Scan(&title, &s, &expiry)
	check(err)
	if time.Now().Format("2006-01-02 15:04:05") > expiry {
		stmt, err := db.Prepare("delete from pastebin where id=?")
		check(err)
		_, err = stmt.Exec(param1)
		check(err)
		return "Error invalid paste", ""
	}

	if err == sql.ErrNoRows {
		return "Error invalid paste", ""
	} else {
		if lang == "" {
			return html.UnescapeString(s), html.UnescapeString(title)
		} else {
			high, err := highlight(s, lang)
			check(err)
			return high, html.UnescapeString(title)

		}
	}

}

var templates = template.Must(template.ParseFiles("assets/paste.html", "assets/index.html", "assets/clone.html"))
var syntax, _ = ioutil.ReadFile("assets/syntax.html")

func pasteHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	lang := vars["lang"]
	s, title := getPaste(paste, lang)
	link := ADDRESS + "/raw/" + paste
	download := ADDRESS + "/download/" + paste
	clone := ADDRESS + "/clone/" + paste
	p := &Page{
		Title:    title,
		Body:     []byte(s),
		Raw:      link,
		Home:     ADDRESS,
		Download: download,
		Clone:    clone,
	}
	if lang == "" {

		err := templates.ExecuteTemplate(w, "paste.html", p)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}

	} else {
		fmt.Fprintf(w, string(syntax), p.Title, p.Title, s, p.Home, p.Download, p.Raw, p.Clone)

	}
}

func cloneHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	s, title := getPaste(paste, "")
	link := ADDRESS + "/raw/" + paste
	download := ADDRESS + "/download/" + paste
	clone := ADDRESS + "/clone/" + paste
	p := &Page{
		Title:    title,
		Body:     []byte(s),
		Raw:      link,
		Home:     ADDRESS,
		Download: download,
		Clone:    clone,
	}
	err := templates.ExecuteTemplate(w, "clone.html", p)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

}
func downloadHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	s, _ := getPaste(paste, "")
	w.Header().Set("Content-Disposition", "attachment; filename="+paste)
	w.Header().Set("Content-Type", r.Header.Get("Content-Type"))
	io.WriteString(w, s)

}
func rawHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	s, _ := getPaste(paste, "")
	io.WriteString(w, s)

}

func main() {
	router := mux.NewRouter()
	router.HandleFunc("/p/{pasteId}", pasteHandler)
	router.HandleFunc("/raw/{pasteId}", rawHandler)
	router.HandleFunc("/p/{pasteId}/{lang}", pasteHandler)
	router.HandleFunc("/clone/{pasteId}", cloneHandler)
	router.HandleFunc("/download/{pasteId}", downloadHandler)
	router.HandleFunc("/save", saveHandler)
	router.HandleFunc("/save/{output}", saveHandler)
	router.HandleFunc("/del/{pasteId}/{delKey}", delHandler)
	router.PathPrefix("/").Handler(http.StripPrefix("/", http.FileServer(http.Dir("assets/"))))
	err := http.ListenAndServe(PORT, router)
	if err != nil {
		log.Fatal(err)
	}

}
<commit_msg>Reduce code for handling titles<commit_after>// pastebin is a simple modern and powerful pastebin service
package pastebin

import (
	"crypto/sha1"
	"database/sql"
	"encoding/base64"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"html"
	"html/template"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"time"

	// uniuri is used for easy random string generation
	"github.com/dchest/uniuri"
	// pygments is used for syntax highlighting
	"github.com/ewhal/pygments"
	// mysql driver
	_ "github.com/go-sql-driver/mysql"
	// mux is used for url routing
	"github.com/gorilla/mux"
)

const (
	// ADDRESS that pastebin will return links for
	ADDRESS = "http://localhost:9900"
	// LENGTH of paste id
	LENGTH = 6
	// PORT that pastebin will listen on
	PORT = ":9900"
	//  USERNAME for databse
	USERNAME = ""
	// PASS database password
	PASS = ""
	// NAME database name
	NAME = ""
	// DATABASE connection String
	DATABASE = USERNAME + ":" + PASS + "@/" + NAME + "?charset=utf8"
)

// Response API struct
type Response struct {
	ID     string `json:"id"`
	TITLE  string `json:"title"`
	HASH   string `json:"hash"`
	URL    string `json:"url"`
	SIZE   int    `json:"size"`
	DELKEY string `json:"delkey"`
}

// Page generation struct
type Page struct {
	Title    string
	Body     []byte
	Raw      string
	Home     string
	Download string
	Clone    string
}

// check error handling function
func check(err error) {
	if err != nil {
		fmt.Println(err)
	}
}

// generateName uses uniuri to generate a random string that isn't in the
// database
func generateName() string {
	id := uniuri.NewLen(LENGTH)
	db, err := sql.Open("mysql", DATABASE)
	check(err)
	defer db.Close()

	query, err := db.Query("select id from pastebin where id=?", id)
	if err != sql.ErrNoRows {
		for query.Next() {
			generateName()
		}
	}

	return id

}

// hash hashes paste into a sha1 hash
func hash(paste string) string {
	hasher := sha1.New()

	hasher.Write([]byte(paste))
	sha := base64.URLEncoding.EncodeToString(hasher.Sum(nil))
	return sha
}

// durationFromExpiry takes the expiry in string format and returns the duration
// that the paste will exist for
func durationFromExpiry(expiry string) time.Duration {
	switch expiry {
	case "5 minutes":
		return time.Minute * 5
	case "1 hour":
		return time.Hour + 1 // XXX: did you mean '*'?
	case "1 day":
		return time.Hour * 24
	case "1 week":
		return time.Hour * 24 * 7
	case "1 month":
		return time.Hour * 24 * 30
	case "1 year":
		return time.Hour * 24 * 365
	case "forever":
		return time.Hour * 24 * (365 * 20)
	}
	return time.Hour * 24 * (365 * 20)
}

// save function handles the saving of each paste.
// raw string is the raw paste input
// lang string is the user specified language for syntax highlighting
// title string user customized title
// expiry string duration that the paste will exist for
// Returns Response struct
func save(raw string, lang string, title string, expiry string) Response {

	db, err := sql.Open("mysql", DATABASE)
	check(err)
	defer db.Close()

	sha := hash(raw)
	query, err := db.Query("select id, title, hash, data, delkey from pastebin where hash=?", sha)
	if err != sql.ErrNoRows {
		for query.Next() {
			var id, title, hash, paste, delkey string
			err := query.Scan(&id, &title, &hash, &paste, &delkey)
			check(err)
			url := ADDRESS + "/p/" + id
			return Response{id, title, hash, url, len(paste), delkey}
		}
	}
	id := generateName()
	url := ADDRESS + "/p/" + id
	if lang != "" {
		url += "/" + lang
	}

	const timeFormat = "2006-01-02 15:04:05"
	expiryTime := time.Now().Add(durationFromExpiry(expiry)).Format(timeFormat)

	delKey := uniuri.NewLen(40)
	dataEscaped := html.EscapeString(raw)

	stmt, err := db.Prepare("INSERT INTO pastebin(id, title, hash, data, delkey, expiry) values(?,?,?,?,?,?)")
	check(err)
	if title == "" {
		title = id
	}
	_, err = stmt.Exec(id, html.EscapeString(title), sha, dataEscaped, delKey, expiryTime)
	check(err)
	return Response{id, title, sha, url, len(dataEscaped), delKey}
}

// delHandler checks to see if delkey and pasteid exist in the database.
// if both exist and are correct the paste will be removed.
func delHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id := vars["pasteId"]
	delkey := vars["delKey"]

	db, err := sql.Open("mysql", DATABASE)
	check(err)
	defer db.Close()

	stmt, err := db.Prepare("delete from pastebin where delkey=? and id=?")
	check(err)

	res, err := stmt.Exec(html.EscapeString(delkey), html.EscapeString(id))
	check(err)

	_, err = res.RowsAffected()
	if err == sql.ErrNoRows {
		io.WriteString(w, "Error invalid paste")
	} else {
		io.WriteString(w, id+" deleted")
	}

}

// saveHandler
func saveHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	output := vars["output"]
	switch r.Method {
	case "POST":
		paste := r.FormValue("p")
		lang := r.FormValue("lang")
		title := r.FormValue("title")
		expiry := r.FormValue("expiry")
		if paste == "" {
			http.Error(w, "Empty paste", 500)
			return
		}
		b := save(paste, lang, title, expiry)

		switch output {
		case "json":

			w.Header().Set("Content-Type", "application/json")
			err := json.NewEncoder(w).Encode(b)

			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
		case "xml":
			x, err := xml.MarshalIndent(b, "", "  ")
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}

			w.Header().Set("Content-Type", "application/xml")
			w.Write(x)

		case "html":
			w.Header().Set("Content-Type", "text/html")
			io.WriteString(w, "
URL: "+b.URL+"
")
			io.WriteString(w, "
Delete Key: "+b.DELKEY+"
")

		case "redirect":
			http.Redirect(w, r, b.URL, 301)

		default:
			w.Header().Set("Content-Type", "plain/text")
			io.WriteString(w, b.URL+"\n")
			io.WriteString(w, "delete key: "+b.DELKEY+"\n")
		}
	}

}

// highlight uses user specified input to call pygments library to highlight the
// paste
func highlight(s string, lang string) (string, error) {

	highlight, err := pygments.Highlight(html.UnescapeString(s), html.EscapeString(lang), "html", "style=autumn,linenos=True, lineanchors=True,anchorlinenos=True,noclasses=True,", "utf-8")
	if err != nil {
		return "", err
	}
	return highlight, nil

}

// getPaste
func getPaste(paste string, lang string) (string, string) {
	param1 := html.EscapeString(paste)
	db, err := sql.Open("mysql", DATABASE)
	check(err)
	defer db.Close()
	var title, s string
	var expiry string
	err = db.QueryRow("select title, data, expiry from pastebin where id=?", param1).Scan(&title, &s, &expiry)
	check(err)
	if time.Now().Format("2006-01-02 15:04:05") > expiry {
		stmt, err := db.Prepare("delete from pastebin where id=?")
		check(err)
		_, err = stmt.Exec(param1)
		check(err)
		return "Error invalid paste", ""
	}

	if err == sql.ErrNoRows {
		return "Error invalid paste", ""
	} else {
		if lang == "" {
			return html.UnescapeString(s), html.UnescapeString(title)
		} else {
			high, err := highlight(s, lang)
			check(err)
			return high, html.UnescapeString(title)

		}
	}

}

var templates = template.Must(template.ParseFiles("assets/paste.html", "assets/index.html", "assets/clone.html"))
var syntax, _ = ioutil.ReadFile("assets/syntax.html")

func pasteHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	lang := vars["lang"]
	s, title := getPaste(paste, lang)
	link := ADDRESS + "/raw/" + paste
	download := ADDRESS + "/download/" + paste
	clone := ADDRESS + "/clone/" + paste
	p := &Page{
		Title:    title,
		Body:     []byte(s),
		Raw:      link,
		Home:     ADDRESS,
		Download: download,
		Clone:    clone,
	}
	if lang == "" {

		err := templates.ExecuteTemplate(w, "paste.html", p)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}

	} else {
		fmt.Fprintf(w, string(syntax), p.Title, p.Title, s, p.Home, p.Download, p.Raw, p.Clone)

	}
}

func cloneHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	s, title := getPaste(paste, "")
	link := ADDRESS + "/raw/" + paste
	download := ADDRESS + "/download/" + paste
	clone := ADDRESS + "/clone/" + paste
	p := &Page{
		Title:    title,
		Body:     []byte(s),
		Raw:      link,
		Home:     ADDRESS,
		Download: download,
		Clone:    clone,
	}
	err := templates.ExecuteTemplate(w, "clone.html", p)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

}
func downloadHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	s, _ := getPaste(paste, "")
	w.Header().Set("Content-Disposition", "attachment; filename="+paste)
	w.Header().Set("Content-Type", r.Header.Get("Content-Type"))
	io.WriteString(w, s)

}
func rawHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	paste := vars["pasteId"]
	s, _ := getPaste(paste, "")
	io.WriteString(w, s)

}

func main() {
	router := mux.NewRouter()
	router.HandleFunc("/p/{pasteId}", pasteHandler)
	router.HandleFunc("/raw/{pasteId}", rawHandler)
	router.HandleFunc("/p/{pasteId}/{lang}", pasteHandler)
	router.HandleFunc("/clone/{pasteId}", cloneHandler)
	router.HandleFunc("/download/{pasteId}", downloadHandler)
	router.HandleFunc("/save", saveHandler)
	router.HandleFunc("/save/{output}", saveHandler)
	router.HandleFunc("/del/{pasteId}/{delKey}", delHandler)
	router.PathPrefix("/").Handler(http.StripPrefix("/", http.FileServer(http.Dir("assets/"))))
	err := http.ListenAndServe(PORT, router)
	if err != nil {
		log.Fatal(err)
	}

}
<|endoftext|>


<commit_before>/* Copyright (c) 2014, Daniel Martí <mvdan@mvdan.cc> */
/* See LICENSE for licensing information */

package main

import (
	"compress/zlib"
	"errors"
	"flag"
	"fmt"
	"html/template"
	"io"
	"log"
	"math/rand"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"time"
)

const (
	idSize    = 8 // should be between 6 and 256
	indexTmpl = "index.html"
	formTmpl  = "form.html"
	chars     = "abcdefghijklmnopqrstuvwxyz0123456789"

	// GET error messages
	invalidId     = "Invalid paste id."
	pasteNotFound = "Paste doesn't exist."
	unknownError  = "Something went terribly wrong."
	// POST error messages
	missingForm = "Paste could not be found inside the posted form."
)

var (
	siteUrl     = flag.String("u", "http://localhost:8080", "URL of the site")
	listen      = flag.String("l", "localhost:8080", "Host and port to listen to")
	dataDir     = flag.String("d", "data", "Directory to store all the pastes in")
	lifeTimeStr = flag.String("t", "12h", "Lifetime of the pastes (units: s,m,h)")
	maxSizeStr  = flag.String("s", "1M", "Maximum size of POSTs in bytes (units: B,K,M)")

	lifeTime time.Duration
	maxSize  ByteSize

	validId       = regexp.MustCompile("^[a-zA-Z0-9]{" + strconv.FormatInt(idSize, 10) + "}$")
	regexByteSize = regexp.MustCompile(`^([\d\.]+)\s*([KM]?B|[BKM])$`)
	indexTemplate *template.Template
	formTemplate  *template.Template
)

func pathId(id string) string {
	return path.Join(id[0:2], id[2:4], id[4:])
}

type ByteSize int64

const (
	B ByteSize = 1 << (10 * iota)
	KB
	MB
)

func (b ByteSize) String() string {
	switch {
	case b >= MB:
		return fmt.Sprintf("%.2f MB", float64(b)/float64(MB))
	case b >= KB:
		return fmt.Sprintf("%.2f KB", float64(b)/float64(KB))
	}
	return fmt.Sprintf("%d B", b)
}

func parseByteSize(str string) (ByteSize, error) {
	if !regexByteSize.MatchString(str) {
		return 0, errors.New("Could not parse size in bytes")
	}
	parts := regexByteSize.FindStringSubmatch(str)
	size, _ := strconv.ParseFloat(string(parts[1]), 64)

	switch string(parts[2]) {
	case "KB", "K":
		size *= float64(KB)
	case "MB", "M":
		size *= float64(MB)
	}
	return ByteSize(size), nil
}

func randomId() string {
	s := make([]byte, idSize)
	var offset uint = 0
MainLoop:
	for {
		r := rand.Int63()
		for i := 0; i < 8; i++ {
			randbyte := int(r&0xff) % len(chars)
			s[offset] = chars[randbyte]
			offset++
			if offset == idSize {
				break MainLoop
			}
			r >>= 8
		}
	}
	return string(s)
}

func endLife(path string) {
	err := os.Remove(path)
	if err == nil {
		log.Printf("Removed paste: %s", path)
	} else {
		log.Printf("Could not end the life of %s: %s", path, err)
		programDeath(path, 2*time.Minute)
	}
}

func programDeath(path string, after time.Duration) {
	timer := time.NewTimer(after)
	go func() {
		<-timer.C
		endLife(path)
	}()
}

func handler(w http.ResponseWriter, r *http.Request) {
	var err error
	switch r.Method {
	case "GET":
		switch r.URL.Path {
		case "/":
			indexTemplate.Execute(w, *siteUrl)
			return
		case "/form":
			formTemplate.Execute(w, *siteUrl)
			return
		}
		id := r.URL.Path[1:]
		if !validId.MatchString(id) {
			w.WriteHeader(http.StatusBadRequest)
			fmt.Fprintf(w, "%s\n", invalidId)
			return
		}
		id = strings.ToLower(id)
		pastePath := pathId(id)
		pasteFile, err := os.Open(pastePath)
		if err != nil {
			w.WriteHeader(http.StatusNotFound)
			fmt.Fprintf(w, "%s\n", pasteNotFound)
			return
		}
		compReader, err := zlib.NewReader(pasteFile)
		if err != nil {
			log.Printf("Could not open a compression reader for %s: %s", pastePath, err)
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintf(w, "%s\n", unknownError)
			return
		}
		io.Copy(w, compReader)
		compReader.Close()
		pasteFile.Close()

	case "POST":
		r.Body = http.MaxBytesReader(w, r.Body, int64(maxSize))
		var id, pastePath string
		found := false
		for i := 0; i < 10; i++ {
			id = randomId()
			pastePath = pathId(id)
			if _, err := os.Stat(pastePath); os.IsNotExist(err) {
				found = true
				break
			}
		}
		if !found {
			log.Printf("Gave up trying to find an unused random id.")
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintf(w, "%s\n", unknownError)
			return
		}
		if err = r.ParseMultipartForm(int64(maxSize)); err != nil {
			log.Printf("Could not parse POST multipart form: %s", err)
			w.WriteHeader(http.StatusBadRequest)
			fmt.Fprintf(w, "%s\n", err)
			return
		}
		var content string
		if vs, found := r.Form["paste"]; found {
			content = vs[0]
		} else {
			w.WriteHeader(http.StatusBadRequest)
			fmt.Fprintf(w, "%s\n", missingForm)
			return
		}
		dir, _ := path.Split(pastePath)
		if err = os.MkdirAll(dir, 0700); err != nil {
			log.Printf("Could not create directories leading to %s: %s", pastePath, err)
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintf(w, "%s\n", unknownError)
			return
		}
		programDeath(pastePath, lifeTime)
		pasteFile, err := os.OpenFile(pastePath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
		if err != nil {
			log.Printf("Could not create new paste pasteFile %s: %s", pastePath, err)
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintf(w, "%s\n", unknownError)
			return
		}
		compWriter := zlib.NewWriter(pasteFile)
		b, err := io.WriteString(compWriter, content)
		compWriter.Close()
		pasteFile.Close()
		if err != nil {
			log.Printf("Could not write compressed data into %s: %s", pastePath, err)
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintf(w, "%s\n", unknownError)
			return
		}
		writtenSize := ByteSize(b)
		log.Printf("Created a new paste: %s (%s)", pastePath, writtenSize)
		fmt.Fprintf(w, "%s/%s\n", *siteUrl, id)
	}
}

func walkFunc(path string, info os.FileInfo, err error) error {
	if err != nil {
		return err
	}
	if info.IsDir() {
		return nil
	}
	deathTime := info.ModTime().Add(lifeTime)
	now := time.Now()
	if deathTime.Before(now) {
		go endLife(path)
		return nil
	}
	var lifeLeft time.Duration
	if deathTime.After(now.Add(lifeTime)) {
		lifeLeft = lifeTime
	} else {
		lifeLeft = deathTime.Sub(now)
	}
	log.Printf("Recovered paste %s has %s left", path, lifeLeft)
	programDeath(path, lifeLeft)
	return nil
}

func main() {
	var err error
	flag.Parse()
	if lifeTime, err = time.ParseDuration(*lifeTimeStr); err != nil {
		log.Fatalf("Invalid lifetime '%s': %s", *lifeTimeStr, err)
	}
	if maxSize, err = parseByteSize(*maxSizeStr); err != nil {
		log.Fatalf("Invalid max size '%s': %s", *maxSizeStr, err)
	}
	if indexTemplate, err = template.ParseFiles(indexTmpl); err != nil {
		log.Fatalf("Could not load template %s: %s", indexTmpl, err)
	}
	if formTemplate, err = template.ParseFiles(formTmpl); err != nil {
		log.Fatalf("Could not load template %s: %s", formTmpl, err)
	}
	if err = os.MkdirAll(*dataDir, 0700); err != nil {
		log.Fatalf("Could not create data directory %s: %s", *dataDir, err)
	}
	if err = os.Chdir(*dataDir); err != nil {
		log.Fatalf("Could not enter data directory %s: %s", *dataDir, err)
	}
	if err = filepath.Walk(".", walkFunc); err != nil {
		log.Fatalf("Could not recover data directory %s: %s", *dataDir, err)
	}
	log.Printf("idSize   = %d", idSize)
	log.Printf("maxSize  = %s", maxSize)
	log.Printf("siteUrl  = %s", *siteUrl)
	log.Printf("listen   = %s", *listen)
	log.Printf("dataDir  = %s", *dataDir)
	log.Printf("lifeTime = %s", lifeTime)
	http.HandleFunc("/", handler)
	log.Fatal(http.ListenAndServe(*listen, nil))
}
<commit_msg>Make idSize a cmdline option as well<commit_after>/* Copyright (c) 2014, Daniel Martí <mvdan@mvdan.cc> */
/* See LICENSE for licensing information */

package main

import (
	"compress/zlib"
	"errors"
	"flag"
	"fmt"
	"html/template"
	"io"
	"log"
	"math/rand"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"time"
)

const (
	indexTmpl = "index.html"
	formTmpl  = "form.html"
	chars     = "abcdefghijklmnopqrstuvwxyz0123456789"

	// GET error messages
	invalidId     = "Invalid paste id."
	pasteNotFound = "Paste doesn't exist."
	unknownError  = "Something went terribly wrong."
	// POST error messages
	missingForm = "Paste could not be found inside the posted form."
)

var (
	siteUrl     = flag.String("u", "http://localhost:8080", "URL of the site")
	listen      = flag.String("l", "localhost:8080", "Host and port to listen to")
	dataDir     = flag.String("d", "data", "Directory to store all the pastes in")
	lifeTimeStr = flag.String("t", "12h", "Lifetime of the pastes (units: s,m,h)")
	maxSizeStr  = flag.String("s", "1M", "Maximum size of POSTs in bytes (units: B,K,M)")
	idSize      = flag.Int("i", 8, "Size of the paste ids (between 6 and 256)")

	lifeTime time.Duration
	maxSize  ByteSize

	validId       = regexp.MustCompile("^[a-zA-Z0-9]{" + strconv.Itoa(*idSize) + "}$")
	regexByteSize = regexp.MustCompile(`^([\d\.]+)\s*([KM]?B|[BKM])$`)
	indexTemplate *template.Template
	formTemplate  *template.Template
)

func pathId(id string) string {
	return path.Join(id[0:2], id[2:4], id[4:])
}

type ByteSize int64

const (
	B ByteSize = 1 << (10 * iota)
	KB
	MB
)

func (b ByteSize) String() string {
	switch {
	case b >= MB:
		return fmt.Sprintf("%.2f MB", float64(b)/float64(MB))
	case b >= KB:
		return fmt.Sprintf("%.2f KB", float64(b)/float64(KB))
	}
	return fmt.Sprintf("%d B", b)
}

func parseByteSize(str string) (ByteSize, error) {
	if !regexByteSize.MatchString(str) {
		return 0, errors.New("Could not parse size in bytes")
	}
	parts := regexByteSize.FindStringSubmatch(str)
	size, _ := strconv.ParseFloat(string(parts[1]), 64)

	switch string(parts[2]) {
	case "KB", "K":
		size *= float64(KB)
	case "MB", "M":
		size *= float64(MB)
	}
	return ByteSize(size), nil
}

func randomId() string {
	s := make([]byte, *idSize)
	var offset int = 0
MainLoop:
	for {
		r := rand.Int63()
		for i := 0; i < 8; i++ {
			randbyte := int(r&0xff) % len(chars)
			s[offset] = chars[randbyte]
			offset++
			if offset == *idSize {
				break MainLoop
			}
			r >>= 8
		}
	}
	return string(s)
}

func endLife(path string) {
	err := os.Remove(path)
	if err == nil {
		log.Printf("Removed paste: %s", path)
	} else {
		log.Printf("Could not end the life of %s: %s", path, err)
		programDeath(path, 2*time.Minute)
	}
}

func programDeath(path string, after time.Duration) {
	timer := time.NewTimer(after)
	go func() {
		<-timer.C
		endLife(path)
	}()
}

func handler(w http.ResponseWriter, r *http.Request) {
	var err error
	switch r.Method {
	case "GET":
		switch r.URL.Path {
		case "/":
			indexTemplate.Execute(w, *siteUrl)
			return
		case "/form":
			formTemplate.Execute(w, *siteUrl)
			return
		}
		id := r.URL.Path[1:]
		if !validId.MatchString(id) {
			w.WriteHeader(http.StatusBadRequest)
			fmt.Fprintf(w, "%s\n", invalidId)
			return
		}
		id = strings.ToLower(id)
		pastePath := pathId(id)
		pasteFile, err := os.Open(pastePath)
		if err != nil {
			w.WriteHeader(http.StatusNotFound)
			fmt.Fprintf(w, "%s\n", pasteNotFound)
			return
		}
		compReader, err := zlib.NewReader(pasteFile)
		if err != nil {
			log.Printf("Could not open a compression reader for %s: %s", pastePath, err)
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintf(w, "%s\n", unknownError)
			return
		}
		io.Copy(w, compReader)
		compReader.Close()
		pasteFile.Close()

	case "POST":
		r.Body = http.MaxBytesReader(w, r.Body, int64(maxSize))
		var id, pastePath string
		found := false
		for i := 0; i < 10; i++ {
			id = randomId()
			pastePath = pathId(id)
			if _, err := os.Stat(pastePath); os.IsNotExist(err) {
				found = true
				break
			}
		}
		if !found {
			log.Printf("Gave up trying to find an unused random id.")
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintf(w, "%s\n", unknownError)
			return
		}
		if err = r.ParseMultipartForm(int64(maxSize)); err != nil {
			log.Printf("Could not parse POST multipart form: %s", err)
			w.WriteHeader(http.StatusBadRequest)
			fmt.Fprintf(w, "%s\n", err)
			return
		}
		var content string
		if vs, found := r.Form["paste"]; found {
			content = vs[0]
		} else {
			w.WriteHeader(http.StatusBadRequest)
			fmt.Fprintf(w, "%s\n", missingForm)
			return
		}
		dir, _ := path.Split(pastePath)
		if err = os.MkdirAll(dir, 0700); err != nil {
			log.Printf("Could not create directories leading to %s: %s", pastePath, err)
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintf(w, "%s\n", unknownError)
			return
		}
		programDeath(pastePath, lifeTime)
		pasteFile, err := os.OpenFile(pastePath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
		if err != nil {
			log.Printf("Could not create new paste pasteFile %s: %s", pastePath, err)
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintf(w, "%s\n", unknownError)
			return
		}
		compWriter := zlib.NewWriter(pasteFile)
		b, err := io.WriteString(compWriter, content)
		compWriter.Close()
		pasteFile.Close()
		if err != nil {
			log.Printf("Could not write compressed data into %s: %s", pastePath, err)
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintf(w, "%s\n", unknownError)
			return
		}
		writtenSize := ByteSize(b)
		log.Printf("Created a new paste: %s (%s)", pastePath, writtenSize)
		fmt.Fprintf(w, "%s/%s\n", *siteUrl, id)
	}
}

func walkFunc(path string, info os.FileInfo, err error) error {
	if err != nil {
		return err
	}
	if info.IsDir() {
		return nil
	}
	deathTime := info.ModTime().Add(lifeTime)
	now := time.Now()
	if deathTime.Before(now) {
		go endLife(path)
		return nil
	}
	var lifeLeft time.Duration
	if deathTime.After(now.Add(lifeTime)) {
		lifeLeft = lifeTime
	} else {
		lifeLeft = deathTime.Sub(now)
	}
	log.Printf("Recovered paste %s has %s left", path, lifeLeft)
	programDeath(path, lifeLeft)
	return nil
}

func main() {
	var err error
	flag.Parse()
	if lifeTime, err = time.ParseDuration(*lifeTimeStr); err != nil {
		log.Fatalf("Invalid lifetime '%s': %s", *lifeTimeStr, err)
	}
	if maxSize, err = parseByteSize(*maxSizeStr); err != nil {
		log.Fatalf("Invalid max size '%s': %s", *maxSizeStr, err)
	}
	if indexTemplate, err = template.ParseFiles(indexTmpl); err != nil {
		log.Fatalf("Could not load template %s: %s", indexTmpl, err)
	}
	if formTemplate, err = template.ParseFiles(formTmpl); err != nil {
		log.Fatalf("Could not load template %s: %s", formTmpl, err)
	}
	if err = os.MkdirAll(*dataDir, 0700); err != nil {
		log.Fatalf("Could not create data directory %s: %s", *dataDir, err)
	}
	if err = os.Chdir(*dataDir); err != nil {
		log.Fatalf("Could not enter data directory %s: %s", *dataDir, err)
	}
	if err = filepath.Walk(".", walkFunc); err != nil {
		log.Fatalf("Could not recover data directory %s: %s", *dataDir, err)
	}
	log.Printf("idSize   = %d", *idSize)
	log.Printf("maxSize  = %s", maxSize)
	log.Printf("siteUrl  = %s", *siteUrl)
	log.Printf("listen   = %s", *listen)
	log.Printf("dataDir  = %s", *dataDir)
	log.Printf("lifeTime = %s", lifeTime)
	http.HandleFunc("/", handler)
	log.Fatal(http.ListenAndServe(*listen, nil))
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/SpaceLeap/go-mavlink/mavlink"
	"github.com/SpaceLeap/go-mavlink/mavlink/common"
	"github.com/ungerik/go-dry"
	"github.com/ungerik/goserial"
)

var (
	port       string
	baud       int
	maxPackets int
	quitAfter  time.Duration
	timeout    time.Duration

	stop bool
)

func main() {
	common.Init()

	flag.StringVar(&port, "port", "", "Serial port to connect to")
	flag.IntVar(&baud, "baud", 115200, "Speed of the connection")
	flag.IntVar(&maxPackets, "max", 10, "Quit program after this number of packets")
	flag.DurationVar(&quitAfter, "quitafter", time.Second*3, "Quit program after this duration")
	flag.DurationVar(&timeout, "timeout", time.Second, "Read timeout per packet")
	flag.Parse()

	if port == "" && flag.NArg() == 0 {
		ports := serial.ListPorts()
		if len(ports) == 1 {
			port = ports[0]
		} else {
			fmt.Fprintln(os.Stderr, "Call listmavlink -port=PORT")
			flag.PrintDefaults()
			fmt.Fprintln(os.Stderr, "\nAvailable as PORT are:")
			for _, p := range ports {
				fmt.Fprintln(os.Stderr, "  ", p)
			}
			return
		}
	}
	if port == "" {
		port = flag.Arg(0)
	}

	serialConn, err := serial.OpenDefault(port, serial.Baud(baud), timeout)
	if err != nil {
		log.Fatal(err)
	}
	log.Println("Opened serial port", port)
	defer func() {
		log.Println("Closed serial port", port, "with error", serialConn.Close())
	}()

	//conn := mavlink.NewConnection(serialConn, 99)

	err = mavlink.Send(serialConn, 0, 0, 0, common.NewHeartbeat())
	if err != nil {
		log.Println(err)
	}

	err = mavlink.Send(serialConn, 0, 0, 0, &common.Ping{})
	if err != nil {
		log.Println(err)
	}

	err = mavlink.Send(serialConn, 0, 0, 0, &common.ParamRequestList{})
	if err != nil {
		log.Println(err)
	}

	go func() {
		dry.WaitForStdin("Press any key to quit")
		stop = true
	}()

	time.AfterFunc(quitAfter, func() { stop = true })

	for i := 0; i < maxPackets && !stop; i++ {
		packet, err := mavlink.Receive(serialConn)
		if err == nil {
			log.Println(packet)
		} else {
			log.Println(err)
		}
	}
}
<commit_msg>connect with default serial port<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/SpaceLeap/go-mavlink/mavlink"
	"github.com/SpaceLeap/go-mavlink/mavlink/common"
	"github.com/ungerik/go-dry"
	"github.com/ungerik/goserial"
)

var (
	port       string
	baud       int
	maxPackets int
	quitAfter  time.Duration
	timeout    time.Duration

	stop bool
)

func main() {
	common.Init()

	flag.StringVar(&port, "port", "", "Serial port to connect to")
	flag.IntVar(&baud, "baud", 115200, "Speed of the connection")
	flag.IntVar(&maxPackets, "max", 10, "Quit program after this number of packets")
	flag.DurationVar(&quitAfter, "quitafter", time.Second*3, "Quit program after this duration")
	flag.DurationVar(&timeout, "timeout", time.Second, "Read timeout per packet")
	flag.Parse()

	if port == "" {
		if flag.NArg() > 0 {
			port = flag.Arg(0)
		} else {
			ports := serial.ListPorts()
			if len(ports) == 1 {
				port = ports[0]
			} else {
				fmt.Fprintln(os.Stderr, "Call with -port=PORT")
				flag.PrintDefaults()
				fmt.Fprintln(os.Stderr, "\nAvailable as PORT are:")
				for _, p := range ports {
					fmt.Fprintln(os.Stderr, "  ", p)
				}
				return
			}
		}
	}

	serialConn, err := serial.OpenDefault(port, serial.Baud(baud), timeout)
	if err != nil {
		log.Fatal(err)
	}
	log.Println("Opened serial port", port)
	defer func() {
		log.Println("Closed serial port", port, "with error", serialConn.Close())
	}()

	//conn := mavlink.NewConnection(serialConn, 99)

	err = mavlink.Send(serialConn, 0, 0, 0, common.NewHeartbeat())
	if err != nil {
		log.Println(err)
	}

	err = mavlink.Send(serialConn, 0, 0, 0, &common.Ping{})
	if err != nil {
		log.Println(err)
	}

	err = mavlink.Send(serialConn, 0, 0, 0, &common.ParamRequestList{})
	if err != nil {
		log.Println(err)
	}

	go func() {
		dry.WaitForStdin("Press any key to quit")
		stop = true
	}()

	time.AfterFunc(quitAfter, func() { stop = true })

	for i := 0; i < maxPackets && !stop; i++ {
		packet, err := mavlink.Receive(serialConn)
		if err == nil {
			log.Println(packet)
		} else {
			log.Println(err)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2013 The Go 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 oracle_test

// This file defines a test framework for oracle queries.
//
// The files beneath testdata/src/main contain Go programs containing
// query annotations of the form:
//
//   @verb id "select"
//
// where verb is the query mode (e.g. "callers"), id is a unique name
// for this query, and "select" is a regular expression matching the
// substring of the current line that is the query's input selection.
//
// The expected output for each query is provided in the accompanying
// .golden file.
//
// (Location information is not included because it's too fragile to
// display as text.  TODO(adonovan): think about how we can test its
// correctness, since it is critical information.)
//
// Run this test with:
// 	% go test code.google.com/p/go.tools/oracle -update
// to update the golden files.

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"go/build"
	"go/parser"
	"go/token"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"testing"

	"code.google.com/p/go.tools/go/loader"
	"code.google.com/p/go.tools/oracle"
)

var updateFlag = flag.Bool("update", false, "Update the golden files.")

type query struct {
	id       string         // unique id
	verb     string         // query mode, e.g. "callees"
	posn     token.Position // position of of query
	filename string
	queryPos string // value of -pos flag
}

func parseRegexp(text string) (*regexp.Regexp, error) {
	pattern, err := strconv.Unquote(text)
	if err != nil {
		return nil, fmt.Errorf("can't unquote %s", text)
	}
	return regexp.Compile(pattern)
}

// parseQueries parses and returns the queries in the named file.
func parseQueries(t *testing.T, filename string) []*query {
	filedata, err := ioutil.ReadFile(filename)
	if err != nil {
		t.Fatal(err)
	}

	// Parse the file once to discover the test queries.
	var fset token.FileSet
	f, err := parser.ParseFile(&fset, filename, filedata, parser.ParseComments)
	if err != nil {
		t.Fatal(err)
	}

	lines := bytes.Split(filedata, []byte("\n"))

	var queries []*query
	queriesById := make(map[string]*query)

	// Find all annotations of these forms:
	expectRe := regexp.MustCompile(`@([a-z]+)\s+(\S+)\s+(\".*)$`) // @verb id "regexp"
	for _, c := range f.Comments {
		text := strings.TrimSpace(c.Text())
		if text == "" || text[0] != '@' {
			continue
		}
		posn := fset.Position(c.Pos())

		// @verb id "regexp"
		match := expectRe.FindStringSubmatch(text)
		if match == nil {
			t.Errorf("%s: ill-formed query: %s", posn, text)
			continue
		}

		id := match[2]
		if prev, ok := queriesById[id]; ok {
			t.Errorf("%s: duplicate id %s", posn, id)
			t.Errorf("%s: previously used here", prev.posn)
			continue
		}

		q := &query{
			id:       id,
			verb:     match[1],
			filename: filename,
			posn:     posn,
		}

		if match[3] != `"nopos"` {
			selectRe, err := parseRegexp(match[3])
			if err != nil {
				t.Errorf("%s: %s", posn, err)
				continue
			}

			// Find text of the current line, sans query.
			// (Queries must be // not /**/ comments.)
			line := lines[posn.Line-1][:posn.Column-1]

			// Apply regexp to current line to find input selection.
			loc := selectRe.FindIndex(line)
			if loc == nil {
				t.Errorf("%s: selection pattern %s doesn't match line %q",
					posn, match[3], string(line))
				continue
			}

			// Assumes ASCII. TODO(adonovan): test on UTF-8.
			linestart := posn.Offset - (posn.Column - 1)

			// Compute the file offsets.
			q.queryPos = fmt.Sprintf("%s:#%d,#%d",
				filename, linestart+loc[0], linestart+loc[1])
		}

		queries = append(queries, q)
		queriesById[id] = q
	}

	// Return the slice, not map, for deterministic iteration.
	return queries
}

// WriteResult writes res (-format=plain) to w, stripping file locations.
func WriteResult(w io.Writer, res *oracle.Result) {
	capture := new(bytes.Buffer) // capture standard output
	res.WriteTo(capture)
	for _, line := range strings.Split(capture.String(), "\n") {
		// Remove a "file:line: " prefix.
		if i := strings.Index(line, ": "); i >= 0 {
			line = line[i+2:]
		}
		fmt.Fprintf(w, "%s\n", line)
	}
}

// doQuery poses query q to the oracle and writes its response and
// error (if any) to out.
func doQuery(out io.Writer, q *query, useJson bool) {
	fmt.Fprintf(out, "-------- @%s %s --------\n", q.verb, q.id)

	var buildContext = build.Default
	buildContext.GOPATH = "testdata"
	res, err := oracle.Query([]string{q.filename},
		q.verb,
		q.queryPos,
		nil, // ptalog,
		&buildContext,
		true) // reflection
	if err != nil {
		fmt.Fprintf(out, "\nError: %s\n", err)
		return
	}

	if useJson {
		// JSON output
		b, err := json.MarshalIndent(res.Serial(), "", "\t")
		if err != nil {
			fmt.Fprintf(out, "JSON error: %s\n", err.Error())
			return
		}
		out.Write(b)
	} else {
		// "plain" (compiler diagnostic format) output
		WriteResult(out, res)
	}
}

func TestOracle(t *testing.T) {
	switch runtime.GOOS {
	case "windows":
		t.Skipf("skipping test on %q (no /usr/bin/diff)", runtime.GOOS)
	}

	for _, filename := range []string{
		"testdata/src/main/calls.go",
		"testdata/src/main/callgraph.go",
		"testdata/src/main/callgraph2.go",
		"testdata/src/main/describe.go",
		"testdata/src/main/freevars.go",
		"testdata/src/main/implements.go",
		"testdata/src/main/imports.go",
		"testdata/src/main/peers.go",
		"testdata/src/main/pointsto.go",
		"testdata/src/main/reflection.go",
		"testdata/src/main/what.go",
		// JSON:
		// TODO(adonovan): most of these are very similar; combine them.
		"testdata/src/main/callgraph-json.go",
		"testdata/src/main/calls-json.go",
		"testdata/src/main/peers-json.go",
		"testdata/src/main/describe-json.go",
		"testdata/src/main/implements-json.go",
		"testdata/src/main/pointsto-json.go",
		"testdata/src/main/referrers-json.go",
		"testdata/src/main/what-json.go",
	} {
		useJson := strings.HasSuffix(filename, "-json.go")
		queries := parseQueries(t, filename)
		golden := filename + "lden"
		got := filename + "t"
		gotfh, err := os.Create(got)
		if err != nil {
			t.Errorf("Create(%s) failed: %s", got, err)
			continue
		}
		defer gotfh.Close()
		defer os.Remove(got)

		// Run the oracle on each query, redirecting its output
		// and error (if any) to the foo.got file.
		for _, q := range queries {
			doQuery(gotfh, q, useJson)
		}

		// Compare foo.got with foo.golden.
		var cmd *exec.Cmd
		switch runtime.GOOS {
		case "plan9":
			cmd = exec.Command("/bin/diff", "-c", golden, got)
		default:
			cmd = exec.Command("/usr/bin/diff", "-u", golden, got)
		}
		buf := new(bytes.Buffer)
		cmd.Stdout = buf
		if err := cmd.Run(); err != nil {
			t.Errorf("Oracle tests for %s failed: %s.\n%s\n",
				filename, err, buf)

			if *updateFlag {
				t.Logf("Updating %s...", golden)
				if err := exec.Command("/bin/cp", got, golden).Run(); err != nil {
					t.Errorf("Update failed: %s", err)
				}
			}
		}
	}
}

func TestMultipleQueries(t *testing.T) {
	// Loader
	var buildContext = build.Default
	buildContext.GOPATH = "testdata"
	conf := loader.Config{Build: &buildContext, SourceImports: true}
	filename := "testdata/src/main/multi.go"
	conf.CreateFromFilenames("", filename)
	iprog, err := conf.Load()
	if err != nil {
		t.Fatalf("Load failed: %s", err)
	}

	// Oracle
	o, err := oracle.New(iprog, nil, true)
	if err != nil {
		t.Fatalf("oracle.New failed: %s", err)
	}

	// QueryPos
	pos := filename + ":#54,#58"
	qpos, err := oracle.ParseQueryPos(iprog, pos, true)
	if err != nil {
		t.Fatalf("oracle.ParseQueryPos(%q) failed: %s", pos, err)
	}
	// SSA is built and we have the QueryPos.
	// Release the other ASTs and type info to the GC.
	iprog = nil

	// Run different query modes on same scope and selection.
	out := new(bytes.Buffer)
	for _, mode := range [...]string{"callers", "describe", "freevars"} {
		res, err := o.Query(mode, qpos)
		if err != nil {
			t.Errorf("(*oracle.Oracle).Query(%q) failed: %s", pos, err)
		}
		WriteResult(out, res)
	}
	want := `multi.f is called from these 1 sites:
	static function call from multi.main

function call (or conversion) of type ()

Free identifiers:
var x int

`
	if got := out.String(); got != want {
		t.Errorf("Query output differs; want <<%s>>, got <<%s>>\n", want, got)
	}
}
<commit_msg>go.tools/oracle: don't discard /usr/bin/diff's stderr<commit_after>// Copyright 2013 The Go 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 oracle_test

// This file defines a test framework for oracle queries.
//
// The files beneath testdata/src/main contain Go programs containing
// query annotations of the form:
//
//   @verb id "select"
//
// where verb is the query mode (e.g. "callers"), id is a unique name
// for this query, and "select" is a regular expression matching the
// substring of the current line that is the query's input selection.
//
// The expected output for each query is provided in the accompanying
// .golden file.
//
// (Location information is not included because it's too fragile to
// display as text.  TODO(adonovan): think about how we can test its
// correctness, since it is critical information.)
//
// Run this test with:
// 	% go test code.google.com/p/go.tools/oracle -update
// to update the golden files.

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"go/build"
	"go/parser"
	"go/token"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"testing"

	"code.google.com/p/go.tools/go/loader"
	"code.google.com/p/go.tools/oracle"
)

var updateFlag = flag.Bool("update", false, "Update the golden files.")

type query struct {
	id       string         // unique id
	verb     string         // query mode, e.g. "callees"
	posn     token.Position // position of of query
	filename string
	queryPos string // value of -pos flag
}

func parseRegexp(text string) (*regexp.Regexp, error) {
	pattern, err := strconv.Unquote(text)
	if err != nil {
		return nil, fmt.Errorf("can't unquote %s", text)
	}
	return regexp.Compile(pattern)
}

// parseQueries parses and returns the queries in the named file.
func parseQueries(t *testing.T, filename string) []*query {
	filedata, err := ioutil.ReadFile(filename)
	if err != nil {
		t.Fatal(err)
	}

	// Parse the file once to discover the test queries.
	var fset token.FileSet
	f, err := parser.ParseFile(&fset, filename, filedata, parser.ParseComments)
	if err != nil {
		t.Fatal(err)
	}

	lines := bytes.Split(filedata, []byte("\n"))

	var queries []*query
	queriesById := make(map[string]*query)

	// Find all annotations of these forms:
	expectRe := regexp.MustCompile(`@([a-z]+)\s+(\S+)\s+(\".*)$`) // @verb id "regexp"
	for _, c := range f.Comments {
		text := strings.TrimSpace(c.Text())
		if text == "" || text[0] != '@' {
			continue
		}
		posn := fset.Position(c.Pos())

		// @verb id "regexp"
		match := expectRe.FindStringSubmatch(text)
		if match == nil {
			t.Errorf("%s: ill-formed query: %s", posn, text)
			continue
		}

		id := match[2]
		if prev, ok := queriesById[id]; ok {
			t.Errorf("%s: duplicate id %s", posn, id)
			t.Errorf("%s: previously used here", prev.posn)
			continue
		}

		q := &query{
			id:       id,
			verb:     match[1],
			filename: filename,
			posn:     posn,
		}

		if match[3] != `"nopos"` {
			selectRe, err := parseRegexp(match[3])
			if err != nil {
				t.Errorf("%s: %s", posn, err)
				continue
			}

			// Find text of the current line, sans query.
			// (Queries must be // not /**/ comments.)
			line := lines[posn.Line-1][:posn.Column-1]

			// Apply regexp to current line to find input selection.
			loc := selectRe.FindIndex(line)
			if loc == nil {
				t.Errorf("%s: selection pattern %s doesn't match line %q",
					posn, match[3], string(line))
				continue
			}

			// Assumes ASCII. TODO(adonovan): test on UTF-8.
			linestart := posn.Offset - (posn.Column - 1)

			// Compute the file offsets.
			q.queryPos = fmt.Sprintf("%s:#%d,#%d",
				filename, linestart+loc[0], linestart+loc[1])
		}

		queries = append(queries, q)
		queriesById[id] = q
	}

	// Return the slice, not map, for deterministic iteration.
	return queries
}

// WriteResult writes res (-format=plain) to w, stripping file locations.
func WriteResult(w io.Writer, res *oracle.Result) {
	capture := new(bytes.Buffer) // capture standard output
	res.WriteTo(capture)
	for _, line := range strings.Split(capture.String(), "\n") {
		// Remove a "file:line: " prefix.
		if i := strings.Index(line, ": "); i >= 0 {
			line = line[i+2:]
		}
		fmt.Fprintf(w, "%s\n", line)
	}
}

// doQuery poses query q to the oracle and writes its response and
// error (if any) to out.
func doQuery(out io.Writer, q *query, useJson bool) {
	fmt.Fprintf(out, "-------- @%s %s --------\n", q.verb, q.id)

	var buildContext = build.Default
	buildContext.GOPATH = "testdata"
	res, err := oracle.Query([]string{q.filename},
		q.verb,
		q.queryPos,
		nil, // ptalog,
		&buildContext,
		true) // reflection
	if err != nil {
		fmt.Fprintf(out, "\nError: %s\n", err)
		return
	}

	if useJson {
		// JSON output
		b, err := json.MarshalIndent(res.Serial(), "", "\t")
		if err != nil {
			fmt.Fprintf(out, "JSON error: %s\n", err.Error())
			return
		}
		out.Write(b)
	} else {
		// "plain" (compiler diagnostic format) output
		WriteResult(out, res)
	}
}

func TestOracle(t *testing.T) {
	switch runtime.GOOS {
	case "windows":
		t.Skipf("skipping test on %q (no /usr/bin/diff)", runtime.GOOS)
	}

	for _, filename := range []string{
		"testdata/src/main/calls.go",
		"testdata/src/main/callgraph.go",
		"testdata/src/main/callgraph2.go",
		"testdata/src/main/describe.go",
		"testdata/src/main/freevars.go",
		"testdata/src/main/implements.go",
		"testdata/src/main/imports.go",
		"testdata/src/main/peers.go",
		"testdata/src/main/pointsto.go",
		"testdata/src/main/reflection.go",
		"testdata/src/main/what.go",
		// JSON:
		// TODO(adonovan): most of these are very similar; combine them.
		"testdata/src/main/callgraph-json.go",
		"testdata/src/main/calls-json.go",
		"testdata/src/main/peers-json.go",
		"testdata/src/main/describe-json.go",
		"testdata/src/main/implements-json.go",
		"testdata/src/main/pointsto-json.go",
		"testdata/src/main/referrers-json.go",
		"testdata/src/main/what-json.go",
	} {
		useJson := strings.HasSuffix(filename, "-json.go")
		queries := parseQueries(t, filename)
		golden := filename + "lden"
		got := filename + "t"
		gotfh, err := os.Create(got)
		if err != nil {
			t.Errorf("Create(%s) failed: %s", got, err)
			continue
		}
		defer gotfh.Close()
		defer os.Remove(got)

		// Run the oracle on each query, redirecting its output
		// and error (if any) to the foo.got file.
		for _, q := range queries {
			doQuery(gotfh, q, useJson)
		}

		// Compare foo.got with foo.golden.
		var cmd *exec.Cmd
		switch runtime.GOOS {
		case "plan9":
			cmd = exec.Command("/bin/diff", "-c", golden, got)
		default:
			cmd = exec.Command("/usr/bin/diff", "-u", golden, got)
		}
		buf := new(bytes.Buffer)
		cmd.Stdout = buf
		cmd.Stderr = os.Stderr
		if err := cmd.Run(); err != nil {
			t.Errorf("Oracle tests for %s failed: %s.\n%s\n",
				filename, err, buf)

			if *updateFlag {
				t.Logf("Updating %s...", golden)
				if err := exec.Command("/bin/cp", got, golden).Run(); err != nil {
					t.Errorf("Update failed: %s", err)
				}
			}
		}
	}
}

func TestMultipleQueries(t *testing.T) {
	// Loader
	var buildContext = build.Default
	buildContext.GOPATH = "testdata"
	conf := loader.Config{Build: &buildContext, SourceImports: true}
	filename := "testdata/src/main/multi.go"
	conf.CreateFromFilenames("", filename)
	iprog, err := conf.Load()
	if err != nil {
		t.Fatalf("Load failed: %s", err)
	}

	// Oracle
	o, err := oracle.New(iprog, nil, true)
	if err != nil {
		t.Fatalf("oracle.New failed: %s", err)
	}

	// QueryPos
	pos := filename + ":#54,#58"
	qpos, err := oracle.ParseQueryPos(iprog, pos, true)
	if err != nil {
		t.Fatalf("oracle.ParseQueryPos(%q) failed: %s", pos, err)
	}
	// SSA is built and we have the QueryPos.
	// Release the other ASTs and type info to the GC.
	iprog = nil

	// Run different query modes on same scope and selection.
	out := new(bytes.Buffer)
	for _, mode := range [...]string{"callers", "describe", "freevars"} {
		res, err := o.Query(mode, qpos)
		if err != nil {
			t.Errorf("(*oracle.Oracle).Query(%q) failed: %s", pos, err)
		}
		WriteResult(out, res)
	}
	want := `multi.f is called from these 1 sites:
	static function call from multi.main

function call (or conversion) of type ()

Free identifiers:
var x int

`
	if got := out.String(); got != want {
		t.Errorf("Query output differs; want <<%s>>, got <<%s>>\n", want, got)
	}
}
<|endoftext|>