<commit_before>package generator

import (
	"io/ioutil"
	"log"
	"testing"

	reporter "github.com/joefitzgerald/rainbow-reporter"
	. "github.com/onsi/gomega"
	"github.com/sclevine/spec"
	"golang.org/x/tools/go/packages"
)

func TestGenerator(t *testing.T) {
	log.SetOutput(ioutil.Discard) // Comment this out to see verbose log output
	log.SetFlags(log.Llongfile)
	spec.Run(t, "Generator", testGenerator, spec.Report(reporter.Rainbow{}))
}

func testGenerator(t *testing.T, when spec.G, it spec.S) {
	var (
		f   *Fake
		err error
	)

	it.Before(func() {
		RegisterTestingT(t)
	})

	when("constructing a fake with NewFake()", func() {
		when("the target is a nonexistent package", func() {
			it("errors", func() {
				c := &Cache{}
				f, err = NewFake(InterfaceOrFunction, "NonExistent", "nonexistentpackage", "FakeNonExistent", "nonexistentpackagefakes", "", c)
				Expect(err).To(HaveOccurred())
				Expect(f).To(BeNil())
			})
		})

		when("the target is a package with a nonexistent interface", func() {
			it("errors", func() {
				c := &Cache{}
				f, err = NewFake(InterfaceOrFunction, "NonExistent", "os", "FakeNonExistent", "osfakes", "", c)
				Expect(err).To(HaveOccurred())
				Expect(f).To(BeNil())
			})
		})

		when("the target is an interface that exists", func() {
			it("succeeds", func() {
				c := &Cache{}
				f, err = NewFake(InterfaceOrFunction, "FileInfo", "os", "FakeFileInfo", "osfakes", "", c)
				Expect(err).NotTo(HaveOccurred())
				Expect(f).NotTo(BeNil())
				Expect(f.TargetAlias).To(Equal("os"))
				Expect(f.TargetName).To(Equal("FileInfo"))
				Expect(f.TargetPackage).To(Equal("os"))
				Expect(f.Name).To(Equal("FakeFileInfo"))
				Expect(f.Mode).To(Equal(InterfaceOrFunction))
				Expect(f.DestinationPackage).To(Equal("osfakes"))
				Expect(f.Imports).To(BeEquivalentTo(Imports{
					ByAlias: map[string]Import{
						"os":   {Alias: "os", PkgPath: "os"},
						"sync": {Alias: "sync", PkgPath: "sync"},
						"time": {Alias: "time", PkgPath: "time"},
					},
					ByPkgPath: map[string]Import{
						"os":   {Alias: "os", PkgPath: "os"},
						"sync": {Alias: "sync", PkgPath: "sync"},
						"time": {Alias: "time", PkgPath: "time"},
					},
				}))
				Expect(f.Function).To(BeZero())
				Expect(f.Packages).NotTo(BeNil())
				Expect(f.Package).NotTo(BeNil())
				Expect(f.Methods).To(HaveLen(6))
			})
		})

		when("the target is a function that exists", func() {
			it("succeeds", func() {
				c := &Cache{}
				f, err = NewFake(InterfaceOrFunction, "HandlerFunc", "net/http", "FakeHandlerFunc", "httpfakes", "", c)
				Expect(err).NotTo(HaveOccurred())

				Expect(f).NotTo(BeNil())
				Expect(f.TargetAlias).To(Equal("http"))
				Expect(f.TargetName).To(Equal("HandlerFunc"))
				Expect(f.TargetPackage).To(Equal("net/http"))
				Expect(f.Name).To(Equal("FakeHandlerFunc"))
				Expect(f.Mode).To(Equal(InterfaceOrFunction))
				Expect(f.DestinationPackage).To(Equal("httpfakes"))
				Expect(f.Imports).To(BeEquivalentTo(Imports{
					ByAlias: map[string]Import{
						"http": {Alias: "http", PkgPath: "net/http"},
						"sync": {Alias: "sync", PkgPath: "sync"},
					},
					ByPkgPath: map[string]Import{
						"net/http": {Alias: "http", PkgPath: "net/http"},
						"sync":     {Alias: "sync", PkgPath: "sync"},
					},
				}))
				Expect(f.Function).NotTo(BeZero())
				Expect(f.Packages).NotTo(BeNil())
				Expect(f.Package).NotTo(BeNil())
				Expect(f.Methods).To(HaveLen(0))
				Expect(f.Function.Name).To(Equal("HandlerFunc"))
				Expect(f.Function.Params).To(HaveLen(2))
				Expect(f.Function.Returns).To(BeEmpty())
			})
		})
	})

	when("manually constructing a fake", func() {
		it.Before(func() {
			f = &Fake{Imports: newImports()}
		})

		when("duplicate import package names are added", func() {
			it.Before(func() {
				f.Imports.Add("sync", "sync")
				f.Imports.Add("sync", "github.com/maxbrunsfeld/counterfeiter/v6/fixtures/sync")
				f.Imports.Add("sync", "github.com/maxbrunsfeld/counterfeiter/v6/fixtures/othersync")
			})

			it("all packages have unique aliases", func() {
				Expect(f.Imports).To(BeEquivalentTo(Imports{
					ByAlias: map[string]Import{
						"sync":  {Alias: "sync", PkgPath: "sync"},
						"synca": {Alias: "synca", PkgPath: "github.com/maxbrunsfeld/counterfeiter/v6/fixtures/sync"},
						"syncb": {Alias: "syncb", PkgPath: "github.com/maxbrunsfeld/counterfeiter/v6/fixtures/othersync"},
					},
					ByPkgPath: map[string]Import{
						"sync": {Alias: "sync", PkgPath: "sync"},
						"github.com/maxbrunsfeld/counterfeiter/v6/fixtures/sync":      {Alias: "synca", PkgPath: "github.com/maxbrunsfeld/counterfeiter/v6/fixtures/sync"},
						"github.com/maxbrunsfeld/counterfeiter/v6/fixtures/othersync": {Alias: "syncb", PkgPath: "github.com/maxbrunsfeld/counterfeiter/v6/fixtures/othersync"},
					},
				}))
			})
		})

		when("inspecting the target", func() {
			when("the target is not set", func() {
				it("IsInterface() is false", func() {
					Expect(f.IsInterface()).To(BeFalse())
				})

				it("IsFunction() is false", func() {
					Expect(f.IsFunction()).To(BeFalse())
				})
			})

			when("the target is an interface", func() {
				it.Before(func() {
					f.Mode = InterfaceOrFunction
					f.TargetPackage = "os"
					f.TargetName = "FileInfo"
					c := &Cache{}
					err := f.loadPackages(c, "")
					Expect(err).NotTo(HaveOccurred())
					err = f.findPackage()
					Expect(err).NotTo(HaveOccurred())
				})

				it("IsInterface() is true", func() {
					Expect(f.IsInterface()).To(BeTrue())
				})

				it("IsFunction() is false", func() {
					Expect(f.IsFunction()).To(BeFalse())
				})
			})

			when("the target is a function", func() {
				it.Before(func() {
					f.Mode = InterfaceOrFunction
					f.TargetPackage = "net/http"
					f.TargetName = "HandlerFunc"
					c := &Cache{}
					err := f.loadPackages(c, "")
					Expect(err).NotTo(HaveOccurred())
					err = f.findPackage()
					Expect(err).NotTo(HaveOccurred())
				})

				it("IsInterface() is false", func() {
					Expect(f.IsInterface()).To(BeFalse())
				})

				it("IsFunction() is true", func() {
					Expect(f.IsFunction()).To(BeTrue())
				})
			})

			when("the target is a struct", func() {
				it.Before(func() {
					f.Mode = InterfaceOrFunction
					f.TargetPackage = "net/http"
					f.TargetName = "Client"
					c := &Cache{}
					err := f.loadPackages(c, "")
					Expect(err).NotTo(HaveOccurred())
					err = f.findPackage()
					Expect(err).To(HaveOccurred())
				})

				it("is not a function", func() {
					Expect(f.IsFunction()).To(BeFalse())
				})

				it("is not an interface", func() {
					Expect(f.IsInterface()).To(BeFalse())
				})
			})
		})

		when("in interface mode", func() {
			it.Before(func() {
				f.Mode = InterfaceOrFunction
			})

			when("targeting the os.FileInfo interface", func() {
				it.Before(func() {
					f.TargetPackage = "os"
					f.TargetName = "FileInfo"
					c := &Cache{}
					err := f.loadPackages(c, "")
					Expect(err).NotTo(HaveOccurred())
				})
			})
		})

		when("in package mode", func() {
			it.Before(func() {
				f.Mode = Package
			})

			when("targeting a nonexistent package", func() {
				it("returns an error", func() {
					f.TargetPackage = "counterfeiternonexistentpackage"
					c := &Cache{}
					err := f.loadPackages(c, "")
					Expect(err).To(HaveOccurred())
				})
			})

			when("targeting the os package", func() {
				it.Before(func() {
					f.TargetPackage = "os"
					c := &Cache{}
					err := f.loadPackages(c, "")
					Expect(err).NotTo(HaveOccurred())
				})

				it("can load packages", func() {
					Expect(len(f.Packages)).To(BeNumerically(">=", 1))
					Expect(f.Packages[0].Name).To(Equal("os"))
				})

				it("can find the package with the os package path", func() {
					err := f.findPackage()
					Expect(err).NotTo(HaveOccurred())
					Expect(f.Package).NotTo(BeNil())
					Expect(f.Package).To(Equal(f.Packages[0]))
				})

				it("skips invalid packages", func() {
					var p []*packages.Package
					empty := &packages.Package{}
					p = append(p, empty)
					p = append(p, f.Packages...)
					f.Packages = p
					err := f.findPackage()
					Expect(err).NotTo(HaveOccurred())
					Expect(f.Package).NotTo(BeNil())
					Expect(f.Package).To(Equal(f.Packages[1]))
				})

				it("can identify the method set for the package", func() {
					err := f.findPackage()
					Expect(err).NotTo(HaveOccurred())
					methods := packageMethodSet(f.Package)
					Expect(len(methods)).To(BeNumerically(">=", 51)) // yes, this is crazy because go 1.11 added a function
					Expect(len(methods)).To(BeNumerically("<=", 53))
				})

				it("can load the methods", func() {
					err := f.findPackage()
					Expect(err).NotTo(HaveOccurred())
					f.loadMethods()
					Expect(len(f.Methods)).To(BeNumerically(">=", 51)) // yes, this is crazy because go 1.11 added a function
					Expect(len(f.Methods)).To(BeNumerically("<=", 53))
					Expect(len(f.Imports.ByAlias)).To(Equal(2))
				})
			})
		})

		when("working with imports", func() {
			when("there are no imports", func() {
				it("returns an empty alias map", func() {
					Expect(f.Imports.ByAlias).To(BeEmpty())
				})

				it("turns a vendor path into the correct import", func() {
					i := f.Imports.Add("apackage", "github.com/maxbrunsfeld/counterfeiter/v6/fixtures/vendored/vendor/apackage")
					Expect(i.Alias).To(Equal("apackage"))
					Expect(i.PkgPath).To(Equal("apackage"))

					i = f.Imports.Add("anotherpackage", "vendor/anotherpackage")
					Expect(i.Alias).To(Equal("anotherpackage"))
					Expect(i.PkgPath).To(Equal("anotherpackage"))
				})
			})

			when("there is a single import", func() {
				it.Before(func() {
					f.Imports.Add("os", "os")
				})

				it("is present in the map", func() {
					Expect(f.Imports).To(BeEquivalentTo(Imports{
						ByAlias: map[string]Import{
							"os": {Alias: "os", PkgPath: "os"},
						},
						ByPkgPath: map[string]Import{
							"os": {Alias: "os", PkgPath: "os"},
						},
					}))
				})

				it("returns the existing imports if there is a path match", func() {
					i := f.Imports.Add("aliasedos", "os")
					Expect(i.Alias).To(Equal("os"))
					Expect(i.PkgPath).To(Equal("os"))
					Expect(f.Imports).To(BeEquivalentTo(Imports{
						ByAlias: map[string]Import{
							"os": {Alias: "os", PkgPath: "os"},
						},
						ByPkgPath: map[string]Import{
							"os": {Alias: "os", PkgPath: "os"},
						},
					}))
				})
			})
		})
	})

	when("helper functions", func() {
		when("unexport()", func() {
			it("is a no-op on an empty string", func() {
				Expect(unexport("")).To(Equal(""))
				Expect(unexport(" ")).To(Equal(""))
			})

			it("makes the first letter lowercase", func() {
				Expect(unexport("TheExportedThing")).To(Equal("theExportedThing"))
			})

			it("leaves unexported things unchanged", func() {
				Expect(unexport("theUnexportedThing")).To(Equal("theUnexportedThing"))
			})
		})

		when("isExported()", func() {
			it("returns false for an empty string", func() {
				Expect(isExported("")).To(BeFalse())
				Expect(isExported(" ")).To(BeFalse())
			})

			it("returns true when the first rune is upper case", func() {
				Expect(isExported("Identifier")).To(BeTrue())
				Expect(isExported("Ʊpsilon")).To(BeTrue())
			})

			it("returns false when the first rune not upper case", func() {
				Expect(isExported("identifier")).To(BeFalse())
				Expect(isExported("ʊpsilon")).To(BeFalse())
			})
		})
	})
}
<commit_msg>Fix test failure on go 1.13<commit_after>package generator

import (
	"io/ioutil"
	"log"
	"testing"

	reporter "github.com/joefitzgerald/rainbow-reporter"
	. "github.com/onsi/gomega"
	"github.com/sclevine/spec"
	"golang.org/x/tools/go/packages"
)

func TestGenerator(t *testing.T) {
	log.SetOutput(ioutil.Discard) // Comment this out to see verbose log output
	log.SetFlags(log.Llongfile)
	spec.Run(t, "Generator", testGenerator, spec.Report(reporter.Rainbow{}))
}

func testGenerator(t *testing.T, when spec.G, it spec.S) {
	var (
		f   *Fake
		err error
	)

	it.Before(func() {
		RegisterTestingT(t)
	})

	when("constructing a fake with NewFake()", func() {
		when("the target is a nonexistent package", func() {
			it("errors", func() {
				c := &Cache{}
				f, err = NewFake(InterfaceOrFunction, "NonExistent", "nonexistentpackage", "FakeNonExistent", "nonexistentpackagefakes", "", c)
				Expect(err).To(HaveOccurred())
				Expect(f).To(BeNil())
			})
		})

		when("the target is a package with a nonexistent interface", func() {
			it("errors", func() {
				c := &Cache{}
				f, err = NewFake(InterfaceOrFunction, "NonExistent", "os", "FakeNonExistent", "osfakes", "", c)
				Expect(err).To(HaveOccurred())
				Expect(f).To(BeNil())
			})
		})

		when("the target is an interface that exists", func() {
			it("succeeds", func() {
				c := &Cache{}
				f, err = NewFake(InterfaceOrFunction, "FileInfo", "os", "FakeFileInfo", "osfakes", "", c)
				Expect(err).NotTo(HaveOccurred())
				Expect(f).NotTo(BeNil())
				Expect(f.TargetAlias).To(Equal("os"))
				Expect(f.TargetName).To(Equal("FileInfo"))
				Expect(f.TargetPackage).To(Equal("os"))
				Expect(f.Name).To(Equal("FakeFileInfo"))
				Expect(f.Mode).To(Equal(InterfaceOrFunction))
				Expect(f.DestinationPackage).To(Equal("osfakes"))
				Expect(f.Imports).To(BeEquivalentTo(Imports{
					ByAlias: map[string]Import{
						"os":   {Alias: "os", PkgPath: "os"},
						"sync": {Alias: "sync", PkgPath: "sync"},
						"time": {Alias: "time", PkgPath: "time"},
					},
					ByPkgPath: map[string]Import{
						"os":   {Alias: "os", PkgPath: "os"},
						"sync": {Alias: "sync", PkgPath: "sync"},
						"time": {Alias: "time", PkgPath: "time"},
					},
				}))
				Expect(f.Function).To(BeZero())
				Expect(f.Packages).NotTo(BeNil())
				Expect(f.Package).NotTo(BeNil())
				Expect(f.Methods).To(HaveLen(6))
			})
		})

		when("the target is a function that exists", func() {
			it("succeeds", func() {
				c := &Cache{}
				f, err = NewFake(InterfaceOrFunction, "HandlerFunc", "net/http", "FakeHandlerFunc", "httpfakes", "", c)
				Expect(err).NotTo(HaveOccurred())

				Expect(f).NotTo(BeNil())
				Expect(f.TargetAlias).To(Equal("http"))
				Expect(f.TargetName).To(Equal("HandlerFunc"))
				Expect(f.TargetPackage).To(Equal("net/http"))
				Expect(f.Name).To(Equal("FakeHandlerFunc"))
				Expect(f.Mode).To(Equal(InterfaceOrFunction))
				Expect(f.DestinationPackage).To(Equal("httpfakes"))
				Expect(f.Imports).To(BeEquivalentTo(Imports{
					ByAlias: map[string]Import{
						"http": {Alias: "http", PkgPath: "net/http"},
						"sync": {Alias: "sync", PkgPath: "sync"},
					},
					ByPkgPath: map[string]Import{
						"net/http": {Alias: "http", PkgPath: "net/http"},
						"sync":     {Alias: "sync", PkgPath: "sync"},
					},
				}))
				Expect(f.Function).NotTo(BeZero())
				Expect(f.Packages).NotTo(BeNil())
				Expect(f.Package).NotTo(BeNil())
				Expect(f.Methods).To(HaveLen(0))
				Expect(f.Function.Name).To(Equal("HandlerFunc"))
				Expect(f.Function.Params).To(HaveLen(2))
				Expect(f.Function.Returns).To(BeEmpty())
			})
		})
	})

	when("manually constructing a fake", func() {
		it.Before(func() {
			f = &Fake{Imports: newImports()}
		})

		when("duplicate import package names are added", func() {
			it.Before(func() {
				f.Imports.Add("sync", "sync")
				f.Imports.Add("sync", "github.com/maxbrunsfeld/counterfeiter/v6/fixtures/sync")
				f.Imports.Add("sync", "github.com/maxbrunsfeld/counterfeiter/v6/fixtures/othersync")
			})

			it("all packages have unique aliases", func() {
				Expect(f.Imports).To(BeEquivalentTo(Imports{
					ByAlias: map[string]Import{
						"sync":  {Alias: "sync", PkgPath: "sync"},
						"synca": {Alias: "synca", PkgPath: "github.com/maxbrunsfeld/counterfeiter/v6/fixtures/sync"},
						"syncb": {Alias: "syncb", PkgPath: "github.com/maxbrunsfeld/counterfeiter/v6/fixtures/othersync"},
					},
					ByPkgPath: map[string]Import{
						"sync": {Alias: "sync", PkgPath: "sync"},
						"github.com/maxbrunsfeld/counterfeiter/v6/fixtures/sync":      {Alias: "synca", PkgPath: "github.com/maxbrunsfeld/counterfeiter/v6/fixtures/sync"},
						"github.com/maxbrunsfeld/counterfeiter/v6/fixtures/othersync": {Alias: "syncb", PkgPath: "github.com/maxbrunsfeld/counterfeiter/v6/fixtures/othersync"},
					},
				}))
			})
		})

		when("inspecting the target", func() {
			when("the target is not set", func() {
				it("IsInterface() is false", func() {
					Expect(f.IsInterface()).To(BeFalse())
				})

				it("IsFunction() is false", func() {
					Expect(f.IsFunction()).To(BeFalse())
				})
			})

			when("the target is an interface", func() {
				it.Before(func() {
					f.Mode = InterfaceOrFunction
					f.TargetPackage = "os"
					f.TargetName = "FileInfo"
					c := &Cache{}
					err := f.loadPackages(c, "")
					Expect(err).NotTo(HaveOccurred())
					err = f.findPackage()
					Expect(err).NotTo(HaveOccurred())
				})

				it("IsInterface() is true", func() {
					Expect(f.IsInterface()).To(BeTrue())
				})

				it("IsFunction() is false", func() {
					Expect(f.IsFunction()).To(BeFalse())
				})
			})

			when("the target is a function", func() {
				it.Before(func() {
					f.Mode = InterfaceOrFunction
					f.TargetPackage = "net/http"
					f.TargetName = "HandlerFunc"
					c := &Cache{}
					err := f.loadPackages(c, "")
					Expect(err).NotTo(HaveOccurred())
					err = f.findPackage()
					Expect(err).NotTo(HaveOccurred())
				})

				it("IsInterface() is false", func() {
					Expect(f.IsInterface()).To(BeFalse())
				})

				it("IsFunction() is true", func() {
					Expect(f.IsFunction()).To(BeTrue())
				})
			})

			when("the target is a struct", func() {
				it.Before(func() {
					f.Mode = InterfaceOrFunction
					f.TargetPackage = "net/http"
					f.TargetName = "Client"
					c := &Cache{}
					err := f.loadPackages(c, "")
					Expect(err).NotTo(HaveOccurred())
					err = f.findPackage()
					Expect(err).To(HaveOccurred())
				})

				it("is not a function", func() {
					Expect(f.IsFunction()).To(BeFalse())
				})

				it("is not an interface", func() {
					Expect(f.IsInterface()).To(BeFalse())
				})
			})
		})

		when("in interface mode", func() {
			it.Before(func() {
				f.Mode = InterfaceOrFunction
			})

			when("targeting the os.FileInfo interface", func() {
				it.Before(func() {
					f.TargetPackage = "os"
					f.TargetName = "FileInfo"
					c := &Cache{}
					err := f.loadPackages(c, "")
					Expect(err).NotTo(HaveOccurred())
				})
			})
		})

		when("in package mode", func() {
			it.Before(func() {
				f.Mode = Package
			})

			when("targeting a nonexistent package", func() {
				it("returns an error", func() {
					f.TargetPackage = "counterfeiternonexistentpackage"
					c := &Cache{}
					err := f.loadPackages(c, "")
					Expect(err).To(HaveOccurred())
				})
			})

			when("targeting the os package", func() {
				it.Before(func() {
					f.TargetPackage = "os"
					c := &Cache{}
					err := f.loadPackages(c, "")
					Expect(err).NotTo(HaveOccurred())
				})

				it("can load packages", func() {
					Expect(len(f.Packages)).To(BeNumerically(">=", 1))
					Expect(f.Packages[0].Name).To(Equal("os"))
				})

				it("can find the package with the os package path", func() {
					err := f.findPackage()
					Expect(err).NotTo(HaveOccurred())
					Expect(f.Package).NotTo(BeNil())
					Expect(f.Package).To(Equal(f.Packages[0]))
				})

				it("skips invalid packages", func() {
					var p []*packages.Package
					empty := &packages.Package{}
					p = append(p, empty)
					p = append(p, f.Packages...)
					f.Packages = p
					err := f.findPackage()
					Expect(err).NotTo(HaveOccurred())
					Expect(f.Package).NotTo(BeNil())
					Expect(f.Package).To(Equal(f.Packages[1]))
				})

				it("can identify the method set for the package", func() {
					err := f.findPackage()
					Expect(err).NotTo(HaveOccurred())
					methods := packageMethodSet(f.Package)
					Expect(len(methods)).To(BeNumerically(">=", 51)) // yes, this is crazy because go 1.11 added a function
					Expect(len(methods)).To(BeNumerically("<=", 54)) // go 1.12 added another one, and go 1.13 yet another one
				})

				it("can load the methods", func() {
					err := f.findPackage()
					Expect(err).NotTo(HaveOccurred())
					f.loadMethods()
					Expect(len(f.Methods)).To(BeNumerically(">=", 51)) // yes, this is crazy because go 1.11 added a function
					Expect(len(f.Methods)).To(BeNumerically("<=", 54)) // go 1.12 added another one, and go 1.13 yet another one
					Expect(len(f.Imports.ByAlias)).To(Equal(2))
				})
			})
		})

		when("working with imports", func() {
			when("there are no imports", func() {
				it("returns an empty alias map", func() {
					Expect(f.Imports.ByAlias).To(BeEmpty())
				})

				it("turns a vendor path into the correct import", func() {
					i := f.Imports.Add("apackage", "github.com/maxbrunsfeld/counterfeiter/v6/fixtures/vendored/vendor/apackage")
					Expect(i.Alias).To(Equal("apackage"))
					Expect(i.PkgPath).To(Equal("apackage"))

					i = f.Imports.Add("anotherpackage", "vendor/anotherpackage")
					Expect(i.Alias).To(Equal("anotherpackage"))
					Expect(i.PkgPath).To(Equal("anotherpackage"))
				})
			})

			when("there is a single import", func() {
				it.Before(func() {
					f.Imports.Add("os", "os")
				})

				it("is present in the map", func() {
					Expect(f.Imports).To(BeEquivalentTo(Imports{
						ByAlias: map[string]Import{
							"os": {Alias: "os", PkgPath: "os"},
						},
						ByPkgPath: map[string]Import{
							"os": {Alias: "os", PkgPath: "os"},
						},
					}))
				})

				it("returns the existing imports if there is a path match", func() {
					i := f.Imports.Add("aliasedos", "os")
					Expect(i.Alias).To(Equal("os"))
					Expect(i.PkgPath).To(Equal("os"))
					Expect(f.Imports).To(BeEquivalentTo(Imports{
						ByAlias: map[string]Import{
							"os": {Alias: "os", PkgPath: "os"},
						},
						ByPkgPath: map[string]Import{
							"os": {Alias: "os", PkgPath: "os"},
						},
					}))
				})
			})
		})
	})

	when("helper functions", func() {
		when("unexport()", func() {
			it("is a no-op on an empty string", func() {
				Expect(unexport("")).To(Equal(""))
				Expect(unexport(" ")).To(Equal(""))
			})

			it("makes the first letter lowercase", func() {
				Expect(unexport("TheExportedThing")).To(Equal("theExportedThing"))
			})

			it("leaves unexported things unchanged", func() {
				Expect(unexport("theUnexportedThing")).To(Equal("theUnexportedThing"))
			})
		})

		when("isExported()", func() {
			it("returns false for an empty string", func() {
				Expect(isExported("")).To(BeFalse())
				Expect(isExported(" ")).To(BeFalse())
			})

			it("returns true when the first rune is upper case", func() {
				Expect(isExported("Identifier")).To(BeTrue())
				Expect(isExported("Ʊpsilon")).To(BeTrue())
			})

			it("returns false when the first rune not upper case", func() {
				Expect(isExported("identifier")).To(BeFalse())
				Expect(isExported("ʊpsilon")).To(BeFalse())
			})
		})
	})
}
<|endoftext|>


<commit_before>package handler

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"strconv"
	"sync"
	"time"

	"github.com/m-lab/annotation-service/metrics"
)

// A mutex to make sure that we are not reading from the dataset while
// trying to update it
var currentDataMutex = &sync.RWMutex{}

// A function to set up any handlers that are needed, including url
// handlers and pubsub handlers
func SetupHandlers() {
	http.HandleFunc("/annotate", Annotate)
	go waitForDownloaderMessages()
}

// Annotate looks up IP address and returns geodata.
func Annotate(w http.ResponseWriter, r *http.Request) {
	_, _, _, err := validate(w, r)
	if err != nil {
		fmt.Fprintf(w, "Invalid request")
	} else {
		// Fake response
		currentDataMutex.RLock()
		defer currentDataMutex.RUnlock()
		fmt.Fprintf(w, `{"Geo":{"city": "%s", "postal_code":"10583"},"ASN":{}}`, "Not A Real City")
		// TODO: Figure out which table to use
		// TODO: Handle request
	}
}

// validates request syntax
// parses request and returns parameters
// 0 for IPversion means that there was an error.
func validate(w http.ResponseWriter, r *http.Request) (IPversion int, s string, num time.Time, err error) {
	// Setup timers and counters for prometheus metrics.
	timerStart := time.Now()
	defer func(tStart time.Time) {
		metrics.Metrics_requestTimes.Observe(float64(time.Since(tStart).Nanoseconds()))
	}(timerStart)

	metrics.Metrics_activeRequests.Inc()
	defer metrics.Metrics_activeRequests.Dec()

	query := r.URL.Query()

	time_milli, err := strconv.ParseInt(query.Get("since_epoch"), 10, 64)
	if err != nil {
		return 0, s, num, errors.New("Invalid time")
	}

	ip := query.Get("ip_addr")

	newIP := net.ParseIP(ip)
	if newIP == nil {
		return 0, s, num, errors.New("Invalid IP address.")
	}
	if newIP.To4() != nil {
		return 4, ip, time.Unix(time_milli, 0), nil
	}
	return 6, ip, time.Unix(time_milli, 0), nil
}

func BatchAnnotate(w http.ResponseWriter, r *http.Request) {

}

func BatchValidateAndParse(source io.ReadCloser) ([]RequestData, error) {
	jsonBuffer, err := ioutil.ReadAll(source)
	validatedData := []RequestData{}
	if err != nil {
		return nil, err
	}
	uncheckedData := []struct {
		ip      string
		unix_ts int64
	}{}

	err = json.Unmarshal(jsonBuffer, &uncheckedData)
	if err != nil {
		return nil, err
	}
	for _, data := range uncheckedData {
		newIP := net.ParseIP(data.ip)
		if newIP == nil {
			return nil, errors.New("Invalid IP address.")
		}
		ipType := 6
		if newIP.To4() != nil {
			ipType = 4
		}
		validatedData = append(validatedData, RequestData{data.ip, ipType, time.Unix(data.unix_ts, 0)})
	}
	return validatedData, nil
}

func GetMetadataForSingleIP(IPVersion int, ip string, timestamp time.Time) *MetaData {
	return nil
}
<commit_msg>Minor refactoring.<commit_after>package handler

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"strconv"
	"sync"
	"time"

	"github.com/m-lab/annotation-service/metrics"
)

// A mutex to make sure that we are not reading from the dataset while
// trying to update it
var currentDataMutex = &sync.RWMutex{}

// A function to set up any handlers that are needed, including url
// handlers and pubsub handlers
func SetupHandlers() {
	http.HandleFunc("/annotate", Annotate)
	go waitForDownloaderMessages()
}

// Annotate looks up IP address and returns geodata.
func Annotate(w http.ResponseWriter, r *http.Request) {
	_, err := ValidateAndParse(r)
	if err != nil {
		fmt.Fprintf(w, "Invalid request")
	} else {
		// Fake response
		currentDataMutex.RLock()
		defer currentDataMutex.RUnlock()
		fmt.Fprintf(w, `{"Geo":{"city": "%s", "postal_code":"10583"},"ASN":{}}`, "Not A Real City")
		// TODO: Figure out which table to use
		// TODO: Handle request
	}
}

// validates request syntax
// parses request and returns parameters
// 0 for IPversion means that there was an error.
func ValidateAndParse(r *http.Request) (*RequestData, error) {
	// Setup timers and counters for prometheus metrics.
	timerStart := time.Now()
	defer func(tStart time.Time) {
		metrics.Metrics_requestTimes.Observe(float64(time.Since(tStart).Nanoseconds()))
	}(timerStart)

	metrics.Metrics_activeRequests.Inc()
	defer metrics.Metrics_activeRequests.Dec()

	query := r.URL.Query()

	time_milli, err := strconv.ParseInt(query.Get("since_epoch"), 10, 64)
	if err != nil {
		return nil, errors.New("Invalid time")
	}

	ip := query.Get("ip_addr")

	newIP := net.ParseIP(ip)
	if newIP == nil {
		return nil, errors.New("Invalid IP address.")
	}
	if newIP.To4() != nil {
		return &RequestData{ip, 4, time.Unix(time_milli, 0)}, nil
	}
	return &RequestData{ip, 6, time.Unix(time_milli, 0)}, nil
}

func BatchAnnotate(w http.ResponseWriter, r *http.Request) {

}

func BatchValidateAndParse(source io.ReadCloser) ([]RequestData, error) {
	jsonBuffer, err := ioutil.ReadAll(source)
	validatedData := []RequestData{}
	if err != nil {
		return nil, err
	}
	uncheckedData := []struct {
		ip      string
		unix_ts int64
	}{}

	err = json.Unmarshal(jsonBuffer, &uncheckedData)
	if err != nil {
		return nil, err
	}
	for _, data := range uncheckedData {
		newIP := net.ParseIP(data.ip)
		if newIP == nil {
			return nil, errors.New("Invalid IP address.")
		}
		ipType := 6
		if newIP.To4() != nil {
			ipType = 4
		}
		validatedData = append(validatedData, RequestData{data.ip, ipType, time.Unix(data.unix_ts, 0)})
	}
	return validatedData, nil
}

func GetMetadataForSingleIP(request *RequestData) *MetaData {
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes Authors.

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

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

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

package aws

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/aws/aws-sdk-go/aws/endpoints"
	"io/ioutil"
	klog "k8s.io/klog/v2"
	"net/http"
	"os"
	"regexp"
	"strconv"
	"strings"
)

var (
	ec2MetaDataServiceUrl          = "http://169.254.169.254/latest/dynamic/instance-identity/document"
	ec2PricingServiceUrlTemplate   = "https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonEC2/current/%s/index.json"
	ec2PricingServiceUrlTemplateCN = "https://pricing.cn-north-1.amazonaws.com.cn/offers/v1.0/cn/AmazonEC2/current/%s/index.json"
	staticListLastUpdateTime       = "2020-12-07"
)

type response struct {
	Products map[string]product `json:"products"`
}

type product struct {
	Attributes productAttributes `json:"attributes"`
}

type productAttributes struct {
	InstanceType string `json:"instanceType"`
	VCPU         string `json:"vcpu"`
	Memory       string `json:"memory"`
	GPU          string `json:"gpu"`
}

// GenerateEC2InstanceTypes returns a map of ec2 resources
func GenerateEC2InstanceTypes(region string) (map[string]*InstanceType, error) {
	var pricingUrlTemplate string
	if strings.HasPrefix(region, "cn-") {
		pricingUrlTemplate = ec2PricingServiceUrlTemplateCN
	} else {
		pricingUrlTemplate = ec2PricingServiceUrlTemplate
	}

	instanceTypes := make(map[string]*InstanceType)

	resolver := endpoints.DefaultResolver()
	partitions := resolver.(endpoints.EnumPartitions).Partitions()

	for _, p := range partitions {
		for _, r := range p.Regions() {
			if region != "" && region != r.ID() {
				continue
			}

			url := fmt.Sprintf(pricingUrlTemplate, r.ID())
			klog.V(1).Infof("fetching %s\n", url)
			res, err := http.Get(url)
			if err != nil {
				klog.Warningf("Error fetching %s skipping...\n", url)
				continue
			}

			defer res.Body.Close()

			body, err := ioutil.ReadAll(res.Body)
			if err != nil {
				klog.Warningf("Error parsing %s skipping...\n", url)
				continue
			}

			var unmarshalled = response{}
			err = json.Unmarshal(body, &unmarshalled)
			if err != nil {
				klog.Warningf("Error unmarshalling %s, skip...\n", url)
				continue
			}

			for _, product := range unmarshalled.Products {
				attr := product.Attributes
				if attr.InstanceType != "" {
					instanceTypes[attr.InstanceType] = &InstanceType{
						InstanceType: attr.InstanceType,
					}
					if attr.Memory != "" && attr.Memory != "NA" {
						instanceTypes[attr.InstanceType].MemoryMb = parseMemory(attr.Memory)
					}
					if attr.VCPU != "" {
						instanceTypes[attr.InstanceType].VCPU = parseCPU(attr.VCPU)
					}
					if attr.GPU != "" {
						instanceTypes[attr.InstanceType].GPU = parseCPU(attr.GPU)
					}
				}
			}
		}
	}

	if len(instanceTypes) == 0 {
		return nil, errors.New("unable to load EC2 Instance Type list")
	}

	return instanceTypes, nil
}

// GetStaticEC2InstanceTypes return pregenerated ec2 instance type list
func GetStaticEC2InstanceTypes() (map[string]*InstanceType, string) {
	return InstanceTypes, staticListLastUpdateTime
}

func parseMemory(memory string) int64 {
	reg, err := regexp.Compile("[^0-9\\.]+")
	if err != nil {
		klog.Fatal(err)
	}

	parsed := strings.TrimSpace(reg.ReplaceAllString(memory, ""))
	mem, err := strconv.ParseFloat(parsed, 64)
	if err != nil {
		klog.Fatal(err)
	}

	return int64(mem * float64(1024))
}

func parseCPU(cpu string) int64 {
	i, err := strconv.ParseInt(cpu, 10, 64)
	if err != nil {
		klog.Fatal(err)
	}
	return i
}

// GetCurrentAwsRegion return region of current cluster without building awsManager
func GetCurrentAwsRegion() (string, error) {
	region, present := os.LookupEnv("AWS_REGION")

	if !present {
		klog.V(1).Infof("fetching %s\n", ec2MetaDataServiceUrl)
		res, err := http.Get(ec2MetaDataServiceUrl)
		if err != nil {
			return "", fmt.Errorf("Error fetching %s", ec2MetaDataServiceUrl)
		}

		defer res.Body.Close()

		body, err := ioutil.ReadAll(res.Body)
		if err != nil {
			return "", fmt.Errorf("Error parsing %s", ec2MetaDataServiceUrl)
		}

		var unmarshalled = map[string]string{}
		err = json.Unmarshal(body, &unmarshalled)
		if err != nil {
			klog.Warningf("Error unmarshalling %s, skip...\n", ec2MetaDataServiceUrl)
		}

		region = unmarshalled["region"]
	}

	return region, nil
}
<commit_msg>Add Fetch Error Output to Warning Log<commit_after>/*
Copyright 2019 The Kubernetes Authors.

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

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

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

package aws

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/aws/aws-sdk-go/aws/endpoints"
	"io/ioutil"
	klog "k8s.io/klog/v2"
	"net/http"
	"os"
	"regexp"
	"strconv"
	"strings"
)

var (
	ec2MetaDataServiceUrl          = "http://169.254.169.254/latest/dynamic/instance-identity/document"
	ec2PricingServiceUrlTemplate   = "https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonEC2/current/%s/index.json"
	ec2PricingServiceUrlTemplateCN = "https://pricing.cn-north-1.amazonaws.com.cn/offers/v1.0/cn/AmazonEC2/current/%s/index.json"
	staticListLastUpdateTime       = "2020-12-07"
)

type response struct {
	Products map[string]product `json:"products"`
}

type product struct {
	Attributes productAttributes `json:"attributes"`
}

type productAttributes struct {
	InstanceType string `json:"instanceType"`
	VCPU         string `json:"vcpu"`
	Memory       string `json:"memory"`
	GPU          string `json:"gpu"`
}

// GenerateEC2InstanceTypes returns a map of ec2 resources
func GenerateEC2InstanceTypes(region string) (map[string]*InstanceType, error) {
	var pricingUrlTemplate string
	if strings.HasPrefix(region, "cn-") {
		pricingUrlTemplate = ec2PricingServiceUrlTemplateCN
	} else {
		pricingUrlTemplate = ec2PricingServiceUrlTemplate
	}

	instanceTypes := make(map[string]*InstanceType)

	resolver := endpoints.DefaultResolver()
	partitions := resolver.(endpoints.EnumPartitions).Partitions()

	for _, p := range partitions {
		for _, r := range p.Regions() {
			if region != "" && region != r.ID() {
				continue
			}

			url := fmt.Sprintf(pricingUrlTemplate, r.ID())
			klog.V(1).Infof("fetching %s\n", url)
			res, err := http.Get(url)
			if err != nil {
				klog.Warningf("Error fetching %s skipping...\n%s\n", url, err)
				continue
			}

			defer res.Body.Close()

			body, err := ioutil.ReadAll(res.Body)
			if err != nil {
				klog.Warningf("Error parsing %s skipping...\n", url)
				continue
			}

			var unmarshalled = response{}
			err = json.Unmarshal(body, &unmarshalled)
			if err != nil {
				klog.Warningf("Error unmarshalling %s, skip...\n", url)
				continue
			}

			for _, product := range unmarshalled.Products {
				attr := product.Attributes
				if attr.InstanceType != "" {
					instanceTypes[attr.InstanceType] = &InstanceType{
						InstanceType: attr.InstanceType,
					}
					if attr.Memory != "" && attr.Memory != "NA" {
						instanceTypes[attr.InstanceType].MemoryMb = parseMemory(attr.Memory)
					}
					if attr.VCPU != "" {
						instanceTypes[attr.InstanceType].VCPU = parseCPU(attr.VCPU)
					}
					if attr.GPU != "" {
						instanceTypes[attr.InstanceType].GPU = parseCPU(attr.GPU)
					}
				}
			}
		}
	}

	if len(instanceTypes) == 0 {
		return nil, errors.New("unable to load EC2 Instance Type list")
	}

	return instanceTypes, nil
}

// GetStaticEC2InstanceTypes return pregenerated ec2 instance type list
func GetStaticEC2InstanceTypes() (map[string]*InstanceType, string) {
	return InstanceTypes, staticListLastUpdateTime
}

func parseMemory(memory string) int64 {
	reg, err := regexp.Compile("[^0-9\\.]+")
	if err != nil {
		klog.Fatal(err)
	}

	parsed := strings.TrimSpace(reg.ReplaceAllString(memory, ""))
	mem, err := strconv.ParseFloat(parsed, 64)
	if err != nil {
		klog.Fatal(err)
	}

	return int64(mem * float64(1024))
}

func parseCPU(cpu string) int64 {
	i, err := strconv.ParseInt(cpu, 10, 64)
	if err != nil {
		klog.Fatal(err)
	}
	return i
}

// GetCurrentAwsRegion return region of current cluster without building awsManager
func GetCurrentAwsRegion() (string, error) {
	region, present := os.LookupEnv("AWS_REGION")

	if !present {
		klog.V(1).Infof("fetching %s\n", ec2MetaDataServiceUrl)
		res, err := http.Get(ec2MetaDataServiceUrl)
		if err != nil {
			return "", fmt.Errorf("Error fetching %s", ec2MetaDataServiceUrl)
		}

		defer res.Body.Close()

		body, err := ioutil.ReadAll(res.Body)
		if err != nil {
			return "", fmt.Errorf("Error parsing %s", ec2MetaDataServiceUrl)
		}

		var unmarshalled = map[string]string{}
		err = json.Unmarshal(body, &unmarshalled)
		if err != nil {
			klog.Warningf("Error unmarshalling %s, skip...\n", ec2MetaDataServiceUrl)
		}

		region = unmarshalled["region"]
	}

	return region, nil
}
<|endoftext|>


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

// +build darwin freebsd linux windows
// +build !js
// +build !android
// +build !ios

package ui

import (
	"errors"
	"runtime"
	"sync"
	"time"

	"github.com/go-gl/glfw/v3.2/glfw"
	"github.com/hajimehoshi/ebiten/internal/opengl"
)

type userInterface struct {
	title           string
	window          *glfw.Window
	width           int
	height          int
	scale           float64
	deviceScale     float64
	glfwScale       float64
	fullscreen      bool
	fullscreenScale float64
	funcs           chan func()
	running         bool
	sizeChanged     bool
	origPosX        int
	origPosY        int
	m               sync.Mutex
}

var currentUI *userInterface

func init() {
	if err := initialize(); err != nil {
		panic(err)
	}
}

func initialize() error {
	runtime.LockOSThread()

	if err := glfw.Init(); err != nil {
		return err
	}
	glfw.WindowHint(glfw.Visible, glfw.False)
	glfw.WindowHint(glfw.Resizable, glfw.False)
	glfw.WindowHint(glfw.ContextVersionMajor, 2)
	glfw.WindowHint(glfw.ContextVersionMinor, 1)

	// As start, create an window with temporary size to create OpenGL context thread.
	window, err := glfw.CreateWindow(16, 16, "", nil, nil)
	if err != nil {
		return err
	}
	hideConsoleWindowOnWindows()
	u := &userInterface{
		window:      window,
		funcs:       make(chan func()),
		sizeChanged: true,
		origPosX:    -1,
		origPosY:    -1,
	}
	u.window.MakeContextCurrent()
	currentUI = u
	return nil
}

func RunMainThreadLoop(ch <-chan error) error {
	// TODO: Check this is done on the main thread.
	currentUI.setRunning(true)
	defer func() {
		currentUI.setRunning(false)
	}()
	for {
		select {
		case f := <-currentUI.funcs:
			f()
		case err := <-ch:
			// ch returns a value not only when an error occur but also it is closed.
			return err
		}
	}
}

func (u *userInterface) isRunning() bool {
	u.m.Lock()
	defer u.m.Unlock()
	return u.running
}

func (u *userInterface) setRunning(running bool) {
	u.m.Lock()
	defer u.m.Unlock()
	u.running = running
}

func (u *userInterface) runOnMainThread(f func() error) error {
	if u.funcs == nil {
		// already closed
		return nil
	}
	ch := make(chan struct{})
	var err error
	u.funcs <- func() {
		err = f()
		close(ch)
	}
	<-ch
	return err
}

func SetScreenSize(width, height int) bool {
	u := currentUI
	if !u.isRunning() {
		panic("ui: Run is not called yet")
	}
	r := false
	_ = u.runOnMainThread(func() error {
		r = u.setScreenSize(width, height, u.scale, u.fullscreen)
		return nil
	})
	return r
}

func SetScreenScale(scale float64) bool {
	u := currentUI
	if !u.isRunning() {
		panic("ui: Run is not called yet")
	}
	r := false
	_ = u.runOnMainThread(func() error {
		r = u.setScreenSize(u.width, u.height, scale, u.fullscreen)
		return nil
	})
	return r
}

func SetFullscreen(fullscreen bool) bool {
	u := currentUI
	if !u.isRunning() {
		panic("ui: Run is not called yet")
	}
	r := false
	_ = u.runOnMainThread(func() error {
		r = u.setScreenSize(u.width, u.height, u.scale, fullscreen)
		return nil
	})
	return r
}

func ScreenScale() float64 {
	u := currentUI
	if !u.isRunning() {
		return 0
	}
	s := 0.0
	_ = u.runOnMainThread(func() error {
		s = u.scale
		return nil
	})
	return s
}

func IsFullscreen() bool {
	u := currentUI
	if !u.isRunning() {
		return false
	}
	f := false
	_ = u.runOnMainThread(func() error {
		f = u.fullscreen
		return nil
	})
	return f
}

func ScreenOffset() (float64, float64) {
	u := currentUI
	if !u.isRunning() {
		return 0, 0
	}
	if !IsFullscreen() {
		return 0, 0
	}
	ox := 0.0
	oy := 0.0
	m := glfw.GetPrimaryMonitor()
	v := m.GetVideoMode()
	_ = u.runOnMainThread(func() error {
		ox = (float64(v.Width)*u.deviceScale/u.glfwScale - float64(u.width)*u.actualScreenScale()) / 2
		oy = (float64(v.Height)*u.deviceScale/u.glfwScale - float64(u.height)*u.actualScreenScale()) / 2
		return nil
	})
	return ox, oy
}

func adjustCursorPosition(x, y int) (int, int) {
	u := currentUI
	if !u.isRunning() {
		return x, y
	}
	ox, oy := ScreenOffset()
	s := 0.0
	_ = currentUI.runOnMainThread(func() error {
		s = currentUI.actualScreenScale()
		return nil
	})
	return x - int(ox/s), y - int(oy/s)
}

func SetCursorVisibility(visible bool) {
	// This can be called before Run: change the state asyncly.
	go func() {
		_ = currentUI.runOnMainThread(func() error {
			c := glfw.CursorNormal
			if !visible {
				c = glfw.CursorHidden
			}
			currentUI.window.SetInputMode(glfw.CursorMode, c)
			return nil
		})
	}()
}

func Run(width, height int, scale float64, title string, g GraphicsContext) error {
	u := currentUI
	// GLContext must be created before setting the screen size, which requires
	// swapping buffers.
	opengl.Init(currentUI.runOnMainThread)
	if err := u.runOnMainThread(func() error {
		m := glfw.GetPrimaryMonitor()
		v := m.GetVideoMode()
		if !u.setScreenSize(width, height, scale, false) {
			return errors.New("ui: Fail to set the screen size")
		}
		u.title = title
		u.window.SetTitle(title)
		u.window.Show()

		w, h := u.glfwSize()
		x := (v.Width - w) / 2
		y := (v.Height - h) / 3
		x, y = adjustWindowPosition(x, y)
		u.window.SetPos(x, y)
		return nil
	}); err != nil {
		return err
	}
	return u.loop(g)
}

func (u *userInterface) glfwSize() (int, int) {
	if u.glfwScale == 0 {
		u.glfwScale = glfwScale()
	}
	return int(float64(u.width) * u.scale * u.glfwScale), int(float64(u.height) * u.scale * u.glfwScale)
}

func (u *userInterface) getScale() float64 {
	if !u.fullscreen {
		return u.scale
	}
	if u.fullscreenScale == 0 {
		if u.glfwScale == 0 {
			u.glfwScale = glfwScale()
		}
		m := glfw.GetPrimaryMonitor()
		v := m.GetVideoMode()
		sw := float64(v.Width) / u.glfwScale / float64(u.width)
		sh := float64(v.Height) / u.glfwScale / float64(u.height)
		s := sw
		if s > sh {
			s = sh
		}
		u.fullscreenScale = s
	}
	return u.fullscreenScale
}

func (u *userInterface) actualScreenScale() float64 {
	if u.deviceScale == 0 {
		u.deviceScale = deviceScale()
	}
	return u.getScale() * u.deviceScale
}

func (u *userInterface) pollEvents() {
	glfw.PollEvents()
	if u.glfwScale == 0 {
		u.glfwScale = glfwScale()
	}
	currentInput.update(u.window, u.getScale()*u.glfwScale)
}

func (u *userInterface) update(g GraphicsContext) error {
	shouldClose := false
	_ = u.runOnMainThread(func() error {
		shouldClose = u.window.ShouldClose()
		return nil
	})
	if shouldClose {
		return &RegularTermination{}
	}

	actualScale := 0.0
	sizeChanged := false
	_ = u.runOnMainThread(func() error {
		if !u.sizeChanged {
			return nil
		}
		u.sizeChanged = false
		actualScale = u.actualScreenScale()
		sizeChanged = true
		return nil
	})
	if sizeChanged {
		g.SetSize(u.width, u.height, actualScale)
	}

	_ = u.runOnMainThread(func() error {
		u.pollEvents()
		for u.window.GetAttrib(glfw.Focused) == 0 {
			// Wait for an arbitrary period to avoid busy loop.
			time.Sleep(time.Second / 60)
			u.pollEvents()
			if u.window.ShouldClose() {
				return nil
			}
		}
		return nil
	})
	if err := g.Update(); err != nil {
		return err
	}
	return nil
}

func (u *userInterface) loop(g GraphicsContext) error {
	defer func() {
		_ = u.runOnMainThread(func() error {
			glfw.Terminate()
			return nil
		})
	}()
	for {
		if err := u.update(g); err != nil {
			return err
		}
		// The bound framebuffer must be the default one (0) before swapping buffers.
		if err := opengl.GetContext().BindScreenFramebuffer(); err != nil {
			return err
		}
		_ = u.runOnMainThread(func() error {
			u.swapBuffers()
			return nil
		})
	}
}

func (u *userInterface) swapBuffers() {
	u.window.SwapBuffers()
}

func (u *userInterface) setScreenSize(width, height int, scale float64, fullscreen bool) bool {
	if u.width == width && u.height == height && u.scale == scale && u.fullscreen == fullscreen {
		return false
	}

	origScale := u.scale
	u.scale = scale

	// On Windows, giving a too small width doesn't call a callback (#165).
	// To prevent hanging up, return asap if the width is too small.
	// 252 is an arbitrary number and I guess this is small enough.
	// TODO: The same check should be in ui_js.go
	const minWindowWidth = 252
	if int(float64(width)*u.actualScreenScale()) < minWindowWidth {
		u.scale = origScale
		return false
	}
	if u.width != width || u.height != height {
		u.width = width
		u.height = height
		u.fullscreenScale = 0
	}

	// To make sure the current existing framebuffers are rendered,
	// swap buffers here before SetSize is called.
	u.swapBuffers()

	u.fullscreen = fullscreen

	if u.fullscreen {
		if u.origPosX < 0 && u.origPosY < 0 {
			u.origPosX, u.origPosY = u.window.GetPos()
		}
		m := glfw.GetPrimaryMonitor()
		v := m.GetVideoMode()
		u.window.SetMonitor(m, 0, 0, v.Width, v.Height, v.RefreshRate)
	} else {
		if u.origPosX >= 0 && u.origPosY >= 0 {
			x := u.origPosX
			y := u.origPosY
			u.window.SetMonitor(nil, x, y, 16, 16, 0)
			u.origPosX = -1
			u.origPosY = -1
		}
		ch := make(chan struct{})
		u.window.SetFramebufferSizeCallback(func(_ *glfw.Window, width, height int) {
			u.window.SetFramebufferSizeCallback(nil)
			close(ch)
		})
		w, h := u.glfwSize()
		u.window.SetSize(w, h)
	event:
		for {
			glfw.PollEvents()
			select {
			case <-ch:
				break event
			default:
			}
		}
		// Window title might lost on macOS after coming back from fullscreen.
		u.window.SetTitle(u.title)
	}
	// SwapInterval is affected by the current monitor of the window.
	// This needs to be called at least after SetMonitor.
	// Without SwapInterval after SetMonitor, vsynch doesn't work (#375).
	glfw.SwapInterval(1)
	// TODO: Rename this variable?
	u.sizeChanged = true
	return true
}
<commit_msg>ui: Allow SetFullscreen without Run<commit_after>// Copyright 2015 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build darwin freebsd linux windows
// +build !js
// +build !android
// +build !ios

package ui

import (
	"errors"
	"runtime"
	"sync"
	"time"

	"github.com/go-gl/glfw/v3.2/glfw"
	"github.com/hajimehoshi/ebiten/internal/opengl"
)

type userInterface struct {
	title           string
	window          *glfw.Window
	width           int
	height          int
	scale           float64
	deviceScale     float64
	glfwScale       float64
	fullscreen      bool
	fullscreenScale float64
	funcs           chan func()
	running         bool
	sizeChanged     bool
	origPosX        int
	origPosY        int
	m               sync.Mutex
}

var currentUI *userInterface

func init() {
	if err := initialize(); err != nil {
		panic(err)
	}
}

func initialize() error {
	runtime.LockOSThread()

	if err := glfw.Init(); err != nil {
		return err
	}
	glfw.WindowHint(glfw.Visible, glfw.False)
	glfw.WindowHint(glfw.Resizable, glfw.False)
	glfw.WindowHint(glfw.ContextVersionMajor, 2)
	glfw.WindowHint(glfw.ContextVersionMinor, 1)

	// As start, create an window with temporary size to create OpenGL context thread.
	window, err := glfw.CreateWindow(16, 16, "", nil, nil)
	if err != nil {
		return err
	}
	hideConsoleWindowOnWindows()
	u := &userInterface{
		window:      window,
		funcs:       make(chan func()),
		sizeChanged: true,
		origPosX:    -1,
		origPosY:    -1,
	}
	u.window.MakeContextCurrent()
	currentUI = u
	return nil
}

func RunMainThreadLoop(ch <-chan error) error {
	// TODO: Check this is done on the main thread.
	currentUI.setRunning(true)
	defer func() {
		currentUI.setRunning(false)
	}()
	for {
		select {
		case f := <-currentUI.funcs:
			f()
		case err := <-ch:
			// ch returns a value not only when an error occur but also it is closed.
			return err
		}
	}
}

func (u *userInterface) isRunning() bool {
	u.m.Lock()
	defer u.m.Unlock()
	return u.running
}

func (u *userInterface) setRunning(running bool) {
	u.m.Lock()
	defer u.m.Unlock()
	u.running = running
}

func (u *userInterface) runOnMainThread(f func() error) error {
	if u.funcs == nil {
		// already closed
		return nil
	}
	ch := make(chan struct{})
	var err error
	u.funcs <- func() {
		err = f()
		close(ch)
	}
	<-ch
	return err
}

func SetScreenSize(width, height int) bool {
	u := currentUI
	if !u.isRunning() {
		panic("ui: Run is not called yet")
	}
	r := false
	_ = u.runOnMainThread(func() error {
		r = u.setScreenSize(width, height, u.scale, u.fullscreen)
		return nil
	})
	return r
}

func SetScreenScale(scale float64) bool {
	u := currentUI
	if !u.isRunning() {
		panic("ui: Run is not called yet")
	}
	r := false
	_ = u.runOnMainThread(func() error {
		r = u.setScreenSize(u.width, u.height, scale, u.fullscreen)
		return nil
	})
	return r
}

func SetFullscreen(fullscreen bool) {
	// This can be called before Run: change the state asyncly.
	go func() {
		_ = currentUI.runOnMainThread(func() error {
			u := currentUI
			u.setScreenSize(u.width, u.height, u.scale, fullscreen)
			return nil
		})
	}()
}

func ScreenScale() float64 {
	u := currentUI
	if !u.isRunning() {
		return 0
	}
	s := 0.0
	_ = u.runOnMainThread(func() error {
		s = u.scale
		return nil
	})
	return s
}

func IsFullscreen() bool {
	u := currentUI
	if !u.isRunning() {
		return false
	}
	f := false
	_ = u.runOnMainThread(func() error {
		f = u.fullscreen
		return nil
	})
	return f
}

func ScreenOffset() (float64, float64) {
	u := currentUI
	if !u.isRunning() {
		return 0, 0
	}
	if !IsFullscreen() {
		return 0, 0
	}
	ox := 0.0
	oy := 0.0
	m := glfw.GetPrimaryMonitor()
	v := m.GetVideoMode()
	_ = u.runOnMainThread(func() error {
		ox = (float64(v.Width)*u.deviceScale/u.glfwScale - float64(u.width)*u.actualScreenScale()) / 2
		oy = (float64(v.Height)*u.deviceScale/u.glfwScale - float64(u.height)*u.actualScreenScale()) / 2
		return nil
	})
	return ox, oy
}

func adjustCursorPosition(x, y int) (int, int) {
	u := currentUI
	if !u.isRunning() {
		return x, y
	}
	ox, oy := ScreenOffset()
	s := 0.0
	_ = currentUI.runOnMainThread(func() error {
		s = currentUI.actualScreenScale()
		return nil
	})
	return x - int(ox/s), y - int(oy/s)
}

func SetCursorVisibility(visible bool) {
	// This can be called before Run: change the state asyncly.
	go func() {
		_ = currentUI.runOnMainThread(func() error {
			c := glfw.CursorNormal
			if !visible {
				c = glfw.CursorHidden
			}
			currentUI.window.SetInputMode(glfw.CursorMode, c)
			return nil
		})
	}()
}

func Run(width, height int, scale float64, title string, g GraphicsContext) error {
	u := currentUI
	// GLContext must be created before setting the screen size, which requires
	// swapping buffers.
	opengl.Init(currentUI.runOnMainThread)
	if err := u.runOnMainThread(func() error {
		m := glfw.GetPrimaryMonitor()
		v := m.GetVideoMode()
		if !u.setScreenSize(width, height, scale, false) {
			return errors.New("ui: Fail to set the screen size")
		}
		u.title = title
		u.window.SetTitle(title)
		u.window.Show()

		w, h := u.glfwSize()
		x := (v.Width - w) / 2
		y := (v.Height - h) / 3
		x, y = adjustWindowPosition(x, y)
		u.window.SetPos(x, y)
		return nil
	}); err != nil {
		return err
	}
	return u.loop(g)
}

func (u *userInterface) glfwSize() (int, int) {
	if u.glfwScale == 0 {
		u.glfwScale = glfwScale()
	}
	return int(float64(u.width) * u.scale * u.glfwScale), int(float64(u.height) * u.scale * u.glfwScale)
}

func (u *userInterface) getScale() float64 {
	if !u.fullscreen {
		return u.scale
	}
	if u.fullscreenScale == 0 {
		if u.glfwScale == 0 {
			u.glfwScale = glfwScale()
		}
		m := glfw.GetPrimaryMonitor()
		v := m.GetVideoMode()
		sw := float64(v.Width) / u.glfwScale / float64(u.width)
		sh := float64(v.Height) / u.glfwScale / float64(u.height)
		s := sw
		if s > sh {
			s = sh
		}
		u.fullscreenScale = s
	}
	return u.fullscreenScale
}

func (u *userInterface) actualScreenScale() float64 {
	if u.deviceScale == 0 {
		u.deviceScale = deviceScale()
	}
	return u.getScale() * u.deviceScale
}

func (u *userInterface) pollEvents() {
	glfw.PollEvents()
	if u.glfwScale == 0 {
		u.glfwScale = glfwScale()
	}
	currentInput.update(u.window, u.getScale()*u.glfwScale)
}

func (u *userInterface) update(g GraphicsContext) error {
	shouldClose := false
	_ = u.runOnMainThread(func() error {
		shouldClose = u.window.ShouldClose()
		return nil
	})
	if shouldClose {
		return &RegularTermination{}
	}

	actualScale := 0.0
	sizeChanged := false
	_ = u.runOnMainThread(func() error {
		if !u.sizeChanged {
			return nil
		}
		u.sizeChanged = false
		actualScale = u.actualScreenScale()
		sizeChanged = true
		return nil
	})
	if sizeChanged {
		g.SetSize(u.width, u.height, actualScale)
	}

	_ = u.runOnMainThread(func() error {
		u.pollEvents()
		for u.window.GetAttrib(glfw.Focused) == 0 {
			// Wait for an arbitrary period to avoid busy loop.
			time.Sleep(time.Second / 60)
			u.pollEvents()
			if u.window.ShouldClose() {
				return nil
			}
		}
		return nil
	})
	if err := g.Update(); err != nil {
		return err
	}
	return nil
}

func (u *userInterface) loop(g GraphicsContext) error {
	defer func() {
		_ = u.runOnMainThread(func() error {
			glfw.Terminate()
			return nil
		})
	}()
	for {
		if err := u.update(g); err != nil {
			return err
		}
		// The bound framebuffer must be the default one (0) before swapping buffers.
		if err := opengl.GetContext().BindScreenFramebuffer(); err != nil {
			return err
		}
		_ = u.runOnMainThread(func() error {
			u.swapBuffers()
			return nil
		})
	}
}

func (u *userInterface) swapBuffers() {
	u.window.SwapBuffers()
}

func (u *userInterface) setScreenSize(width, height int, scale float64, fullscreen bool) bool {
	if u.width == width && u.height == height && u.scale == scale && u.fullscreen == fullscreen {
		return false
	}

	origScale := u.scale
	u.scale = scale

	// On Windows, giving a too small width doesn't call a callback (#165).
	// To prevent hanging up, return asap if the width is too small.
	// 252 is an arbitrary number and I guess this is small enough.
	// TODO: The same check should be in ui_js.go
	const minWindowWidth = 252
	if int(float64(width)*u.actualScreenScale()) < minWindowWidth {
		u.scale = origScale
		return false
	}
	if u.width != width || u.height != height {
		u.width = width
		u.height = height
		u.fullscreenScale = 0
	}

	// To make sure the current existing framebuffers are rendered,
	// swap buffers here before SetSize is called.
	u.swapBuffers()

	u.fullscreen = fullscreen

	if u.fullscreen {
		if u.origPosX < 0 && u.origPosY < 0 {
			u.origPosX, u.origPosY = u.window.GetPos()
		}
		m := glfw.GetPrimaryMonitor()
		v := m.GetVideoMode()
		u.window.SetMonitor(m, 0, 0, v.Width, v.Height, v.RefreshRate)
	} else {
		if u.origPosX >= 0 && u.origPosY >= 0 {
			x := u.origPosX
			y := u.origPosY
			u.window.SetMonitor(nil, x, y, 16, 16, 0)
			u.origPosX = -1
			u.origPosY = -1
		}
		ch := make(chan struct{})
		u.window.SetFramebufferSizeCallback(func(_ *glfw.Window, width, height int) {
			u.window.SetFramebufferSizeCallback(nil)
			close(ch)
		})
		w, h := u.glfwSize()
		u.window.SetSize(w, h)
	event:
		for {
			glfw.PollEvents()
			select {
			case <-ch:
				break event
			default:
			}
		}
		// Window title might lost on macOS after coming back from fullscreen.
		u.window.SetTitle(u.title)
	}
	// SwapInterval is affected by the current monitor of the window.
	// This needs to be called at least after SetMonitor.
	// Without SwapInterval after SetMonitor, vsynch doesn't work (#375).
	glfw.SwapInterval(1)
	// TODO: Rename this variable?
	u.sizeChanged = true
	return true
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/base64"
	"io"
	"net/http"
	"runtime/pprof"
	"strconv"
	"strings"
	"time"

	cache "github.com/pmylund/go-cache"

	"fmt"

	"github.com/TykTechnologies/tyk/config"
	"github.com/TykTechnologies/tyk/user"
)

// Enums for keys to be stored in a session context - this is how gorilla expects
// these to be implemented and is lifted pretty much from docs
const (
	SessionData = iota
	AuthHeaderValue
	VersionData
	OrgSessionContext
	ContextData
	RetainHost
	TrackThisEndpoint
	DoNotTrackThisEndpoint
)

var SessionCache = cache.New(10*time.Second, 5*time.Second)
var ExpiryCache = cache.New(600*time.Second, 10*time.Minute)

type ReturningHttpHandler interface {
	ServeHTTP(http.ResponseWriter, *http.Request) *http.Response
	ServeHTTPForCache(http.ResponseWriter, *http.Request) *http.Response
	CopyResponse(io.Writer, io.Reader)
}

// SuccessHandler represents the final ServeHTTP() request for a proxied API request
type SuccessHandler struct {
	BaseMiddleware
}

func tagHeaders(r *http.Request, th []string, tags []string) []string {
	for k, v := range r.Header {
		cleanK := strings.ToLower(k)
		ok := false
		for _, hname := range th {
			if hname == cleanK {
				ok = true
				break
			}
		}

		if ok {
			for _, val := range v {
				tagName := fmt.Sprintf("%s-%s", cleanK, val)
				tags = append(tags, tagName)
			}
		}
	}

	return tags
}

func (s *SuccessHandler) RecordHit(r *http.Request, timing int64, code int, requestCopy *http.Request, responseCopy *http.Response) {

	if s.Spec.DoNotTrack {
		return
	}

	ip := requestIP(r)
	if config.Global.StoreAnalytics(ip) {

		t := time.Now()

		// Track the key ID if it exists
		token := ctxGetAuthToken(r)

		// Track version data
		version := s.Spec.getVersionFromRequest(r)
		if version == "" {
			version = "Non Versioned"
		}

		// If OAuth, we need to grab it from the session, which may or may not exist
		oauthClientID := ""
		tags := make([]string, 0)
		var alias string
		session := ctxGetSession(r)

		if session != nil {
			oauthClientID = session.OauthClientID
			tags = session.Tags
			alias = session.Alias
		}

		if len(s.Spec.TagHeaders) > 0 {
			tags = tagHeaders(r, s.Spec.TagHeaders, tags)
		}

		rawRequest := ""
		rawResponse := ""
		if recordDetail(r) {
			// Get the wire format representation
			var wireFormatReq bytes.Buffer
			requestCopy.Write(&wireFormatReq)
			rawRequest = base64.StdEncoding.EncodeToString(wireFormatReq.Bytes())
			// Get the wire format representation
			var wireFormatRes bytes.Buffer
			responseCopy.Write(&wireFormatRes)
			rawResponse = base64.StdEncoding.EncodeToString(wireFormatRes.Bytes())
		}

		trackEP := false
		trackedPath := r.URL.Path
		if p := ctxGetTrackedPath(r); p != "" && !ctxGetDoNotTrack(r) {
			trackEP = true
			trackedPath = p
		}

		record := AnalyticsRecord{
			r.Method,
			trackedPath,
			r.URL.Path,
			r.ContentLength,
			r.Header.Get("User-Agent"),
			t.Day(),
			t.Month(),
			t.Year(),
			t.Hour(),
			code,
			token,
			t,
			version,
			s.Spec.Name,
			s.Spec.APIID,
			s.Spec.OrgID,
			oauthClientID,
			timing,
			rawRequest,
			rawResponse,
			ip,
			GeoData{},
			tags,
			alias,
			trackEP,
			time.Now(),
		}

		record.GetGeo(ip)

		expiresAfter := s.Spec.ExpireAnalyticsAfter
		if config.Global.EnforceOrgDataAge {
			orgExpireDataTime := s.OrgSessionExpiry(s.Spec.OrgID)

			if orgExpireDataTime > 0 {
				expiresAfter = orgExpireDataTime
			}
		}

		record.SetExpiry(expiresAfter)

		if config.Global.AnalyticsConfig.NormaliseUrls.Enabled {
			record.NormalisePath()
		}

		go analytics.RecordHit(record)
	}

	// Report in health check
	reportHealthValue(s.Spec, RequestLog, strconv.FormatInt(timing, 10))

	if memProfFile != nil {
		pprof.WriteHeapProfile(memProfFile)
	}
}

func recordDetail(r *http.Request) bool {
	// Are we even checking?
	if !config.Global.EnforceOrgDataDeailLogging {
		return config.Global.AnalyticsConfig.EnableDetailedRecording
	}

	// We are, so get session data
	ses := r.Context().Value(OrgSessionContext)
	if ses == nil {
		// no session found, use global config
		return config.Global.AnalyticsConfig.EnableDetailedRecording
	}

	// Session found
	return ses.(user.SessionState).EnableDetailedRecording
}

// ServeHTTP will store the request details in the analytics store if necessary and proxy the request to it's
// final destination, this is invoked by the ProxyHandler or right at the start of a request chain if the URL
// Spec states the path is Ignored
func (s *SuccessHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) *http.Response {
	log.Debug("Started proxy")
	// Make sure we get the correct target URL
	if s.Spec.Proxy.StripListenPath {
		log.Debug("Stripping: ", s.Spec.Proxy.ListenPath)
		r.URL.Path = strings.Replace(r.URL.Path, s.Spec.Proxy.ListenPath, "", 1)
		log.Debug("Upstream Path is: ", r.URL.Path)
	}

	var copiedRequest *http.Request
	if recordDetail(r) {
		copiedRequest = copyRequest(r)
	}

	t1 := time.Now()
	resp := s.Proxy.ServeHTTP(w, r)
	t2 := time.Now()

	millisec := float64(t2.UnixNano()-t1.UnixNano()) * 0.000001
	log.Debug("Upstream request took (ms): ", millisec)

	if resp != nil {
		var copiedResponse *http.Response
		if recordDetail(r) {
			copiedResponse = copyResponse(resp)
		}
		s.RecordHit(r, int64(millisec), resp.StatusCode, copiedRequest, copiedResponse)
	}
	log.Debug("Done proxy")
	return nil
}

// ServeHTTPWithCache will store the request details in the analytics store if necessary and proxy the request to it's
// final destination, this is invoked by the ProxyHandler or right at the start of a request chain if the URL
// Spec states the path is Ignored Itwill also return a response object for the cache
func (s *SuccessHandler) ServeHTTPWithCache(w http.ResponseWriter, r *http.Request) *http.Response {
	// Make sure we get the correct target URL
	if s.Spec.Proxy.StripListenPath {
		r.URL.Path = strings.Replace(r.URL.Path, s.Spec.Proxy.ListenPath, "", 1)
	}

	var copiedRequest *http.Request
	if recordDetail(r) {
		copiedRequest = copyRequest(r)
	}

	t1 := time.Now()
	inRes := s.Proxy.ServeHTTPForCache(w, r)
	t2 := time.Now()

	var copiedResponse *http.Response
	if recordDetail(r) {
		copiedResponse = copyResponse(inRes)
	}

	millisec := float64(t2.UnixNano()-t1.UnixNano()) * 0.000001
	log.Debug("Upstream request took (ms): ", millisec)

	if inRes != nil {
		s.RecordHit(r, int64(millisec), inRes.StatusCode, copiedRequest, copiedResponse)
	}

	return inRes
}
<commit_msg>analytics: don't panic on nil response copy<commit_after>package main

import (
	"bytes"
	"encoding/base64"
	"io"
	"net/http"
	"runtime/pprof"
	"strconv"
	"strings"
	"time"

	cache "github.com/pmylund/go-cache"

	"fmt"

	"github.com/TykTechnologies/tyk/config"
	"github.com/TykTechnologies/tyk/user"
)

// Enums for keys to be stored in a session context - this is how gorilla expects
// these to be implemented and is lifted pretty much from docs
const (
	SessionData = iota
	AuthHeaderValue
	VersionData
	OrgSessionContext
	ContextData
	RetainHost
	TrackThisEndpoint
	DoNotTrackThisEndpoint
)

var SessionCache = cache.New(10*time.Second, 5*time.Second)
var ExpiryCache = cache.New(600*time.Second, 10*time.Minute)

type ReturningHttpHandler interface {
	ServeHTTP(http.ResponseWriter, *http.Request) *http.Response
	ServeHTTPForCache(http.ResponseWriter, *http.Request) *http.Response
	CopyResponse(io.Writer, io.Reader)
}

// SuccessHandler represents the final ServeHTTP() request for a proxied API request
type SuccessHandler struct {
	BaseMiddleware
}

func tagHeaders(r *http.Request, th []string, tags []string) []string {
	for k, v := range r.Header {
		cleanK := strings.ToLower(k)
		ok := false
		for _, hname := range th {
			if hname == cleanK {
				ok = true
				break
			}
		}

		if ok {
			for _, val := range v {
				tagName := fmt.Sprintf("%s-%s", cleanK, val)
				tags = append(tags, tagName)
			}
		}
	}

	return tags
}

func (s *SuccessHandler) RecordHit(r *http.Request, timing int64, code int, requestCopy *http.Request, responseCopy *http.Response) {

	if s.Spec.DoNotTrack {
		return
	}

	ip := requestIP(r)
	if config.Global.StoreAnalytics(ip) {

		t := time.Now()

		// Track the key ID if it exists
		token := ctxGetAuthToken(r)

		// Track version data
		version := s.Spec.getVersionFromRequest(r)
		if version == "" {
			version = "Non Versioned"
		}

		// If OAuth, we need to grab it from the session, which may or may not exist
		oauthClientID := ""
		tags := make([]string, 0)
		var alias string
		session := ctxGetSession(r)

		if session != nil {
			oauthClientID = session.OauthClientID
			tags = session.Tags
			alias = session.Alias
		}

		if len(s.Spec.TagHeaders) > 0 {
			tags = tagHeaders(r, s.Spec.TagHeaders, tags)
		}

		rawRequest := ""
		rawResponse := ""
		if recordDetail(r) {
			// Get the wire format representation
			var wireFormatReq bytes.Buffer
			requestCopy.Write(&wireFormatReq)
			rawRequest = base64.StdEncoding.EncodeToString(wireFormatReq.Bytes())
			// responseCopy, unlike requestCopy, can be nil
			// here - if the response was cached in
			// mw_redis_cache, RecordHit gets passed a nil
			// response copy.
			// TODO: pass a copy of the cached response in
			// mw_redis_cache instead? is there a reason not
			// to include that in the analytics?
			if responseCopy != nil {
				// Get the wire format representation
				var wireFormatRes bytes.Buffer
				responseCopy.Write(&wireFormatRes)
				rawResponse = base64.StdEncoding.EncodeToString(wireFormatRes.Bytes())
			}
		}

		trackEP := false
		trackedPath := r.URL.Path
		if p := ctxGetTrackedPath(r); p != "" && !ctxGetDoNotTrack(r) {
			trackEP = true
			trackedPath = p
		}

		record := AnalyticsRecord{
			r.Method,
			trackedPath,
			r.URL.Path,
			r.ContentLength,
			r.Header.Get("User-Agent"),
			t.Day(),
			t.Month(),
			t.Year(),
			t.Hour(),
			code,
			token,
			t,
			version,
			s.Spec.Name,
			s.Spec.APIID,
			s.Spec.OrgID,
			oauthClientID,
			timing,
			rawRequest,
			rawResponse,
			ip,
			GeoData{},
			tags,
			alias,
			trackEP,
			time.Now(),
		}

		record.GetGeo(ip)

		expiresAfter := s.Spec.ExpireAnalyticsAfter
		if config.Global.EnforceOrgDataAge {
			orgExpireDataTime := s.OrgSessionExpiry(s.Spec.OrgID)

			if orgExpireDataTime > 0 {
				expiresAfter = orgExpireDataTime
			}
		}

		record.SetExpiry(expiresAfter)

		if config.Global.AnalyticsConfig.NormaliseUrls.Enabled {
			record.NormalisePath()
		}

		go analytics.RecordHit(record)
	}

	// Report in health check
	reportHealthValue(s.Spec, RequestLog, strconv.FormatInt(timing, 10))

	if memProfFile != nil {
		pprof.WriteHeapProfile(memProfFile)
	}
}

func recordDetail(r *http.Request) bool {
	// Are we even checking?
	if !config.Global.EnforceOrgDataDeailLogging {
		return config.Global.AnalyticsConfig.EnableDetailedRecording
	}

	// We are, so get session data
	ses := r.Context().Value(OrgSessionContext)
	if ses == nil {
		// no session found, use global config
		return config.Global.AnalyticsConfig.EnableDetailedRecording
	}

	// Session found
	return ses.(user.SessionState).EnableDetailedRecording
}

// ServeHTTP will store the request details in the analytics store if necessary and proxy the request to it's
// final destination, this is invoked by the ProxyHandler or right at the start of a request chain if the URL
// Spec states the path is Ignored
func (s *SuccessHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) *http.Response {
	log.Debug("Started proxy")
	// Make sure we get the correct target URL
	if s.Spec.Proxy.StripListenPath {
		log.Debug("Stripping: ", s.Spec.Proxy.ListenPath)
		r.URL.Path = strings.Replace(r.URL.Path, s.Spec.Proxy.ListenPath, "", 1)
		log.Debug("Upstream Path is: ", r.URL.Path)
	}

	var copiedRequest *http.Request
	if recordDetail(r) {
		copiedRequest = copyRequest(r)
	}

	t1 := time.Now()
	resp := s.Proxy.ServeHTTP(w, r)
	t2 := time.Now()

	millisec := float64(t2.UnixNano()-t1.UnixNano()) * 0.000001
	log.Debug("Upstream request took (ms): ", millisec)

	if resp != nil {
		var copiedResponse *http.Response
		if recordDetail(r) {
			copiedResponse = copyResponse(resp)
		}
		s.RecordHit(r, int64(millisec), resp.StatusCode, copiedRequest, copiedResponse)
	}
	log.Debug("Done proxy")
	return nil
}

// ServeHTTPWithCache will store the request details in the analytics store if necessary and proxy the request to it's
// final destination, this is invoked by the ProxyHandler or right at the start of a request chain if the URL
// Spec states the path is Ignored Itwill also return a response object for the cache
func (s *SuccessHandler) ServeHTTPWithCache(w http.ResponseWriter, r *http.Request) *http.Response {
	// Make sure we get the correct target URL
	if s.Spec.Proxy.StripListenPath {
		r.URL.Path = strings.Replace(r.URL.Path, s.Spec.Proxy.ListenPath, "", 1)
	}

	var copiedRequest *http.Request
	if recordDetail(r) {
		copiedRequest = copyRequest(r)
	}

	t1 := time.Now()
	inRes := s.Proxy.ServeHTTPForCache(w, r)
	t2 := time.Now()

	var copiedResponse *http.Response
	if recordDetail(r) {
		copiedResponse = copyResponse(inRes)
	}

	millisec := float64(t2.UnixNano()-t1.UnixNano()) * 0.000001
	log.Debug("Upstream request took (ms): ", millisec)

	if inRes != nil {
		s.RecordHit(r, int64(millisec), inRes.StatusCode, copiedRequest, copiedResponse)
	}

	return inRes
}
<|endoftext|>


<commit_before>// Package binary provides a collection of binary extensions
// and a utility function to check if given path is a binary
package binary

import (
	"path"
	"strings"
)

var extensions = []string{
	"3ds",
	"3g2",
	"3gp",
	"7z",
	"a",
	"aac",
	"adp",
	"ai",
	"aif",
	"apk",
	"ar",
	"asf",
	"au",
	"avi",
	"bak",
	"bin",
	"bk",
	"bmp",
	"btif",
	"bz2",
	"cab",
	"caf",
	"cgm",
	"cmx",
	"cpio",
	"cr2",
	"dat",
	"deb",
	"djvu",
	"dll",
	"dmg",
	"dng",
	"doc",
	"docx",
	"dra",
	"DS_Store",
	"dsk",
	"dts",
	"dtshd",
	"dvb",
	"dwg",
	"dxf",
	"ecelp4800",
	"ecelp7470",
	"ecelp9600",
	"egg",
	"eol",
	"eot",
	"epub",
	"exe",
	"f4v",
	"fbs",
	"fh",
	"fla",
	"flac",
	"fli",
	"flv",
	"fpx",
	"fst",
	"fvt",
	"g3",
	"gif",
	"gz",
	"h261",
	"h263",
	"h264",
	"ico",
	"ief",
	"img",
	"ipa",
	"iso",
	"jar",
	"jpeg",
	"jpg",
	"jpgv",
	"jpm",
	"jxr",
	"ktx",
	"lvp",
	"lz",
	"lzma",
	"lzo",
	"m3u",
	"m4a",
	"m4v",
	"mar",
	"mdi",
	"mid",
	"mj2",
	"mka",
	"mkv",
	"mmr",
	"mng",
	"mov",
	"movie",
	"mp3",
	"mp4",
	"mp4a",
	"mpeg",
	"mpg",
	"mpga",
	"mxu",
	"nef",
	"npx",
	"o",
	"oga",
	"ogg",
	"ogv",
	"otf",
	"pbm",
	"pcx",
	"pdf",
	"pea",
	"pgm",
	"pic",
	"png",
	"pnm",
	"ppm",
	"psd",
	"pya",
	"pyc",
	"pyo",
	"pyv",
	"qt",
	"rar",
	"ras",
	"raw",
	"rgb",
	"rip",
	"rlc",
	"rz",
	"s3m",
	"s7z",
	"scpt",
	"sgi",
	"shar",
	"sil",
	"smv",
	"so",
	"sub",
	"swf",
	"tar",
	"tbz2",
	"tga",
	"tgz",
	"tif",
	"tiff",
	"tlz",
	"ts",
	"ttf",
	"uvh",
	"uvi",
	"uvm",
	"uvp",
	"uvs",
	"uvu",
	"viv",
	"vob",
	"war",
	"wav",
	"wax",
	"wbmp",
	"wdp",
	"weba",
	"webm",
	"webp",
	"whl",
	"wm",
	"wma",
	"wmv",
	"wmx",
	"woff",
	"woff2",
	"wvx",
	"xbm",
	"xif",
	"xm",
	"xpi",
	"xpm",
	"xwd",
	"xz",
	"z",
	"zip",
	"zipx",
}

// Extensions is the extensions for different binary types
var Extensions map[string]bool

func init() {
	Extensions = map[string]bool{}
	for _, ext := range extensions {
		Extensions[ext] = true
	}
}

// Is checks if a path is a binary
func Is(p string) bool {
	ext := strings.TrimLeft(path.Ext(p), ".")
	return Extensions[strings.ToLower(ext)]
}
<commit_msg>Reuse gobinext package<commit_after>// Package binary provides a collection of binary extensions
// and a utility function to check if given path is a binary
package binary

import (
	"github.com/shamsher31/gobinext"
	"path"
	"strings"
)

var extensions = binext.Get()

// Extensions is the extensions for different binary types
var Extensions map[string]bool

func init() {
	Extensions = map[string]bool{}
	for _, ext := range extensions {
		Extensions[ext] = true
	}
}

// Is checks if a path is a binary
func Is(p string) bool {
	ext := strings.TrimLeft(path.Ext(p), ".")
	return Extensions[strings.ToLower(ext)]
}
<|endoftext|>


<commit_before>package numgo

import (
	"log"
	"numgo/mat"
	"os"
	"testing"
)

func TestMatI(t *testing.T) {
	var (
		row = 4
	)
	m := mat.I(row)
	for i := 0; i < row; i++ {
		for j := 0; j < row; j++ {
			if i == j {
				if m[i][j] != 1.0 {
					t.Errorf("mat.I[%v,%v] == %v, want 1.0", i, j, m[i][j])
				}
			} else {
				if m[i][j] != 0.0 {
					t.Errorf("mat.I[%v,%v] == %v, want 0.0", i, j, m[i][j])
				}
			}
		}
	}
}

func TestMatOnes(t *testing.T) {
	var (
		row = 12
		col = 7
	)
	o := mat.Ones(row, col)
	for i := 0; i < row; i++ {
		for j := 0; j < col; j++ {
			if o[i][j] != 1.0 {
				t.Errorf("mat.Ones[%v][%v]: expected 1.0, got %v", i, j, o[i][j])
			}
		}
	}
}

func TestMatCol(t *testing.T) {
	var (
		row = 3
		col = 4
	)
	m := mat.Inc(row, col)
	got := mat.Col(2, m)
	if len(got) != row {
		t.Errorf("mat.Col: got.NumRows == %v, want %v", len(got), row)
	}
	want := []float64{2.0, 6.0, 10.0}
	for i := 0; i < row; i++ {
		if want[i] != got[i] {
			t.Errorf("mat.Col: m[%v][2] == %v, want %v", i, got[i], want[i])
		}
	}
}

func TestMatRow(t *testing.T) {
	var (
		row = 3
		col = 4
	)
	m := mat.Inc(row, col)
	got := mat.Row(1, m)
	if len(got) != col {
		t.Errorf("mat.Row: len(got) == %v, want %v", len(got), col)
	}
	want := []float64{4.0, 5.0, 6.0, 7.0}
	for i := 0; i < col; i++ {
		if want[i] != got[i] {
			t.Errorf("mat.Row: m[1][%v] is %v, want %v", i, got[i], want[i])
		}
	}
}

func TestMatT(t *testing.T) {
	var (
		row = 5
		col = 7
	)
	m := mat.Inc(row, col)
	n := mat.T(m)
	for i := 0; i < row; i++ {
		for j := 0; j < col; j++ {
			if n[j][i] != m[i][j] {
				t.Errorf("mat.T: [%v][%v] is %v, want %v", i, j, n[j][i], m[i][j])
			}
		}
	}
	s := mat.T(mat.T(m))
	if !mat.Equal(m, s) {
		t.Errorf("mat.T.T != mat")
		for i := 0; i < row; i++ {
			for j := 0; j < col; j++ {
				if s[i][j] != m[i][j] {
					t.Errorf("At [%v][%v]: want %v, got %v", i, j, m[i][j], s[i][j])
				}
			}
		}
	}
}

func TestMatMul(t *testing.T) {
	var (
		row = 5
		col = 7
	)
	m := mat.New(row, row)
	q := mat.I(row)
	if !mat.Equal(mat.Mul(m, q), m) {
		t.Errorf("A Square matrix Mul the identity matrix should be equal to itself")
	}
	m = mat.Inc(row, col)
	n := mat.New(row, col)
	o := mat.Mul(m, n)
	for i := 0; i < row; i++ {
		for j := 0; j < col; j++ {
			if o[i][j] != 0.0 {
				t.Errorf("o[%v][%v] == %v, expected 0.0", i, j, o[i][j])
			}
		}
	}
	o = mat.Mul(m, m)
	m = mat.Map(func(i float64) float64 { return i * i }, m)
	if !mat.Equal(o, m) {
		t.Errorf("m Mul m != m.Map( i * i for each element i in m)")
	}
}

func TestMatMap(t *testing.T) {
	var (
		row = 4
		col = 4
	)
	m := mat.New(row, col)
	n := mat.Map(func(i float64) float64 { return i + 1.0 }, m)
	for i := 0; i < row; i++ {
		for j := 0; j < col; j++ {
			if n[i][j] != 1.0 {
				t.Errorf("expected 1.0, got %v", n[i][j])
			}
		}
	}
}

func TestMatDot(t *testing.T) {
	var (
		row = 10
		col = 4
	)
	m := mat.New(row, col)
	n := mat.New(col, row)
	o := mat.Dot(m, n)
	if len(o) != row {
		t.Errorf("mat.Dot(m, n)'s numRows expected %v, got %v", row, len(o))
	}
	if len(o[0]) != row {
		t.Errorf("mat.Dot(m, n)'s numCols expected %v, got %v", row, len(o[0]))
	}
	o = mat.Inc(row, row)
	p := mat.Dot(mat.I(row), o)
	if !mat.Equal(p, o) {
		t.Errorf("o x I != o...")
	}
}

func TestMatReset(t *testing.T) {
	var (
		row = 21
		col = 13
	)
	m := mat.Inc(row, col)
	m = mat.Reset(m)
	for i := 0; i < row; i++ {
		for j := 0; j < col; j++ {
			if m[i][j] != 0.0 {
				t.Errorf("Reset(m) at m[%v][%v] is equal %v", i, j, m[i][j])
			}
		}
	}
}

func TestMatDump(t *testing.T) {
	fileName := "output"
	m := mat.Inc(20, 30)
	mat.Dump(m, fileName)
	n := mat.Load(fileName)
	if !mat.Equal(m, n) {
		t.Errorf("Dumped 2D slice is not Equal Loaded 2D slice")
	}
	err := os.Remove(fileName)
	if err != nil {
		log.Fatal(err)
	}
}

func TestMatCopy(t *testing.T) {
	m := mat.Inc(3, 4)
	n := mat.Copy(m)
	if !mat.Equal(m, n) {
		t.Errorf("m != its copy")
	}
}

func TestMatAppendCol(t *testing.T) {
	m := mat.Inc(5, 7)
	v := []float64{12, 13, 17, 19, 21}
	m = mat.AppendCol(m, v)
	p := mat.Col(7, m)
	for i := 0; i < len(v); i++ {
		if v[i] != p[i] {
			t.Errorf("In AppendCol, expected %v, got %v", v[i], p[i])
		}
	}
}

func TestMatConcat(t *testing.T) {
	var (
		row = 3
		col = 7
	)
	m := mat.Inc(row, col)
	n := mat.I(row)
	o := mat.Concat(m, n)
	if len(o) != row {
		t.Errorf("len of concatinated 2Dslice is %v, want %v", len(o), row)
	}
	for i := 0; i < len(o); i++ {
		if len(o[i]) != (row + col) {
			t.Errorf("len(o[%v]) is %v, want %v", i, len(o[i]), row+col)
		}
	}
	for i := 0; i < row; i++ {
		for j := 0; j < row+col; j++ {
			if j < col {
				if o[i][j] != m[i][j] {
					t.Errorf("[%v][%v] got %v, want %v", i, j, o[i][j], m[i][j])
				}
			} else {
				if o[i][j] != n[i][j-col] {
					t.Errorf("[%v][%v] got %v, want %v", i, j, o[i][j], n[i][j-col])
				}
			}
		}
	}
}
<commit_msg>Added some benchmarking to mat<commit_after>package numgo

import (
	"log"
	"numgo/mat"
	"os"
	"testing"
)

func TestMatI(t *testing.T) {
	var (
		row = 4
	)
	m := mat.I(row)
	for i := 0; i < row; i++ {
		for j := 0; j < row; j++ {
			if i == j {
				if m[i][j] != 1.0 {
					t.Errorf("mat.I[%v,%v] == %v, want 1.0", i, j, m[i][j])
				}
			} else {
				if m[i][j] != 0.0 {
					t.Errorf("mat.I[%v,%v] == %v, want 0.0", i, j, m[i][j])
				}
			}
		}
	}
}

func TestMatOnes(t *testing.T) {
	var (
		row = 12
		col = 7
	)
	o := mat.Ones(row, col)
	for i := 0; i < row; i++ {
		for j := 0; j < col; j++ {
			if o[i][j] != 1.0 {
				t.Errorf("mat.Ones[%v][%v]: expected 1.0, got %v", i, j, o[i][j])
			}
		}
	}
}

func TestMatCol(t *testing.T) {
	var (
		row = 3
		col = 4
	)
	m := mat.Inc(row, col)
	got := mat.Col(2, m)
	if len(got) != row {
		t.Errorf("mat.Col: got.NumRows == %v, want %v", len(got), row)
	}
	want := []float64{2.0, 6.0, 10.0}
	for i := 0; i < row; i++ {
		if want[i] != got[i] {
			t.Errorf("mat.Col: m[%v][2] == %v, want %v", i, got[i], want[i])
		}
	}
}

func TestMatRow(t *testing.T) {
	var (
		row = 3
		col = 4
	)
	m := mat.Inc(row, col)
	got := mat.Row(1, m)
	if len(got) != col {
		t.Errorf("mat.Row: len(got) == %v, want %v", len(got), col)
	}
	want := []float64{4.0, 5.0, 6.0, 7.0}
	for i := 0; i < col; i++ {
		if want[i] != got[i] {
			t.Errorf("mat.Row: m[1][%v] is %v, want %v", i, got[i], want[i])
		}
	}
}

func TestMatT(t *testing.T) {
	var (
		row = 5
		col = 7
	)
	m := mat.Inc(row, col)
	n := mat.T(m)
	for i := 0; i < row; i++ {
		for j := 0; j < col; j++ {
			if n[j][i] != m[i][j] {
				t.Errorf("mat.T: [%v][%v] is %v, want %v", i, j, n[j][i], m[i][j])
			}
		}
	}
	s := mat.T(mat.T(m))
	if !mat.Equal(m, s) {
		t.Errorf("mat.T.T != mat")
		for i := 0; i < row; i++ {
			for j := 0; j < col; j++ {
				if s[i][j] != m[i][j] {
					t.Errorf("At [%v][%v]: want %v, got %v", i, j, m[i][j], s[i][j])
				}
			}
		}
	}
}

func BenchmarkMatT(b *testing.B) {
	m := mat.Inc(2000, 1333)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = mat.T(m)
	}
}

func TestMatMul(t *testing.T) {
	var (
		row = 5
		col = 7
	)
	m := mat.New(row, row)
	q := mat.I(row)
	if !mat.Equal(mat.Mul(m, q), m) {
		t.Errorf("A Square matrix Mul the identity matrix should be equal to itself")
	}
	m = mat.Inc(row, col)
	n := mat.New(row, col)
	o := mat.Mul(m, n)
	for i := 0; i < row; i++ {
		for j := 0; j < col; j++ {
			if o[i][j] != 0.0 {
				t.Errorf("o[%v][%v] == %v, expected 0.0", i, j, o[i][j])
			}
		}
	}
	o = mat.Mul(m, m)
	m = mat.Map(func(i float64) float64 { return i * i }, m)
	if !mat.Equal(o, m) {
		t.Errorf("m Mul m != m.Map( i * i for each element i in m)")
	}
}

func BenchmarkMatMul(b *testing.B) {
	n := mat.Inc(1000, 1000)
	m := mat.Inc(1000, 1000)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = mat.Mul(m, n)
	}
}

func TestMatMap(t *testing.T) {
	var (
		row = 4
		col = 4
	)
	m := mat.New(row, col)
	n := mat.Map(func(i float64) float64 { return i + 1.0 }, m)
	for i := 0; i < row; i++ {
		for j := 0; j < col; j++ {
			if n[i][j] != 1.0 {
				t.Errorf("expected 1.0, got %v", n[i][j])
			}
		}
	}
}

func TestMatDot(t *testing.T) {
	var (
		row = 10
		col = 4
	)
	m := mat.New(row, col)
	n := mat.New(col, row)
	o := mat.Dot(m, n)
	if len(o) != row {
		t.Errorf("mat.Dot(m, n)'s numRows expected %v, got %v", row, len(o))
	}
	if len(o[0]) != row {
		t.Errorf("mat.Dot(m, n)'s numCols expected %v, got %v", row, len(o[0]))
	}
	o = mat.Inc(row, row)
	p := mat.Dot(mat.I(row), o)
	if !mat.Equal(p, o) {
		t.Errorf("o x I != o...")
	}
}

func BenchmarkMatDot(b *testing.B) {
	m := mat.Inc(1500, 1300)
	n := mat.Inc(1300, 1500)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = mat.Dot(m, n)
	}
}

func TestMatReset(t *testing.T) {
	var (
		row = 21
		col = 13
	)
	m := mat.Inc(row, col)
	m = mat.Reset(m)
	for i := 0; i < row; i++ {
		for j := 0; j < col; j++ {
			if m[i][j] != 0.0 {
				t.Errorf("Reset(m) at m[%v][%v] is equal %v", i, j, m[i][j])
			}
		}
	}
}

func TestMatDump(t *testing.T) {
	fileName := "output"
	m := mat.Inc(20, 30)
	mat.Dump(m, fileName)
	n := mat.Load(fileName)
	if !mat.Equal(m, n) {
		t.Errorf("Dumped 2D slice is not Equal Loaded 2D slice")
	}
	err := os.Remove(fileName)
	if err != nil {
		log.Fatal(err)
	}
}

func TestMatCopy(t *testing.T) {
	m := mat.Inc(3, 4)
	n := mat.Copy(m)
	if !mat.Equal(m, n) {
		t.Errorf("m != its copy")
	}
}

func TestMatAppendCol(t *testing.T) {
	m := mat.Inc(5, 7)
	v := []float64{12, 13, 17, 19, 21}
	m = mat.AppendCol(m, v)
	p := mat.Col(7, m)
	for i := 0; i < len(v); i++ {
		if v[i] != p[i] {
			t.Errorf("In AppendCol, expected %v, got %v", v[i], p[i])
		}
	}
}

func TestMatConcat(t *testing.T) {
	var (
		row = 3
		col = 7
	)
	m := mat.Inc(row, col)
	n := mat.I(row)
	o := mat.Concat(m, n)
	if len(o) != row {
		t.Errorf("len of concatinated 2Dslice is %v, want %v", len(o), row)
	}
	for i := 0; i < len(o); i++ {
		if len(o[i]) != (row + col) {
			t.Errorf("len(o[%v]) is %v, want %v", i, len(o[i]), row+col)
		}
	}
	for i := 0; i < row; i++ {
		for j := 0; j < row+col; j++ {
			if j < col {
				if o[i][j] != m[i][j] {
					t.Errorf("[%v][%v] got %v, want %v", i, j, o[i][j], m[i][j])
				}
			} else {
				if o[i][j] != n[i][j-col] {
					t.Errorf("[%v][%v] got %v, want %v", i, j, o[i][j], n[i][j-col])
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>package api

import (
    "gopkg.in/mgo.v2"
    "gopkg.in/mgo.v2/bson"
)
func testHandlerFn(vars apiVars, db *mgo.Session) *apiResponse{
  res := map[string]interface{}{
    "error": false,
    "text": "this is a test",
  }
  return newOKRes(res)
}

func examHandlerFn(vars apiVars, session *mgo.Session) *apiResponse{

  c := session.DB("ebisc").C("exam")
  m := make(bson.M)
  if vars["date"] != nil {
    if err := c.Find(bson.M{"date": vars["date"]}).One(&m); err != nil {
      if (err == mgo.ErrNotFound) {
        return newNotFoundRes()
      }
      panic(newApiError(err, "Database find error"))
    }
  } else {
    if err := c.Find(nil).Sort("-date").One(&m); err != nil {
      panic(newApiError(err, "Database find error"))
    }
  }

  if qSlice, ok := m["questions"].([]interface{}); ok {
    for _, q := range qSlice {
      if bsonQ, ok := q.(bson.M); !ok {
        panic(newApiError("type conversion of Question to a map", "Unexpected json structure"))
      } else {
        expandQuestion(bsonQ, session)
      }
    }
  }

  delete(m, "_id")
  m["error"] = false

  return newOKRes(m)
}

func expandQuestion(q bson.M, session *mgo.Session) {
  c := session.DB("ebisc").C("question")
  if err := c.Find(bson.M{"module": q["module"]}).Select(bson.M{"title": 1, "description": 1, "_id": 0}).One(&q); err != nil {
    if (err == mgo.ErrNotFound) {
      return // Not an error
    }
    panic(newApiError(err, "Database find error"))
  }
}

func examListHandlerFn(vars apiVars, session *mgo.Session) *apiResponse{
  c := session.DB("ebisc").C("exam")
  var query bson.M = nil
  if vars["date"] != nil {
    query = bson.M{"date": bson.M{"$lt": vars["date"]}};
  }
  var items []interface{}
  if err := c.Find(query).Sort("-date").Limit(10).Select(bson.M{"date": 1, "_id": 0}).All(&items); err != nil {
    panic(newApiError(err, "Database find error"))
  }
  m := bson.M{"items": items, "error": false}
  return newOKRes(m)
}

func failHandlerFn(vars apiVars, session *mgo.Session) *apiResponse{
  c := session.DB("ebisc").C("question_fail")
  queryParams := bson.M{};

  queryParams["date"] = vars["date"]
  if vars["cell_line"] != nil {
    queryParams["cellLine"] = vars["cell_line"]
  }
  if vars["batch"] != nil {
    queryParams["batch"] = vars["batch"]
  }
  if vars["module"] != nil {
    queryParams["module"] = vars["module"]
  }

  query := c.Find(queryParams);
  m := bson.M{}

  if n, err := query.Count(); err != nil {
    panic(newApiError(err, "Database error"))
  } else {
    m["total"] = n
  }

  query = query.Sort("_id").Limit(100).Select(bson.M{"_id": 0})

  if skip := vars["offset"]; skip != nil {
    skipInt := skip.(int)
    query = query.Skip(skipInt)
    m["pageOffset"] = skipInt
  } else {
    m["pageOffset"] = 0
  }

  if limit := vars["limit"]; limit != nil {
    limitInt := limit.(int)
    m["pageLimit"] = limitInt
    query = query.Limit(limitInt)
  } else {
    m["pageLimit"] = 100
    query = query.Limit(100)
  }

  var items []interface{}
  if err := query.All(&items); err != nil {
    panic(newApiError(err, "Database find error"))
  }
  m["items"] = items
  m["error"] = false
  return newOKRes(m)
}

func lineFailHandlerFn(vars apiVars, session *mgo.Session) *apiResponse{
  c := session.DB("ebisc").C("question_fail")
  m := bson.M{}
  var items []interface{}

  queryParams := bson.M{}
  queryParams["date"] = vars["date"]
  if module := vars["module"]; module != nil {
    queryParams["module"] = module
  }

  var skip, limit interface{}
  if skip = vars["offset"]; skip == nil {
    skip = 0
  }
  if limit = vars["limit"]; skip == nil {
    limit = 100
  }

  o1 := bson.M{"$match": queryParams}

  oDistinct2 := bson.M{"$group": bson.M{"_id": "$cellLine"}}
  oDistinct3 := bson.M{"$group": bson.M{"_id": nil, "count": bson.M{"$sum": 1}}}
  operationsDistinct := []bson.M{o1,oDistinct2, oDistinct3}
  pipeDistinct := c.Pipe(operationsDistinct)
  distinct := bson.M{}
  if err := pipeDistinct.One(&distinct); err != nil {
    if (err == mgo.ErrNotFound) {
      m["total"] = 0
    } else {
      panic(newApiError(err, "Database find error"))
    }
  } else {
    m["total"] = distinct["count"]

    o2 := bson.M{
      "$group": bson.M{"_id": "$cellLine",  "modules": bson.M{"$addToSet": "$module"}},
    }
    o3 := bson.M{
      "$project": bson.M{"cellLine": "$_id", "modules": 1, "count": bson.M{"$size": "$modules"}},
    }
    o4 := bson.M{
      "$sort": bson.D{{"count", 1}, {"cellLine", 1}},
    }
    o5 := bson.M{"$skip": skip}
    o6 := bson.M{"$limit": limit}

    o7 := bson.M{
      "$project": bson.M{"cellLine": 1, "modules": 1, "_id": 0},
    }

    operations := []bson.M{o1,o2,o3,o4,o5,o6,o7}
    pipe := c.Pipe(operations)

    if err := pipe.All(&items); err != nil {
      panic(newApiError(err, "Database find error"))
    }
  }

  m["items"] = items
  m["pageLimit"] = limit
  m["pageOffset"] = skip
  m["error"] = false
  return newOKRes(m)
}

<commit_msg>Fixed the routine for expanded details about a question<commit_after>package api

import (
    "gopkg.in/mgo.v2"
    "gopkg.in/mgo.v2/bson"
)
func testHandlerFn(vars apiVars, db *mgo.Session) *apiResponse{
  res := map[string]interface{}{
    "error": false,
    "text": "this is a test",
  }
  return newOKRes(res)
}

func examHandlerFn(vars apiVars, session *mgo.Session) *apiResponse{

  c := session.DB("ebisc").C("exam")
  m := make(bson.M)
  if vars["date"] != nil {
    if err := c.Find(bson.M{"date": vars["date"]}).One(&m); err != nil {
      if (err == mgo.ErrNotFound) {
        return newNotFoundRes()
      }
      panic(newApiError(err, "Database find error"))
    }
  } else {
    if err := c.Find(nil).Sort("-date").One(&m); err != nil {
      panic(newApiError(err, "Database find error"))
    }
  }

  if qSlice, ok := m["questions"].([]interface{}); ok {
    for _, q := range qSlice {
      if bsonQ, ok := q.(bson.M); !ok {
        panic(newApiError("type conversion of Question to a map", "Unexpected json structure"))
      } else {
        expandQuestion(bsonQ, session)
      }
    }
  }

  delete(m, "_id")
  m["error"] = false

  return newOKRes(m)
}

func expandQuestion(q bson.M, session *mgo.Session) {
  c := session.DB("ebisc").C("question_module")
  expandedQ := bson.M{}
  if err := c.Find(bson.M{"module": q["module"]}).Select(bson.M{"title": 1, "description": 1, "_id": 0}).One(expandedQ); err != nil {
    if (err == mgo.ErrNotFound) {
      return // Not an error
    }
    panic(newApiError(err, "Database find error"))
  }
  for key, val := range expandedQ {
    q[key] = val;
  }
}

func examListHandlerFn(vars apiVars, session *mgo.Session) *apiResponse{
  c := session.DB("ebisc").C("exam")
  var query bson.M = nil
  if vars["date"] != nil {
    query = bson.M{"date": bson.M{"$lt": vars["date"]}};
  }
  var items []interface{}
  if err := c.Find(query).Sort("-date").Limit(10).Select(bson.M{"date": 1, "_id": 0}).All(&items); err != nil {
    panic(newApiError(err, "Database find error"))
  }
  m := bson.M{"items": items, "error": false}
  return newOKRes(m)
}

func failHandlerFn(vars apiVars, session *mgo.Session) *apiResponse{
  c := session.DB("ebisc").C("question_fail")
  queryParams := bson.M{};

  queryParams["date"] = vars["date"]
  if vars["cell_line"] != nil {
    queryParams["cellLine"] = vars["cell_line"]
  }
  if vars["batch"] != nil {
    queryParams["batch"] = vars["batch"]
  }
  if vars["module"] != nil {
    queryParams["module"] = vars["module"]
  }

  query := c.Find(queryParams);
  m := bson.M{}

  if n, err := query.Count(); err != nil {
    panic(newApiError(err, "Database error"))
  } else {
    m["total"] = n
  }

  query = query.Sort("_id").Limit(100).Select(bson.M{"_id": 0})

  if skip := vars["offset"]; skip != nil {
    skipInt := skip.(int)
    query = query.Skip(skipInt)
    m["pageOffset"] = skipInt
  } else {
    m["pageOffset"] = 0
  }

  if limit := vars["limit"]; limit != nil {
    limitInt := limit.(int)
    m["pageLimit"] = limitInt
    query = query.Limit(limitInt)
  } else {
    m["pageLimit"] = 100
    query = query.Limit(100)
  }

  var items []interface{}
  if err := query.All(&items); err != nil {
    panic(newApiError(err, "Database find error"))
  }
  m["items"] = items
  m["error"] = false
  return newOKRes(m)
}

func lineFailHandlerFn(vars apiVars, session *mgo.Session) *apiResponse{
  c := session.DB("ebisc").C("question_fail")
  m := bson.M{}
  var items []interface{}

  queryParams := bson.M{}
  queryParams["date"] = vars["date"]
  if module := vars["module"]; module != nil {
    queryParams["module"] = module
  }

  var skip, limit interface{}
  if skip = vars["offset"]; skip == nil {
    skip = 0
  }
  if limit = vars["limit"]; skip == nil {
    limit = 100
  }

  o1 := bson.M{"$match": queryParams}

  oDistinct2 := bson.M{"$group": bson.M{"_id": "$cellLine"}}
  oDistinct3 := bson.M{"$group": bson.M{"_id": nil, "count": bson.M{"$sum": 1}}}
  operationsDistinct := []bson.M{o1,oDistinct2, oDistinct3}
  pipeDistinct := c.Pipe(operationsDistinct)
  distinct := bson.M{}
  if err := pipeDistinct.One(&distinct); err != nil {
    if (err == mgo.ErrNotFound) {
      m["total"] = 0
    } else {
      panic(newApiError(err, "Database find error"))
    }
  } else {
    m["total"] = distinct["count"]

    o2 := bson.M{
      "$group": bson.M{"_id": "$cellLine",  "modules": bson.M{"$addToSet": "$module"}},
    }
    o3 := bson.M{
      "$project": bson.M{"cellLine": "$_id", "modules": 1, "count": bson.M{"$size": "$modules"}},
    }
    o4 := bson.M{
      "$sort": bson.D{{"count", 1}, {"cellLine", 1}},
    }
    o5 := bson.M{"$skip": skip}
    o6 := bson.M{"$limit": limit}

    o7 := bson.M{
      "$project": bson.M{"cellLine": 1, "modules": 1, "_id": 0},
    }

    operations := []bson.M{o1,o2,o3,o4,o5,o6,o7}
    pipe := c.Pipe(operations)

    if err := pipe.All(&items); err != nil {
      panic(newApiError(err, "Database find error"))
    }
  }

  m["items"] = items
  m["pageLimit"] = limit
  m["pageOffset"] = skip
  m["error"] = false
  return newOKRes(m)
}

<|endoftext|>