<commit_before>package integration

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"time"

	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

func TestKismaticPlatform(t *testing.T) {
	if !testing.Short() {
		RegisterFailHandler(Fail)
		RunSpecs(t, "KismaticPlatform Suite")
	}
}

// Given that kismatic relies on local files, we create a temporary directory
// structure before running tests. The tarball for the kismatic build under test is copied
// to a known location. This location is then used to setup a working directory for each
// test, which will have a pristine copy of kismatic.
// This is what the temp directory structure looks like:
// - $TMP/kismatic-${randomString}
//    - current (contains the tarball for the kismatic build under test)
//    - tests (contains the working directory for each test)
//    - test-resources (contains the test resources that are defined in the suite)
//    - releases (contains subdirectories, one for each downloaded version of kismatic)
//

var kismaticTempDir string
var currentKismaticDir string
var testWorkingDirs string
var testResourcesDir string
var releasesDir string

var _ = BeforeSuite(func() {
	var err error
	kismaticTempDir, err = ioutil.TempDir("", "kismatic-")
	if err != nil {
		Fail(fmt.Sprintf("Failed to make temp dir: %v", err))
	}
	By(fmt.Sprintf("Created temp directory %s", kismaticTempDir))
	// Setup the directory structure
	currentKismaticDir = filepath.Join(kismaticTempDir, "current")
	if err = os.Mkdir(currentKismaticDir, 0700); err != nil {
		Fail(fmt.Sprintf("Failed to make temp dir: %v", err))
	}
	testWorkingDirs = filepath.Join(kismaticTempDir, "tests")
	if err = os.Mkdir(testWorkingDirs, 0700); err != nil {
		Fail(fmt.Sprintf("Failed to make temp dir: %v", err))
	}
	releasesDir = filepath.Join(kismaticTempDir, "releases")
	if err = os.Mkdir(releasesDir, 0700); err != nil {
		Fail(fmt.Sprintf("Failed to make temp dir: %v", err))
	}
	// Copy the current version of kismatic to known location
	cmd := exec.Command("cp", "../out/kismatic.tar.gz", currentKismaticDir)
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout
	if err = cmd.Run(); err != nil {
		Fail("Failed to copy kismatic tarball")
	}
	// Copy test resources to known location. This copy creates the dir.
	testResourcesDir = filepath.Join(kismaticTempDir, "test-resources")
	err = CopyDir("test-resources/", testResourcesDir)
	if err != nil {
		Fail("Failed to copy test resources")
	}
})

var _ = AfterSuite(func() {
	uploadTestLogs()
	if !leaveIt() {
		os.RemoveAll(kismaticTempDir)
	}
})

// sets up a working directory for a test by extracting the kismatic build under
// test to a temp directory. returns the path to the temp directory.
func setupTestWorkingDir() string {
	tmp, err := ioutil.TempDir(testWorkingDirs, "test-")
	if err != nil {
		Fail(fmt.Sprintf("failed to create temp dir: %v", err))
	}
	By("Test working directory is " + tmp)
	err = extractCurrentKismatic(tmp)
	if err != nil {
		Fail(fmt.Sprintf("failed to extract kismatic to %s: %v", tmp, err))
	}
	err = CopyDir(testResourcesDir, filepath.Join(tmp, "test-resources"))
	if err != nil {
		Fail(fmt.Sprintf("failed to copy test resources: %v", err))
	}
	return tmp
}

// sets up a working directory for a test that requires a specific version of kismatic.
// the version of kismatic is extracted into the temp directory.
// returns the path to the temp directory.
func setupTestWorkingDirWithVersion(version string) string {
	tmp, err := ioutil.TempDir(testWorkingDirs, "test-")
	if err != nil {
		Fail(fmt.Sprintf("failed to create temp dir: %v", err))
	}
	By("Test working directory is " + tmp)
	tarball, err := getKismaticReleaseTarball(version)
	if err != nil {
		Fail(fmt.Sprintf("failed to get kismatic tarball for version %s: %v", version, err))
	}
	if err = extractTarball(tarball, tmp); err != nil {
		Fail(fmt.Sprintf("failed to extract kismatic to %s: %v", tmp, err))
	}
	err = CopyDir(testResourcesDir, filepath.Join(tmp, "test-resources"))
	if err != nil {
		Fail(fmt.Sprintf("failed to copy test resources: %v", err))
	}
	return tmp
}

func extractTarball(src, dst string) error {
	return exec.Command("tar", "-zxf", src, "-C", dst).Run()
}

// extracts the current build of kismatic (the one being tested)
func extractCurrentKismatic(dest string) error {
	By(fmt.Sprintf("Extracting current kismatic to directory %q", dest))
	if err := extractTarball(filepath.Join(currentKismaticDir, "kismatic.tar.gz"), dest); err != nil {
		return fmt.Errorf("error extracting kismatic to %s: %v", dest, err)
	}
	return nil
}

// gets the given kismatic release tarball from the local filesystem if available.
// otherwise, it will attempt to download it from github.
func getKismaticReleaseTarball(version string) (string, error) {
	tarFile := filepath.Join(releasesDir, version, "kismatic.tar.gz")
	_, err := os.Stat(tarFile)
	if err == nil {
		// we have already downloaded this release
		return tarFile, nil
	}
	if os.IsNotExist(err) {
		// we haven't downloaded this release. download it.
		if err = os.MkdirAll(filepath.Dir(tarFile), 0700); err != nil {
			return "", fmt.Errorf("failed to create download directory: %v", err)
		}
		if err = downloadKismaticReleaseTarball(version, tarFile); err != nil {
			return "", fmt.Errorf("failed to download ket tarball: %v", err)
		}
		return tarFile, nil
	}
	// some other error ocurred
	return "", fmt.Errorf("failed to stat dir: %v", err)
}

// downloads the specified kismatic version and stores it as file
func downloadKismaticReleaseTarball(version string, file string) error {
	url := fmt.Sprintf("https://github.com/apprenda/kismatic/releases/download/%[1]s/kismatic-%[1]s-linux-amd64.tar.gz", version)
	if runtime.GOOS == "darwin" {
		url = fmt.Sprintf("https://github.com/apprenda/kismatic/releases/download/%[1]s/kismatic-%[1]s-darwin-amd64.tar.gz", version)
	}
	return exec.Command("wget", url, "-O", file).Run()
}

func uploadTestLogs() {
	tests, err := ioutil.ReadDir(testWorkingDirs)
	if err != nil {
		return
	}
	for _, t := range tests {
		uploadKismaticLogs(filepath.Join(testWorkingDirs, t.Name()))
	}
}

// Upload the kismatic package to S3
func uploadKismaticLogs(path string) {
	accessKeyID := os.Getenv("AWS_ACCESS_KEY_ID")
	secretAccessKey := os.Getenv("AWS_SECRET_ACCESS_KEY")
	if accessKeyID == "" || secretAccessKey == "" {
		return
	}
	creds := credentials.NewStaticCredentials(accessKeyID, secretAccessKey, "")
	_, err := creds.Get()
	if err != nil {
		fmt.Printf("bad credentials: %s", err)
	}
	cfg := aws.NewConfig().WithRegion("us-east-1").WithCredentials(creds)
	svc := s3.New(session.New(), cfg)

	// Tar the folder
	archiveName := "/tmp/kismatic-integration-"
	snapPullRequest := os.Getenv("SNAP_PULL_REQUEST_NUMBER")
	if snapPullRequest != "" {
		archiveName = archiveName + "pr-#" + snapPullRequest + "-"
	}
	snapPipelineCounter := os.Getenv("SNAP_PIPELINE_COUNTER")
	if snapPipelineCounter != "" {
		archiveName = archiveName + "build-#" + snapPipelineCounter + "-"
	}
	timestamp := time.Now().UTC().Format("2006-01-02T15:04:05-0700")
	archiveName = archiveName + timestamp + ".tar.gz"

	// upload runs directory
	if _, err := os.Stat(filepath.Join(path, "runs")); os.IsNotExist(err) {
		fmt.Printf("Runs directory not found in %s. Skipping upload of logs to S3.\n", path)
		return
	}
	cmd := exec.Command("tar", "czf", archiveName, filepath.Join(path, "runs"))
	// include the diagnostics directory if exists
	if _, err := os.Stat(filepath.Join(path, "diagnostics")); err == nil {
		fmt.Println("Including diagnostics directory")
		cmd.Args = append(cmd.Args, filepath.Join(path, "diagnostics"))
	}
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err = cmd.Run(); err != nil {
		fmt.Printf("failed to tar artifacts")
		return
	}

	file, err := os.Open(archiveName)
	if err != nil {
		fmt.Printf("err opening file: %s", err)
	}
	defer file.Close()
	fileInfo, _ := file.Stat()
	size := fileInfo.Size()
	buffer := make([]byte, size) // read file content to buffer

	file.Read(buffer)
	fileBytes := bytes.NewReader(buffer)
	fileType := http.DetectContentType(buffer)
	s3path := "/logs/"
	if snapPullRequest != "" {
		s3path = s3path + "pull-requests/" + snapPullRequest + "/"
	}
	if snapPipelineCounter != "" {
		s3path = s3path + snapPipelineCounter + "/"
	}
	s3path = s3path + fileInfo.Name()

	s3bucket := "kismatic-integration-tests"
	downloadPath := fmt.Sprintf("https://console.aws.amazon.com/s3/buckets/%s%s", s3bucket, s3path)
	fmt.Println("Uploading logs to S3. Get them at", downloadPath)
	params := &s3.PutObjectInput{
		Bucket:        aws.String(s3bucket),
		Key:           aws.String(s3path),
		Body:          fileBytes,
		ContentLength: aws.Int64(size),
		ContentType:   aws.String(fileType),
	}
	_, err = svc.PutObject(params)
	if err != nil {
		fmt.Printf("Error uploading logs to S3: %v", err)
	}
}
<commit_msg>Fix integration tests timing out<commit_after>package integration

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

func TestKismaticPlatform(t *testing.T) {
	if !testing.Short() {
		RegisterFailHandler(Fail)
		RunSpecs(t, "KismaticPlatform Suite")
	}
}

// Given that kismatic relies on local files, we create a temporary directory
// structure before running tests. The tarball for the kismatic build under test is copied
// to a known location. This location is then used to setup a working directory for each
// test, which will have a pristine copy of kismatic.
// This is what the temp directory structure looks like:
// - $TMP/kismatic-${randomString}
//    - current (contains the tarball for the kismatic build under test)
//    - tests (contains the working directory for each test)
//    - test-resources (contains the test resources that are defined in the suite)
//    - releases (contains subdirectories, one for each downloaded version of kismatic)
//

var kismaticTempDir string
var currentKismaticDir string
var testWorkingDirs string
var testResourcesDir string
var releasesDir string

var _ = BeforeSuite(func() {
	var err error
	kismaticTempDir, err = ioutil.TempDir("", "kismatic-")
	if err != nil {
		Fail(fmt.Sprintf("Failed to make temp dir: %v", err))
	}
	By(fmt.Sprintf("Created temp directory %s", kismaticTempDir))
	// Setup the directory structure
	currentKismaticDir = filepath.Join(kismaticTempDir, "current")
	if err = os.Mkdir(currentKismaticDir, 0700); err != nil {
		Fail(fmt.Sprintf("Failed to make temp dir: %v", err))
	}
	testWorkingDirs = filepath.Join(kismaticTempDir, "tests")
	if err = os.Mkdir(testWorkingDirs, 0700); err != nil {
		Fail(fmt.Sprintf("Failed to make temp dir: %v", err))
	}
	releasesDir = filepath.Join(kismaticTempDir, "releases")
	if err = os.Mkdir(releasesDir, 0700); err != nil {
		Fail(fmt.Sprintf("Failed to make temp dir: %v", err))
	}
	// Copy the current version of kismatic to known location
	cmd := exec.Command("cp", "../out/kismatic.tar.gz", currentKismaticDir)
	cmd.Stderr = os.Stderr
	cmd.Stdout = os.Stdout
	if err = cmd.Run(); err != nil {
		Fail("Failed to copy kismatic tarball")
	}
	// Copy test resources to known location. This copy creates the dir.
	testResourcesDir = filepath.Join(kismaticTempDir, "test-resources")
	err = CopyDir("test-resources/", testResourcesDir)
	if err != nil {
		Fail("Failed to copy test resources")
	}
})

var _ = AfterSuite(func() {
	uploadTestLogs()
	if !leaveIt() {
		os.RemoveAll(kismaticTempDir)
	}
})

// sets up a working directory for a test by extracting the kismatic build under
// test to a temp directory. returns the path to the temp directory.
func setupTestWorkingDir() string {
	tmp, err := ioutil.TempDir(testWorkingDirs, "test-")
	if err != nil {
		Fail(fmt.Sprintf("failed to create temp dir: %v", err))
	}
	By("Test working directory is " + tmp)
	err = extractCurrentKismatic(tmp)
	if err != nil {
		Fail(fmt.Sprintf("failed to extract kismatic to %s: %v", tmp, err))
	}
	err = CopyDir(testResourcesDir, filepath.Join(tmp, "test-resources"))
	if err != nil {
		Fail(fmt.Sprintf("failed to copy test resources: %v", err))
	}
	return tmp
}

// sets up a working directory for a test that requires a specific version of kismatic.
// the version of kismatic is extracted into the temp directory.
// returns the path to the temp directory.
func setupTestWorkingDirWithVersion(version string) string {
	tmp, err := ioutil.TempDir(testWorkingDirs, "test-")
	if err != nil {
		Fail(fmt.Sprintf("failed to create temp dir: %v", err))
	}
	By("Test working directory is " + tmp)
	tarball, err := getKismaticReleaseTarball(version)
	if err != nil {
		Fail(fmt.Sprintf("failed to get kismatic tarball for version %s: %v", version, err))
	}
	if err = extractTarball(tarball, tmp); err != nil {
		Fail(fmt.Sprintf("failed to extract kismatic to %s: %v", tmp, err))
	}
	err = CopyDir(testResourcesDir, filepath.Join(tmp, "test-resources"))
	if err != nil {
		Fail(fmt.Sprintf("failed to copy test resources: %v", err))
	}
	return tmp
}

func extractTarball(src, dst string) error {
	return exec.Command("tar", "-zxf", src, "-C", dst).Run()
}

// extracts the current build of kismatic (the one being tested)
func extractCurrentKismatic(dest string) error {
	By(fmt.Sprintf("Extracting current kismatic to directory %q", dest))
	if err := extractTarball(filepath.Join(currentKismaticDir, "kismatic.tar.gz"), dest); err != nil {
		return fmt.Errorf("error extracting kismatic to %s: %v", dest, err)
	}
	return nil
}

// gets the given kismatic release tarball from the local filesystem if available.
// otherwise, it will attempt to download it from github.
func getKismaticReleaseTarball(version string) (string, error) {
	tarFile := filepath.Join(releasesDir, version, "kismatic.tar.gz")
	_, err := os.Stat(tarFile)
	if err == nil {
		// we have already downloaded this release
		return tarFile, nil
	}
	if os.IsNotExist(err) {
		// we haven't downloaded this release. download it.
		if err = os.MkdirAll(filepath.Dir(tarFile), 0700); err != nil {
			return "", fmt.Errorf("failed to create download directory: %v", err)
		}
		if err = downloadKismaticReleaseTarball(version, tarFile); err != nil {
			return "", fmt.Errorf("failed to download ket tarball: %v", err)
		}
		return tarFile, nil
	}
	// some other error ocurred
	return "", fmt.Errorf("failed to stat dir: %v", err)
}

// downloads the specified kismatic version and stores it as file
func downloadKismaticReleaseTarball(version string, file string) error {
	url := fmt.Sprintf("https://github.com/apprenda/kismatic/releases/download/%[1]s/kismatic-%[1]s-linux-amd64.tar.gz", version)
	if runtime.GOOS == "darwin" {
		url = fmt.Sprintf("https://github.com/apprenda/kismatic/releases/download/%[1]s/kismatic-%[1]s-darwin-amd64.tar.gz", version)
	}
	return exec.Command("wget", url, "-O", file).Run()
}

func uploadTestLogs() {
	tests, err := ioutil.ReadDir(testWorkingDirs)
	if err != nil {
		return
	}
	for _, t := range tests {
		if strings.Contains(t.Name(), "test-") {
			uploadKismaticLogs(filepath.Join(testWorkingDirs, t.Name()))
		}
	}
}

// Upload the kismatic package to S3
func uploadKismaticLogs(path string) {
	accessKeyID := os.Getenv("AWS_ACCESS_KEY_ID")
	secretAccessKey := os.Getenv("AWS_SECRET_ACCESS_KEY")
	if accessKeyID == "" || secretAccessKey == "" {
		return
	}
	creds := credentials.NewStaticCredentials(accessKeyID, secretAccessKey, "")
	_, err := creds.Get()
	if err != nil {
		fmt.Printf("bad credentials: %s", err)
	}
	cfg := aws.NewConfig().WithRegion("us-east-1").WithCredentials(creds)
	svc := s3.New(session.New(), cfg)

	// Tar the folder
	archiveName := "/tmp/kismatic-integration-"
	snapPullRequest := os.Getenv("SNAP_PULL_REQUEST_NUMBER")
	if snapPullRequest != "" {
		archiveName = archiveName + "pr-#" + snapPullRequest + "-"
	}
	snapPipelineCounter := os.Getenv("SNAP_PIPELINE_COUNTER")
	if snapPipelineCounter != "" {
		archiveName = archiveName + "build-#" + snapPipelineCounter + "-"
	}
	timestamp := time.Now().UTC().Format("2006-01-02T15:04:05-0700")
	archiveName = archiveName + timestamp + ".tar.gz"

	// upload runs directory
	if _, err := os.Stat(filepath.Join(path, "runs")); os.IsNotExist(err) {
		fmt.Printf("Runs directory not found in %s. Skipping upload of logs to S3.\n", path)
		return
	}
	cmd := exec.Command("tar", "czf", archiveName, filepath.Join(path, "runs"))
	// include the diagnostics directory if exists
	if _, err := os.Stat(filepath.Join(path, "diagnostics")); err == nil {
		fmt.Println("Including diagnostics directory")
		cmd.Args = append(cmd.Args, filepath.Join(path, "diagnostics"))
	}
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		fmt.Println("failed to tar artifacts")
		return
	}

	file, err := os.Open(archiveName)
	if err != nil {
		fmt.Printf("err opening file: %s", err)
	}
	defer file.Close()
	fileInfo, _ := file.Stat()
	size := fileInfo.Size()
	buffer := make([]byte, size) // read file content to buffer

	file.Read(buffer)
	fileBytes := bytes.NewReader(buffer)
	fileType := http.DetectContentType(buffer)
	s3path := "/logs/"
	if snapPullRequest != "" {
		s3path = s3path + "pull-requests/" + snapPullRequest + "/"
	}
	if snapPipelineCounter != "" {
		s3path = s3path + snapPipelineCounter + "/"
	}
	s3path = s3path + fileInfo.Name()

	s3bucket := "kismatic-integration-tests"
	downloadPath := fmt.Sprintf("https://console.aws.amazon.com/s3/buckets/%s%s", s3bucket, s3path)
	fmt.Println("Uploading logs to S3. Get them at", downloadPath)
	params := &s3.PutObjectInput{
		Bucket:        aws.String(s3bucket),
		Key:           aws.String(s3path),
		Body:          fileBytes,
		ContentLength: aws.Int64(size),
		ContentType:   aws.String(fileType),
	}
	_, err = svc.PutObject(params)
	if err != nil {
		fmt.Printf("Error uploading logs to S3: %v", err)
	}
}
<|endoftext|>


<commit_before>package sql

import (
	"database/sql"
	"fmt"
)

func (c *conn) migrate() (int, error) {
	_, err := c.Exec(`
		create table if not exists migrations (
			num integer not null,
			at timestamptz not null
		);
	`)
	if err != nil {
		return 0, fmt.Errorf("creating migration table: %v", err)
	}

	i := 0
	done := false

	var flavorMigrations []migration
	for _, m := range migrations {
		if m.flavor == nil || m.flavor == c.flavor {
			flavorMigrations = append(flavorMigrations, m)
		}
	}

	for {
		err := c.ExecTx(func(tx *trans) error {
			// Within a transaction, perform a single migration.
			var (
				num sql.NullInt64
				n   int
			)
			if err := tx.QueryRow(`select max(num) from migrations;`).Scan(&num); err != nil {
				return fmt.Errorf("select max migration: %v", err)
			}
			if num.Valid {
				n = int(num.Int64)
			}
			if n >= len(flavorMigrations) {
				done = true
				return nil
			}

			migrationNum := n + 1
			m := flavorMigrations[n]
			for i := range m.stmts {
				if _, err := tx.Exec(m.stmts[i]); err != nil {
					return fmt.Errorf("migration %d statement %d failed: %v", migrationNum, i+1, err)
				}
			}

			q := `insert into migrations (num, at) values ($1, now());`
			if _, err := tx.Exec(q, migrationNum); err != nil {
				return fmt.Errorf("update migration table: %v", err)
			}
			return nil
		})
		if err != nil {
			return i, err
		}
		if done {
			break
		}
		i++
	}

	return i, nil
}

type migration struct {
	stmts []string

	// If flavor is nil the migration will take place for all database backend flavors.
	// If specified, only for that corresponding flavor, in that case stmts can be written
	// in the specific SQL dialect.
	flavor *flavor
}

// All SQL flavors share migration strategies.
var migrations = []migration{
	{
		stmts: []string{`
			create table client (
				id text not null primary key,
				secret text not null,
				redirect_uris bytea not null, -- JSON array of strings
				trusted_peers bytea not null, -- JSON array of strings
				public boolean not null,
				name text not null,
				logo_url text not null
			);`,
			`
			create table auth_request (
				id text not null primary key,
				client_id text not null,
				response_types bytea not null, -- JSON array of strings
				scopes bytea not null,         -- JSON array of strings
				redirect_uri text not null,
				nonce text not null,
				state text not null,
				force_approval_prompt boolean not null,

				logged_in boolean not null,

				claims_user_id text not null,
				claims_username text not null,
				claims_email text not null,
				claims_email_verified boolean not null,
				claims_groups bytea not null, -- JSON array of strings

				connector_id text not null,
				connector_data bytea,

				expiry timestamptz not null
			);`,
			`
			create table auth_code (
				id text not null primary key,
				client_id text not null,
				scopes bytea not null, -- JSON array of strings
				nonce text not null,
				redirect_uri text not null,

				claims_user_id text not null,
				claims_username text not null,
				claims_email text not null,
				claims_email_verified boolean not null,
				claims_groups bytea not null, -- JSON array of strings

				connector_id text not null,
				connector_data bytea,

				expiry timestamptz not null
			);`,
			`
			create table refresh_token (
				id text not null primary key,
				client_id text not null,
				scopes bytea not null, -- JSON array of strings
				nonce text not null,

				claims_user_id text not null,
				claims_username text not null,
				claims_email text not null,
				claims_email_verified boolean not null,
				claims_groups bytea not null, -- JSON array of strings

				connector_id text not null,
				connector_data bytea
			);`,
			`
			create table password (
				email text not null primary key,
				hash bytea not null,
				username text not null,
				user_id text not null
			);`,
			`
			-- keys is a weird table because we only ever expect there to be a single row
			create table keys (
				id text not null primary key,
				verification_keys bytea not null, -- JSON array
				signing_key bytea not null,       -- JSON object
				signing_key_pub bytea not null,   -- JSON object
				next_rotation timestamptz not null
			);`,
		},
	},
	{
		stmts: []string{`
			alter table refresh_token
				add column token text not null default '';`,
			`
			alter table refresh_token
				add column created_at timestamptz not null default '0001-01-01 00:00:00 UTC';`,
			`
			alter table refresh_token
				add column last_used timestamptz not null default '0001-01-01 00:00:00 UTC';`,
		},
	},
	{
		stmts: []string{`
			create table offline_session (
				user_id text not null,
				conn_id text not null,
				refresh bytea not null,
				PRIMARY KEY (user_id, conn_id)
			);`,
		},
	},
	{
		stmts: []string{`
			create table connector (
				id text not null primary key,
				type text not null,
				name text not null,
				resource_version text not null,
				config bytea
			);`,
		},
	},
	{
		stmts: []string{`
			alter table auth_code
				add column claims_preferred_username text not null default '';`,
			`
			alter table auth_request
				add column claims_preferred_username text not null default '';`,
			`
			alter table refresh_token
				add column claims_preferred_username text not null default '';`,
		},
	},
	{
		stmts: []string{`
			alter table offline_session
				add column connector_data bytea;
			`,
		},
	},
}
<commit_msg>storage/mysql: increase auth_request.state length to 4096<commit_after>package sql

import (
	"database/sql"
	"fmt"
)

func (c *conn) migrate() (int, error) {
	_, err := c.Exec(`
		create table if not exists migrations (
			num integer not null,
			at timestamptz not null
		);
	`)
	if err != nil {
		return 0, fmt.Errorf("creating migration table: %v", err)
	}

	i := 0
	done := false

	var flavorMigrations []migration
	for _, m := range migrations {
		if m.flavor == nil || m.flavor == c.flavor {
			flavorMigrations = append(flavorMigrations, m)
		}
	}

	for {
		err := c.ExecTx(func(tx *trans) error {
			// Within a transaction, perform a single migration.
			var (
				num sql.NullInt64
				n   int
			)
			if err := tx.QueryRow(`select max(num) from migrations;`).Scan(&num); err != nil {
				return fmt.Errorf("select max migration: %v", err)
			}
			if num.Valid {
				n = int(num.Int64)
			}
			if n >= len(flavorMigrations) {
				done = true
				return nil
			}

			migrationNum := n + 1
			m := flavorMigrations[n]
			for i := range m.stmts {
				if _, err := tx.Exec(m.stmts[i]); err != nil {
					return fmt.Errorf("migration %d statement %d failed: %v", migrationNum, i+1, err)
				}
			}

			q := `insert into migrations (num, at) values ($1, now());`
			if _, err := tx.Exec(q, migrationNum); err != nil {
				return fmt.Errorf("update migration table: %v", err)
			}
			return nil
		})
		if err != nil {
			return i, err
		}
		if done {
			break
		}
		i++
	}

	return i, nil
}

type migration struct {
	stmts []string

	// If flavor is nil the migration will take place for all database backend flavors.
	// If specified, only for that corresponding flavor, in that case stmts can be written
	// in the specific SQL dialect.
	flavor *flavor
}

// All SQL flavors share migration strategies.
var migrations = []migration{
	{
		stmts: []string{`
			create table client (
				id text not null primary key,
				secret text not null,
				redirect_uris bytea not null, -- JSON array of strings
				trusted_peers bytea not null, -- JSON array of strings
				public boolean not null,
				name text not null,
				logo_url text not null
			);`,
			`
			create table auth_request (
				id text not null primary key,
				client_id text not null,
				response_types bytea not null, -- JSON array of strings
				scopes bytea not null,         -- JSON array of strings
				redirect_uri text not null,
				nonce text not null,
				state text not null,
				force_approval_prompt boolean not null,

				logged_in boolean not null,

				claims_user_id text not null,
				claims_username text not null,
				claims_email text not null,
				claims_email_verified boolean not null,
				claims_groups bytea not null, -- JSON array of strings

				connector_id text not null,
				connector_data bytea,

				expiry timestamptz not null
			);`,
			`
			create table auth_code (
				id text not null primary key,
				client_id text not null,
				scopes bytea not null, -- JSON array of strings
				nonce text not null,
				redirect_uri text not null,

				claims_user_id text not null,
				claims_username text not null,
				claims_email text not null,
				claims_email_verified boolean not null,
				claims_groups bytea not null, -- JSON array of strings

				connector_id text not null,
				connector_data bytea,

				expiry timestamptz not null
			);`,
			`
			create table refresh_token (
				id text not null primary key,
				client_id text not null,
				scopes bytea not null, -- JSON array of strings
				nonce text not null,

				claims_user_id text not null,
				claims_username text not null,
				claims_email text not null,
				claims_email_verified boolean not null,
				claims_groups bytea not null, -- JSON array of strings

				connector_id text not null,
				connector_data bytea
			);`,
			`
			create table password (
				email text not null primary key,
				hash bytea not null,
				username text not null,
				user_id text not null
			);`,
			`
			-- keys is a weird table because we only ever expect there to be a single row
			create table keys (
				id text not null primary key,
				verification_keys bytea not null, -- JSON array
				signing_key bytea not null,       -- JSON object
				signing_key_pub bytea not null,   -- JSON object
				next_rotation timestamptz not null
			);`,
		},
	},
	{
		stmts: []string{`
			alter table refresh_token
				add column token text not null default '';`,
			`
			alter table refresh_token
				add column created_at timestamptz not null default '0001-01-01 00:00:00 UTC';`,
			`
			alter table refresh_token
				add column last_used timestamptz not null default '0001-01-01 00:00:00 UTC';`,
		},
	},
	{
		stmts: []string{`
			create table offline_session (
				user_id text not null,
				conn_id text not null,
				refresh bytea not null,
				PRIMARY KEY (user_id, conn_id)
			);`,
		},
	},
	{
		stmts: []string{`
			create table connector (
				id text not null primary key,
				type text not null,
				name text not null,
				resource_version text not null,
				config bytea
			);`,
		},
	},
	{
		stmts: []string{`
			alter table auth_code
				add column claims_preferred_username text not null default '';`,
			`
			alter table auth_request
				add column claims_preferred_username text not null default '';`,
			`
			alter table refresh_token
				add column claims_preferred_username text not null default '';`,
		},
	},
	{
		stmts: []string{`
			alter table offline_session
				add column connector_data bytea;
			`,
		},
	},
	{
		stmts: []string{`
			alter table auth_request
				modify column state varchar(4096);
			`,
		},
		flavor: &flavorMySQL,
	},
}
<|endoftext|>


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

package storage

import (
	"crypto/sha256"
	"encoding/base64"
	"fmt"
	"net/http"
	"reflect"
	"testing"

	"golang.org/x/net/context"

	"google.golang.org/api/option"
)

type fakeTransport struct {
	gotReq *http.Request
}

func (t *fakeTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	t.gotReq = req
	return nil, fmt.Errorf("error handling request")
}

func TestErrorOnObjectsInsertCall(t *testing.T) {
	ctx := context.Background()
	hc := &http.Client{Transport: &fakeTransport{}}
	client, err := NewClient(ctx, option.WithHTTPClient(hc))
	if err != nil {
		t.Fatalf("error when creating client: %v", err)
	}
	wc := client.Bucket("bucketname").Object("filename1").NewWriter(ctx)
	wc.ContentType = "text/plain"

	// We can't check that the Write fails, since it depends on the write to the
	// underling fakeTransport failing which is racy.
	wc.Write([]byte("hello world"))

	// Close must always return an error though since it waits for the transport to
	// have closed.
	if err := wc.Close(); err == nil {
		t.Errorf("expected error on close, got nil")
	}
}

func TestEncryption(t *testing.T) {
	ctx := context.Background()
	ft := &fakeTransport{}
	hc := &http.Client{Transport: ft}
	client, err := NewClient(ctx, option.WithHTTPClient(hc))
	if err != nil {
		t.Fatalf("error when creating client: %v", err)
	}
	obj := client.Bucket("bucketname").Object("filename1")
	key := []byte("secret-key")
	wc := obj.Key(key).NewWriter(ctx)
	wc.Write([]byte("hello world"))
	wc.Close()
	if got, want := ft.gotReq.Header.Get("x-goog-encryption-algorithm"), "AES256"; got != want {
		t.Errorf("algorithm: got %q, want %q", got, want)
	}
	gotKey, err := base64.StdEncoding.DecodeString(ft.gotReq.Header.Get("x-goog-encryption-key"))
	if err != nil {
		t.Fatalf("decoding key: %v", err)
	}
	if !reflect.DeepEqual(gotKey, key) {
		t.Errorf("key: got %v, want %v", gotKey, key)
	}
	wantHash := sha256.Sum256(key)
	gotHash, err := base64.StdEncoding.DecodeString(ft.gotReq.Header.Get("x-goog-encryption-key-sha256"))
	if err != nil {
		t.Fatalf("decoding hash: %v", err)
	}
	if !reflect.DeepEqual(gotHash, wantHash[:]) { // wantHash is an array
		t.Errorf("hash: got\n%v, want\n%v", gotHash, wantHash)
	}
}
<commit_msg>storage: fix encryption test<commit_after>// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package storage

import (
	"crypto/sha256"
	"encoding/base64"
	"fmt"
	"net/http"
	"reflect"
	"testing"

	"golang.org/x/net/context"

	"google.golang.org/api/option"
)

type fakeTransport struct {
	gotReq *http.Request
}

func (t *fakeTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	t.gotReq = req
	return nil, fmt.Errorf("error handling request")
}

func TestErrorOnObjectsInsertCall(t *testing.T) {
	ctx := context.Background()
	hc := &http.Client{Transport: &fakeTransport{}}
	client, err := NewClient(ctx, option.WithHTTPClient(hc))
	if err != nil {
		t.Fatalf("error when creating client: %v", err)
	}
	wc := client.Bucket("bucketname").Object("filename1").NewWriter(ctx)
	wc.ContentType = "text/plain"

	// We can't check that the Write fails, since it depends on the write to the
	// underling fakeTransport failing which is racy.
	wc.Write([]byte("hello world"))

	// Close must always return an error though since it waits for the transport to
	// have closed.
	if err := wc.Close(); err == nil {
		t.Errorf("expected error on close, got nil")
	}
}

func TestEncryption(t *testing.T) {
	ctx := context.Background()
	ft := &fakeTransport{}
	hc := &http.Client{Transport: ft}
	client, err := NewClient(ctx, option.WithHTTPClient(hc))
	if err != nil {
		t.Fatalf("error when creating client: %v", err)
	}
	obj := client.Bucket("bucketname").Object("filename1")
	key := []byte("secret-key-that-is-32-bytes-long")
	wc := obj.Key(key).NewWriter(ctx)
	// TODO(jba): use something other than fakeTransport, which always returns error.
	wc.Write([]byte("hello world"))
	wc.Close()
	if got, want := ft.gotReq.Header.Get("x-goog-encryption-algorithm"), "AES256"; got != want {
		t.Errorf("algorithm: got %q, want %q", got, want)
	}
	gotKey, err := base64.StdEncoding.DecodeString(ft.gotReq.Header.Get("x-goog-encryption-key"))
	if err != nil {
		t.Fatalf("decoding key: %v", err)
	}
	if !reflect.DeepEqual(gotKey, key) {
		t.Errorf("key: got %v, want %v", gotKey, key)
	}
	wantHash := sha256.Sum256(key)
	gotHash, err := base64.StdEncoding.DecodeString(ft.gotReq.Header.Get("x-goog-encryption-key-sha256"))
	if err != nil {
		t.Fatalf("decoding hash: %v", err)
	}
	if !reflect.DeepEqual(gotHash, wantHash[:]) { // wantHash is an array
		t.Errorf("hash: got\n%v, want\n%v", gotHash, wantHash)
	}
}
<|endoftext|>


<commit_before>package namedParameterQuery

import (
	"testing"
)

/*
	Represents a single test of query parsing.
	Given an [Input] query, if the actual result of parsing
	does not match the [Expected] string, the test fails
*/
type QueryParsingTest struct {
	Name string
	Input string
	Expected string
	ExpectedParameters int
}

/*
	Represents a single test of parameter parsing.
	Given the [Query] and a set of [Parameters], if the actual parameter output
	from GetParsedParameters() matches the given [ExpectedParameters].
	These tests specifically check type of output parameters, too.
*/
type ParameterParsingTest struct {

	Name string
	Query string
	Parameters []TestQueryParameter
	ExpectedParameters []interface{}
}

type TestQueryParameter struct {
	Name string
	Value interface{}
}

func TestQueryParsing(test *testing.T) {

	var query *NamedParameterQuery

	// Each of these represents a single test.
	queryParsingTests := []QueryParsingTest {
		QueryParsingTest {
			Input: "SELECT * FROM table WHERE col1 = 1",
			Expected: "SELECT * FROM table WHERE col1 = 1",
			Name: "NoParameter",
		},
		QueryParsingTest {
			Input: "SELECT * FROM table WHERE col1 = :name",
			Expected: "SELECT * FROM table WHERE col1 = ?",
			ExpectedParameters: 1,
			Name: "SingleParameter",
		},
		QueryParsingTest {
			Input: "SELECT * FROM table WHERE col1 = :name AND col2 = :occupation",
			Expected: "SELECT * FROM table WHERE col1 = ? AND col2 = ?",
			ExpectedParameters: 2,
			Name: "TwoParameters",
		},
		QueryParsingTest {
			Input: "SELECT * FROM table WHERE col1 = :name AND col2 = :occupation",
			Expected: "SELECT * FROM table WHERE col1 = ? AND col2 = ?",
			ExpectedParameters: 2,
			Name: "OneParameterMultipleTimes",
		},
		QueryParsingTest {
			Input: "SELECT * FROM table WHERE col1 IN (:something, :else)",
			Expected: "SELECT * FROM table WHERE col1 IN (?, ?)",
			ExpectedParameters: 2,
			Name: "ParametersInParenthesis",
		},
		QueryParsingTest {
			Input: "SELECT * FROM table WHERE col1 = ':literal' AND col2 LIKE ':literal'",
			Expected: "SELECT * FROM table WHERE col1 = ':literal' AND col2 LIKE ':literal'",
			Name: "ParametersInQuotes",
		},
		QueryParsingTest {
			Input: "SELECT * FROM table WHERE col1 = ':literal' AND col2 = :literal AND col3 LIKE ':literal'",
			Expected: "SELECT * FROM table WHERE col1 = ':literal' AND col2 = ? AND col3 LIKE ':literal'",
			ExpectedParameters: 1,
			Name: "ParametersInQuotes2",
		},
		QueryParsingTest {
			Input: "SELECT * FROM table WHERE col1 = :foo AND col2 IN (SELECT id FROM tabl2 WHERE col10 = :bar)",
			Expected: "SELECT * FROM table WHERE col1 = ? AND col2 IN (SELECT id FROM tabl2 WHERE col10 = ?)",
			ExpectedParameters: 2,
			Name: "ParametersInSubclause",
		},
	}

	// Run each test.
	for _, parsingTest := range queryParsingTests {

		query = NewNamedParameterQuery(parsingTest.Input)

		// test query texts
		if(query.GetParsedQuery() != parsingTest.Expected) {
			test.Log("Test '", parsingTest.Name, "': Expected query text did not match actual parsed output")
			test.Log("Actual: ", query.GetParsedQuery())
			test.Fail()
		}

		// test parameters
		if(len(query.GetParsedParameters()) != parsingTest.ExpectedParameters) {
			test.Log("Test '", parsingTest.Name, "': Expected parameters did not match actual parsed parameter count")
			test.Fail()
		}
	}
}

/*
	Tests to ensure that setting parameter values turns out correct when using GetParsedParameters().
	These tests ensure correct positioning and type.
*/
func TestParameterReplacement(test *testing.T) {

	var query *NamedParameterQuery

	queryVariableTests := []ParameterParsingTest {
		ParameterParsingTest {

			Name: "SingleStringParameter",
			Query: "SELECT * FROM table WHERE col1 = :foo",
			Parameters: []TestQueryParameter {
				TestQueryParameter {
					Name: "foo",
					Value: "bar",
				},
			},
			ExpectedParameters: []interface{} {
				"bar",
			},
		},
		ParameterParsingTest {

			Name: "TwoStringParameter",
			Query: "SELECT * FROM table WHERE col1 = :foo AND col2 = :foo2",
			Parameters: []TestQueryParameter {
				TestQueryParameter {
					Name: "foo",
					Value: "bar",
				},
				TestQueryParameter {
					Name: "foo2",
					Value: "bart",
				},
			},
			ExpectedParameters: []interface{} {
				"bar", "bart",
			},
		},
		ParameterParsingTest {

			Name: "TwiceOccurringParameter",
			Query: "SELECT * FROM table WHERE col1 = :foo AND col2 = :foo",
			Parameters: []TestQueryParameter {
				TestQueryParameter {
					Name: "foo",
					Value: "bar",
				},
			},
			ExpectedParameters: []interface{} {
				"bar", "bar",
			},
		},
		ParameterParsingTest {

			Name: "ParameterTyping",
			Query: "SELECT * FROM table WHERE col1 = :str AND col2 = :int AND col3 = :pi",
			Parameters: []TestQueryParameter {
				TestQueryParameter {
					Name: "str",
					Value: "foo",
				},
				TestQueryParameter {
					Name: "int",
					Value: 1,
				},
				TestQueryParameter {
					Name: "pi",
					Value: 3.14,
				},
			},
			ExpectedParameters: []interface{} {
				"foo", 1, 3.14,
			},
		},
		ParameterParsingTest {

			Name: "ParameterOrdering",
			Query: "SELECT * FROM table WHERE col1 = :foo AND col2 = :bar AND col3 = :foo AND col4 = :foo AND col5 = :bar",
			Parameters: []TestQueryParameter {
				TestQueryParameter {
					Name: "foo",
					Value: "something",
				},
				TestQueryParameter {
					Name: "bar",
					Value: "else",
				},
			},
			ExpectedParameters: []interface{} {
				"something", "else", "something", "something", "else",
			},
		},
	}

	// run variable tests.
	for _, variableTest := range queryVariableTests {

		// parse query and set values.
		query = NewNamedParameterQuery(variableTest.Query)
		for _, queryVariable := range variableTest.Parameters {
			query.SetValue(queryVariable.Name, queryVariable.Value)
		}

		// Test outputs
		for index, queryVariable := range query.GetParsedParameters() {

			if(queryVariable != variableTest.ExpectedParameters[index]) {
				test.Log("Test '", variableTest.Name, "' did not produce the expected parameter output. Actual: '", queryVariable, "', Expected: '", variableTest.ExpectedParameters[index], "'")
				test.Fail()
			}
		}
	}
}
<commit_msg>Added tests for parameter case sensitivity and alphanumericism<commit_after>package namedParameterQuery

import (
	"testing"
)

/*
	Represents a single test of query parsing.
	Given an [Input] query, if the actual result of parsing
	does not match the [Expected] string, the test fails
*/
type QueryParsingTest struct {
	Name string
	Input string
	Expected string
	ExpectedParameters int
}

/*
	Represents a single test of parameter parsing.
	Given the [Query] and a set of [Parameters], if the actual parameter output
	from GetParsedParameters() matches the given [ExpectedParameters].
	These tests specifically check type of output parameters, too.
*/
type ParameterParsingTest struct {

	Name string
	Query string
	Parameters []TestQueryParameter
	ExpectedParameters []interface{}
}

type TestQueryParameter struct {
	Name string
	Value interface{}
}

func TestQueryParsing(test *testing.T) {

	var query *NamedParameterQuery

	// Each of these represents a single test.
	queryParsingTests := []QueryParsingTest {
		QueryParsingTest {
			Input: "SELECT * FROM table WHERE col1 = 1",
			Expected: "SELECT * FROM table WHERE col1 = 1",
			Name: "NoParameter",
		},
		QueryParsingTest {
			Input: "SELECT * FROM table WHERE col1 = :name",
			Expected: "SELECT * FROM table WHERE col1 = ?",
			ExpectedParameters: 1,
			Name: "SingleParameter",
		},
		QueryParsingTest {
			Input: "SELECT * FROM table WHERE col1 = :name AND col2 = :occupation",
			Expected: "SELECT * FROM table WHERE col1 = ? AND col2 = ?",
			ExpectedParameters: 2,
			Name: "TwoParameters",
		},
		QueryParsingTest {
			Input: "SELECT * FROM table WHERE col1 = :name AND col2 = :occupation",
			Expected: "SELECT * FROM table WHERE col1 = ? AND col2 = ?",
			ExpectedParameters: 2,
			Name: "OneParameterMultipleTimes",
		},
		QueryParsingTest {
			Input: "SELECT * FROM table WHERE col1 IN (:something, :else)",
			Expected: "SELECT * FROM table WHERE col1 IN (?, ?)",
			ExpectedParameters: 2,
			Name: "ParametersInParenthesis",
		},
		QueryParsingTest {
			Input: "SELECT * FROM table WHERE col1 = ':literal' AND col2 LIKE ':literal'",
			Expected: "SELECT * FROM table WHERE col1 = ':literal' AND col2 LIKE ':literal'",
			Name: "ParametersInQuotes",
		},
		QueryParsingTest {
			Input: "SELECT * FROM table WHERE col1 = ':literal' AND col2 = :literal AND col3 LIKE ':literal'",
			Expected: "SELECT * FROM table WHERE col1 = ':literal' AND col2 = ? AND col3 LIKE ':literal'",
			ExpectedParameters: 1,
			Name: "ParametersInQuotes2",
		},
		QueryParsingTest {
			Input: "SELECT * FROM table WHERE col1 = :foo AND col2 IN (SELECT id FROM tabl2 WHERE col10 = :bar)",
			Expected: "SELECT * FROM table WHERE col1 = ? AND col2 IN (SELECT id FROM tabl2 WHERE col10 = ?)",
			ExpectedParameters: 2,
			Name: "ParametersInSubclause",
		},
		QueryParsingTest {
			Input: "SELECT * FROM table WHERE col1 = :1234567890 AND col2 = :0987654321",
			Expected: "SELECT * FROM table WHERE col1 = ? AND col2 = ?",
			ExpectedParameters: 2,
			Name: "NumericParameters",
		},
		QueryParsingTest {
			Input: "SELECT * FROM table WHERE col1 = :ABCDEFGHIJKLMNOPQRSTUVWXYZ",
			Expected: "SELECT * FROM table WHERE col1 = ?",
			ExpectedParameters: 1,
			Name: "CapsParameters",
		},
		QueryParsingTest {
			Input: "SELECT * FROM table WHERE col1 = :abc123ABC098",
			Expected: "SELECT * FROM table WHERE col1 = ?",
			ExpectedParameters: 1,
			Name: "AltcapsParameters",
		},
	}

	// Run each test.
	for _, parsingTest := range queryParsingTests {

		query = NewNamedParameterQuery(parsingTest.Input)

		// test query texts
		if(query.GetParsedQuery() != parsingTest.Expected) {
			test.Log("Test '", parsingTest.Name, "': Expected query text did not match actual parsed output")
			test.Log("Actual: ", query.GetParsedQuery())
			test.Fail()
		}

		// test parameters
		if(len(query.GetParsedParameters()) != parsingTest.ExpectedParameters) {
			test.Log("Test '", parsingTest.Name, "': Expected parameters did not match actual parsed parameter count")
			test.Fail()
		}
	}
}

/*
	Tests to ensure that setting parameter values turns out correct when using GetParsedParameters().
	These tests ensure correct positioning and type.
*/
func TestParameterReplacement(test *testing.T) {

	var query *NamedParameterQuery

	queryVariableTests := []ParameterParsingTest {
		ParameterParsingTest {

			Name: "SingleStringParameter",
			Query: "SELECT * FROM table WHERE col1 = :foo",
			Parameters: []TestQueryParameter {
				TestQueryParameter {
					Name: "foo",
					Value: "bar",
				},
			},
			ExpectedParameters: []interface{} {
				"bar",
			},
		},
		ParameterParsingTest {

			Name: "TwoStringParameter",
			Query: "SELECT * FROM table WHERE col1 = :foo AND col2 = :foo2",
			Parameters: []TestQueryParameter {
				TestQueryParameter {
					Name: "foo",
					Value: "bar",
				},
				TestQueryParameter {
					Name: "foo2",
					Value: "bart",
				},
			},
			ExpectedParameters: []interface{} {
				"bar", "bart",
			},
		},
		ParameterParsingTest {

			Name: "TwiceOccurringParameter",
			Query: "SELECT * FROM table WHERE col1 = :foo AND col2 = :foo",
			Parameters: []TestQueryParameter {
				TestQueryParameter {
					Name: "foo",
					Value: "bar",
				},
			},
			ExpectedParameters: []interface{} {
				"bar", "bar",
			},
		},
		ParameterParsingTest {

			Name: "ParameterTyping",
			Query: "SELECT * FROM table WHERE col1 = :str AND col2 = :int AND col3 = :pi",
			Parameters: []TestQueryParameter {
				TestQueryParameter {
					Name: "str",
					Value: "foo",
				},
				TestQueryParameter {
					Name: "int",
					Value: 1,
				},
				TestQueryParameter {
					Name: "pi",
					Value: 3.14,
				},
			},
			ExpectedParameters: []interface{} {
				"foo", 1, 3.14,
			},
		},
		ParameterParsingTest {

			Name: "ParameterOrdering",
			Query: "SELECT * FROM table WHERE col1 = :foo AND col2 = :bar AND col3 = :foo AND col4 = :foo AND col5 = :bar",
			Parameters: []TestQueryParameter {
				TestQueryParameter {
					Name: "foo",
					Value: "something",
				},
				TestQueryParameter {
					Name: "bar",
					Value: "else",
				},
			},
			ExpectedParameters: []interface{} {
				"something", "else", "something", "something", "else",
			},
		},
		ParameterParsingTest {

			Name: "ParameterCaseSensitivity",
			Query: "SELECT * FROM table WHERE col1 = :foo AND col2 = :FOO",
			Parameters: []TestQueryParameter {
				TestQueryParameter {
					Name: "foo",
					Value: "baz",
				},
				TestQueryParameter {
					Name: "FOO",
					Value: "quux",
				},
			},
			ExpectedParameters: []interface{} {
				"baz", "quux",
			},
		},
	}

	// run variable tests.
	for _, variableTest := range queryVariableTests {

		// parse query and set values.
		query = NewNamedParameterQuery(variableTest.Query)
		for _, queryVariable := range variableTest.Parameters {
			query.SetValue(queryVariable.Name, queryVariable.Value)
		}

		// Test outputs
		for index, queryVariable := range query.GetParsedParameters() {

			if(queryVariable != variableTest.ExpectedParameters[index]) {
				test.Log("Test '", variableTest.Name, "' did not produce the expected parameter output. Actual: '", queryVariable, "', Expected: '", variableTest.ExpectedParameters[index], "'")
				test.Fail()
			}
		}
	}
}
<|endoftext|>


<commit_before>// iris1 core with unicornhat interface

// +build linux
// +build arm

package unicorn

import (
	"fmt"
	"github.com/DrItanium/cores/iris16"
	"github.com/DrItanium/cores/registration/machine"
	"github.com/DrItanium/cores/registration/parser"
	"github.com/DrItanium/unicornhat"
)

const (
	baseAddress = 0x1000
)

func RegistrationName() string {
	return "iris1-unicorn"
}

func generateCore(a ...interface{}) (machine.Machine, error) {
	return New()
}

func init() {
	machine.Register(RegistrationName(), machine.Registrar(generateCore))
}

func New() (*iris1.Core, error) {
	if c, err := iris1.New(); err != nil {
		return c, err
	} else {
		if err := c.RegisterIoDevice(NewLedArray(baseAddress)); err != nil {
			return nil, err
		} else {
			return c, nil
		}
	}
}

var unicornhatInitialized bool

const (
	commandCell = iota
	brightnessCell
	xCell
	yCell
	redChannel
	blueChannel
	greenChannel
	numCells
)

type ledCommand iris1.Word

const (
	ledCommandClear ledCommand = iota
	ledCommandSetPixel
	ledCommandGetPixel
	ledCommandShow
	ledCommandSetBrightness
	ledCommandGetBrightness
	numLedCommands
)

type LedArray struct {
	base             iris1.Word
	brightness       iris1.Word
	x, y             iris1.Word
	red, green, blue iris1.Word
	initialized      bool
}

func (this *LedArray) Store(address, value iris1.Word) error {
	if address == (this.base + commandCell) {
		cmd := ledCommand(value)
		if cmd >= numLedCommands {
			return fmt.Errorf("Illegal unicornhat opcode %x", cmd)
		} else {
			switch cmd {
			case ledCommandClear:
				unicornhat.ClearLEDBuffer()
			case ledCommandSetPixel:
				if pos, err := unicornhat.PixelPosition(int(this.x), int(this.y)); err != nil {
					return err
				} else {
					unicornhat.SetPixelColor(pos, byte(this.red), byte(this.green), byte(this.blue))
				}
			case ledCommandGetPixel:
				if pos, err := unicornhat.PixelPosition(int(this.x), int(this.y)); err != nil {
					return err
				} else {
					pix := unicornhat.GetPixelColor(pos)
					this.red, this.green, this.blue = iris1.Word(pix.R), iris1.Word(pix.G), iris1.Word(pix.B)
				}
			case ledCommandSetBrightness:
				setBrightness(this.brightness)
			case ledCommandGetBrightness:
				this.brightness = getBrightness()
			case ledCommandShow:
				unicornhat.Show()
			}
			return nil
		}
	} else if address == (this.base + brightnessCell) {
		setBrightness(value)
		return nil
	} else if address == (this.base + xCell) {
		this.x = value
		return nil
	} else if address == (this.base + yCell) {
		this.y = value
		return nil
	} else if address == (this.base + redChannel) {
		this.red = value
		return nil
	} else if address == (this.base + blueChannel) {
		this.blue = value
		return nil
	} else if address == (this.base + greenChannel) {
		this.green = value
		return nil
	} else {
		return fmt.Errorf("Illegal address %x provided!", address)
	}
}

func (this *LedArray) Load(address iris1.Word) (iris1.Word, error) {
	if address == (this.base + commandCell) {
		return 0, fmt.Errorf("Can't read from the command cell of the unicornhat")
	} else if address == (this.base + brightnessCell) {
		return getBrightness(), nil
	} else if address == (this.base + xCell) {
		return this.x, nil
	} else if address == (this.base + yCell) {
		return this.y, nil
	} else if address == (this.base + redChannel) {
		return this.red, nil
	} else if address == (this.base + blueChannel) {
		return this.blue, nil
	} else if address == (this.base + greenChannel) {
		return this.green, nil
	} else {
		return 0, fmt.Errorf("Illegal address %x provided!", address)
	}
}

func setBrightness(brightness iris1.Word) {
	unicornhat.SetBrightness(byte(brightness))
}
func getBrightness() iris1.Word {
	return iris1.Word(unicornhat.GetBrightness())
}
func NewLedArray(baseAddr iris1.Word) *LedArray {
	var l LedArray
	l.base = baseAddr
	return &l
}

func (this *LedArray) Begin() iris1.Word {
	return this.base
}
func (this *LedArray) End() iris1.Word {
	return this.base + numCells
}
func (this *LedArray) RespondsTo(address iris1.Word) bool {
	return this.base <= address && ((this.base + numCells) >= address)
}
func (this *LedArray) Startup() error {
	if this.initialized {
		return fmt.Errorf("Attempted to startup the led array a second time!")
	} else {
		if !unicornhatInitialized {
			unicornhat.SetBrightness(unicornhat.DefaultBrightness / 2)
			if err := unicornhat.Initialize(); err != nil {
				return err
			}
			unicornhat.ClearLEDBuffer()
			unicornhat.Show()
			unicornhatInitialized = true
		}
		this.initialized = true
		return nil
	}
}

func (this *LedArray) Shutdown() error {
	if !this.initialized {
		return fmt.Errorf("Can't shutdown the LedArray when it has either been shutdown or never initialized!")
	} else {
		if unicornhatInitialized {
			unicornhat.Shutdown()
			unicornhatInitialized = false
		}
		this.initialized = false
		return nil
	}
}

func generateParser(args ...interface{}) (parser.Parser, error) {
	// this is a bit of a hack but just call the iris1 parser from the parser list :D
	return parser.New(iris1.RegistrationName(), args)
}

func init() {
	parser.Register(RegistrationName(), parser.Registrar(generateParser))
}
<commit_msg>unicorn hardware compiles once again on arm<commit_after>// iris16 core with unicornhat interface

// +build linux
// +build arm

package unicorn

import (
	"fmt"
	"github.com/DrItanium/cores/iris16"
	"github.com/DrItanium/cores/registration/machine"
	"github.com/DrItanium/cores/registration/parser"
	"github.com/DrItanium/unicornhat"
)

const (
	baseAddress = 0x1000
)

func RegistrationName() string {
	return "iris16-unicorn"
}

func generateCore(a ...interface{}) (machine.Machine, error) {
	return New()
}

func init() {
	machine.Register(RegistrationName(), machine.Registrar(generateCore))
}

func New() (*iris16.Core, error) {
	if c, err := iris16.New(); err != nil {
		return c, err
	} else {
		if err := c.RegisterIoDevice(NewLedArray(baseAddress)); err != nil {
			return nil, err
		} else {
			return c, nil
		}
	}
}

var unicornhatInitialized bool

const (
	commandCell = iota
	brightnessCell
	xCell
	yCell
	redChannel
	blueChannel
	greenChannel
	numCells
)

type ledCommand iris16.Word

const (
	ledCommandClear ledCommand = iota
	ledCommandSetPixel
	ledCommandGetPixel
	ledCommandShow
	ledCommandSetBrightness
	ledCommandGetBrightness
	numLedCommands
)

type LedArray struct {
	base             iris16.Word
	brightness       iris16.Word
	x, y             iris16.Word
	red, green, blue iris16.Word
	initialized      bool
}

func (this *LedArray) Store(address, value iris16.Word) error {
	if address == (this.base + commandCell) {
		cmd := ledCommand(value)
		if cmd >= numLedCommands {
			return fmt.Errorf("Illegal unicornhat opcode %x", cmd)
		} else {
			switch cmd {
			case ledCommandClear:
				unicornhat.ClearLEDBuffer()
			case ledCommandSetPixel:
				if pos, err := unicornhat.PixelPosition(int(this.x), int(this.y)); err != nil {
					return err
				} else {
					unicornhat.SetPixelColor(pos, byte(this.red), byte(this.green), byte(this.blue))
				}
			case ledCommandGetPixel:
				if pos, err := unicornhat.PixelPosition(int(this.x), int(this.y)); err != nil {
					return err
				} else {
					pix := unicornhat.GetPixelColor(pos)
					this.red, this.green, this.blue = iris16.Word(pix.R), iris16.Word(pix.G), iris16.Word(pix.B)
				}
			case ledCommandSetBrightness:
				setBrightness(this.brightness)
			case ledCommandGetBrightness:
				this.brightness = getBrightness()
			case ledCommandShow:
				unicornhat.Show()
			}
			return nil
		}
	} else if address == (this.base + brightnessCell) {
		setBrightness(value)
		return nil
	} else if address == (this.base + xCell) {
		this.x = value
		return nil
	} else if address == (this.base + yCell) {
		this.y = value
		return nil
	} else if address == (this.base + redChannel) {
		this.red = value
		return nil
	} else if address == (this.base + blueChannel) {
		this.blue = value
		return nil
	} else if address == (this.base + greenChannel) {
		this.green = value
		return nil
	} else {
		return fmt.Errorf("Illegal address %x provided!", address)
	}
}

func (this *LedArray) Load(address iris16.Word) (iris16.Word, error) {
	if address == (this.base + commandCell) {
		return 0, fmt.Errorf("Can't read from the command cell of the unicornhat")
	} else if address == (this.base + brightnessCell) {
		return getBrightness(), nil
	} else if address == (this.base + xCell) {
		return this.x, nil
	} else if address == (this.base + yCell) {
		return this.y, nil
	} else if address == (this.base + redChannel) {
		return this.red, nil
	} else if address == (this.base + blueChannel) {
		return this.blue, nil
	} else if address == (this.base + greenChannel) {
		return this.green, nil
	} else {
		return 0, fmt.Errorf("Illegal address %x provided!", address)
	}
}

func setBrightness(brightness iris16.Word) {
	unicornhat.SetBrightness(byte(brightness))
}
func getBrightness() iris16.Word {
	return iris16.Word(unicornhat.GetBrightness())
}
func NewLedArray(baseAddr iris16.Word) *LedArray {
	var l LedArray
	l.base = baseAddr
	return &l
}

func (this *LedArray) Begin() iris16.Word {
	return this.base
}
func (this *LedArray) End() iris16.Word {
	return this.base + numCells
}
func (this *LedArray) RespondsTo(address iris16.Word) bool {
	return this.base <= address && ((this.base + numCells) >= address)
}
func (this *LedArray) Startup() error {
	if this.initialized {
		return fmt.Errorf("Attempted to startup the led array a second time!")
	} else {
		if !unicornhatInitialized {
			unicornhat.SetBrightness(unicornhat.DefaultBrightness / 2)
			if err := unicornhat.Initialize(); err != nil {
				return err
			}
			unicornhat.ClearLEDBuffer()
			unicornhat.Show()
			unicornhatInitialized = true
		}
		this.initialized = true
		return nil
	}
}

func (this *LedArray) Shutdown() error {
	if !this.initialized {
		return fmt.Errorf("Can't shutdown the LedArray when it has either been shutdown or never initialized!")
	} else {
		if unicornhatInitialized {
			unicornhat.Shutdown()
			unicornhatInitialized = false
		}
		this.initialized = false
		return nil
	}
}

func generateParser(args ...interface{}) (parser.Parser, error) {
	// this is a bit of a hack but just call the iris16 parser from the parser list :D
	return parser.New(iris16.RegistrationName(), args)
}

func init() {
	parser.Register(RegistrationName(), parser.Registrar(generateParser))
}
<|endoftext|>


<commit_before><commit_msg>update test to see want vs. got in check<commit_after><|endoftext|>


<commit_before>/**********************************************************\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: http://www.hprose.com/                 |
|                   http://www.hprose.org/                 |
|                                                          |
\**********************************************************/
/**********************************************************\
 *                                                        *
 * rpc/method.go                                          *
 *                                                        *
 * hprose method manager for Go.                          *
 *                                                        *
 * LastModified: Oct 19, 2016                             *
 * Author: Ma Bingyao <andot@hprose.com>                  *
 *                                                        *
\**********************************************************/

package rpc

import (
	"reflect"
	"strings"
	"sync"
)

// Options is the options of the published service method
type Options struct {
	Mode           ResultMode
	Simple         bool
	Oneway         bool
	NameSpace      string
	JSONCompatible bool
}

// Method is the published service method
type Method struct {
	Function reflect.Value
	Options
}

// methodManager manages published service methods
type methodManager struct {
	MethodNames   []string
	RemoteMethods map[string]*Method
	mmLocker      sync.Mutex
}

func (mm *methodManager) initMethodManager() {
	mm.MethodNames = make([]string, 0, 64)
	mm.RemoteMethods = make(map[string]*Method)
}

// AddFunction publish a func or bound method
// name is the method name
// function is a func or bound method
// options includes Mode, Simple, Oneway and NameSpace
func (mm *methodManager) AddFunction(
	name string, function interface{}, options Options) {
	if name == "" {
		panic("name can't be empty")
	}
	if function == nil {
		panic("function can't be nil")
	}
	f, ok := function.(reflect.Value)
	if !ok {
		f = reflect.ValueOf(function)
	}
	if f.Kind() != reflect.Func {
		panic("function must be func or bound method")
	}
	if options.NameSpace != "" && name != "*" {
		name = options.NameSpace + "_" + name
	}
	mm.mmLocker.Lock()
	mm.MethodNames = append(mm.MethodNames, name)
	mm.RemoteMethods[strings.ToLower(name)] = &Method{f, options}
	mm.mmLocker.Unlock()
}

// AddFunctions is used for batch publishing service method
func (mm *methodManager) AddFunctions(
	names []string, functions []interface{}, options Options) {
	count := len(names)
	if count != len(functions) {
		panic("names and functions must have the same length")
	}
	for i := 0; i < count; i++ {
		mm.AddFunction(names[i], functions[i], options)
	}
}

// AddMethod is used for publishing a method on the obj with an alias
func (mm *methodManager) AddMethod(
	name string, obj interface{}, options Options, alias ...string) {
	if obj == nil {
		panic("obj can't be nil")
	}
	f := reflect.ValueOf(obj).MethodByName(name)
	if len(alias) == 1 && alias[0] != "" {
		name = alias[0]
	}
	if f.CanInterface() {
		mm.AddFunction(name, f, options)
	}
}

// AddMethods is used for batch publishing methods on the obj with aliases
func (mm *methodManager) AddMethods(
	names []string, obj interface{}, options Options, aliases ...[]string) {
	if obj == nil {
		panic("obj can't be nil")
	}
	count := len(names)
	if len(aliases) == 1 {
		if len(aliases[0]) != count {
			panic("names and aliases must have the same length")
		}
		for i := 0; i < count; i++ {
			mm.AddMethod(names[i], obj, options, aliases[0][i])
		}
		return
	}
	for i := 0; i < count; i++ {
		mm.AddMethod(names[i], obj, options)
	}
}

func (mm *methodManager) addMethods(
	v reflect.Value, t reflect.Type, options Options) {
	n := t.NumMethod()
	for i := 0; i < n; i++ {
		name := t.Method(i).Name
		method := v.Method(i)
		if method.CanInterface() {
			mm.AddFunction(name, method, options)
		}
	}
}

func getPtrTo(v reflect.Value, t reflect.Type) (reflect.Value, reflect.Type) {
	for ; t.Kind() == reflect.Ptr && !v.IsNil(); v = v.Elem() {
		t = t.Elem()
	}
	return v, t
}

func (mm *methodManager) addFuncField(
	v reflect.Value, t reflect.Type, i int, options Options) {
	f := v.Field(i)
	name := t.Field(i).Name
	if !f.CanInterface() || !f.IsValid() {
		return
	}
	f, _ = getPtrTo(f, f.Type())
	if f.Kind() == reflect.Func && !f.IsNil() {
		mm.AddFunction(name, f, options)
	}
}

func (mm *methodManager) recursiveAddFuncFields(
	v reflect.Value, t reflect.Type, i int, options Options) {
	f := v.Field(i)
	fs := t.Field(i)
	name := fs.Name
	if !f.CanInterface() || !f.IsValid() {
		return
	}
	f, _ = getPtrTo(f, f.Type())
	if f.Kind() == reflect.Func && !f.IsNil() {
		mm.AddFunction(name, f, options)
		return
	}
	if f.Kind() != reflect.Struct {
		return
	}
	if fs.Anonymous {
		mm.AddAllMethods(f.Interface(), options)
	} else {
		newOptions := options
		if newOptions.NameSpace == "" {
			newOptions.NameSpace = name
		} else {
			newOptions.NameSpace += "_" + name
		}
		mm.AddAllMethods(f.Interface(), newOptions)
	}
}

type addFuncFunc func(
	v reflect.Value,
	t reflect.Type,
	i int,
	options Options)

func (mm *methodManager) addInstanceMethods(
	obj interface{}, options Options, addFunc addFuncFunc) {
	if obj == nil {
		panic("obj can't be nil")
	}
	v := reflect.ValueOf(obj)
	t := v.Type()
	mm.addMethods(v, t, options)
	v, t = getPtrTo(v, t)
	if t.Kind() == reflect.Struct {
		n := t.NumField()
		for i := 0; i < n; i++ {
			addFunc(v, t, i, options)
		}
	}
}

// AddInstanceMethods is used for publishing all the public methods and func fields with options.
func (mm *methodManager) AddInstanceMethods(
	obj interface{}, options Options) {
	mm.addInstanceMethods(obj, options, mm.addFuncField)
}

// AddAllMethods will publish all methods and non-nil function fields on the
// obj self and on its anonymous or non-anonymous struct fields (or pointer to
// pointer ... to pointer struct fields). This is a recursive operation.
// So it's a pit, if you do not know what you are doing, do not step on.
func (mm *methodManager) AddAllMethods(
	obj interface{}, options Options) {
	mm.addInstanceMethods(obj, options, mm.recursiveAddFuncFields)
}

// MissingMethod is missing method
type MissingMethod func(name string, args []reflect.Value, context Context) (result []reflect.Value)

// AddMissingMethod is used for publishing a method,
// all methods not explicitly published will be redirected to this method.
func (mm *methodManager) AddMissingMethod(
	method MissingMethod, options Options) {
	mm.AddFunction("*", method, options)
}

// AddNetRPCMethods is used for publishing methods defined for net/rpc.
//
// For example:
//
//		package server
//
//		type Args struct {
//			A, B int
//		}
//
//		type Quotient struct {
//			Quo, Rem int
//		}
//
//		type Arith int
//
//		func (t *Arith) Multiply(args *Args, reply *int) error {
//			*reply = args.A * args.B
//			return nil
//		}
//
//		func (t *Arith) Divide(args *Args, quo *Quotient) error {
//			if args.B == 0 {
//				return errors.New("divide by zero")
//			}
//			quo.Quo = args.A / args.B
//			quo.Rem = args.A % args.B
//			return nil
//		}
//
// You can publish it in hprose like this:
//
//		service := rpc.NewHTTPService()
//		service.AddNetRPCMethods(new(Arith), rpc.Options{})
//		http.ListenAndServe(":8080", service)
//
//
// Then define stub struct in client:
//
//		type Stub struct {
//			// Synchronous call
//			Multiply func(args *Args) int
//			// Asynchronous call
//			Divide func(func(*Quotient, error), *Args)
//		}
//
// Then it can make a remote call:
//
//		client := rpc.NewClient("http://127.0.0.1:8080")
//		var stub *Stub
//		client.UseService(&stub)
//		fmt.Println(stub.Multiply(&Args{8, 7}))
//		stub.Divide(func(result *Quotient, err error) {
//			if err != nil {
//				log.Fatal("arith error:", err)
//			} else {
//				fmt.Println(result.Quo, result.Rem)
//			}
//		}, &Args{8, 7})
//
// You can also call it in other languages.
//
// Here is a simple example in JavaScript:
//
//		// The method name is case-insensitive
//		var methods = ['multiply', 'divide'];
//		var client = hprose.Client.create('http://127.0.0.1:8080/', methods);
//		// The first letter of the field name is convert to lowercase.
//      client.multiply({a:8, b:7}).then(function(product) {
//			return client.divide({a:product, b:6});
//		}).then(function(quotient) {
//			// The first letter of the field name is convert to lowercase.
//			console.log(quotient.quo, quotient.rem);
//		}).catch(function(err) {
//			// The returned err is here
//			console.error(err);
//		});
//
func (mm *methodManager) AddNetRPCMethods(rcvr interface{}, options Options) {
	if rcvr == nil {
		panic("rcvr can't be nil")
	}
	v := reflect.ValueOf(rcvr)
	t := v.Type()
	n := t.NumMethod()
	for i := 0; i < n; i++ {
		name := t.Method(i).Name
		method := v.Method(i)
		if method.CanInterface() {
			mm.addNetRPCMethod(name, method, options)
		}
	}
}

func (mm *methodManager) addNetRPCMethod(
	name string, method reflect.Value, options Options) {
	ft := method.Type()
	if ft.NumIn() != 2 || ft.IsVariadic() {
		panic("the method " + name + " must has two arguments")
	}
	if ft.In(1).Kind() != reflect.Ptr {
		panic("the second argument of method " + name + " must be a pointer")
	}
	if ft.NumOut() != 1 || ft.Out(0) != errorType {
		panic("the result type of method " + name + " must be error")
	}
	argsType := ft.In(0)
	resultType := ft.In(1).Elem()
	in := []reflect.Type{argsType}
	out := []reflect.Type{resultType, errorType}
	newft := reflect.FuncOf(in, out, false)
	newMethod := reflect.MakeFunc(newft, func(
		args []reflect.Value) (results []reflect.Value) {
		result := reflect.New(resultType)
		in := []reflect.Value{args[0], result}
		err := method.Call(in)[0]
		results = []reflect.Value{result.Elem(), err}
		return
	})
	mm.AddFunction(name, newMethod, options)
}

// Remove the published func or method by name
func (mm *methodManager) Remove(name string) {
	name = strings.ToLower(name)
	mm.mmLocker.Lock()
	n := len(mm.MethodNames)
	for i := 0; i < n; i++ {
		if strings.ToLower(mm.MethodNames[i]) == name {
			mm.MethodNames = append(mm.MethodNames[:i], mm.MethodNames[i+1:]...)
			break
		}
	}
	delete(mm.RemoteMethods, name)
	mm.mmLocker.Unlock()
}
<commit_msg>Fixed AddAllMethods<commit_after>/**********************************************************\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: http://www.hprose.com/                 |
|                   http://www.hprose.org/                 |
|                                                          |
\**********************************************************/
/**********************************************************\
 *                                                        *
 * rpc/method.go                                          *
 *                                                        *
 * hprose method manager for Go.                          *
 *                                                        *
 * LastModified: Oct 20, 2016                             *
 * Author: Ma Bingyao <andot@hprose.com>                  *
 *                                                        *
\**********************************************************/

package rpc

import (
	"reflect"
	"strings"
	"sync"
)

// Options is the options of the published service method
type Options struct {
	Mode           ResultMode
	Simple         bool
	Oneway         bool
	NameSpace      string
	JSONCompatible bool
}

// Method is the published service method
type Method struct {
	Function reflect.Value
	Options
}

// methodManager manages published service methods
type methodManager struct {
	MethodNames   []string
	RemoteMethods map[string]*Method
	mmLocker      sync.Mutex
}

func (mm *methodManager) initMethodManager() {
	mm.MethodNames = make([]string, 0, 64)
	mm.RemoteMethods = make(map[string]*Method)
}

// AddFunction publish a func or bound method
// name is the method name
// function is a func or bound method
// options includes Mode, Simple, Oneway and NameSpace
func (mm *methodManager) AddFunction(
	name string, function interface{}, options Options) {
	if name == "" {
		panic("name can't be empty")
	}
	if function == nil {
		panic("function can't be nil")
	}
	f, ok := function.(reflect.Value)
	if !ok {
		f = reflect.ValueOf(function)
	}
	if f.Kind() != reflect.Func {
		panic("function must be func or bound method")
	}
	if options.NameSpace != "" && name != "*" {
		name = options.NameSpace + "_" + name
	}
	mm.mmLocker.Lock()
	mm.MethodNames = append(mm.MethodNames, name)
	mm.RemoteMethods[strings.ToLower(name)] = &Method{f, options}
	mm.mmLocker.Unlock()
}

// AddFunctions is used for batch publishing service method
func (mm *methodManager) AddFunctions(
	names []string, functions []interface{}, options Options) {
	count := len(names)
	if count != len(functions) {
		panic("names and functions must have the same length")
	}
	for i := 0; i < count; i++ {
		mm.AddFunction(names[i], functions[i], options)
	}
}

// AddMethod is used for publishing a method on the obj with an alias
func (mm *methodManager) AddMethod(
	name string, obj interface{}, options Options, alias ...string) {
	if obj == nil {
		panic("obj can't be nil")
	}
	f := reflect.ValueOf(obj).MethodByName(name)
	if len(alias) == 1 && alias[0] != "" {
		name = alias[0]
	}
	if f.CanInterface() {
		mm.AddFunction(name, f, options)
	}
}

// AddMethods is used for batch publishing methods on the obj with aliases
func (mm *methodManager) AddMethods(
	names []string, obj interface{}, options Options, aliases ...[]string) {
	if obj == nil {
		panic("obj can't be nil")
	}
	count := len(names)
	if len(aliases) == 1 {
		if len(aliases[0]) != count {
			panic("names and aliases must have the same length")
		}
		for i := 0; i < count; i++ {
			mm.AddMethod(names[i], obj, options, aliases[0][i])
		}
		return
	}
	for i := 0; i < count; i++ {
		mm.AddMethod(names[i], obj, options)
	}
}

func (mm *methodManager) addMethods(
	v reflect.Value, t reflect.Type, options Options) {
	n := t.NumMethod()
	for i := 0; i < n; i++ {
		name := t.Method(i).Name
		method := v.Method(i)
		if method.CanInterface() {
			mm.AddFunction(name, method, options)
		}
	}
}

func getPtrTo(v reflect.Value, t reflect.Type) (reflect.Value, reflect.Type) {
	for ; t.Kind() == reflect.Ptr && !v.IsNil(); v = v.Elem() {
		t = t.Elem()
	}
	return v, t
}

func (mm *methodManager) addFuncField(
	v reflect.Value, t reflect.Type, i int, options Options) {
	f := v.Field(i)
	name := t.Field(i).Name
	if !f.CanInterface() || !f.IsValid() {
		return
	}
	f, _ = getPtrTo(f, f.Type())
	if f.Kind() == reflect.Func && !f.IsNil() {
		mm.AddFunction(name, f, options)
	}
}

func (mm *methodManager) recursiveAddFuncFields(
	v reflect.Value, t reflect.Type, i int, options Options) {
	f := v.Field(i)
	fs := t.Field(i)
	name := fs.Name
	if !f.CanInterface() || !f.IsValid() {
		return
	}
	f, _ = getPtrTo(f, f.Type())
	if f.Kind() == reflect.Func && !f.IsNil() {
		mm.AddFunction(name, f, options)
		return
	}
	if fs.Anonymous {
		mm.AddAllMethods(f.Interface(), options)
	} else {
		newOptions := options
		if newOptions.NameSpace == "" {
			newOptions.NameSpace = name
		} else {
			newOptions.NameSpace += "_" + name
		}
		mm.AddAllMethods(f.Interface(), newOptions)
	}
}

type addFuncFunc func(
	v reflect.Value,
	t reflect.Type,
	i int,
	options Options)

func (mm *methodManager) addInstanceMethods(
	obj interface{}, options Options, addFunc addFuncFunc) {
	if obj == nil {
		panic("obj can't be nil")
	}
	v := reflect.ValueOf(obj)
	t := v.Type()
	mm.addMethods(v, t, options)
	v, t = getPtrTo(v, t)
	if t.Kind() == reflect.Struct {
		n := t.NumField()
		for i := 0; i < n; i++ {
			addFunc(v, t, i, options)
		}
	}
}

// AddInstanceMethods is used for publishing all the public methods and func fields with options.
func (mm *methodManager) AddInstanceMethods(
	obj interface{}, options Options) {
	mm.addInstanceMethods(obj, options, mm.addFuncField)
}

// AddAllMethods will publish all methods and non-nil function fields on the
// obj self and on its anonymous or non-anonymous struct fields (or pointer to
// pointer ... to pointer struct fields). This is a recursive operation.
// So it's a pit, if you do not know what you are doing, do not step on.
func (mm *methodManager) AddAllMethods(
	obj interface{}, options Options) {
	mm.addInstanceMethods(obj, options, mm.recursiveAddFuncFields)
}

// MissingMethod is missing method
type MissingMethod func(name string, args []reflect.Value, context Context) (result []reflect.Value)

// AddMissingMethod is used for publishing a method,
// all methods not explicitly published will be redirected to this method.
func (mm *methodManager) AddMissingMethod(
	method MissingMethod, options Options) {
	mm.AddFunction("*", method, options)
}

// AddNetRPCMethods is used for publishing methods defined for net/rpc.
//
// For example:
//
//		package server
//
//		type Args struct {
//			A, B int
//		}
//
//		type Quotient struct {
//			Quo, Rem int
//		}
//
//		type Arith int
//
//		func (t *Arith) Multiply(args *Args, reply *int) error {
//			*reply = args.A * args.B
//			return nil
//		}
//
//		func (t *Arith) Divide(args *Args, quo *Quotient) error {
//			if args.B == 0 {
//				return errors.New("divide by zero")
//			}
//			quo.Quo = args.A / args.B
//			quo.Rem = args.A % args.B
//			return nil
//		}
//
// You can publish it in hprose like this:
//
//		service := rpc.NewHTTPService()
//		service.AddNetRPCMethods(new(Arith), rpc.Options{})
//		http.ListenAndServe(":8080", service)
//
//
// Then define stub struct in client:
//
//		type Stub struct {
//			// Synchronous call
//			Multiply func(args *Args) int
//			// Asynchronous call
//			Divide func(func(*Quotient, error), *Args)
//		}
//
// Then it can make a remote call:
//
//		client := rpc.NewClient("http://127.0.0.1:8080")
//		var stub *Stub
//		client.UseService(&stub)
//		fmt.Println(stub.Multiply(&Args{8, 7}))
//		stub.Divide(func(result *Quotient, err error) {
//			if err != nil {
//				log.Fatal("arith error:", err)
//			} else {
//				fmt.Println(result.Quo, result.Rem)
//			}
//		}, &Args{8, 7})
//
// You can also call it in other languages.
//
// Here is a simple example in JavaScript:
//
//		// The method name is case-insensitive
//		var methods = ['multiply', 'divide'];
//		var client = hprose.Client.create('http://127.0.0.1:8080/', methods);
//		// The first letter of the field name is convert to lowercase.
//      client.multiply({a:8, b:7}).then(function(product) {
//			return client.divide({a:product, b:6});
//		}).then(function(quotient) {
//			// The first letter of the field name is convert to lowercase.
//			console.log(quotient.quo, quotient.rem);
//		}).catch(function(err) {
//			// The returned err is here
//			console.error(err);
//		});
//
func (mm *methodManager) AddNetRPCMethods(rcvr interface{}, options Options) {
	if rcvr == nil {
		panic("rcvr can't be nil")
	}
	v := reflect.ValueOf(rcvr)
	t := v.Type()
	n := t.NumMethod()
	for i := 0; i < n; i++ {
		name := t.Method(i).Name
		method := v.Method(i)
		if method.CanInterface() {
			mm.addNetRPCMethod(name, method, options)
		}
	}
}

func (mm *methodManager) addNetRPCMethod(
	name string, method reflect.Value, options Options) {
	ft := method.Type()
	if ft.NumIn() != 2 || ft.IsVariadic() {
		panic("the method " + name + " must has two arguments")
	}
	if ft.In(1).Kind() != reflect.Ptr {
		panic("the second argument of method " + name + " must be a pointer")
	}
	if ft.NumOut() != 1 || ft.Out(0) != errorType {
		panic("the result type of method " + name + " must be error")
	}
	argsType := ft.In(0)
	resultType := ft.In(1).Elem()
	in := []reflect.Type{argsType}
	out := []reflect.Type{resultType, errorType}
	newft := reflect.FuncOf(in, out, false)
	newMethod := reflect.MakeFunc(newft, func(
		args []reflect.Value) (results []reflect.Value) {
		result := reflect.New(resultType)
		in := []reflect.Value{args[0], result}
		err := method.Call(in)[0]
		results = []reflect.Value{result.Elem(), err}
		return
	})
	mm.AddFunction(name, newMethod, options)
}

// Remove the published func or method by name
func (mm *methodManager) Remove(name string) {
	name = strings.ToLower(name)
	mm.mmLocker.Lock()
	n := len(mm.MethodNames)
	for i := 0; i < n; i++ {
		if strings.ToLower(mm.MethodNames[i]) == name {
			mm.MethodNames = append(mm.MethodNames[:i], mm.MethodNames[i+1:]...)
			break
		}
	}
	delete(mm.RemoteMethods, name)
	mm.mmLocker.Unlock()
}
<|endoftext|>


<commit_before>package rpc

import (
	"net"
	"sync"

	"github.com/appleboy/gorush/gorush"
	"github.com/appleboy/gorush/rpc/proto"

	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/reflection"
	"google.golang.org/grpc/status"
)

const (
	port = ":9000"
)

// Server is used to implement gorush grpc server.
type Server struct {
	mu sync.Mutex
	// statusMap stores the serving status of the services this Server monitors.
	statusMap map[string]proto.HealthCheckResponse_ServingStatus
}

// NewServer returns a new Server.
func NewServer() *Server {
	return &Server{
		statusMap: make(map[string]proto.HealthCheckResponse_ServingStatus),
	}
}

// Check implements `service Health`.
func (s *Server) Check(ctx context.Context, in *proto.HealthCheckRequest) (*proto.HealthCheckResponse, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	if in.Service == "" {
		// check the server overall health status.
		return &proto.HealthCheckResponse{
			Status: proto.HealthCheckResponse_SERVING,
		}, nil
	}
	if status, ok := s.statusMap[in.Service]; ok {
		return &proto.HealthCheckResponse{
			Status: status,
		}, nil
	}
	return nil, status.Error(codes.NotFound, "unknown service")
}

// Send implements helloworld.GreeterServer
func (s *Server) Send(ctx context.Context, in *proto.NotificationRequest) (*proto.NotificationReply, error) {
	notification := gorush.PushNotification{
		Platform: int(in.Platform),
		Tokens:   in.Tokens,
		Message:  in.Message,
		Title:    in.Title,
		Topic:    in.Topic,
		APIKey:   in.Key,
	}

	go gorush.SendNotification(notification)

	return &proto.NotificationReply{
		Success: false,
		Counts:  int32(len(notification.Tokens)),
	}, nil
}

// RunGRPCServer run gorush grpc server
func RunGRPCServer() error {
	if !gorush.PushConf.GRPC.Enabled {
		gorush.LogAccess.Debug("gRPC server is disabled.")
		return nil
	}

	lis, err := net.Listen("tcp", ":"+gorush.PushConf.GRPC.Port)
	if err != nil {
		gorush.LogError.Errorf("failed to listen: %v", err)
		return err
	}
	s := grpc.NewServer()
	srv := NewServer()
	proto.RegisterGorushServer(s, srv)
	proto.RegisterHealthServer(s, srv)
	// Register reflection service on gRPC server.
	reflection.Register(s)
	gorush.LogAccess.Debug("gRPC server is running on " + gorush.PushConf.GRPC.Port + " port.")
	if err := s.Serve(lis); err != nil {
		gorush.LogError.Errorf("failed to serve: %v", err)
		return err
	}

	return nil
}
<commit_msg>remove unused const<commit_after>package rpc

import (
	"net"
	"sync"

	"github.com/appleboy/gorush/gorush"
	"github.com/appleboy/gorush/rpc/proto"

	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/reflection"
	"google.golang.org/grpc/status"
)

// Server is used to implement gorush grpc server.
type Server struct {
	mu sync.Mutex
	// statusMap stores the serving status of the services this Server monitors.
	statusMap map[string]proto.HealthCheckResponse_ServingStatus
}

// NewServer returns a new Server.
func NewServer() *Server {
	return &Server{
		statusMap: make(map[string]proto.HealthCheckResponse_ServingStatus),
	}
}

// Check implements `service Health`.
func (s *Server) Check(ctx context.Context, in *proto.HealthCheckRequest) (*proto.HealthCheckResponse, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	if in.Service == "" {
		// check the server overall health status.
		return &proto.HealthCheckResponse{
			Status: proto.HealthCheckResponse_SERVING,
		}, nil
	}
	if status, ok := s.statusMap[in.Service]; ok {
		return &proto.HealthCheckResponse{
			Status: status,
		}, nil
	}
	return nil, status.Error(codes.NotFound, "unknown service")
}

// Send implements helloworld.GreeterServer
func (s *Server) Send(ctx context.Context, in *proto.NotificationRequest) (*proto.NotificationReply, error) {
	notification := gorush.PushNotification{
		Platform: int(in.Platform),
		Tokens:   in.Tokens,
		Message:  in.Message,
		Title:    in.Title,
		Topic:    in.Topic,
		APIKey:   in.Key,
	}

	go gorush.SendNotification(notification)

	return &proto.NotificationReply{
		Success: false,
		Counts:  int32(len(notification.Tokens)),
	}, nil
}

// RunGRPCServer run gorush grpc server
func RunGRPCServer() error {
	if !gorush.PushConf.GRPC.Enabled {
		gorush.LogAccess.Debug("gRPC server is disabled.")
		return nil
	}

	lis, err := net.Listen("tcp", ":"+gorush.PushConf.GRPC.Port)
	if err != nil {
		gorush.LogError.Errorf("failed to listen: %v", err)
		return err
	}
	s := grpc.NewServer()
	srv := NewServer()
	proto.RegisterGorushServer(s, srv)
	proto.RegisterHealthServer(s, srv)
	// Register reflection service on gRPC server.
	reflection.Register(s)
	gorush.LogAccess.Debug("gRPC server is running on " + gorush.PushConf.GRPC.Port + " port.")
	if err := s.Serve(lis); err != nil {
		gorush.LogError.Errorf("failed to serve: %v", err)
		return err
	}

	return nil
}
<|endoftext|>


<commit_before><commit_msg>Just moving code around.<commit_after><|endoftext|>