forked from mystiq/dex
vendor: revendor
This commit is contained in:
parent
d87a4c35b9
commit
1451213dd7
268 changed files with 484 additions and 59530 deletions
1
vendor/github.com/Sirupsen/logrus/.gitignore
generated
vendored
1
vendor/github.com/Sirupsen/logrus/.gitignore
generated
vendored
|
@ -1 +0,0 @@
|
||||||
logrus
|
|
10
vendor/github.com/Sirupsen/logrus/.travis.yml
generated
vendored
10
vendor/github.com/Sirupsen/logrus/.travis.yml
generated
vendored
|
@ -1,10 +0,0 @@
|
||||||
language: go
|
|
||||||
go:
|
|
||||||
- 1.3
|
|
||||||
- 1.4
|
|
||||||
- 1.5
|
|
||||||
- 1.6
|
|
||||||
- tip
|
|
||||||
install:
|
|
||||||
- go get -t ./...
|
|
||||||
script: GOMAXPROCS=4 GORACE="halt_on_error=1" go test -race -v ./...
|
|
66
vendor/github.com/Sirupsen/logrus/CHANGELOG.md
generated
vendored
66
vendor/github.com/Sirupsen/logrus/CHANGELOG.md
generated
vendored
|
@ -1,66 +0,0 @@
|
||||||
# 0.10.0
|
|
||||||
|
|
||||||
* feature: Add a test hook (#180)
|
|
||||||
* feature: `ParseLevel` is now case-insensitive (#326)
|
|
||||||
* feature: `FieldLogger` interface that generalizes `Logger` and `Entry` (#308)
|
|
||||||
* performance: avoid re-allocations on `WithFields` (#335)
|
|
||||||
|
|
||||||
# 0.9.0
|
|
||||||
|
|
||||||
* logrus/text_formatter: don't emit empty msg
|
|
||||||
* logrus/hooks/airbrake: move out of main repository
|
|
||||||
* logrus/hooks/sentry: move out of main repository
|
|
||||||
* logrus/hooks/papertrail: move out of main repository
|
|
||||||
* logrus/hooks/bugsnag: move out of main repository
|
|
||||||
* logrus/core: run tests with `-race`
|
|
||||||
* logrus/core: detect TTY based on `stderr`
|
|
||||||
* logrus/core: support `WithError` on logger
|
|
||||||
* logrus/core: Solaris support
|
|
||||||
|
|
||||||
# 0.8.7
|
|
||||||
|
|
||||||
* logrus/core: fix possible race (#216)
|
|
||||||
* logrus/doc: small typo fixes and doc improvements
|
|
||||||
|
|
||||||
|
|
||||||
# 0.8.6
|
|
||||||
|
|
||||||
* hooks/raven: allow passing an initialized client
|
|
||||||
|
|
||||||
# 0.8.5
|
|
||||||
|
|
||||||
* logrus/core: revert #208
|
|
||||||
|
|
||||||
# 0.8.4
|
|
||||||
|
|
||||||
* formatter/text: fix data race (#218)
|
|
||||||
|
|
||||||
# 0.8.3
|
|
||||||
|
|
||||||
* logrus/core: fix entry log level (#208)
|
|
||||||
* logrus/core: improve performance of text formatter by 40%
|
|
||||||
* logrus/core: expose `LevelHooks` type
|
|
||||||
* logrus/core: add support for DragonflyBSD and NetBSD
|
|
||||||
* formatter/text: print structs more verbosely
|
|
||||||
|
|
||||||
# 0.8.2
|
|
||||||
|
|
||||||
* logrus: fix more Fatal family functions
|
|
||||||
|
|
||||||
# 0.8.1
|
|
||||||
|
|
||||||
* logrus: fix not exiting on `Fatalf` and `Fatalln`
|
|
||||||
|
|
||||||
# 0.8.0
|
|
||||||
|
|
||||||
* logrus: defaults to stderr instead of stdout
|
|
||||||
* hooks/sentry: add special field for `*http.Request`
|
|
||||||
* formatter/text: ignore Windows for colors
|
|
||||||
|
|
||||||
# 0.7.3
|
|
||||||
|
|
||||||
* formatter/\*: allow configuration of timestamp layout
|
|
||||||
|
|
||||||
# 0.7.2
|
|
||||||
|
|
||||||
* formatter/text: Add configuration option for time format (#158)
|
|
425
vendor/github.com/Sirupsen/logrus/README.md
generated
vendored
425
vendor/github.com/Sirupsen/logrus/README.md
generated
vendored
|
@ -1,425 +0,0 @@
|
||||||
# Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/> [![Build Status](https://travis-ci.org/Sirupsen/logrus.svg?branch=master)](https://travis-ci.org/Sirupsen/logrus) [![GoDoc](https://godoc.org/github.com/Sirupsen/logrus?status.svg)](https://godoc.org/github.com/Sirupsen/logrus)
|
|
||||||
|
|
||||||
Logrus is a structured logger for Go (golang), completely API compatible with
|
|
||||||
the standard library logger. [Godoc][godoc]. **Please note the Logrus API is not
|
|
||||||
yet stable (pre 1.0). Logrus itself is completely stable and has been used in
|
|
||||||
many large deployments. The core API is unlikely to change much but please
|
|
||||||
version control your Logrus to make sure you aren't fetching latest `master` on
|
|
||||||
every build.**
|
|
||||||
|
|
||||||
Nicely color-coded in development (when a TTY is attached, otherwise just
|
|
||||||
plain text):
|
|
||||||
|
|
||||||
![Colored](http://i.imgur.com/PY7qMwd.png)
|
|
||||||
|
|
||||||
With `log.SetFormatter(&log.JSONFormatter{})`, for easy parsing by logstash
|
|
||||||
or Splunk:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the
|
|
||||||
ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
|
|
||||||
|
|
||||||
{"level":"warning","msg":"The group's number increased tremendously!",
|
|
||||||
"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"}
|
|
||||||
|
|
||||||
{"animal":"walrus","level":"info","msg":"A giant walrus appears!",
|
|
||||||
"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"}
|
|
||||||
|
|
||||||
{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.",
|
|
||||||
"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"}
|
|
||||||
|
|
||||||
{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true,
|
|
||||||
"time":"2014-03-10 19:57:38.562543128 -0400 EDT"}
|
|
||||||
```
|
|
||||||
|
|
||||||
With the default `log.SetFormatter(&log.TextFormatter{})` when a TTY is not
|
|
||||||
attached, the output is compatible with the
|
|
||||||
[logfmt](http://godoc.org/github.com/kr/logfmt) format:
|
|
||||||
|
|
||||||
```text
|
|
||||||
time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8
|
|
||||||
time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10
|
|
||||||
time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true
|
|
||||||
time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4
|
|
||||||
time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009
|
|
||||||
time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true
|
|
||||||
exit status 1
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
|
|
||||||
The simplest way to use Logrus is simply the package-level exported logger:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
log "github.com/Sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"animal": "walrus",
|
|
||||||
}).Info("A walrus appears")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that it's completely api-compatible with the stdlib logger, so you can
|
|
||||||
replace your `log` imports everywhere with `log "github.com/Sirupsen/logrus"`
|
|
||||||
and you'll now have the flexibility of Logrus. You can customize it all you
|
|
||||||
want:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
log "github.com/Sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// Log as JSON instead of the default ASCII formatter.
|
|
||||||
log.SetFormatter(&log.JSONFormatter{})
|
|
||||||
|
|
||||||
// Output to stderr instead of stdout, could also be a file.
|
|
||||||
log.SetOutput(os.Stderr)
|
|
||||||
|
|
||||||
// Only log the warning severity or above.
|
|
||||||
log.SetLevel(log.WarnLevel)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"animal": "walrus",
|
|
||||||
"size": 10,
|
|
||||||
}).Info("A group of walrus emerges from the ocean")
|
|
||||||
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"omg": true,
|
|
||||||
"number": 122,
|
|
||||||
}).Warn("The group's number increased tremendously!")
|
|
||||||
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"omg": true,
|
|
||||||
"number": 100,
|
|
||||||
}).Fatal("The ice breaks!")
|
|
||||||
|
|
||||||
// A common pattern is to re-use fields between logging statements by re-using
|
|
||||||
// the logrus.Entry returned from WithFields()
|
|
||||||
contextLogger := log.WithFields(log.Fields{
|
|
||||||
"common": "this is a common field",
|
|
||||||
"other": "I also should be logged always",
|
|
||||||
})
|
|
||||||
|
|
||||||
contextLogger.Info("I'll be logged with common and other field")
|
|
||||||
contextLogger.Info("Me too")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
For more advanced usage such as logging to multiple locations from the same
|
|
||||||
application, you can also create an instance of the `logrus` Logger:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Create a new instance of the logger. You can have any number of instances.
|
|
||||||
var log = logrus.New()
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// The API for setting attributes is a little different than the package level
|
|
||||||
// exported logger. See Godoc.
|
|
||||||
log.Out = os.Stderr
|
|
||||||
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"animal": "walrus",
|
|
||||||
"size": 10,
|
|
||||||
}).Info("A group of walrus emerges from the ocean")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Fields
|
|
||||||
|
|
||||||
Logrus encourages careful, structured logging though logging fields instead of
|
|
||||||
long, unparseable error messages. For example, instead of: `log.Fatalf("Failed
|
|
||||||
to send event %s to topic %s with key %d")`, you should log the much more
|
|
||||||
discoverable:
|
|
||||||
|
|
||||||
```go
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"event": event,
|
|
||||||
"topic": topic,
|
|
||||||
"key": key,
|
|
||||||
}).Fatal("Failed to send event")
|
|
||||||
```
|
|
||||||
|
|
||||||
We've found this API forces you to think about logging in a way that produces
|
|
||||||
much more useful logging messages. We've been in countless situations where just
|
|
||||||
a single added field to a log statement that was already there would've saved us
|
|
||||||
hours. The `WithFields` call is optional.
|
|
||||||
|
|
||||||
In general, with Logrus using any of the `printf`-family functions should be
|
|
||||||
seen as a hint you should add a field, however, you can still use the
|
|
||||||
`printf`-family functions with Logrus.
|
|
||||||
|
|
||||||
#### Hooks
|
|
||||||
|
|
||||||
You can add hooks for logging levels. For example to send errors to an exception
|
|
||||||
tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to
|
|
||||||
multiple places simultaneously, e.g. syslog.
|
|
||||||
|
|
||||||
Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in
|
|
||||||
`init`:
|
|
||||||
|
|
||||||
```go
|
|
||||||
import (
|
|
||||||
log "github.com/Sirupsen/logrus"
|
|
||||||
"gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "aibrake"
|
|
||||||
logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog"
|
|
||||||
"log/syslog"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
|
|
||||||
// Use the Airbrake hook to report errors that have Error severity or above to
|
|
||||||
// an exception tracker. You can create custom hooks, see the Hooks section.
|
|
||||||
log.AddHook(airbrake.NewHook(123, "xyz", "production"))
|
|
||||||
|
|
||||||
hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Unable to connect to local syslog daemon")
|
|
||||||
} else {
|
|
||||||
log.AddHook(hook)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md).
|
|
||||||
|
|
||||||
| Hook | Description |
|
|
||||||
| ----- | ----------- |
|
|
||||||
| [Airbrake](https://github.com/gemnasium/logrus-airbrake-hook) | Send errors to the Airbrake API V3. Uses the official [`gobrake`](https://github.com/airbrake/gobrake) behind the scenes. |
|
|
||||||
| [Airbrake "legacy"](https://github.com/gemnasium/logrus-airbrake-legacy-hook) | Send errors to an exception tracking service compatible with the Airbrake API V2. Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes. |
|
|
||||||
| [Papertrail](https://github.com/polds/logrus-papertrail-hook) | Send errors to the [Papertrail](https://papertrailapp.com) hosted logging service via UDP. |
|
|
||||||
| [Syslog](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go) | Send errors to remote syslog server. Uses standard library `log/syslog` behind the scenes. |
|
|
||||||
| [Bugsnag](https://github.com/Shopify/logrus-bugsnag/blob/master/bugsnag.go) | Send errors to the Bugsnag exception tracking service. |
|
|
||||||
| [Sentry](https://github.com/evalphobia/logrus_sentry) | Send errors to the Sentry error logging and aggregation service. |
|
|
||||||
| [Hiprus](https://github.com/nubo/hiprus) | Send errors to a channel in hipchat. |
|
|
||||||
| [Logrusly](https://github.com/sebest/logrusly) | Send logs to [Loggly](https://www.loggly.com/) |
|
|
||||||
| [Slackrus](https://github.com/johntdyer/slackrus) | Hook for Slack chat. |
|
|
||||||
| [Journalhook](https://github.com/wercker/journalhook) | Hook for logging to `systemd-journald` |
|
|
||||||
| [Graylog](https://github.com/gemnasium/logrus-graylog-hook) | Hook for logging to [Graylog](http://graylog2.org/) |
|
|
||||||
| [Raygun](https://github.com/squirkle/logrus-raygun-hook) | Hook for logging to [Raygun.io](http://raygun.io/) |
|
|
||||||
| [LFShook](https://github.com/rifflock/lfshook) | Hook for logging to the local filesystem |
|
|
||||||
| [Honeybadger](https://github.com/agonzalezro/logrus_honeybadger) | Hook for sending exceptions to Honeybadger |
|
|
||||||
| [Mail](https://github.com/zbindenren/logrus_mail) | Hook for sending exceptions via mail |
|
|
||||||
| [Rollrus](https://github.com/heroku/rollrus) | Hook for sending errors to rollbar |
|
|
||||||
| [Fluentd](https://github.com/evalphobia/logrus_fluent) | Hook for logging to fluentd |
|
|
||||||
| [Mongodb](https://github.com/weekface/mgorus) | Hook for logging to mongodb |
|
|
||||||
| [Influxus] (http://github.com/vlad-doru/influxus) | Hook for concurrently logging to [InfluxDB] (http://influxdata.com/) |
|
|
||||||
| [InfluxDB](https://github.com/Abramovic/logrus_influxdb) | Hook for logging to influxdb |
|
|
||||||
| [Octokit](https://github.com/dorajistyle/logrus-octokit-hook) | Hook for logging to github via octokit |
|
|
||||||
| [DeferPanic](https://github.com/deferpanic/dp-logrus) | Hook for logging to DeferPanic |
|
|
||||||
| [Redis-Hook](https://github.com/rogierlommers/logrus-redis-hook) | Hook for logging to a ELK stack (through Redis) |
|
|
||||||
| [Amqp-Hook](https://github.com/vladoatanasov/logrus_amqp) | Hook for logging to Amqp broker (Like RabbitMQ) |
|
|
||||||
| [KafkaLogrus](https://github.com/goibibo/KafkaLogrus) | Hook for logging to kafka |
|
|
||||||
| [Typetalk](https://github.com/dragon3/logrus-typetalk-hook) | Hook for logging to [Typetalk](https://www.typetalk.in/) |
|
|
||||||
| [ElasticSearch](https://github.com/sohlich/elogrus) | Hook for logging to ElasticSearch|
|
|
||||||
| [Sumorus](https://github.com/doublefree/sumorus) | Hook for logging to [SumoLogic](https://www.sumologic.com/)|
|
|
||||||
| [Scribe](https://github.com/sagar8192/logrus-scribe-hook) | Hook for logging to [Scribe](https://github.com/facebookarchive/scribe)|
|
|
||||||
| [Logstash](https://github.com/bshuster-repo/logrus-logstash-hook) | Hook for logging to [Logstash](https://www.elastic.co/products/logstash) |
|
|
||||||
| [logz.io](https://github.com/ripcurld00d/logrus-logzio-hook) | Hook for logging to [logz.io](https://logz.io), a Log as a Service using Logstash |
|
|
||||||
| [Logmatic.io](https://github.com/logmatic/logmatic-go) | Hook for logging to [Logmatic.io](http://logmatic.io/) |
|
|
||||||
| [Pushover](https://github.com/toorop/logrus_pushover) | Send error via [Pushover](https://pushover.net) |
|
|
||||||
|
|
||||||
|
|
||||||
#### Level logging
|
|
||||||
|
|
||||||
Logrus has six logging levels: Debug, Info, Warning, Error, Fatal and Panic.
|
|
||||||
|
|
||||||
```go
|
|
||||||
log.Debug("Useful debugging information.")
|
|
||||||
log.Info("Something noteworthy happened!")
|
|
||||||
log.Warn("You should probably take a look at this.")
|
|
||||||
log.Error("Something failed but I'm not quitting.")
|
|
||||||
// Calls os.Exit(1) after logging
|
|
||||||
log.Fatal("Bye.")
|
|
||||||
// Calls panic() after logging
|
|
||||||
log.Panic("I'm bailing.")
|
|
||||||
```
|
|
||||||
|
|
||||||
You can set the logging level on a `Logger`, then it will only log entries with
|
|
||||||
that severity or anything above it:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Will log anything that is info or above (warn, error, fatal, panic). Default.
|
|
||||||
log.SetLevel(log.InfoLevel)
|
|
||||||
```
|
|
||||||
|
|
||||||
It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose
|
|
||||||
environment if your application has that.
|
|
||||||
|
|
||||||
#### Entries
|
|
||||||
|
|
||||||
Besides the fields added with `WithField` or `WithFields` some fields are
|
|
||||||
automatically added to all logging events:
|
|
||||||
|
|
||||||
1. `time`. The timestamp when the entry was created.
|
|
||||||
2. `msg`. The logging message passed to `{Info,Warn,Error,Fatal,Panic}` after
|
|
||||||
the `AddFields` call. E.g. `Failed to send event.`
|
|
||||||
3. `level`. The logging level. E.g. `info`.
|
|
||||||
|
|
||||||
#### Environments
|
|
||||||
|
|
||||||
Logrus has no notion of environment.
|
|
||||||
|
|
||||||
If you wish for hooks and formatters to only be used in specific environments,
|
|
||||||
you should handle that yourself. For example, if your application has a global
|
|
||||||
variable `Environment`, which is a string representation of the environment you
|
|
||||||
could do:
|
|
||||||
|
|
||||||
```go
|
|
||||||
import (
|
|
||||||
log "github.com/Sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
init() {
|
|
||||||
// do something here to set environment depending on an environment variable
|
|
||||||
// or command-line flag
|
|
||||||
if Environment == "production" {
|
|
||||||
log.SetFormatter(&log.JSONFormatter{})
|
|
||||||
} else {
|
|
||||||
// The TextFormatter is default, you don't actually have to do this.
|
|
||||||
log.SetFormatter(&log.TextFormatter{})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This configuration is how `logrus` was intended to be used, but JSON in
|
|
||||||
production is mostly only useful if you do log aggregation with tools like
|
|
||||||
Splunk or Logstash.
|
|
||||||
|
|
||||||
#### Formatters
|
|
||||||
|
|
||||||
The built-in logging formatters are:
|
|
||||||
|
|
||||||
* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise
|
|
||||||
without colors.
|
|
||||||
* *Note:* to force colored output when there is no TTY, set the `ForceColors`
|
|
||||||
field to `true`. To force no colored output even if there is a TTY set the
|
|
||||||
`DisableColors` field to `true`
|
|
||||||
* `logrus.JSONFormatter`. Logs fields as JSON.
|
|
||||||
|
|
||||||
Third party logging formatters:
|
|
||||||
|
|
||||||
* [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events.
|
|
||||||
* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout.
|
|
||||||
* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
|
|
||||||
|
|
||||||
You can define your formatter by implementing the `Formatter` interface,
|
|
||||||
requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
|
|
||||||
`Fields` type (`map[string]interface{}`) with all your fields as well as the
|
|
||||||
default ones (see Entries section above):
|
|
||||||
|
|
||||||
```go
|
|
||||||
type MyJSONFormatter struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
log.SetFormatter(new(MyJSONFormatter))
|
|
||||||
|
|
||||||
func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) {
|
|
||||||
// Note this doesn't include Time, Level and Message which are available on
|
|
||||||
// the Entry. Consult `godoc` on information about those fields or read the
|
|
||||||
// source of the official loggers.
|
|
||||||
serialized, err := json.Marshal(entry.Data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
|
|
||||||
}
|
|
||||||
return append(serialized, '\n'), nil
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Logger as an `io.Writer`
|
|
||||||
|
|
||||||
Logrus can be transformed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it.
|
|
||||||
|
|
||||||
```go
|
|
||||||
w := logger.Writer()
|
|
||||||
defer w.Close()
|
|
||||||
|
|
||||||
srv := http.Server{
|
|
||||||
// create a stdlib log.Logger that writes to
|
|
||||||
// logrus.Logger.
|
|
||||||
ErrorLog: log.New(w, "", 0),
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Each line written to that writer will be printed the usual way, using formatters
|
|
||||||
and hooks. The level for those entries is `info`.
|
|
||||||
|
|
||||||
#### Rotation
|
|
||||||
|
|
||||||
Log rotation is not provided with Logrus. Log rotation should be done by an
|
|
||||||
external program (like `logrotate(8)`) that can compress and delete old log
|
|
||||||
entries. It should not be a feature of the application-level logger.
|
|
||||||
|
|
||||||
#### Tools
|
|
||||||
|
|
||||||
| Tool | Description |
|
|
||||||
| ---- | ----------- |
|
|
||||||
|[Logrus Mate](https://github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will generated with different config at different environment.|
|
|
||||||
|[Logrus Viper Helper](https://github.com/heirko/go-contrib/tree/master/logrusHelper)|An Helper arround Logrus to wrap with spf13/Viper to load configuration with fangs! And to simplify Logrus configuration use some behavior of [Logrus Mate](https://github.com/gogap/logrus_mate). [sample](https://github.com/heirko/iris-contrib/blob/master/middleware/logrus-logger/example) |
|
|
||||||
|
|
||||||
#### Testing
|
|
||||||
|
|
||||||
Logrus has a built in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides:
|
|
||||||
|
|
||||||
* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just add the `test` hook
|
|
||||||
* a test logger (`test.NewNullLogger`) that just records log messages (and does not output any):
|
|
||||||
|
|
||||||
```go
|
|
||||||
logger, hook := NewNullLogger()
|
|
||||||
logger.Error("Hello error")
|
|
||||||
|
|
||||||
assert.Equal(1, len(hook.Entries))
|
|
||||||
assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level)
|
|
||||||
assert.Equal("Hello error", hook.LastEntry().Message)
|
|
||||||
|
|
||||||
hook.Reset()
|
|
||||||
assert.Nil(hook.LastEntry())
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Fatal handlers
|
|
||||||
|
|
||||||
Logrus can register one or more functions that will be called when any `fatal`
|
|
||||||
level message is logged. The registered handlers will be executed before
|
|
||||||
logrus performs a `os.Exit(1)`. This behavior may be helpful if callers need
|
|
||||||
to gracefully shutdown. Unlike a `panic("Something went wrong...")` call which can be intercepted with a deferred `recover` a call to `os.Exit(1)` can not be intercepted.
|
|
||||||
|
|
||||||
```
|
|
||||||
...
|
|
||||||
handler := func() {
|
|
||||||
// gracefully shutdown something...
|
|
||||||
}
|
|
||||||
logrus.RegisterExitHandler(handler)
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Thread safty
|
|
||||||
|
|
||||||
By default Logger is protected by mutex for concurrent writes, this mutex is invoked when calling hooks and writing logs.
|
|
||||||
If you are sure such locking is not needed, you can call logger.SetNoLock() to disable the locking.
|
|
||||||
|
|
||||||
Situation when locking is not needed includes:
|
|
||||||
|
|
||||||
* You have no hooks registered, or hooks calling is already thread-safe.
|
|
||||||
|
|
||||||
* Writing to logger.Out is already thread-safe, for example:
|
|
||||||
|
|
||||||
1) logger.Out is protected by locks.
|
|
||||||
|
|
||||||
2) logger.Out is a os.File handler opened with `O_APPEND` flag, and every write is smaller than 4k. (This allow multi-thread/multi-process writing)
|
|
||||||
|
|
||||||
(Refer to http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/)
|
|
74
vendor/github.com/Sirupsen/logrus/alt_exit_test.go
generated
vendored
74
vendor/github.com/Sirupsen/logrus/alt_exit_test.go
generated
vendored
|
@ -1,74 +0,0 @@
|
||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os/exec"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestRegister(t *testing.T) {
|
|
||||||
current := len(handlers)
|
|
||||||
RegisterExitHandler(func() {})
|
|
||||||
if len(handlers) != current+1 {
|
|
||||||
t.Fatalf("can't add handler")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHandler(t *testing.T) {
|
|
||||||
gofile := "/tmp/testprog.go"
|
|
||||||
if err := ioutil.WriteFile(gofile, testprog, 0666); err != nil {
|
|
||||||
t.Fatalf("can't create go file")
|
|
||||||
}
|
|
||||||
|
|
||||||
outfile := "/tmp/testprog.out"
|
|
||||||
arg := time.Now().UTC().String()
|
|
||||||
err := exec.Command("go", "run", gofile, outfile, arg).Run()
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("completed normally, should have failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := ioutil.ReadFile(outfile)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("can't read output file %s", outfile)
|
|
||||||
}
|
|
||||||
|
|
||||||
if string(data) != arg {
|
|
||||||
t.Fatalf("bad data")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var testprog = []byte(`
|
|
||||||
// Test program for atexit, gets output file and data as arguments and writes
|
|
||||||
// data to output file in atexit handler.
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
var outfile = ""
|
|
||||||
var data = ""
|
|
||||||
|
|
||||||
func handler() {
|
|
||||||
ioutil.WriteFile(outfile, []byte(data), 0666)
|
|
||||||
}
|
|
||||||
|
|
||||||
func badHandler() {
|
|
||||||
n := 0
|
|
||||||
fmt.Println(1/n)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
flag.Parse()
|
|
||||||
outfile = flag.Arg(0)
|
|
||||||
data = flag.Arg(1)
|
|
||||||
|
|
||||||
logrus.RegisterExitHandler(handler)
|
|
||||||
logrus.RegisterExitHandler(badHandler)
|
|
||||||
logrus.Fatal("Bye bye")
|
|
||||||
}
|
|
||||||
`)
|
|
77
vendor/github.com/Sirupsen/logrus/entry_test.go
generated
vendored
77
vendor/github.com/Sirupsen/logrus/entry_test.go
generated
vendored
|
@ -1,77 +0,0 @@
|
||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestEntryWithError(t *testing.T) {
|
|
||||||
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
ErrorKey = "error"
|
|
||||||
}()
|
|
||||||
|
|
||||||
err := fmt.Errorf("kaboom at layer %d", 4711)
|
|
||||||
|
|
||||||
assert.Equal(err, WithError(err).Data["error"])
|
|
||||||
|
|
||||||
logger := New()
|
|
||||||
logger.Out = &bytes.Buffer{}
|
|
||||||
entry := NewEntry(logger)
|
|
||||||
|
|
||||||
assert.Equal(err, entry.WithError(err).Data["error"])
|
|
||||||
|
|
||||||
ErrorKey = "err"
|
|
||||||
|
|
||||||
assert.Equal(err, entry.WithError(err).Data["err"])
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEntryPanicln(t *testing.T) {
|
|
||||||
errBoom := fmt.Errorf("boom time")
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
p := recover()
|
|
||||||
assert.NotNil(t, p)
|
|
||||||
|
|
||||||
switch pVal := p.(type) {
|
|
||||||
case *Entry:
|
|
||||||
assert.Equal(t, "kaboom", pVal.Message)
|
|
||||||
assert.Equal(t, errBoom, pVal.Data["err"])
|
|
||||||
default:
|
|
||||||
t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
logger := New()
|
|
||||||
logger.Out = &bytes.Buffer{}
|
|
||||||
entry := NewEntry(logger)
|
|
||||||
entry.WithField("err", errBoom).Panicln("kaboom")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEntryPanicf(t *testing.T) {
|
|
||||||
errBoom := fmt.Errorf("boom again")
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
p := recover()
|
|
||||||
assert.NotNil(t, p)
|
|
||||||
|
|
||||||
switch pVal := p.(type) {
|
|
||||||
case *Entry:
|
|
||||||
assert.Equal(t, "kaboom true", pVal.Message)
|
|
||||||
assert.Equal(t, errBoom, pVal.Data["err"])
|
|
||||||
default:
|
|
||||||
t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
logger := New()
|
|
||||||
logger.Out = &bytes.Buffer{}
|
|
||||||
entry := NewEntry(logger)
|
|
||||||
entry.WithField("err", errBoom).Panicf("kaboom %v", true)
|
|
||||||
}
|
|
98
vendor/github.com/Sirupsen/logrus/formatter_bench_test.go
generated
vendored
98
vendor/github.com/Sirupsen/logrus/formatter_bench_test.go
generated
vendored
|
@ -1,98 +0,0 @@
|
||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// smallFields is a small size data set for benchmarking
|
|
||||||
var smallFields = Fields{
|
|
||||||
"foo": "bar",
|
|
||||||
"baz": "qux",
|
|
||||||
"one": "two",
|
|
||||||
"three": "four",
|
|
||||||
}
|
|
||||||
|
|
||||||
// largeFields is a large size data set for benchmarking
|
|
||||||
var largeFields = Fields{
|
|
||||||
"foo": "bar",
|
|
||||||
"baz": "qux",
|
|
||||||
"one": "two",
|
|
||||||
"three": "four",
|
|
||||||
"five": "six",
|
|
||||||
"seven": "eight",
|
|
||||||
"nine": "ten",
|
|
||||||
"eleven": "twelve",
|
|
||||||
"thirteen": "fourteen",
|
|
||||||
"fifteen": "sixteen",
|
|
||||||
"seventeen": "eighteen",
|
|
||||||
"nineteen": "twenty",
|
|
||||||
"a": "b",
|
|
||||||
"c": "d",
|
|
||||||
"e": "f",
|
|
||||||
"g": "h",
|
|
||||||
"i": "j",
|
|
||||||
"k": "l",
|
|
||||||
"m": "n",
|
|
||||||
"o": "p",
|
|
||||||
"q": "r",
|
|
||||||
"s": "t",
|
|
||||||
"u": "v",
|
|
||||||
"w": "x",
|
|
||||||
"y": "z",
|
|
||||||
"this": "will",
|
|
||||||
"make": "thirty",
|
|
||||||
"entries": "yeah",
|
|
||||||
}
|
|
||||||
|
|
||||||
var errorFields = Fields{
|
|
||||||
"foo": fmt.Errorf("bar"),
|
|
||||||
"baz": fmt.Errorf("qux"),
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkErrorTextFormatter(b *testing.B) {
|
|
||||||
doBenchmark(b, &TextFormatter{DisableColors: true}, errorFields)
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkSmallTextFormatter(b *testing.B) {
|
|
||||||
doBenchmark(b, &TextFormatter{DisableColors: true}, smallFields)
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkLargeTextFormatter(b *testing.B) {
|
|
||||||
doBenchmark(b, &TextFormatter{DisableColors: true}, largeFields)
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkSmallColoredTextFormatter(b *testing.B) {
|
|
||||||
doBenchmark(b, &TextFormatter{ForceColors: true}, smallFields)
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkLargeColoredTextFormatter(b *testing.B) {
|
|
||||||
doBenchmark(b, &TextFormatter{ForceColors: true}, largeFields)
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkSmallJSONFormatter(b *testing.B) {
|
|
||||||
doBenchmark(b, &JSONFormatter{}, smallFields)
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkLargeJSONFormatter(b *testing.B) {
|
|
||||||
doBenchmark(b, &JSONFormatter{}, largeFields)
|
|
||||||
}
|
|
||||||
|
|
||||||
func doBenchmark(b *testing.B, formatter Formatter, fields Fields) {
|
|
||||||
entry := &Entry{
|
|
||||||
Time: time.Time{},
|
|
||||||
Level: InfoLevel,
|
|
||||||
Message: "message",
|
|
||||||
Data: fields,
|
|
||||||
}
|
|
||||||
var d []byte
|
|
||||||
var err error
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
d, err = formatter.Format(entry)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
b.SetBytes(int64(len(d)))
|
|
||||||
}
|
|
||||||
}
|
|
122
vendor/github.com/Sirupsen/logrus/hook_test.go
generated
vendored
122
vendor/github.com/Sirupsen/logrus/hook_test.go
generated
vendored
|
@ -1,122 +0,0 @@
|
||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TestHook struct {
|
|
||||||
Fired bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hook *TestHook) Fire(entry *Entry) error {
|
|
||||||
hook.Fired = true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hook *TestHook) Levels() []Level {
|
|
||||||
return []Level{
|
|
||||||
DebugLevel,
|
|
||||||
InfoLevel,
|
|
||||||
WarnLevel,
|
|
||||||
ErrorLevel,
|
|
||||||
FatalLevel,
|
|
||||||
PanicLevel,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHookFires(t *testing.T) {
|
|
||||||
hook := new(TestHook)
|
|
||||||
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.Hooks.Add(hook)
|
|
||||||
assert.Equal(t, hook.Fired, false)
|
|
||||||
|
|
||||||
log.Print("test")
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, hook.Fired, true)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type ModifyHook struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hook *ModifyHook) Fire(entry *Entry) error {
|
|
||||||
entry.Data["wow"] = "whale"
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hook *ModifyHook) Levels() []Level {
|
|
||||||
return []Level{
|
|
||||||
DebugLevel,
|
|
||||||
InfoLevel,
|
|
||||||
WarnLevel,
|
|
||||||
ErrorLevel,
|
|
||||||
FatalLevel,
|
|
||||||
PanicLevel,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHookCanModifyEntry(t *testing.T) {
|
|
||||||
hook := new(ModifyHook)
|
|
||||||
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.Hooks.Add(hook)
|
|
||||||
log.WithField("wow", "elephant").Print("test")
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, fields["wow"], "whale")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCanFireMultipleHooks(t *testing.T) {
|
|
||||||
hook1 := new(ModifyHook)
|
|
||||||
hook2 := new(TestHook)
|
|
||||||
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.Hooks.Add(hook1)
|
|
||||||
log.Hooks.Add(hook2)
|
|
||||||
|
|
||||||
log.WithField("wow", "elephant").Print("test")
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, fields["wow"], "whale")
|
|
||||||
assert.Equal(t, hook2.Fired, true)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type ErrorHook struct {
|
|
||||||
Fired bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hook *ErrorHook) Fire(entry *Entry) error {
|
|
||||||
hook.Fired = true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hook *ErrorHook) Levels() []Level {
|
|
||||||
return []Level{
|
|
||||||
ErrorLevel,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestErrorHookShouldntFireOnInfo(t *testing.T) {
|
|
||||||
hook := new(ErrorHook)
|
|
||||||
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.Hooks.Add(hook)
|
|
||||||
log.Info("test")
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, hook.Fired, false)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestErrorHookShouldFireOnError(t *testing.T) {
|
|
||||||
hook := new(ErrorHook)
|
|
||||||
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.Hooks.Add(hook)
|
|
||||||
log.Error("test")
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, hook.Fired, true)
|
|
||||||
})
|
|
||||||
}
|
|
120
vendor/github.com/Sirupsen/logrus/json_formatter_test.go
generated
vendored
120
vendor/github.com/Sirupsen/logrus/json_formatter_test.go
generated
vendored
|
@ -1,120 +0,0 @@
|
||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestErrorNotLost(t *testing.T) {
|
|
||||||
formatter := &JSONFormatter{}
|
|
||||||
|
|
||||||
b, err := formatter.Format(WithField("error", errors.New("wild walrus")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Unable to format entry: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
entry := make(map[string]interface{})
|
|
||||||
err = json.Unmarshal(b, &entry)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Unable to unmarshal formatted entry: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if entry["error"] != "wild walrus" {
|
|
||||||
t.Fatal("Error field not set")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestErrorNotLostOnFieldNotNamedError(t *testing.T) {
|
|
||||||
formatter := &JSONFormatter{}
|
|
||||||
|
|
||||||
b, err := formatter.Format(WithField("omg", errors.New("wild walrus")))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Unable to format entry: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
entry := make(map[string]interface{})
|
|
||||||
err = json.Unmarshal(b, &entry)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Unable to unmarshal formatted entry: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if entry["omg"] != "wild walrus" {
|
|
||||||
t.Fatal("Error field not set")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFieldClashWithTime(t *testing.T) {
|
|
||||||
formatter := &JSONFormatter{}
|
|
||||||
|
|
||||||
b, err := formatter.Format(WithField("time", "right now!"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Unable to format entry: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
entry := make(map[string]interface{})
|
|
||||||
err = json.Unmarshal(b, &entry)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Unable to unmarshal formatted entry: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if entry["fields.time"] != "right now!" {
|
|
||||||
t.Fatal("fields.time not set to original time field")
|
|
||||||
}
|
|
||||||
|
|
||||||
if entry["time"] != "0001-01-01T00:00:00Z" {
|
|
||||||
t.Fatal("time field not set to current time, was: ", entry["time"])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFieldClashWithMsg(t *testing.T) {
|
|
||||||
formatter := &JSONFormatter{}
|
|
||||||
|
|
||||||
b, err := formatter.Format(WithField("msg", "something"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Unable to format entry: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
entry := make(map[string]interface{})
|
|
||||||
err = json.Unmarshal(b, &entry)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Unable to unmarshal formatted entry: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if entry["fields.msg"] != "something" {
|
|
||||||
t.Fatal("fields.msg not set to original msg field")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFieldClashWithLevel(t *testing.T) {
|
|
||||||
formatter := &JSONFormatter{}
|
|
||||||
|
|
||||||
b, err := formatter.Format(WithField("level", "something"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Unable to format entry: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
entry := make(map[string]interface{})
|
|
||||||
err = json.Unmarshal(b, &entry)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Unable to unmarshal formatted entry: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if entry["fields.level"] != "something" {
|
|
||||||
t.Fatal("fields.level not set to original level field")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestJSONEntryEndsWithNewline(t *testing.T) {
|
|
||||||
formatter := &JSONFormatter{}
|
|
||||||
|
|
||||||
b, err := formatter.Format(WithField("level", "something"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Unable to format entry: ", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if b[len(b)-1] != '\n' {
|
|
||||||
t.Fatal("Expected JSON log entry to end with a newline")
|
|
||||||
}
|
|
||||||
}
|
|
61
vendor/github.com/Sirupsen/logrus/logger_bench_test.go
generated
vendored
61
vendor/github.com/Sirupsen/logrus/logger_bench_test.go
generated
vendored
|
@ -1,61 +0,0 @@
|
||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// smallFields is a small size data set for benchmarking
|
|
||||||
var loggerFields = Fields{
|
|
||||||
"foo": "bar",
|
|
||||||
"baz": "qux",
|
|
||||||
"one": "two",
|
|
||||||
"three": "four",
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkDummyLogger(b *testing.B) {
|
|
||||||
nullf, err := os.OpenFile("/dev/null", os.O_WRONLY, 0666)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatalf("%v", err)
|
|
||||||
}
|
|
||||||
defer nullf.Close()
|
|
||||||
doLoggerBenchmark(b, nullf, &TextFormatter{DisableColors: true}, smallFields)
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkDummyLoggerNoLock(b *testing.B) {
|
|
||||||
nullf, err := os.OpenFile("/dev/null", os.O_WRONLY|os.O_APPEND, 0666)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatalf("%v", err)
|
|
||||||
}
|
|
||||||
defer nullf.Close()
|
|
||||||
doLoggerBenchmarkNoLock(b, nullf, &TextFormatter{DisableColors: true}, smallFields)
|
|
||||||
}
|
|
||||||
|
|
||||||
func doLoggerBenchmark(b *testing.B, out *os.File, formatter Formatter, fields Fields) {
|
|
||||||
logger := Logger{
|
|
||||||
Out: out,
|
|
||||||
Level: InfoLevel,
|
|
||||||
Formatter: formatter,
|
|
||||||
}
|
|
||||||
entry := logger.WithFields(fields)
|
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
|
||||||
for pb.Next() {
|
|
||||||
entry.Info("aaa")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func doLoggerBenchmarkNoLock(b *testing.B, out *os.File, formatter Formatter, fields Fields) {
|
|
||||||
logger := Logger{
|
|
||||||
Out: out,
|
|
||||||
Level: InfoLevel,
|
|
||||||
Formatter: formatter,
|
|
||||||
}
|
|
||||||
logger.SetNoLock()
|
|
||||||
entry := logger.WithFields(fields)
|
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
|
||||||
for pb.Next() {
|
|
||||||
entry.Info("aaa")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
361
vendor/github.com/Sirupsen/logrus/logrus_test.go
generated
vendored
361
vendor/github.com/Sirupsen/logrus/logrus_test.go
generated
vendored
|
@ -1,361 +0,0 @@
|
||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func LogAndAssertJSON(t *testing.T, log func(*Logger), assertions func(fields Fields)) {
|
|
||||||
var buffer bytes.Buffer
|
|
||||||
var fields Fields
|
|
||||||
|
|
||||||
logger := New()
|
|
||||||
logger.Out = &buffer
|
|
||||||
logger.Formatter = new(JSONFormatter)
|
|
||||||
|
|
||||||
log(logger)
|
|
||||||
|
|
||||||
err := json.Unmarshal(buffer.Bytes(), &fields)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
|
|
||||||
assertions(fields)
|
|
||||||
}
|
|
||||||
|
|
||||||
func LogAndAssertText(t *testing.T, log func(*Logger), assertions func(fields map[string]string)) {
|
|
||||||
var buffer bytes.Buffer
|
|
||||||
|
|
||||||
logger := New()
|
|
||||||
logger.Out = &buffer
|
|
||||||
logger.Formatter = &TextFormatter{
|
|
||||||
DisableColors: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
log(logger)
|
|
||||||
|
|
||||||
fields := make(map[string]string)
|
|
||||||
for _, kv := range strings.Split(buffer.String(), " ") {
|
|
||||||
if !strings.Contains(kv, "=") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
kvArr := strings.Split(kv, "=")
|
|
||||||
key := strings.TrimSpace(kvArr[0])
|
|
||||||
val := kvArr[1]
|
|
||||||
if kvArr[1][0] == '"' {
|
|
||||||
var err error
|
|
||||||
val, err = strconv.Unquote(val)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
fields[key] = val
|
|
||||||
}
|
|
||||||
assertions(fields)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPrint(t *testing.T) {
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.Print("test")
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, fields["msg"], "test")
|
|
||||||
assert.Equal(t, fields["level"], "info")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInfo(t *testing.T) {
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.Info("test")
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, fields["msg"], "test")
|
|
||||||
assert.Equal(t, fields["level"], "info")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWarn(t *testing.T) {
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.Warn("test")
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, fields["msg"], "test")
|
|
||||||
assert.Equal(t, fields["level"], "warning")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInfolnShouldAddSpacesBetweenStrings(t *testing.T) {
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.Infoln("test", "test")
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, fields["msg"], "test test")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInfolnShouldAddSpacesBetweenStringAndNonstring(t *testing.T) {
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.Infoln("test", 10)
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, fields["msg"], "test 10")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInfolnShouldAddSpacesBetweenTwoNonStrings(t *testing.T) {
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.Infoln(10, 10)
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, fields["msg"], "10 10")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInfoShouldAddSpacesBetweenTwoNonStrings(t *testing.T) {
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.Infoln(10, 10)
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, fields["msg"], "10 10")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInfoShouldNotAddSpacesBetweenStringAndNonstring(t *testing.T) {
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.Info("test", 10)
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, fields["msg"], "test10")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInfoShouldNotAddSpacesBetweenStrings(t *testing.T) {
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.Info("test", "test")
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, fields["msg"], "testtest")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWithFieldsShouldAllowAssignments(t *testing.T) {
|
|
||||||
var buffer bytes.Buffer
|
|
||||||
var fields Fields
|
|
||||||
|
|
||||||
logger := New()
|
|
||||||
logger.Out = &buffer
|
|
||||||
logger.Formatter = new(JSONFormatter)
|
|
||||||
|
|
||||||
localLog := logger.WithFields(Fields{
|
|
||||||
"key1": "value1",
|
|
||||||
})
|
|
||||||
|
|
||||||
localLog.WithField("key2", "value2").Info("test")
|
|
||||||
err := json.Unmarshal(buffer.Bytes(), &fields)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, "value2", fields["key2"])
|
|
||||||
assert.Equal(t, "value1", fields["key1"])
|
|
||||||
|
|
||||||
buffer = bytes.Buffer{}
|
|
||||||
fields = Fields{}
|
|
||||||
localLog.Info("test")
|
|
||||||
err = json.Unmarshal(buffer.Bytes(), &fields)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
|
|
||||||
_, ok := fields["key2"]
|
|
||||||
assert.Equal(t, false, ok)
|
|
||||||
assert.Equal(t, "value1", fields["key1"])
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUserSuppliedFieldDoesNotOverwriteDefaults(t *testing.T) {
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.WithField("msg", "hello").Info("test")
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, fields["msg"], "test")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUserSuppliedMsgFieldHasPrefix(t *testing.T) {
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.WithField("msg", "hello").Info("test")
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, fields["msg"], "test")
|
|
||||||
assert.Equal(t, fields["fields.msg"], "hello")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUserSuppliedTimeFieldHasPrefix(t *testing.T) {
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.WithField("time", "hello").Info("test")
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, fields["fields.time"], "hello")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUserSuppliedLevelFieldHasPrefix(t *testing.T) {
|
|
||||||
LogAndAssertJSON(t, func(log *Logger) {
|
|
||||||
log.WithField("level", 1).Info("test")
|
|
||||||
}, func(fields Fields) {
|
|
||||||
assert.Equal(t, fields["level"], "info")
|
|
||||||
assert.Equal(t, fields["fields.level"], 1.0) // JSON has floats only
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDefaultFieldsAreNotPrefixed(t *testing.T) {
|
|
||||||
LogAndAssertText(t, func(log *Logger) {
|
|
||||||
ll := log.WithField("herp", "derp")
|
|
||||||
ll.Info("hello")
|
|
||||||
ll.Info("bye")
|
|
||||||
}, func(fields map[string]string) {
|
|
||||||
for _, fieldName := range []string{"fields.level", "fields.time", "fields.msg"} {
|
|
||||||
if _, ok := fields[fieldName]; ok {
|
|
||||||
t.Fatalf("should not have prefixed %q: %v", fieldName, fields)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDoubleLoggingDoesntPrefixPreviousFields(t *testing.T) {
|
|
||||||
|
|
||||||
var buffer bytes.Buffer
|
|
||||||
var fields Fields
|
|
||||||
|
|
||||||
logger := New()
|
|
||||||
logger.Out = &buffer
|
|
||||||
logger.Formatter = new(JSONFormatter)
|
|
||||||
|
|
||||||
llog := logger.WithField("context", "eating raw fish")
|
|
||||||
|
|
||||||
llog.Info("looks delicious")
|
|
||||||
|
|
||||||
err := json.Unmarshal(buffer.Bytes(), &fields)
|
|
||||||
assert.NoError(t, err, "should have decoded first message")
|
|
||||||
assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields")
|
|
||||||
assert.Equal(t, fields["msg"], "looks delicious")
|
|
||||||
assert.Equal(t, fields["context"], "eating raw fish")
|
|
||||||
|
|
||||||
buffer.Reset()
|
|
||||||
|
|
||||||
llog.Warn("omg it is!")
|
|
||||||
|
|
||||||
err = json.Unmarshal(buffer.Bytes(), &fields)
|
|
||||||
assert.NoError(t, err, "should have decoded second message")
|
|
||||||
assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields")
|
|
||||||
assert.Equal(t, fields["msg"], "omg it is!")
|
|
||||||
assert.Equal(t, fields["context"], "eating raw fish")
|
|
||||||
assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConvertLevelToString(t *testing.T) {
|
|
||||||
assert.Equal(t, "debug", DebugLevel.String())
|
|
||||||
assert.Equal(t, "info", InfoLevel.String())
|
|
||||||
assert.Equal(t, "warning", WarnLevel.String())
|
|
||||||
assert.Equal(t, "error", ErrorLevel.String())
|
|
||||||
assert.Equal(t, "fatal", FatalLevel.String())
|
|
||||||
assert.Equal(t, "panic", PanicLevel.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseLevel(t *testing.T) {
|
|
||||||
l, err := ParseLevel("panic")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, PanicLevel, l)
|
|
||||||
|
|
||||||
l, err = ParseLevel("PANIC")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, PanicLevel, l)
|
|
||||||
|
|
||||||
l, err = ParseLevel("fatal")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, FatalLevel, l)
|
|
||||||
|
|
||||||
l, err = ParseLevel("FATAL")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, FatalLevel, l)
|
|
||||||
|
|
||||||
l, err = ParseLevel("error")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, ErrorLevel, l)
|
|
||||||
|
|
||||||
l, err = ParseLevel("ERROR")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, ErrorLevel, l)
|
|
||||||
|
|
||||||
l, err = ParseLevel("warn")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, WarnLevel, l)
|
|
||||||
|
|
||||||
l, err = ParseLevel("WARN")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, WarnLevel, l)
|
|
||||||
|
|
||||||
l, err = ParseLevel("warning")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, WarnLevel, l)
|
|
||||||
|
|
||||||
l, err = ParseLevel("WARNING")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, WarnLevel, l)
|
|
||||||
|
|
||||||
l, err = ParseLevel("info")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, InfoLevel, l)
|
|
||||||
|
|
||||||
l, err = ParseLevel("INFO")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, InfoLevel, l)
|
|
||||||
|
|
||||||
l, err = ParseLevel("debug")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, DebugLevel, l)
|
|
||||||
|
|
||||||
l, err = ParseLevel("DEBUG")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, DebugLevel, l)
|
|
||||||
|
|
||||||
l, err = ParseLevel("invalid")
|
|
||||||
assert.Equal(t, "not a valid logrus Level: \"invalid\"", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetSetLevelRace(t *testing.T) {
|
|
||||||
wg := sync.WaitGroup{}
|
|
||||||
for i := 0; i < 100; i++ {
|
|
||||||
wg.Add(1)
|
|
||||||
go func(i int) {
|
|
||||||
defer wg.Done()
|
|
||||||
if i%2 == 0 {
|
|
||||||
SetLevel(InfoLevel)
|
|
||||||
} else {
|
|
||||||
GetLevel()
|
|
||||||
}
|
|
||||||
}(i)
|
|
||||||
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLoggingRace(t *testing.T) {
|
|
||||||
logger := New()
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
wg.Add(100)
|
|
||||||
|
|
||||||
for i := 0; i < 100; i++ {
|
|
||||||
go func() {
|
|
||||||
logger.Info("info")
|
|
||||||
wg.Done()
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compile test
|
|
||||||
func TestLogrusInterface(t *testing.T) {
|
|
||||||
var buffer bytes.Buffer
|
|
||||||
fn := func(l FieldLogger) {
|
|
||||||
b := l.WithField("key", "value")
|
|
||||||
b.Debug("Test")
|
|
||||||
}
|
|
||||||
// test logger
|
|
||||||
logger := New()
|
|
||||||
logger.Out = &buffer
|
|
||||||
fn(logger)
|
|
||||||
|
|
||||||
// test Entry
|
|
||||||
e := logger.WithField("another", "value")
|
|
||||||
fn(e)
|
|
||||||
}
|
|
61
vendor/github.com/Sirupsen/logrus/text_formatter_test.go
generated
vendored
61
vendor/github.com/Sirupsen/logrus/text_formatter_test.go
generated
vendored
|
@ -1,61 +0,0 @@
|
||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestQuoting(t *testing.T) {
|
|
||||||
tf := &TextFormatter{DisableColors: true}
|
|
||||||
|
|
||||||
checkQuoting := func(q bool, value interface{}) {
|
|
||||||
b, _ := tf.Format(WithField("test", value))
|
|
||||||
idx := bytes.Index(b, ([]byte)("test="))
|
|
||||||
cont := bytes.Contains(b[idx+5:], []byte{'"'})
|
|
||||||
if cont != q {
|
|
||||||
if q {
|
|
||||||
t.Errorf("quoting expected for: %#v", value)
|
|
||||||
} else {
|
|
||||||
t.Errorf("quoting not expected for: %#v", value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
checkQuoting(false, "abcd")
|
|
||||||
checkQuoting(false, "v1.0")
|
|
||||||
checkQuoting(false, "1234567890")
|
|
||||||
checkQuoting(true, "/foobar")
|
|
||||||
checkQuoting(true, "x y")
|
|
||||||
checkQuoting(true, "x,y")
|
|
||||||
checkQuoting(false, errors.New("invalid"))
|
|
||||||
checkQuoting(true, errors.New("invalid argument"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTimestampFormat(t *testing.T) {
|
|
||||||
checkTimeStr := func(format string) {
|
|
||||||
customFormatter := &TextFormatter{DisableColors: true, TimestampFormat: format}
|
|
||||||
customStr, _ := customFormatter.Format(WithField("test", "test"))
|
|
||||||
timeStart := bytes.Index(customStr, ([]byte)("time="))
|
|
||||||
timeEnd := bytes.Index(customStr, ([]byte)("level="))
|
|
||||||
timeStr := customStr[timeStart+5 : timeEnd-1]
|
|
||||||
if timeStr[0] == '"' && timeStr[len(timeStr)-1] == '"' {
|
|
||||||
timeStr = timeStr[1 : len(timeStr)-1]
|
|
||||||
}
|
|
||||||
if format == "" {
|
|
||||||
format = time.RFC3339
|
|
||||||
}
|
|
||||||
_, e := time.Parse(format, (string)(timeStr))
|
|
||||||
if e != nil {
|
|
||||||
t.Errorf("time string \"%s\" did not match provided time format \"%s\": %s", timeStr, format, e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
checkTimeStr("2006-01-02T15:04:05.000000000Z07:00")
|
|
||||||
checkTimeStr("Mon Jan _2 15:04:05 2006")
|
|
||||||
checkTimeStr("")
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO add tests for sorting etc., this requires a parser for the text
|
|
||||||
// formatter output.
|
|
125
vendor/github.com/cockroachdb/cockroach-go/crdb/tx_test.go
generated
vendored
125
vendor/github.com/cockroachdb/cockroach-go/crdb/tx_test.go
generated
vendored
|
@ -1,125 +0,0 @@
|
||||||
// Copyright 2016 The Cockroach 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.
|
|
||||||
//
|
|
||||||
// Author: Spencer Kimball (spencer@cockroachlabs.com)
|
|
||||||
|
|
||||||
package crdb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/cockroachdb/cockroach-go/testserver"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestExecuteTx verifies transaction retry using the classic
|
|
||||||
// example of write skew in bank account balance transfers.
|
|
||||||
func TestExecuteTx(t *testing.T) {
|
|
||||||
db, stop := testserver.NewDBForTest(t)
|
|
||||||
defer stop()
|
|
||||||
|
|
||||||
initStmt := `
|
|
||||||
CREATE DATABASE d;
|
|
||||||
CREATE TABLE d.t (acct INT PRIMARY KEY, balance INT);
|
|
||||||
INSERT INTO d.t (acct, balance) VALUES (1, 100), (2, 100);
|
|
||||||
`
|
|
||||||
if _, err := db.Exec(initStmt); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
type queryI interface {
|
|
||||||
Query(string, ...interface{}) (*sql.Rows, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
getBalances := func(q queryI) (bal1, bal2 int, err error) {
|
|
||||||
var rows *sql.Rows
|
|
||||||
rows, err = q.Query(`SELECT balance FROM d.t WHERE acct IN (1, 2);`)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
balances := []*int{&bal1, &bal2}
|
|
||||||
i := 0
|
|
||||||
for ; rows.Next(); i += 1 {
|
|
||||||
if err = rows.Scan(balances[i]); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if i != 2 {
|
|
||||||
err = fmt.Errorf("expected two balances; got %d", i)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
runTxn := func(wg *sync.WaitGroup, iter *int) <-chan error {
|
|
||||||
errCh := make(chan error, 1)
|
|
||||||
go func() {
|
|
||||||
*iter = 0
|
|
||||||
errCh <- ExecuteTx(db, func(tx *sql.Tx) error {
|
|
||||||
*iter++
|
|
||||||
bal1, bal2, err := getBalances(tx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// If this is the first iteration, wait for the other tx to also read.
|
|
||||||
if *iter == 1 {
|
|
||||||
wg.Done()
|
|
||||||
wg.Wait()
|
|
||||||
}
|
|
||||||
// Now, subtract from one account and give to the other.
|
|
||||||
if bal1 > bal2 {
|
|
||||||
if _, err := tx.Exec(`
|
|
||||||
UPDATE d.t SET balance=balance-100 WHERE acct=1;
|
|
||||||
UPDATE d.t SET balance=balance+100 WHERE acct=2;
|
|
||||||
`); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if _, err := tx.Exec(`
|
|
||||||
UPDATE d.t SET balance=balance+100 WHERE acct=1;
|
|
||||||
UPDATE d.t SET balance=balance-100 WHERE acct=2;
|
|
||||||
`); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}()
|
|
||||||
return errCh
|
|
||||||
}
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
wg.Add(2)
|
|
||||||
var iters1, iters2 int
|
|
||||||
txn1Err := runTxn(&wg, &iters1)
|
|
||||||
txn2Err := runTxn(&wg, &iters2)
|
|
||||||
if err := <-txn1Err; err != nil {
|
|
||||||
t.Errorf("expected success in txn1; got %s", err)
|
|
||||||
}
|
|
||||||
if err := <-txn2Err; err != nil {
|
|
||||||
t.Errorf("expected success in txn2; got %s", err)
|
|
||||||
}
|
|
||||||
if iters1+iters2 <= 2 {
|
|
||||||
t.Errorf("expected at least one retry between the competing transactions; "+
|
|
||||||
"got txn1=%d, txn2=%d", iters1, iters2)
|
|
||||||
}
|
|
||||||
bal1, bal2, err := getBalances(db)
|
|
||||||
if err != nil || bal1 != 100 || bal2 != 100 {
|
|
||||||
t.Errorf("expected balances to be restored without error; "+
|
|
||||||
"got acct1=%d, acct2=%d: %s", bal1, bal2, err)
|
|
||||||
}
|
|
||||||
}
|
|
2
vendor/github.com/coreos/go-oidc/.gitignore
generated
vendored
2
vendor/github.com/coreos/go-oidc/.gitignore
generated
vendored
|
@ -1,2 +0,0 @@
|
||||||
/bin
|
|
||||||
/gopath
|
|
16
vendor/github.com/coreos/go-oidc/.travis.yml
generated
vendored
16
vendor/github.com/coreos/go-oidc/.travis.yml
generated
vendored
|
@ -1,16 +0,0 @@
|
||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.7.3
|
|
||||||
- 1.6.3
|
|
||||||
|
|
||||||
install:
|
|
||||||
- go get -v -t github.com/coreos/go-oidc/...
|
|
||||||
- go get golang.org/x/tools/cmd/cover
|
|
||||||
- go get github.com/golang/lint/golint
|
|
||||||
|
|
||||||
script:
|
|
||||||
- ./test
|
|
||||||
|
|
||||||
notifications:
|
|
||||||
email: false
|
|
71
vendor/github.com/coreos/go-oidc/CONTRIBUTING.md
generated
vendored
71
vendor/github.com/coreos/go-oidc/CONTRIBUTING.md
generated
vendored
|
@ -1,71 +0,0 @@
|
||||||
# How to Contribute
|
|
||||||
|
|
||||||
CoreOS projects are [Apache 2.0 licensed](LICENSE) and accept contributions via
|
|
||||||
GitHub pull requests. This document outlines some of the conventions on
|
|
||||||
development workflow, commit message formatting, contact points and other
|
|
||||||
resources to make it easier to get your contribution accepted.
|
|
||||||
|
|
||||||
# Certificate of Origin
|
|
||||||
|
|
||||||
By contributing to this project you agree to the Developer Certificate of
|
|
||||||
Origin (DCO). This document was created by the Linux Kernel community and is a
|
|
||||||
simple statement that you, as a contributor, have the legal right to make the
|
|
||||||
contribution. See the [DCO](DCO) file for details.
|
|
||||||
|
|
||||||
# Email and Chat
|
|
||||||
|
|
||||||
The project currently uses the general CoreOS email list and IRC channel:
|
|
||||||
- Email: [coreos-dev](https://groups.google.com/forum/#!forum/coreos-dev)
|
|
||||||
- IRC: #[coreos](irc://irc.freenode.org:6667/#coreos) IRC channel on freenode.org
|
|
||||||
|
|
||||||
Please avoid emailing maintainers found in the MAINTAINERS file directly. They
|
|
||||||
are very busy and read the mailing lists.
|
|
||||||
|
|
||||||
## Getting Started
|
|
||||||
|
|
||||||
- Fork the repository on GitHub
|
|
||||||
- Read the [README](README.md) for build and test instructions
|
|
||||||
- Play with the project, submit bugs, submit patches!
|
|
||||||
|
|
||||||
## Contribution Flow
|
|
||||||
|
|
||||||
This is a rough outline of what a contributor's workflow looks like:
|
|
||||||
|
|
||||||
- Create a topic branch from where you want to base your work (usually master).
|
|
||||||
- Make commits of logical units.
|
|
||||||
- Make sure your commit messages are in the proper format (see below).
|
|
||||||
- Push your changes to a topic branch in your fork of the repository.
|
|
||||||
- Make sure the tests pass, and add any new tests as appropriate.
|
|
||||||
- Submit a pull request to the original repository.
|
|
||||||
|
|
||||||
Thanks for your contributions!
|
|
||||||
|
|
||||||
### Format of the Commit Message
|
|
||||||
|
|
||||||
We follow a rough convention for commit messages that is designed to answer two
|
|
||||||
questions: what changed and why. The subject line should feature the what and
|
|
||||||
the body of the commit should describe the why.
|
|
||||||
|
|
||||||
```
|
|
||||||
scripts: add the test-cluster command
|
|
||||||
|
|
||||||
this uses tmux to setup a test cluster that you can easily kill and
|
|
||||||
start for debugging.
|
|
||||||
|
|
||||||
Fixes #38
|
|
||||||
```
|
|
||||||
|
|
||||||
The format can be described more formally as follows:
|
|
||||||
|
|
||||||
```
|
|
||||||
<subsystem>: <what changed>
|
|
||||||
<BLANK LINE>
|
|
||||||
<why this change was made>
|
|
||||||
<BLANK LINE>
|
|
||||||
<footer>
|
|
||||||
```
|
|
||||||
|
|
||||||
The first line is the subject and should be no longer than 70 characters, the
|
|
||||||
second line is always blank, and other lines should be wrapped at 80 characters.
|
|
||||||
This allows the message to be easier to read on GitHub as well as in various
|
|
||||||
git tools.
|
|
36
vendor/github.com/coreos/go-oidc/DCO
generated
vendored
36
vendor/github.com/coreos/go-oidc/DCO
generated
vendored
|
@ -1,36 +0,0 @@
|
||||||
Developer Certificate of Origin
|
|
||||||
Version 1.1
|
|
||||||
|
|
||||||
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
|
||||||
660 York Street, Suite 102,
|
|
||||||
San Francisco, CA 94110 USA
|
|
||||||
|
|
||||||
Everyone is permitted to copy and distribute verbatim copies of this
|
|
||||||
license document, but changing it is not allowed.
|
|
||||||
|
|
||||||
|
|
||||||
Developer's Certificate of Origin 1.1
|
|
||||||
|
|
||||||
By making a contribution to this project, I certify that:
|
|
||||||
|
|
||||||
(a) The contribution was created in whole or in part by me and I
|
|
||||||
have the right to submit it under the open source license
|
|
||||||
indicated in the file; or
|
|
||||||
|
|
||||||
(b) The contribution is based upon previous work that, to the best
|
|
||||||
of my knowledge, is covered under an appropriate open source
|
|
||||||
license and I have the right under that license to submit that
|
|
||||||
work with modifications, whether created in whole or in part
|
|
||||||
by me, under the same open source license (unless I am
|
|
||||||
permitted to submit under a different license), as indicated
|
|
||||||
in the file; or
|
|
||||||
|
|
||||||
(c) The contribution was provided directly to me by some other
|
|
||||||
person who certified (a), (b) or (c) and I have not modified
|
|
||||||
it.
|
|
||||||
|
|
||||||
(d) I understand and agree that this project and the contribution
|
|
||||||
are public and that a record of the contribution (including all
|
|
||||||
personal information I submit with it, including my sign-off) is
|
|
||||||
maintained indefinitely and may be redistributed consistent with
|
|
||||||
this project or the open source license(s) involved.
|
|
3
vendor/github.com/coreos/go-oidc/MAINTAINERS
generated
vendored
3
vendor/github.com/coreos/go-oidc/MAINTAINERS
generated
vendored
|
@ -1,3 +0,0 @@
|
||||||
Bobby Rullo <bobby.rullo@coreos.com> (@bobbyrullo)
|
|
||||||
Ed Rooth <ed.rooth@coreos.com> (@sym3tri)
|
|
||||||
Eric Chiang <eric.chiang@coreos.com> (@ericchiang)
|
|
72
vendor/github.com/coreos/go-oidc/README.md
generated
vendored
72
vendor/github.com/coreos/go-oidc/README.md
generated
vendored
|
@ -1,72 +0,0 @@
|
||||||
# go-oidc
|
|
||||||
|
|
||||||
[![GoDoc](https://godoc.org/github.com/coreos/go-oidc?status.svg)](https://godoc.org/github.com/coreos/go-oidc)
|
|
||||||
[![Build Status](https://travis-ci.org/coreos/go-oidc.png?branch=master)](https://travis-ci.org/coreos/go-oidc)
|
|
||||||
|
|
||||||
## OpenID Connect support for Go
|
|
||||||
|
|
||||||
This package enables OpenID Connect support for the [golang.org/x/oauth2](https://godoc.org/golang.org/x/oauth2) package.
|
|
||||||
|
|
||||||
```go
|
|
||||||
provider, err := oidc.NewProvider(ctx, "https://accounts.google.com")
|
|
||||||
if err != nil {
|
|
||||||
// handle error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure an OpenID Connect aware OAuth2 client.
|
|
||||||
oauth2Config := oauth2.Config{
|
|
||||||
ClientID: clientID,
|
|
||||||
ClientSecret: clientSecret,
|
|
||||||
RedirectURL: redirectURL,
|
|
||||||
|
|
||||||
// Discovery returns the OAuth2 endpoints.
|
|
||||||
Endpoint: provider.Endpoint(),
|
|
||||||
|
|
||||||
// "openid" is a required scope for OpenID Connect flows.
|
|
||||||
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
OAuth2 redirects are unchanged.
|
|
||||||
|
|
||||||
```go
|
|
||||||
func handleRedirect(w http.ResponseWriter, r *http.Request) {
|
|
||||||
http.Redirect(w, r, oauth2Config.AuthCodeURL(state), http.StatusFound)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The on responses, the provider can be used to verify ID Tokens.
|
|
||||||
|
|
||||||
```go
|
|
||||||
var verifier = provider.Verifier()
|
|
||||||
|
|
||||||
func handleOAuth2Callback(w http.ResponseWriter, r *http.Request) {
|
|
||||||
// Verify state and errors.
|
|
||||||
|
|
||||||
oauth2Token, err := oauth2Config.Exchange(ctx, r.URL.Query().Get("code"))
|
|
||||||
if err != nil {
|
|
||||||
// handle error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the ID Token from OAuth2 token.
|
|
||||||
rawIDToken, ok := oauth2Token.Extra("id_token").(string)
|
|
||||||
if !ok {
|
|
||||||
// handle missing token
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse and verify ID Token payload.
|
|
||||||
idToken, err := verifier.Verify(ctx, rawIDToken)
|
|
||||||
if err != nil {
|
|
||||||
// handle error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract custom claims
|
|
||||||
var claims struct {
|
|
||||||
Email string `json:"email"`
|
|
||||||
Verified bool `json:"email_verified"`
|
|
||||||
}
|
|
||||||
if err := idToken.Claims(&claims); err != nil {
|
|
||||||
// handle error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
405
vendor/github.com/coreos/go-oidc/jose_test.go
generated
vendored
405
vendor/github.com/coreos/go-oidc/jose_test.go
generated
vendored
|
@ -1,405 +0,0 @@
|
||||||
// +build !golint
|
|
||||||
|
|
||||||
// This file contains statically created JWKs for tests created by gen.go
|
|
||||||
|
|
||||||
package oidc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
jose "gopkg.in/square/go-jose.v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
func mustLoadJWK(s string) jose.JSONWebKey {
|
|
||||||
var jwk jose.JSONWebKey
|
|
||||||
if err := json.Unmarshal([]byte(s), &jwk); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return jwk
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
testKeyECDSA_256_0 = mustLoadJWK(`{
|
|
||||||
"kty": "EC",
|
|
||||||
"kid": "bd06f3e11f523e310f8c2c8b20a892727fb558e0e23602312568b20a41e11188",
|
|
||||||
"crv": "P-256",
|
|
||||||
"x": "xK5N69f0-SAgWbjw2otcQeCGs3qqYMyqOWk4Os5Z_Xc",
|
|
||||||
"y": "AXSaOPcMklJY9UKZhkGzVevqhAIUEzE3cfZ8o-ML5xE"
|
|
||||||
}`)
|
|
||||||
testKeyECDSA_256_0_Priv = mustLoadJWK(`{
|
|
||||||
"kty": "EC",
|
|
||||||
"kid": "bd06f3e11f523e310f8c2c8b20a892727fb558e0e23602312568b20a41e11188",
|
|
||||||
"crv": "P-256",
|
|
||||||
"x": "xK5N69f0-SAgWbjw2otcQeCGs3qqYMyqOWk4Os5Z_Xc",
|
|
||||||
"y": "AXSaOPcMklJY9UKZhkGzVevqhAIUEzE3cfZ8o-ML5xE",
|
|
||||||
"d": "L7jynYt-fMRPqw1e9vgXCGTg4yhGU4tlLxiFyNVimG4"
|
|
||||||
}`)
|
|
||||||
|
|
||||||
testKeyECDSA_256_1 = mustLoadJWK(`{
|
|
||||||
"kty": "EC",
|
|
||||||
"kid": "27ebd2f5bf6723e4de06caaab03703be458a37bf28a9fa748576a0826acb6ec2",
|
|
||||||
"crv": "P-256",
|
|
||||||
"x": "KZisP6wLCph4q6056jr7BH_asiX9RcLcS3HrNjdCpkw",
|
|
||||||
"y": "5DrW-kEge0sePHlKmh1d2kqd10r32JEW6eyyewy18j8"
|
|
||||||
}`)
|
|
||||||
testKeyECDSA_256_1_Priv = mustLoadJWK(`{
|
|
||||||
"kty": "EC",
|
|
||||||
"kid": "27ebd2f5bf6723e4de06caaab03703be458a37bf28a9fa748576a0826acb6ec2",
|
|
||||||
"crv": "P-256",
|
|
||||||
"x": "KZisP6wLCph4q6056jr7BH_asiX9RcLcS3HrNjdCpkw",
|
|
||||||
"y": "5DrW-kEge0sePHlKmh1d2kqd10r32JEW6eyyewy18j8",
|
|
||||||
"d": "r6CiIpv0icIq5U4LYO39nBDVhCHCLObDFYC5IG9Y8Hk"
|
|
||||||
}`)
|
|
||||||
|
|
||||||
testKeyECDSA_256_2 = mustLoadJWK(`{
|
|
||||||
"kty": "EC",
|
|
||||||
"kid": "2c7d179db6006c90ece4d91f554791ff35156693c693102c00f66f473d15df17",
|
|
||||||
"crv": "P-256",
|
|
||||||
"x": "oDwcKp7SqgeRvycK5GgYjrlW4fbHn2Ybfd5iG7kDiPc",
|
|
||||||
"y": "qazib9UwdUdbHSFzdy_HN10xZEItLvufPw0v7nIJOWA"
|
|
||||||
}`)
|
|
||||||
testKeyECDSA_256_2_Priv = mustLoadJWK(`{
|
|
||||||
"kty": "EC",
|
|
||||||
"kid": "2c7d179db6006c90ece4d91f554791ff35156693c693102c00f66f473d15df17",
|
|
||||||
"crv": "P-256",
|
|
||||||
"x": "oDwcKp7SqgeRvycK5GgYjrlW4fbHn2Ybfd5iG7kDiPc",
|
|
||||||
"y": "qazib9UwdUdbHSFzdy_HN10xZEItLvufPw0v7nIJOWA",
|
|
||||||
"d": "p79U6biKrOyrKzg-i3C7FVJiqzlqBhYQqmyOiZ9bhVM"
|
|
||||||
}`)
|
|
||||||
|
|
||||||
testKeyECDSA_256_3 = mustLoadJWK(`{
|
|
||||||
"kty": "EC",
|
|
||||||
"kid": "aba0a256999af470c8b2449103ae75b257d907da4d1cbfc73c1d45ab1098f544",
|
|
||||||
"crv": "P-256",
|
|
||||||
"x": "CKRYWVt1R7FzuJ43vEprfIzgB-KgIhRDhxLmd5ixiXY",
|
|
||||||
"y": "QCxTVmK31ee710OYkNqdqEgHH3rqRQNQj3Wyq0xtYq0"
|
|
||||||
}`)
|
|
||||||
testKeyECDSA_256_3_Priv = mustLoadJWK(`{
|
|
||||||
"kty": "EC",
|
|
||||||
"kid": "aba0a256999af470c8b2449103ae75b257d907da4d1cbfc73c1d45ab1098f544",
|
|
||||||
"crv": "P-256",
|
|
||||||
"x": "CKRYWVt1R7FzuJ43vEprfIzgB-KgIhRDhxLmd5ixiXY",
|
|
||||||
"y": "QCxTVmK31ee710OYkNqdqEgHH3rqRQNQj3Wyq0xtYq0",
|
|
||||||
"d": "c_WflwwUxT_B5izk4iET49qoob0RH5hccnEScfBS9Qk"
|
|
||||||
}`)
|
|
||||||
|
|
||||||
testKeyECDSA_384_0 = mustLoadJWK(`{
|
|
||||||
"kty": "EC",
|
|
||||||
"kid": "bc271d0a68251171e0c5edfd384b32d154e1717af27c5089bda8fb598a474b7b",
|
|
||||||
"crv": "P-384",
|
|
||||||
"x": "FZEhxw06oB86xCAZCtKTfX3ze9tgxkdN199g-cWrpQTWF-2m6Blg1MN5D60Z9KoX",
|
|
||||||
"y": "EyWjZ46gLlqfoU08iv0zcDWut1nbfoUoTd-7La2VY4PqQeDSFrxPCOyplxFMGJIW"
|
|
||||||
}`)
|
|
||||||
testKeyECDSA_384_0_Priv = mustLoadJWK(`{
|
|
||||||
"kty": "EC",
|
|
||||||
"kid": "bc271d0a68251171e0c5edfd384b32d154e1717af27c5089bda8fb598a474b7b",
|
|
||||||
"crv": "P-384",
|
|
||||||
"x": "FZEhxw06oB86xCAZCtKTfX3ze9tgxkdN199g-cWrpQTWF-2m6Blg1MN5D60Z9KoX",
|
|
||||||
"y": "EyWjZ46gLlqfoU08iv0zcDWut1nbfoUoTd-7La2VY4PqQeDSFrxPCOyplxFMGJIW",
|
|
||||||
"d": "t6oJHJBH2rvH3uyQkK_JwoUEwE1QHjwTnGLSDMZbNEDrEaR8BBiAo3s0p8rzGz5-"
|
|
||||||
}`)
|
|
||||||
|
|
||||||
testKeyECDSA_384_1 = mustLoadJWK(`{
|
|
||||||
"kty": "EC",
|
|
||||||
"kid": "58ef375d6e63cedc637fed59f4012a779cd749a83373237cf3972bba74ebd738",
|
|
||||||
"crv": "P-384",
|
|
||||||
"x": "Gj3zkiRR4RCJb0Tke3lD2spG2jzuXgX50fEwDZTRjlQIzz3Rc96Fw32FCSDectxQ",
|
|
||||||
"y": "6alqN7ilqJAmqCU1BFrJJeivJiM0s1-RqBewRoNkTQinOLLbaiZlBAQxS4iq2dRv"
|
|
||||||
}`)
|
|
||||||
testKeyECDSA_384_1_Priv = mustLoadJWK(`{
|
|
||||||
"kty": "EC",
|
|
||||||
"kid": "58ef375d6e63cedc637fed59f4012a779cd749a83373237cf3972bba74ebd738",
|
|
||||||
"crv": "P-384",
|
|
||||||
"x": "Gj3zkiRR4RCJb0Tke3lD2spG2jzuXgX50fEwDZTRjlQIzz3Rc96Fw32FCSDectxQ",
|
|
||||||
"y": "6alqN7ilqJAmqCU1BFrJJeivJiM0s1-RqBewRoNkTQinOLLbaiZlBAQxS4iq2dRv",
|
|
||||||
"d": "-4eaoElyb_YANRownMtId_-glX2o45oc4_L_vgo3YOW5hxCq3KIBIdrhvBx1nw8B"
|
|
||||||
}`)
|
|
||||||
|
|
||||||
testKeyECDSA_384_2 = mustLoadJWK(`{
|
|
||||||
"kty": "EC",
|
|
||||||
"kid": "a8bab6f438b6cd2080711404549c978d1c00dd77a3c532bba12c964c4883b2ec",
|
|
||||||
"crv": "P-384",
|
|
||||||
"x": "CBKwYgZFLdTBBFnWD20q2YNUnRnOsDgTxG3y-dzCUrb65kOKm0ZFaZQPe5ZPjvDS",
|
|
||||||
"y": "WlubxLv2qH-Aw-LsESKXAwm4HF3l4H1rVn3DZRpqcac6p-QSrXmfKCtxJVHDaTNi"
|
|
||||||
}`)
|
|
||||||
testKeyECDSA_384_2_Priv = mustLoadJWK(`{
|
|
||||||
"kty": "EC",
|
|
||||||
"kid": "a8bab6f438b6cd2080711404549c978d1c00dd77a3c532bba12c964c4883b2ec",
|
|
||||||
"crv": "P-384",
|
|
||||||
"x": "CBKwYgZFLdTBBFnWD20q2YNUnRnOsDgTxG3y-dzCUrb65kOKm0ZFaZQPe5ZPjvDS",
|
|
||||||
"y": "WlubxLv2qH-Aw-LsESKXAwm4HF3l4H1rVn3DZRpqcac6p-QSrXmfKCtxJVHDaTNi",
|
|
||||||
"d": "XfoOcxV3yfoAcRquJ9eaBvcY-H71B0XzXwx2eidolHDKLK7GHygEt9ToYSY_DvxO"
|
|
||||||
}`)
|
|
||||||
|
|
||||||
testKeyECDSA_384_3 = mustLoadJWK(`{
|
|
||||||
"kty": "EC",
|
|
||||||
"kid": "32f0cfc686455840530d727fb029002b9757ffff15272f98f53be9f77560718a",
|
|
||||||
"crv": "P-384",
|
|
||||||
"x": "xxED1z8EUCOUQ_jwS9nuVUDVzxs-U1rl19y8jMWrv4TdPeGHTRTgNUE57-YAL3ly",
|
|
||||||
"y": "OsCN6-HmM-LP1itE5eW15WsSQoe3dZBX7AoHyKJUKtjzWKCJNUcyu3Np07xta1Cr"
|
|
||||||
}`)
|
|
||||||
testKeyECDSA_384_3_Priv = mustLoadJWK(`{
|
|
||||||
"kty": "EC",
|
|
||||||
"kid": "32f0cfc686455840530d727fb029002b9757ffff15272f98f53be9f77560718a",
|
|
||||||
"crv": "P-384",
|
|
||||||
"x": "xxED1z8EUCOUQ_jwS9nuVUDVzxs-U1rl19y8jMWrv4TdPeGHTRTgNUE57-YAL3ly",
|
|
||||||
"y": "OsCN6-HmM-LP1itE5eW15WsSQoe3dZBX7AoHyKJUKtjzWKCJNUcyu3Np07xta1Cr",
|
|
||||||
"d": "gfQA6wn4brWVT3OkeGiaCXGsQyTybZB9SdqTULsiSg8n6FS2T8hvK0doPwMTT5Gw"
|
|
||||||
}`)
|
|
||||||
|
|
||||||
testKeyECDSA_521_0 = mustLoadJWK(`{
|
|
||||||
"kty": "EC",
|
|
||||||
"kid": "f7b325c848a6c9fc5b72f131f179d2f37296837262797983c632597a4927726e",
|
|
||||||
"crv": "P-521",
|
|
||||||
"x": "AUaLqCAMEWPqiYgd-D_6F5kxpOgqnbQnjIHZ-NhjRnKKYuij9Iz7bq9pZU4F79wsODFpWxMFfISrveUfgEGt4Hy2",
|
|
||||||
"y": "AeAKfmFsfcFttwQqv2B-fUfgLhw837YoGoNWh5qNE_LqTBxbYKRUkbSLxVRHEcVNnU1t3z9yMMdYXtuMlfJ0bhjr"
|
|
||||||
}`)
|
|
||||||
testKeyECDSA_521_0_Priv = mustLoadJWK(`{
|
|
||||||
"kty": "EC",
|
|
||||||
"kid": "f7b325c848a6c9fc5b72f131f179d2f37296837262797983c632597a4927726e",
|
|
||||||
"crv": "P-521",
|
|
||||||
"x": "AUaLqCAMEWPqiYgd-D_6F5kxpOgqnbQnjIHZ-NhjRnKKYuij9Iz7bq9pZU4F79wsODFpWxMFfISrveUfgEGt4Hy2",
|
|
||||||
"y": "AeAKfmFsfcFttwQqv2B-fUfgLhw837YoGoNWh5qNE_LqTBxbYKRUkbSLxVRHEcVNnU1t3z9yMMdYXtuMlfJ0bhjr",
|
|
||||||
"d": "AbD3guIvlVd8CZD0xUNuebgnQkE24XJnxCQ69P5VL3etEMmdr4HPJLPMQfMs10Gz8RTmrGKo-bdsU-cjS3d7dKIC"
|
|
||||||
}`)
|
|
||||||
|
|
||||||
testKeyECDSA_521_1 = mustLoadJWK(`{
|
|
||||||
"kty": "EC",
|
|
||||||
"kid": "4ea260ba2c9ed5fe9d6adeb998bb68b3edbab839d8bfd2be09fa628680793b90",
|
|
||||||
"crv": "P-521",
|
|
||||||
"x": "AE8-53o64GiywneanVZAHb96NcPbq0Ml6zynIcgLdKUiHGVs2te7SABq_9keFZJC6wooACeNWWT7VDK3kY77fjdh",
|
|
||||||
"y": "AAnrIu0-D7JEd3-mqTR8Rdz53kw7DLAIypv0-_u4rqn0glwTZCkMpQ17wFH71bMInXaTi2Z_uq67NuVxFvUCaTvS"
|
|
||||||
}`)
|
|
||||||
testKeyECDSA_521_1_Priv = mustLoadJWK(`{
|
|
||||||
"kty": "EC",
|
|
||||||
"kid": "4ea260ba2c9ed5fe9d6adeb998bb68b3edbab839d8bfd2be09fa628680793b90",
|
|
||||||
"crv": "P-521",
|
|
||||||
"x": "AE8-53o64GiywneanVZAHb96NcPbq0Ml6zynIcgLdKUiHGVs2te7SABq_9keFZJC6wooACeNWWT7VDK3kY77fjdh",
|
|
||||||
"y": "AAnrIu0-D7JEd3-mqTR8Rdz53kw7DLAIypv0-_u4rqn0glwTZCkMpQ17wFH71bMInXaTi2Z_uq67NuVxFvUCaTvS",
|
|
||||||
"d": "AVVL9TIWcJCYH5r3QUhHXtsbkVXhLQSpp4sL1ta_H2_3bpRb9ZdVv10YOA-xN7Yz2wa-FMhIhj1ULe9z18ZM8dDF"
|
|
||||||
}`)
|
|
||||||
|
|
||||||
testKeyECDSA_521_2 = mustLoadJWK(`{
|
|
||||||
"kty": "EC",
|
|
||||||
"kid": "d69caddc821807ea63a46519b538a7d2f3135dbdda1ba628781f8567a47893d8",
|
|
||||||
"crv": "P-521",
|
|
||||||
"x": "AO618rH8GP78-mi9Z5FaPGqpyc_OVMK-BajZK-pwL89ZQdOvFa0fY0ENpB_KaRf5ELw9IP17lQh3T-O9O7jePDFj",
|
|
||||||
"y": "AT1x9pCMbY-BXqAJPQDQxp6j8Gca7IpdxL8OS4td_XPiwXtX4JKcj_VKxtOw6k64yr_VuFYCs6wImOc72jNRBjA7"
|
|
||||||
}`)
|
|
||||||
testKeyECDSA_521_2_Priv = mustLoadJWK(`{
|
|
||||||
"kty": "EC",
|
|
||||||
"kid": "d69caddc821807ea63a46519b538a7d2f3135dbdda1ba628781f8567a47893d8",
|
|
||||||
"crv": "P-521",
|
|
||||||
"x": "AO618rH8GP78-mi9Z5FaPGqpyc_OVMK-BajZK-pwL89ZQdOvFa0fY0ENpB_KaRf5ELw9IP17lQh3T-O9O7jePDFj",
|
|
||||||
"y": "AT1x9pCMbY-BXqAJPQDQxp6j8Gca7IpdxL8OS4td_XPiwXtX4JKcj_VKxtOw6k64yr_VuFYCs6wImOc72jNRBjA7",
|
|
||||||
"d": "AYmtaW9ojb0Gb8zSqrlRnEKzRVIBIM5dsb1qWkd3mfr4Wl5tbPuiEctGLN9s6LDtY0JOL3nukOVoDbrmS4qCW64"
|
|
||||||
}`)
|
|
||||||
|
|
||||||
testKeyECDSA_521_3 = mustLoadJWK(`{
|
|
||||||
"kty": "EC",
|
|
||||||
"kid": "7193cba4fd9c72153133ce5cffc423857517ab8eb176a827e1a002eb5621edca",
|
|
||||||
"crv": "P-521",
|
|
||||||
"x": "AQfpLehZCER3ZTw1V1pN0RsX8-4WEW4IDxFDSGwrULQ79YHiLNmubrOSlSxiOSv2S-tHq-hxgma1PZlQRghJfemx",
|
|
||||||
"y": "AAF41XT7jptLsy8FAaVRnex2WcSfdebcjjXMGO4rn6IlD3u9qnvrpR8MBp1gz5G1C5S7_NVgIeLSIYbdfd_wjVkd"
|
|
||||||
}`)
|
|
||||||
testKeyECDSA_521_3_Priv = mustLoadJWK(`{
|
|
||||||
"kty": "EC",
|
|
||||||
"kid": "7193cba4fd9c72153133ce5cffc423857517ab8eb176a827e1a002eb5621edca",
|
|
||||||
"crv": "P-521",
|
|
||||||
"x": "AQfpLehZCER3ZTw1V1pN0RsX8-4WEW4IDxFDSGwrULQ79YHiLNmubrOSlSxiOSv2S-tHq-hxgma1PZlQRghJfemx",
|
|
||||||
"y": "AAF41XT7jptLsy8FAaVRnex2WcSfdebcjjXMGO4rn6IlD3u9qnvrpR8MBp1gz5G1C5S7_NVgIeLSIYbdfd_wjVkd",
|
|
||||||
"d": "hzh0loqrhYFUik86SYBv3CC_GKNYankLMK95-Cfr1gLBD1l0M6W-7gn3XlyaQEInz0TBIbaQ1fL78HHjbVsNLrY"
|
|
||||||
}`)
|
|
||||||
|
|
||||||
testKeyRSA_1024_0 = mustLoadJWK(`{
|
|
||||||
"kty": "RSA",
|
|
||||||
"kid": "979d62e7114052027b11bcb51282d8228790134ef34e746f6372fa5aadaa9bf2",
|
|
||||||
"n": "6zxmGE5X74SUBWfDEo8Tl-YxrkVfvxljQG9vKmNPQ2RhEmJ8eplZpKn9_nlxVDHGLzfJMqqUBS19EarIfDnOqyFBRkyKRbsQdUVU9XLjDIXqImJ8aN-UmShAYoeOClJVDsBJuxBGgS3pdgG7u3YQpvTV_hGuMoJr7UYgIrqb0qc",
|
|
||||||
"e": "AQAB"
|
|
||||||
}`)
|
|
||||||
testKeyRSA_1024_0_Priv = mustLoadJWK(`{
|
|
||||||
"kty": "RSA",
|
|
||||||
"kid": "979d62e7114052027b11bcb51282d8228790134ef34e746f6372fa5aadaa9bf2",
|
|
||||||
"n": "6zxmGE5X74SUBWfDEo8Tl-YxrkVfvxljQG9vKmNPQ2RhEmJ8eplZpKn9_nlxVDHGLzfJMqqUBS19EarIfDnOqyFBRkyKRbsQdUVU9XLjDIXqImJ8aN-UmShAYoeOClJVDsBJuxBGgS3pdgG7u3YQpvTV_hGuMoJr7UYgIrqb0qc",
|
|
||||||
"e": "AQAB",
|
|
||||||
"d": "IARggP5oyZjp7LJqwqPmrs4OBQI8Pe5eq-5-2u4ZY7rN24q8FpO4t8jLYU92NVdw-gxFvjepXesLEtSD5SSZFD7s-5rqmX9tW_t5t1a1sBY6IKz_YBSf2p2LtdTyrqgo4nRD0nYH3sMvGtOKCTV4K_vwfWPD3elXq752trG-HwE",
|
|
||||||
"p": "9tgo12L3earNNrJfyIIDD2dqgKuNfWhQzhbe-Ju17dF37uGF6xnLqR0WXU-agGpUdYTMOd7IQi_bX_xkjQBYlw",
|
|
||||||
"q": "8_YDvufPdccjGM2rAXs-2LbeP_LrzLUk1uGNpEjIt2lXEm6sQKjIfNLcAfVKh9zsqwqroveL2iI1ijsDgNAIcQ"
|
|
||||||
}`)
|
|
||||||
|
|
||||||
testKeyRSA_1024_1 = mustLoadJWK(`{
|
|
||||||
"kty": "RSA",
|
|
||||||
"kid": "4a3a37dc10aec5f427509e4014683102a0fafa32bec2ff9921ff19e1a55c79e8",
|
|
||||||
"n": "sn7sU7dBEkU1RBIz_LKgrswSS7-68vTlOe7n-lanAqAlczm01_6IWrvcIC7lPv1iHQqWngusskANZirCZGTv6kK8kJLHBVRSROB9VkPTPNFYnSB6dacyPa0ty0otsaYOVM2RFvcCX7lBKKat2Tmst8vITdpvnoEzTgT_eGOKGAs",
|
|
||||||
"e": "AQAB"
|
|
||||||
}`)
|
|
||||||
testKeyRSA_1024_1_Priv = mustLoadJWK(`{
|
|
||||||
"kty": "RSA",
|
|
||||||
"kid": "4a3a37dc10aec5f427509e4014683102a0fafa32bec2ff9921ff19e1a55c79e8",
|
|
||||||
"n": "sn7sU7dBEkU1RBIz_LKgrswSS7-68vTlOe7n-lanAqAlczm01_6IWrvcIC7lPv1iHQqWngusskANZirCZGTv6kK8kJLHBVRSROB9VkPTPNFYnSB6dacyPa0ty0otsaYOVM2RFvcCX7lBKKat2Tmst8vITdpvnoEzTgT_eGOKGAs",
|
|
||||||
"e": "AQAB",
|
|
||||||
"d": "KTXqpE1sBaba7HNzc0Vemdzd4IVMyWlHPz_saTz2ZEHLQ7YwDapjmudCpF-PaCKiM2hNbAHwBluJfGwk4372cPHcJyri3Gs4GGj-cOlbXJh9aUp5o8fqn904B1UcPxMZ2DrZAKjseuLj3zyJgCsSJOGU1kJFRAkM8UN4a9cE8sE",
|
|
||||||
"p": "3ntHMGDJEXwqldLNR-DucKGp32aJVAaKnn4j9qy1Tj6KhH70o3HyFVO_TKYxGg-pG3zTV-VzyMqmeh9hDdnyKw",
|
|
||||||
"q": "zWMxEjyxvp-P-mNxhWQe1pJXZi6iPpcnt0SZLfweT3AtAzEaoSs4-4D1jT911LD4xDI8_a7-gYHgD8ME62PhoQ"
|
|
||||||
}`)
|
|
||||||
|
|
||||||
testKeyRSA_1024_2 = mustLoadJWK(`{
|
|
||||||
"kty": "RSA",
|
|
||||||
"kid": "d1c32aaea6c9527038e589dd94a546ca1beedb90f90e073228986cc258cf1fda",
|
|
||||||
"n": "3mZrDaB5-7e29zok9XkGuu6FXYB00FqFgnGTJAGCMpql5uHz1h9p0DljZL4vsGkkYOZUMvqFS1pCEuzdSsupPNClf0NKMRux6yLv6iIR9C4pE9RBKPUrinzJuYs634rq5JOEP4IpP_fJfKxMw4Na85otd9KposKwP14cCkOibYM",
|
|
||||||
"e": "AQAB"
|
|
||||||
}`)
|
|
||||||
testKeyRSA_1024_2_Priv = mustLoadJWK(`{
|
|
||||||
"kty": "RSA",
|
|
||||||
"kid": "d1c32aaea6c9527038e589dd94a546ca1beedb90f90e073228986cc258cf1fda",
|
|
||||||
"n": "3mZrDaB5-7e29zok9XkGuu6FXYB00FqFgnGTJAGCMpql5uHz1h9p0DljZL4vsGkkYOZUMvqFS1pCEuzdSsupPNClf0NKMRux6yLv6iIR9C4pE9RBKPUrinzJuYs634rq5JOEP4IpP_fJfKxMw4Na85otd9KposKwP14cCkOibYM",
|
|
||||||
"e": "AQAB",
|
|
||||||
"d": "Ft2M0B_ZqsmepBh0SFCjIoD3cT-NwwYrh9fJewA0tKM1v2EnwrIEHQZpc6giGw8UUGod6gfbwH2NIYj8z33U7lyrKWPR7F8gJRm1KR5NLCBCnAnz0ukhsg24ktB25LZLZRCStRRhtCev95Vvmew0ip5081hv730Z2T_PsEyOU6E",
|
|
||||||
"p": "8moFzLKJSQuhaIYTo1qNX0w8o4NBFb1atOlthHmq6Y6rdYTm_nyM9Q3mrNBolPS7LHTiBXtCEJ_m4V5hWDuGKw",
|
|
||||||
"q": "6t0_mZKOoZ5NAE--MxkuOCcRhuqNo9VoacfPEH39CeoKAam4v5k56R7aQcb_raQK396pgV-evM2dpfdUaasiCQ"
|
|
||||||
}`)
|
|
||||||
|
|
||||||
testKeyRSA_1024_3 = mustLoadJWK(`{
|
|
||||||
"kty": "RSA",
|
|
||||||
"kid": "838742d0e3195b19f974fa30352db79070fa19aaaeb678ec95c499de7bd48d52",
|
|
||||||
"n": "17Uc89-QvCjqBLXDJSCWxUoohjwFPI63Gub8g-lH1GSK3flXqiohz33KfKhqrdKsLrpRjskGTMg3Vo0IcLBnMYdp1i1nceORghtYLQsDNS7tqlHiKx725fLmWldGgiuP_0Ak0Knisw-j_q7Jx3OVAJnuS1o3vPLfJxJdyq6yV1k",
|
|
||||||
"e": "AQAB"
|
|
||||||
}`)
|
|
||||||
testKeyRSA_1024_3_Priv = mustLoadJWK(`{
|
|
||||||
"kty": "RSA",
|
|
||||||
"kid": "838742d0e3195b19f974fa30352db79070fa19aaaeb678ec95c499de7bd48d52",
|
|
||||||
"n": "17Uc89-QvCjqBLXDJSCWxUoohjwFPI63Gub8g-lH1GSK3flXqiohz33KfKhqrdKsLrpRjskGTMg3Vo0IcLBnMYdp1i1nceORghtYLQsDNS7tqlHiKx725fLmWldGgiuP_0Ak0Knisw-j_q7Jx3OVAJnuS1o3vPLfJxJdyq6yV1k",
|
|
||||||
"e": "AQAB",
|
|
||||||
"d": "B8cM-zIVauNixLa1CZKqPQTWfziMy8ktivfHJQ51O5BAfY5u_cC1JWEYuvPrnMba1Hh9VlOjOYOCk0lUg5OotMx5hxym6M5y_2rIrW90a8r3gGttPlZmyHQnFIgj2QZHlEyZGU1SIPTOtoECW5RGk7cYpA1s1_zfz8uyG-MiCPE",
|
|
||||||
"p": "2dwia5iWWLZwFcCVylPh_7QDr3Wxt3kW_TmHIbaRT10Rborj1lAwltyEx7aVpHq-aNfvrYIEBbOXWwUU8PDUrQ",
|
|
||||||
"q": "_XiDT55hn-Txi2C_peMVSDgXozf01qHKvdLirXM2uT_CI8kruYZYjWYn6_cSUptJ60eioM3WNTxjrSxWRS923Q"
|
|
||||||
}`)
|
|
||||||
|
|
||||||
testKeyRSA_2048_0 = mustLoadJWK(`{
|
|
||||||
"kty": "RSA",
|
|
||||||
"kid": "13b668c7b4f5d46d53de65e4b9f9c522fad4870e4d1656e459c88de6e90984d6",
|
|
||||||
"n": "1j-CxBxkn20C_M24t6ueLp02T4MMAiPLXf5yaDcj7EcsXGbnsGEhrAUwCZwdEJAxeZ0PolmlE0XBOhQfRtsCVJmwu918aknptToyDbOUBr6WtPIK_c_BuGVanLCx3SnczV4jle9Bz7tGfpj2vAXytSBrfnZhdCHNEFeefQTQMnavfMhfWf_njiTa76BRyAHjb-XZIJHKovwBu0y3glmzhSKYNsUrW11RsWx6DbueWEbE3FpsHTiEdnJipcP3UKl3Z2z6t6n9ZYtFWkx4zCVVBQu-RWUQwjr2XnR1LFwXgL9xQocDBmS1O-wMTHqL3_oosNUdV3vuMPdxs_SEoys2rQ",
|
|
||||||
"e": "AQAB"
|
|
||||||
}`)
|
|
||||||
testKeyRSA_2048_0_Priv = mustLoadJWK(`{
|
|
||||||
"kty": "RSA",
|
|
||||||
"kid": "13b668c7b4f5d46d53de65e4b9f9c522fad4870e4d1656e459c88de6e90984d6",
|
|
||||||
"n": "1j-CxBxkn20C_M24t6ueLp02T4MMAiPLXf5yaDcj7EcsXGbnsGEhrAUwCZwdEJAxeZ0PolmlE0XBOhQfRtsCVJmwu918aknptToyDbOUBr6WtPIK_c_BuGVanLCx3SnczV4jle9Bz7tGfpj2vAXytSBrfnZhdCHNEFeefQTQMnavfMhfWf_njiTa76BRyAHjb-XZIJHKovwBu0y3glmzhSKYNsUrW11RsWx6DbueWEbE3FpsHTiEdnJipcP3UKl3Z2z6t6n9ZYtFWkx4zCVVBQu-RWUQwjr2XnR1LFwXgL9xQocDBmS1O-wMTHqL3_oosNUdV3vuMPdxs_SEoys2rQ",
|
|
||||||
"e": "AQAB",
|
|
||||||
"d": "C8F4X2Jfcw_8Nfrjw9A64bvmmv5JzmRAaGvpwyYjZneRS5Cp7demjVXLiPtz7NC8pjuj-_iHQkN1ksY_4RdrTVERjX1dskdT94m17WKJIMWcZ1lQmRSpQIDvM-HOIKCHaQ1dToDOT6Oq_o9OGosJAj9BJrNALasdIWRtYda9xcb7roLl_U3AtOlK9RiFygtt5uVBIPh1rsxaT0Y1MvMk4EMbFnv7NXXk65UM_3p2leSoPpO7LvlYs3WoRRX9ABH7zI-ppwLGEDuxfnwKzAOaRBe9oUh5rTYdazaY9XqofvekBc9Xqa2HpjkYX-L4Zy3oj04u-u5zX8KTh25jFC2a0Q",
|
|
||||||
"p": "6L6icXqV7jLSXHrhMyLpW-tdFFp2XRQ_uKk972jmUJ-sEeh1YYSx_JpK6oaUnYshGbRLfEAOLy03iweAL4uMZJcASOR44TwSnn_qJZ72PkwhD42uKhE0eJRpxVht6jf5qhsynaMNUWc_FIW5ESpQIf6j4rqFV5WrKHAt0ypChTc",
|
|
||||||
"q": "66fAza4rLvyjqQAIGQ07RiRYW4no1h4cK8RH_CDUgI3CCFQSuqTB8ZqM38r2u0tZoKzCNgHcnde6Jk07X6-LSQW2kypMXt3idbgaeVSYSbdZndgG_jTibJvoybxSjo6HfLpPvCrfsihXo7O5M13LE7VqBLbGTLI5iubOxcrfFTs"
|
|
||||||
}`)
|
|
||||||
|
|
||||||
testKeyRSA_2048_1 = mustLoadJWK(`{
|
|
||||||
"kty": "RSA",
|
|
||||||
"kid": "6d2b0efff0011bbbbf694de684b5b97cf454fd4086fa53b9af92545f3fa65459",
|
|
||||||
"n": "1CmSBUbxU1jjnmaBG0r0cLLmyZgCOMbfpG6Z3HwfVDgCK6P-rU3F6QQazrOgGJJ0sz6vP50VK5u7BR6vSrPBBX5CicJPM2iNdz2JuV9ODEIkDQBLeI6TfIGhNOls-14tXKOPExY8b6JdyDMP31Hwo_0pF1FaunZ7yY1bgoKCtDV5-RKGd2EgylDGNPu0Ilr92MqCsAntBC8eQSkO4CcTcti4t9cX45VY5nPtwQRmp5zIgUHnU4LV3QVTLJnU-uidaAxRVQbS1pVql5xR6nYZHYvFk1IU-wTY6gGk5WvGWWQ440UTTaMAfnJP6VFDggUXeGSlKfKkDcz7JLT2Ma2KXQ",
|
|
||||||
"e": "AQAB"
|
|
||||||
}`)
|
|
||||||
testKeyRSA_2048_1_Priv = mustLoadJWK(`{
|
|
||||||
"kty": "RSA",
|
|
||||||
"kid": "6d2b0efff0011bbbbf694de684b5b97cf454fd4086fa53b9af92545f3fa65459",
|
|
||||||
"n": "1CmSBUbxU1jjnmaBG0r0cLLmyZgCOMbfpG6Z3HwfVDgCK6P-rU3F6QQazrOgGJJ0sz6vP50VK5u7BR6vSrPBBX5CicJPM2iNdz2JuV9ODEIkDQBLeI6TfIGhNOls-14tXKOPExY8b6JdyDMP31Hwo_0pF1FaunZ7yY1bgoKCtDV5-RKGd2EgylDGNPu0Ilr92MqCsAntBC8eQSkO4CcTcti4t9cX45VY5nPtwQRmp5zIgUHnU4LV3QVTLJnU-uidaAxRVQbS1pVql5xR6nYZHYvFk1IU-wTY6gGk5WvGWWQ440UTTaMAfnJP6VFDggUXeGSlKfKkDcz7JLT2Ma2KXQ",
|
|
||||||
"e": "AQAB",
|
|
||||||
"d": "DYJcHuPmh-UYEUT7oY5DRE3P7jQ0qALZyLGWMHji0c0DLl4x4D0chfrR7il33zisH6G1LPrGl1FCNlA-3yXU-5GPkRADVQWqRFZxx5Du-k7X1tAW_iUt9PaYGjNm0hasEsMDYDbBQGZ5TD8cGp8wEHEVRbvTaB4VQb8zfXrr8ad8XCFdtYQF5Sw_VuvUBcn-50kdl7S0MmQiwLx5xkisJHkVdVsrWbq-JJPMYrjYzPGo07kt8pQH2ecxrQD2waTzkLiAg-nytPBkAyMIFQ-XCulWCNiI-V_HJ0pqNctgVpdaSihlPCYhfllxUSU8yg4UvcfFEAN2S5yjvPyrJeJLAQ",
|
|
||||||
"p": "4db4J-vxAyb_3otKidzYISIo8NuPxPXuYeImbvVbY6CWHdAYAkPgbwADh592D1vM6kwqZLot4VQ4XUVtX7Vf8tCaq5BAzcVAslK0rhK86kSmzuvtpS1ruzm1DGwJcQWOl_9qYnaAov8Ny0NG8fTm8iwvWJH7rVa3XNtPL0t_fx0",
|
|
||||||
"q": "8H7_sX4kY3_4t90HbA8IvVkTf3-OGaIjtcDn2J3HLq1wMKAtdxwsbMtjhLYP70PPBe_FBNpcWhE7p-tOVhH1i0N0wwFqwlRQy-3Z4oiLeSsy0Npl29cfP7teO5UTrbrqfmHB0j2u_nEqA4n4iDF5QhPr_9yzxSR4Pg56z4PgFEE"
|
|
||||||
}`)
|
|
||||||
|
|
||||||
testKeyRSA_2048_2 = mustLoadJWK(`{
|
|
||||||
"kty": "RSA",
|
|
||||||
"kid": "7988e9dea9647281b04d094e4dc5737eaf739a412a570aa4d6deed7ee5640c30",
|
|
||||||
"n": "wWOFgHHiJ54ZjQEKxvfqYDuhYymbfvYxvreroqx7E-cAuU4QBsOvKV3HNnH_vDRQE1AlqihNWmPFLptp7wxdMrLEtDnRFqTxyTXJ7wLCaaaB6Wwx1dhpr9QEG5-8rxLGFYv2w5i0-o76JPHG0tuqf0YNmHp9oWcv524XnDBuji4-u6km1KynT1EQKHYgy57JWgUpukgJGImBEvf0hC2Y5s5mHvVby3xAD9-cFa2o9Vj3G-SBYFsrRIVR8GcVNwo88oj8F7-2nGD_wOOu_qT_5I3vfToUYGj5-2rAK1ja0bZpXFibrkPrW4w9paG-3F7I0sPeL3e_BDC_rQ8TgL03uQ",
|
|
||||||
"e": "AQAB"
|
|
||||||
}`)
|
|
||||||
testKeyRSA_2048_2_Priv = mustLoadJWK(`{
|
|
||||||
"kty": "RSA",
|
|
||||||
"kid": "7988e9dea9647281b04d094e4dc5737eaf739a412a570aa4d6deed7ee5640c30",
|
|
||||||
"n": "wWOFgHHiJ54ZjQEKxvfqYDuhYymbfvYxvreroqx7E-cAuU4QBsOvKV3HNnH_vDRQE1AlqihNWmPFLptp7wxdMrLEtDnRFqTxyTXJ7wLCaaaB6Wwx1dhpr9QEG5-8rxLGFYv2w5i0-o76JPHG0tuqf0YNmHp9oWcv524XnDBuji4-u6km1KynT1EQKHYgy57JWgUpukgJGImBEvf0hC2Y5s5mHvVby3xAD9-cFa2o9Vj3G-SBYFsrRIVR8GcVNwo88oj8F7-2nGD_wOOu_qT_5I3vfToUYGj5-2rAK1ja0bZpXFibrkPrW4w9paG-3F7I0sPeL3e_BDC_rQ8TgL03uQ",
|
|
||||||
"e": "AQAB",
|
|
||||||
"d": "RPC4j-CJUbw_uY-Miv-oMuQvFU2o3Crh8u5BJn28ZozsKiMU_YRW9jUzJkqfczVm8muY8b7qTHXSvlmy-v_6XW9zRhhyXFMyypr9QNJIAifUmiTy4xwCGSdIy5w3RGY57UZ3EqVmpwe_TtpOGa8rabHMePX5wUcqwaLykcCGOPLOFKfPF93GqZYi2StS1IaiijLi2IAMhxQGqS6Ct3dA7yacQwegPiDzjb4Is3V9UQ9k_aS3I1w56tz5qspVmfDEuuWToc-2Qyk-eMKWl1tTDJuGTCiS5IFtMPerRpPq9GCpycV0csIW-6AnW79b038DxWjDAUsNnDPnX0y1dQ1G3Q",
|
|
||||||
"p": "yh2KH_JqU29L2SLG5-ul1EBbaSbt3F5UUZbCvm_MIsiH1TzafVyBd8FeG1d-pMR8bgGjaDSA-DNkOEDAeAlLx-UCTl2Uk2ySoMnZr5JjVLW2DTuF7e8ySKrKQSSLe63aQyKuIZh9P1RNrwrmFwvJgXdzudVjirmdTwxAN0lijE8",
|
|
||||||
"q": "9PJitAE_kk6SoKvJisXbDb7Xursj26xiYIdWuzlzQ1CbwYcJ1oORDpo0bEpy0BfIbZDiJJ79Xrh5Lo1BVysQ-IZJCXK7mavqiSa8g3B8rmqLXmAtjDbwLGsJOayCpYnsGgOLT7XIUYXCFH7C-tSSydhJ1j8JAbsJveMiBPKnUXc"
|
|
||||||
}`)
|
|
||||||
|
|
||||||
testKeyRSA_2048_3 = mustLoadJWK(`{
|
|
||||||
"kty": "RSA",
|
|
||||||
"kid": "d214aea9b78d15dd2667f5d19c36df6647fa50023140fd4f5866d285718b897c",
|
|
||||||
"n": "qFnqWpBOZ_5jnIr_XkXnonmHII5gKzxPhUBfvWBhGN2eH8nmnGh6aUnKyKuCLP_qfYLU7cf9bah-e00451iGite8Tg9ZMYAPFX4NM5j0rGWNv9Z6lSn1xXezJv-FU9VUwXm0DG5eVcB8OV99JWxHivQUSBzY0Q3DUlgrD7FhMd7BrrcmTUO07KnW6SxN3oTbT7fNMxfJSTRxmvU-t-6sLhYP4Wbg39zEUgI8M7b7tvHp34klSqOn2DibVBhvWGF1-IgNT7ng6pS5iAZN7Cz7NjPc9ZM_IWyngPKZV49Tf-ikX2O8uiCb4ccgRur6znwkE7MPrDTXSMHALn10sUcO_w",
|
|
||||||
"e": "AQAB"
|
|
||||||
}`)
|
|
||||||
testKeyRSA_2048_3_Priv = mustLoadJWK(`{
|
|
||||||
"kty": "RSA",
|
|
||||||
"kid": "d214aea9b78d15dd2667f5d19c36df6647fa50023140fd4f5866d285718b897c",
|
|
||||||
"n": "qFnqWpBOZ_5jnIr_XkXnonmHII5gKzxPhUBfvWBhGN2eH8nmnGh6aUnKyKuCLP_qfYLU7cf9bah-e00451iGite8Tg9ZMYAPFX4NM5j0rGWNv9Z6lSn1xXezJv-FU9VUwXm0DG5eVcB8OV99JWxHivQUSBzY0Q3DUlgrD7FhMd7BrrcmTUO07KnW6SxN3oTbT7fNMxfJSTRxmvU-t-6sLhYP4Wbg39zEUgI8M7b7tvHp34klSqOn2DibVBhvWGF1-IgNT7ng6pS5iAZN7Cz7NjPc9ZM_IWyngPKZV49Tf-ikX2O8uiCb4ccgRur6znwkE7MPrDTXSMHALn10sUcO_w",
|
|
||||||
"e": "AQAB",
|
|
||||||
"d": "cCIT2uarktD6gFaE6cIeGzZfLuwmWiX9wX-zRWxgwDM9E2dj12IvxtmD3E2Ak4CSK69tLEQ9JUFJnc89y7pHQ0uW_VdzzWjCo0omeOu0bO_njpPJanlcXn7wMVWY9NHvdj8eEfmhk_R1ybE0piyNKpyQtcehEv3bz4kyhW1ck936zafn5GWxCEWKIF-7OcotxtOl6z1GrJ7WqglMk8ooLucnAXzaPtcvD6seUhVH0vG60yI2AEInI_jMHR-mfDxrebG2xPPJLsqH3GChqTsxG5vjuaq71sNBiY_TbaUDbmWZxV66zdjhN93Rw-lc3vCPwG5vmuA2igWH7SKsHmQOAQ",
|
|
||||||
"p": "xvTQi8nCJGyZQm3fr7HU23FkSUYkKFUN1FRDWqjyz7kFj5rJxq5dSsNrrgVBvIBNY4TwDDcYia4rvx7KSRRwfV_rsU2-TCOxBebqVlbeV0HI7xMcVZXsv61Eugz-dznUU4wqmP_rCQ8vyNcVvCBbBLBW92n-IRQXsfuSUfuHnT8",
|
|
||||||
"q": "2J64YbgvH62PB4h-0iYuaykgUzWVSrXENg3_GaF8877AMTe98gjcH7HzTDW1fOhIcNZtmYTwlA2SgFudLsIJLoOn0-kmHzZjZJTWcsZsfTVwKhHD8Lzic7QkrrlbTIZlVu4mfT2f0kvaQiZp6zDCke3SLvVjlD-ebqTFCDIlXkE"
|
|
||||||
}`)
|
|
||||||
|
|
||||||
testKeyRSA_4096_0 = mustLoadJWK(`{
|
|
||||||
"kty": "RSA",
|
|
||||||
"kid": "4941c1e95df518587e54ddde3510c084fedf92098473537fb3fe022eaece9b42",
|
|
||||||
"n": "ypJYaH9_PLdEgGN3WFPL1V9qhC2BRXr24f1kGocaQe7wORFlZGX6gRpOCQ1lLmziLafERoSqZuBWF4vSGLeanCRQ7UKObYalMRR_rTJk6D_VD95LNOmtR4DrucdZvjQrAuLXhM5dNLGVoSq_zQowLDMeLWDUBB_TK4WlofoxI0RVoUOt4UXnSev8M_6yIYASpOAk8gG4FAqRfmU-3JhgkZOQCD0BhKYZVw_kEhRBrS3aik-1pQkan9hYIxWwc4KKtrLHNls7--vk5xDk2Vod5sZYRLlimbqHavN2IBeAeJGJqt1grXOVlnagKqmyq2MoPKufwptJBiVrVsTplyUB9FZe9FxUfxrkkxYJHufwP-3wXhaXpPFdL86TCUuOz-jTfng1rsTpwzmwm3RqFzz01yMc1RhaeWininQqj42bhW4bgFGVRP2QqSFbCbRZ4WMW3qWr3aR2QpJ7b8ac7JUhAJoh7-UtMmyGx9UJP0gjA8Wm-O81UENoDjMMdNUaVTfpSUKB7xZppg244jNZDIm1ptq4QyvvWkr0brp9Ymxpu0e3g2HyywyPjbeyNrIelb4E2tU5vCR4Fs_zGFG71AieruJGfzPTXE7ZaiLhP8n1652NCzIH76Y7OGz4ap2D55-RKpUkSI3KqWxUKxkM0tuZ7LS2F-1wEVs8P01K6RuQpxE",
|
|
||||||
"e": "AQAB"
|
|
||||||
}`)
|
|
||||||
testKeyRSA_4096_0_Priv = mustLoadJWK(`{
|
|
||||||
"kty": "RSA",
|
|
||||||
"kid": "4941c1e95df518587e54ddde3510c084fedf92098473537fb3fe022eaece9b42",
|
|
||||||
"n": "ypJYaH9_PLdEgGN3WFPL1V9qhC2BRXr24f1kGocaQe7wORFlZGX6gRpOCQ1lLmziLafERoSqZuBWF4vSGLeanCRQ7UKObYalMRR_rTJk6D_VD95LNOmtR4DrucdZvjQrAuLXhM5dNLGVoSq_zQowLDMeLWDUBB_TK4WlofoxI0RVoUOt4UXnSev8M_6yIYASpOAk8gG4FAqRfmU-3JhgkZOQCD0BhKYZVw_kEhRBrS3aik-1pQkan9hYIxWwc4KKtrLHNls7--vk5xDk2Vod5sZYRLlimbqHavN2IBeAeJGJqt1grXOVlnagKqmyq2MoPKufwptJBiVrVsTplyUB9FZe9FxUfxrkkxYJHufwP-3wXhaXpPFdL86TCUuOz-jTfng1rsTpwzmwm3RqFzz01yMc1RhaeWininQqj42bhW4bgFGVRP2QqSFbCbRZ4WMW3qWr3aR2QpJ7b8ac7JUhAJoh7-UtMmyGx9UJP0gjA8Wm-O81UENoDjMMdNUaVTfpSUKB7xZppg244jNZDIm1ptq4QyvvWkr0brp9Ymxpu0e3g2HyywyPjbeyNrIelb4E2tU5vCR4Fs_zGFG71AieruJGfzPTXE7ZaiLhP8n1652NCzIH76Y7OGz4ap2D55-RKpUkSI3KqWxUKxkM0tuZ7LS2F-1wEVs8P01K6RuQpxE",
|
|
||||||
"e": "AQAB",
|
|
||||||
"d": "VQagPRxm16FFC260hUqG4ASwvNIs1HEMd0bYYZobl1knU4zNthpnzxCveHU65wWk2ez1IXRF4fB_slpp0R4fszI7FZs-FRLS-4rTHGtul11TnNl9T7RVmxGt38ihDojvFMMKGyBTVu7DE2bSIsoH9kVugTWHSEPjav0pzJcrUNY56vpxXYDt18VJkrlxI0aSjMnYOAwoq6DT-O2eORFsVy5M4mhY3sipEjYFUOFXv8zjUfKrF55-omE4fWF5MsK0XoMjwtkAkHkvFx2sMN72dgsCubXmgQgeFvIhvs6eifzsf99z2NoPC5y3FbEs4Ws5VF3lLNXpDL9gEoeMVHigHKNoztIzbJnFLjzEag4wOBqbig4nWIlYEcLwd5-E0oQ8GHAhWtJn5AJQKOwTcasRWiUnRAulCcI8f9R2ATTSjAGU-juFUfhProp4w3ZlnUbO6U3tbh90KdFSJa-Bapts6ijPgEp6MHubVCHIxh1KGmod_CuGGjze1GuISEwI_4Yh0SrFlp4Wy6ecg7mKNnXvkBMd6h90LJaTzubnts33cCs-5UzCtYqmWHwXrMA6dRJuicY6su3NXxStuZn2bxU_Dn8LrS1vx_lhDU__NqMNbYj5ZvHkPvZiJNBI6Z1R5SY3pQzpH6vh9W2KRKF8cKOkushIaHNnHo7W5o4eZ9HjJFE",
|
|
||||||
"p": "7Bxd2wTrXfeSZAEGZEnfoJaMh6hKFG-fx0wLmF9U-Myq1fCIQ-gdl_Atqy0xQxYtQl2lOPUWfySegJSxpdRF8pwvLq9Yt7xFQDrV0B8d5-FjYJ_Mk2bbyHv_PhNmnfcYHqE_VdADeSaU-MRV1iknvjdbxSxYUF-U2C6llfJhn-ZbGPmZJyz8fTWHSh8aKLPJkuYV84wcNL04hpFPJq7Bhy7HAFhRYNpxXyDO3I36DCU94uvUCY758VKLl8IYMMSqjh-thluKJSkWibo85ZOJR38XFksMr70pH-DkT0WMv3PpLtNu6ac6Ry4CEkZexSWmbVk-pduRooo0On1CMsrsLw",
|
|
||||||
"q": "26K4gFJ0Ygqr4KaJ4QcRGN29XcjDpX5XTlHy3hCs_wEWc5wedydVgIH4tadiZcu0WinTtl0I6FYewWNpI8nFh8XJXsLOLepdKKR798zW8kEhzxdYdP1MTtGfy12KUCMeS2RQRsBJlmNdgrI0_m0JvW2LlGeK0EqvrqeS06L8bKGXkfY2aFGHl6ipSfUd5OpuSUqgfrj4-HgB8zywm-yAvn7UK-ySyQrP3K4PuOiY5QZmcPDxl8yqDAjorStrjPieYrlMqREHMsyq2FtAXhx4FzpKN7OljbE3EpgyFKkFN-wDTWRGxu3au0hQJx0-XthZ9i5r8zGcEbArQ5jZH7CQvw"
|
|
||||||
}`)
|
|
||||||
|
|
||||||
testKeyRSA_4096_1 = mustLoadJWK(`{
|
|
||||||
"kty": "RSA",
|
|
||||||
"kid": "d01929d205e83facdc5080a0a4ade80853978298a5c73780e863c035d32127c9",
|
|
||||||
"n": "ve_rSTi1vwfV2Oj7YNu1kv8Xz2ImAEqf7qo2RehTYW4FJpbj0wkMBVGmAulm4dz-1knnaPdpbrrIwkEYr1XR6kSIeB8aXkCZqDGZATxLSRqGEzu2J8Xt9z_qTQLWYD-NuMEf9H3B1CiP3Q2RnoWUlGvOr_KI5h6anzeqgZxlQLgqQujXscwqWpX7MVh3sheZ5SbyTkDcFWvQS_NEPaBGso-Au9X-yhZsHU1Ky02Nd_DPOrrRzl7uymE07hy3bIbxmrh4ZeEz6P2ixsHHYbd15GNMRlr1cWbg91RBB-akSxX0VYoLjjuqwo33UHk1hBbSATbobfpOKRruuZPZ_xOiPi-m0tUdD1Pj-h6xRcA5sZ1d55IdL8IY_9kgLc_WyU2RWFTXV6zA2SDDdE6EdEB_8q6U0oLU5T-YRxd-V2qOTqW1388qUaL5BalSvqM0g8kVfBYIM3uwLqQI_hCer4CL7_0DGlUtheye3qcoyqWVJd9iqfcWC-1S8NljAJwM9sehx6kOSM85UuUTH89VM35oAVp0rSPcLy20dPzEVQ0LAcy_iRHTkqy9nZ1z6mo-IWQE2ZyPCuESEfG7nFJ3YlUfwBJ5uZnk0N1FVYX5zgEKdkP322vShrv1blB_JtUedpbCcuS-qCaywpwjL0pzTDKpJMEqHds0-DFT7AbULcujw_U",
|
|
||||||
"e": "AQAB"
|
|
||||||
}`)
|
|
||||||
testKeyRSA_4096_1_Priv = mustLoadJWK(`{
|
|
||||||
"kty": "RSA",
|
|
||||||
"kid": "d01929d205e83facdc5080a0a4ade80853978298a5c73780e863c035d32127c9",
|
|
||||||
"n": "ve_rSTi1vwfV2Oj7YNu1kv8Xz2ImAEqf7qo2RehTYW4FJpbj0wkMBVGmAulm4dz-1knnaPdpbrrIwkEYr1XR6kSIeB8aXkCZqDGZATxLSRqGEzu2J8Xt9z_qTQLWYD-NuMEf9H3B1CiP3Q2RnoWUlGvOr_KI5h6anzeqgZxlQLgqQujXscwqWpX7MVh3sheZ5SbyTkDcFWvQS_NEPaBGso-Au9X-yhZsHU1Ky02Nd_DPOrrRzl7uymE07hy3bIbxmrh4ZeEz6P2ixsHHYbd15GNMRlr1cWbg91RBB-akSxX0VYoLjjuqwo33UHk1hBbSATbobfpOKRruuZPZ_xOiPi-m0tUdD1Pj-h6xRcA5sZ1d55IdL8IY_9kgLc_WyU2RWFTXV6zA2SDDdE6EdEB_8q6U0oLU5T-YRxd-V2qOTqW1388qUaL5BalSvqM0g8kVfBYIM3uwLqQI_hCer4CL7_0DGlUtheye3qcoyqWVJd9iqfcWC-1S8NljAJwM9sehx6kOSM85UuUTH89VM35oAVp0rSPcLy20dPzEVQ0LAcy_iRHTkqy9nZ1z6mo-IWQE2ZyPCuESEfG7nFJ3YlUfwBJ5uZnk0N1FVYX5zgEKdkP322vShrv1blB_JtUedpbCcuS-qCaywpwjL0pzTDKpJMEqHds0-DFT7AbULcujw_U",
|
|
||||||
"e": "AQAB",
|
|
||||||
"d": "mCjZ3wDVaMJIKMsMhx28KpS9aGACfX1K_pHRhNOH6KeQ7Mc4oFnBDYnJas-8ofi_FsCB6G88QX7VUfmAYwZncjuQ8FpKb3NlJX8GSh0ZWukqu8G8PcSszMShWSyKvPRs_rOIe_87BlGwXrB-FfaBfx2WqRGtZlziFecsa0T1QJHJGW0bTs52p7c7Ut7ClSOfIBrBRrtjFK4YYp_x7US3HlkkElZvFUo9NoQzBQeN66Y4_Z2ocqFOv0Z8drz-nKzGZOKfYU62nVKD0qJurfOhOGPsOPipZD28v6b5qfC1cYmXAefjNgDK3a2JkShpHPaDKoHoViKN9xQiZvzxSQ1bjQCgB6xQWk61YwmiB44gFLshXvXkx_wcccO2XC4m-Pk0r-Uj9Ugvgizk8HYp1H9eqnp8A6sqU44mrmdt14vM68SGzEqYpLoUkcxPttHvNoORAzgQfuK3SOIFyuwFqKTMBupR4A42XvxLj_PuKEKFekS2JEK8fON2t_LuUDVemjMxwXD0gjljHSKD9HnguihI5IEd6Kgr-HlTwiiDQfzcmWQdoFUs_Je4tVCSbGRrJYju01gLouDu7gzN14pZcEyoBLPkj-_MDiiymvlBLOBU2s5nQRZCaC_0IKgHLaT_rzQaXwmVhU-OURdwdoViMVc_cdXtle4TqU1g41nNE_0tiwE",
|
|
||||||
"p": "2h_9HZCOpf3S9qpAkNS2ncMsRjBlWCpRltoCs-XBF_IgOJlCkJ-42k_V1zaf6YrgMy00XD4Ij5MMiUOharvc3w_WKJF-AwGu9JEpi38Wzj1nkY86sdB1ZPu4ihSRZm81jIjuLZ2siILqkupXaY0S_0CeNLKPoVpvfPWCb3psCWuLVRDhYECPCx9FYg2tzisbFpVAPPqNCFswMYNvhyJ2NQ0hzueKDc1uHBUanCSG6gIbeY1bQvYYFGySYkWZcQg98RSeDbLysHalCSQCgPjss3likTT9OjWLv23vpXURFhgywv6Phx4He0yWB8ZRrnskfW0S76kZ325SPUlnOhf31Q",
|
|
||||||
"q": "3urwELOa7TDpwLMeMEOf2aspz1gkMjoN-V4SMzRGxIoPy93-bQ62Z8nPduma1gK0zHOIs0gU8dDqjy8P8Z7vudJqRFJIrzjD-RnDqQDz7KM_6x0kbEN4GLQiNGVAckRhoEmYjDXXkG3teD2ST5NqlYwxGvFmCvw5RCJZG7eNjXY3KnJQrDyx61NIFEvSm3sBlJkOVhSanuqHBL6T-efaBkUcQ7mY7_iYtCt7cjMG8qQtF0KovVsMF6BUc9KDobpUvDsvPrSTAzsYJvVNkzGJtF8Strr-61VA6qZnRcDW3ukYvmJp_SdAcv3KzSkWwEmJEaAtaiOh2jWkRG9QpV_LoQ"
|
|
||||||
}`)
|
|
||||||
|
|
||||||
testKeyRSA_4096_2 = mustLoadJWK(`{
|
|
||||||
"kty": "RSA",
|
|
||||||
"kid": "822512e8098b9fa56c9152a56d2607bda912c89f6f4fe1b6ccf41627bf06de29",
|
|
||||||
"n": "3bcg0diA0TPvK283P02o5UT8cBf5q48uUsfuCkIbLqNFtskGuoft8OJLmik7c7pLNSK3Jad_-xRlfsrcV1fDBs8EJDFnoY-4r_xk_-GQl-OsHzuz7HMvjVYUSYeVZMtzPAfCIFJxSIaiIW9MEwCwYyrtRefedfKUV7Ax6hjOUXtyRZ5DRHj2FnNl6MjE8mTSJ8OQI9ETErg0wF448fJb6hYCidVHfeBSCOceljXMg8a9iDYHVnzsUb9ETl7xlRZDvUpIoG0jU-aC3oZdM0YgXspvw4FcXg1-ad-TbgRjEm_iJ-4KUhAYso6-Y6eimDT_TMhbKBLGE86pUjRPIXVNl6odjkXmcmh7bz9v0-gIfHB9zVbrqE8i8iIwkux-Uhq_VMiIFikjtjmDo5W-7qA_cjvq1dA1QqOJ54ijbdvglUtneuA5CnwfGHAvf94lEPebgP5eCMZjz_Dtl0FTX0u5lOVBs4qeyEfV1XANOq_h1gHmwJDlspGZVuPfSk4-YMCeBy5Ua_gNDqOIQo3EQbmjyN5yD9byFh3WDSQJN1kKT3BZSou-Q8-KT2lu9KT_CpTfVMaNXnPpXJ_H0S9Q7sbUzA6dF1h0Q_mu_bOlWTRlH3vXfZ3_3Vikjk-jhQdaRv8yjTdhX33wAVHk7omKlo76G3QJdnvjysJ6aagFPZ3qhmc",
|
|
||||||
"e": "AQAB"
|
|
||||||
}`)
|
|
||||||
testKeyRSA_4096_2_Priv = mustLoadJWK(`{
|
|
||||||
"kty": "RSA",
|
|
||||||
"kid": "822512e8098b9fa56c9152a56d2607bda912c89f6f4fe1b6ccf41627bf06de29",
|
|
||||||
"n": "3bcg0diA0TPvK283P02o5UT8cBf5q48uUsfuCkIbLqNFtskGuoft8OJLmik7c7pLNSK3Jad_-xRlfsrcV1fDBs8EJDFnoY-4r_xk_-GQl-OsHzuz7HMvjVYUSYeVZMtzPAfCIFJxSIaiIW9MEwCwYyrtRefedfKUV7Ax6hjOUXtyRZ5DRHj2FnNl6MjE8mTSJ8OQI9ETErg0wF448fJb6hYCidVHfeBSCOceljXMg8a9iDYHVnzsUb9ETl7xlRZDvUpIoG0jU-aC3oZdM0YgXspvw4FcXg1-ad-TbgRjEm_iJ-4KUhAYso6-Y6eimDT_TMhbKBLGE86pUjRPIXVNl6odjkXmcmh7bz9v0-gIfHB9zVbrqE8i8iIwkux-Uhq_VMiIFikjtjmDo5W-7qA_cjvq1dA1QqOJ54ijbdvglUtneuA5CnwfGHAvf94lEPebgP5eCMZjz_Dtl0FTX0u5lOVBs4qeyEfV1XANOq_h1gHmwJDlspGZVuPfSk4-YMCeBy5Ua_gNDqOIQo3EQbmjyN5yD9byFh3WDSQJN1kKT3BZSou-Q8-KT2lu9KT_CpTfVMaNXnPpXJ_H0S9Q7sbUzA6dF1h0Q_mu_bOlWTRlH3vXfZ3_3Vikjk-jhQdaRv8yjTdhX33wAVHk7omKlo76G3QJdnvjysJ6aagFPZ3qhmc",
|
|
||||||
"e": "AQAB",
|
|
||||||
"d": "wcO4mAxJUAu-OsxgkR9SusPWhjQ9y5Q_XLNDso1hahng5ES9b6k55mouvlTIk3Q9I_vp6auAKrMBnJS3ilG1rK6hJOxUcBrFwm-m6QV9s3CSzV0E-mEULsYKxtQKWOOBGvaAznSeck7PRL8a0gSpIpGyeYSRo6zTverLRJZXQVjMXlFY4m-ASdCiQJWtoVVBYOUFhHfE3nhECdaOl8xCTcrcfw75AuZXa1ZpIcd0q7m1jGQDd6-HbE3m6UMKiEvD-ZsA68tVs45h0w3ER_pCcfUjRc45Ji1OzEJLezu0RbmoAVOEi4FrxCkB9N_dNn4inD0BhX0axNa4nZH_kfMNUh06081FaBiBq5bNBPQF7lWIYxAhtd2VgsUTZtNxMfHDTllt5fvbCogYngU5-Wj3UM33R_es_aakt_CFndJPmyenP0J7KEBmf4qnlrbxun-jQNV_cbEzgQA_Pt5RIhB5jgofYzT72Ib-lViZUolXpxg8mFQ5BPghqriuaX8eSc85y5rbu-nEvheDkIC7xjTtDBShXMvKKokx6t4d-x_W4sWDuLKsVNn3Veg6mbbl3O74Fr-joEM9unMg-9KCapAuBKmMv4GejOVOIj43i0lm8dsuMslNKsThWpjApE50-VBuySi1f0jD2HNEzjMTt5ZrlNi9bRFzAht46kdc_g4TWLE",
|
|
||||||
"p": "7cl5QebQldiOrfe2Z7E2aP4d1GIuX4b3u-kXUDa73HI4S2u9TooJF8lfsbrtzJIG60iIzio9Pej8R4ss2_1TPC_F7gQS67ST5yY2zuwUH6jwpt8KtVO3NKd9SNfVJse_mZLp7eAeH8CMq4bqkDig8ZI_Sp1LQtWXvhvhju40i0h1ef-MD36wbc26BE1L6tQMAoJIU-pMDawmWMGG3Q5_LxKHb1cxvV8gVfFgsJbAHMPXxcmpCdp1FqeqpvRs3bch3AMxHwjy1SFRK6lhvliS2ZvD7fdupA09PGJ-cNO3S9iHdCvsnyOHg9pMbxxcYW8Io_t1ihk6JYFvZlDIYvukXw",
|
|
||||||
"q": "7rKFf6190MlunvlLSdMvN_E2Xuow35_EThaxQ9e_7FS32LKPy847vFreXzVivVW1IVXrRSDgU8VIEyBQzftliLSriefKYdx2jtd_A84baha4p6CAK9SwfZcVEcIuU2Ul8XcUpbg53_bxUWDv4mYewZW_AqaZ0ZR1Ug14gCGHELvjs7MmWkCkJB6lEenRQZvzwcmA8tWzZ6Dlqb_RyLtxcVPbeSuc3t6YxbB1-pquwXoQLB9-XvgR_4L6vuGZrp1D-C0lzboUPoftkC_hKYb_xY6LPJTsL8LUypATcTrAnD0TBrqj5-Oy_4dMin6OcsXrfdxflxaJqkh7B-kz20oa-Q"
|
|
||||||
}`)
|
|
||||||
|
|
||||||
testKeyRSA_4096_3 = mustLoadJWK(`{
|
|
||||||
"kty": "RSA",
|
|
||||||
"kid": "ad60df152305985e6bdd8cb1d1f496f766aed097b7464b8b821a09b27f054dcb",
|
|
||||||
"n": "q8mHub9-A7LH2ykklO52CRQ9KYeM9FlHaoLJCMokmwdb8FF8cAMOPDQ4lx7rB107vwX5oHS6T4uhOUH6ppgZnvB083-f5cBfvZPSwvKg1csOPI3Llp-hIO3vnu7v4M-dXRJevmNdTAoHINXP8mJ1KfGL6BuxckxLEOoIX0suSmyudAE_lc6ioMAfwehkH7qTWwllIuReGwxHHYapM0AFIYysJ0z96l-WNkO3ssLm6Q7txpumM_b0d58OkdezBLujEfn0t5H8hsml_LUSE4TfWcGwm3oDBB2q5tsI2_QmVwdV6BcP43Sm8R7EbJvTQq3YXKLlC-Et4-3CDGaMV6z1eFmHBs_FMGzto-kg9MUFockOe0vkfjHj6AafNw7AFyBJRvV3EdMM-O1ysiC5cs45aowbfQIz_9H9RVbR9iMiEjq6nM34bhgfu-FvOmAcliCOJnDlBy2wWe2XyXTLQVkmn1C_w2mitcQAkP-LiktypUwSIbyDqfBai1_imO1SIVzomRLvzSju5qGrcm0ROV6o9zKXCVc546g9la9FUTTPZsI0_s3hcVRVMaF3fa122hE848serjCqNJ0Nb3CYOlVCc6JrkUdeuCj0Y-on6V6aeCFAQxFb1z_p88STsNJkrcNZp87f1JUaSB4POF01dLNQ2HTe7xJCMRmK-fpNuDCOzNM",
|
|
||||||
"e": "AQAB"
|
|
||||||
}`)
|
|
||||||
testKeyRSA_4096_3_Priv = mustLoadJWK(`{
|
|
||||||
"kty": "RSA",
|
|
||||||
"kid": "ad60df152305985e6bdd8cb1d1f496f766aed097b7464b8b821a09b27f054dcb",
|
|
||||||
"n": "q8mHub9-A7LH2ykklO52CRQ9KYeM9FlHaoLJCMokmwdb8FF8cAMOPDQ4lx7rB107vwX5oHS6T4uhOUH6ppgZnvB083-f5cBfvZPSwvKg1csOPI3Llp-hIO3vnu7v4M-dXRJevmNdTAoHINXP8mJ1KfGL6BuxckxLEOoIX0suSmyudAE_lc6ioMAfwehkH7qTWwllIuReGwxHHYapM0AFIYysJ0z96l-WNkO3ssLm6Q7txpumM_b0d58OkdezBLujEfn0t5H8hsml_LUSE4TfWcGwm3oDBB2q5tsI2_QmVwdV6BcP43Sm8R7EbJvTQq3YXKLlC-Et4-3CDGaMV6z1eFmHBs_FMGzto-kg9MUFockOe0vkfjHj6AafNw7AFyBJRvV3EdMM-O1ysiC5cs45aowbfQIz_9H9RVbR9iMiEjq6nM34bhgfu-FvOmAcliCOJnDlBy2wWe2XyXTLQVkmn1C_w2mitcQAkP-LiktypUwSIbyDqfBai1_imO1SIVzomRLvzSju5qGrcm0ROV6o9zKXCVc546g9la9FUTTPZsI0_s3hcVRVMaF3fa122hE848serjCqNJ0Nb3CYOlVCc6JrkUdeuCj0Y-on6V6aeCFAQxFb1z_p88STsNJkrcNZp87f1JUaSB4POF01dLNQ2HTe7xJCMRmK-fpNuDCOzNM",
|
|
||||||
"e": "AQAB",
|
|
||||||
"d": "QFcw8I8aQYRaemlEfEt8BhaAeed9EZ_GscveQ96CK1ZsRuweMU3TrRTaBS_dU1rGH9u7DS_rABQKBIoDuRXKss7Y3sJ0Pvb4ZObSz5VUS_7LjD6HfBi5nr2_O8W-LnNUOyHAPoq0zOAMn221ftEFlPoVLpAAvBB7JRCiph5gbhuak3RMPm2wV4jd3CCQL5oPys8QBCuIW5UTpalkAf_-a_xmFiouB_RZLGXcjaWWGsAuqm5tp5TdJ1h5eoJRWHp2ryrxTzfsXwdzldyzsn_Xr6Rt4y2lp4r9EY4EGW2uVnY25MCOgOCWDkU5yHvselLmcHvKUdK6_11zinV2Jvhuz1FaW10P6lRXxhjuuT4bT7XmY6WkJCUvWf8w_NYrGwDbRvQa6YZcZZWKZ1l2Enkgd_P4qwtrnROQSCe0dJdNG3lSq90lrAwuvb8AhKhpQ8nJSaSQc_h2pa4NJZ-R__9m0_7CRUuEEg9k47AtgdIe09KLSpcACc3W5cBXEw2pL8ihqcf9AVgM0TFQQVrUdIst3YjUiB6r2zLVCRx8KjtT8Pmz6YtMQwDIFTrUopwwai5PEP3QEdxdNF39W1iwkqjA8uw4IlXgZAr4-9s3x617XUL0BQ4TgMpTsEghpV1U8U2HQfYGUKBcAlSqIlAi0MU4QRwcsAJrcoTIi-e34NFMbHE",
|
|
||||||
"p": "xvZ1BKzaRfrmQT2mmbK1MzKdr4amHrx4EJ_4fRsCRBj8MqB92y4Bp0iuO8rZ3iK_yOOgQQTcr4UP5UEACLOqGDPNa92UUpHu1U5XR3MiIY0kGcdovpkkGU8fq78VRpyofc1b9kvZsy4eL0e7W7jApjGu479D_evnpT7lqSOGRl1Z0QHdI3ctKmKw48-AmQWL4BibXTyaXOfRgTHm4AtbrvFshwCZ43QgzrCrwbhZlOfiIIW9wXa_OqqOhV11BLF72qTglecfTgUOxY0W5GWgbwXCVZ8Lwr6cxYKGrKjmny9UNOIjABGsJjZm5egwDNUhhoaEeXXyzXbCKynvUCDxZw",
|
|
||||||
"q": "3Qi1bRzj3TYW3Iw9KSDCuuHWBJRO_3Ke-JLB7PHEXc1hsPwF_XDqXPaBbIUtgKaqNzUehYAQbtHHhJVGqZadTKjsNkO4sO_r5nRoxUpFuGgUEmKNN0QeJa_llHzAZ9JpvUrtoyzVZX-2Em_3aAhtV5pZ6t22YToPDjZIBgqL96MQRCyKsv2FS_dbpoYKdXlG6phzkzKPn3oaWkh-VwmgAsFg7uXdiquQEsXYcOq3-77Umiuke6SBbaHLryLflTzOV7YvY7Kw3s3NycS9MDBESKYXqqX3YKvS25ZFFjYbrVOx5AluLiwajZu2biIPZb9rNbSXE77Hll3tAbmA5syJtQ"
|
|
||||||
}`)
|
|
||||||
)
|
|
99
vendor/github.com/coreos/go-oidc/jwks_test.go
generated
vendored
99
vendor/github.com/coreos/go-oidc/jwks_test.go
generated
vendored
|
@ -1,99 +0,0 @@
|
||||||
package oidc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
jose "gopkg.in/square/go-jose.v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
type keyServer struct {
|
|
||||||
keys jose.JSONWebKeySet
|
|
||||||
}
|
|
||||||
|
|
||||||
func newKeyServer(keys ...jose.JSONWebKey) keyServer {
|
|
||||||
return keyServer{
|
|
||||||
keys: jose.JSONWebKeySet{Keys: keys},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k keyServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if err := json.NewEncoder(w).Encode(k.keys); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestKeysFormID(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
keys []jose.JSONWebKey
|
|
||||||
keyIDs []string
|
|
||||||
wantKeys []jose.JSONWebKey
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "single key",
|
|
||||||
keys: []jose.JSONWebKey{
|
|
||||||
testKeyRSA_2048_0,
|
|
||||||
testKeyECDSA_256_0,
|
|
||||||
},
|
|
||||||
keyIDs: []string{
|
|
||||||
testKeyRSA_2048_0.KeyID,
|
|
||||||
},
|
|
||||||
wantKeys: []jose.JSONWebKey{
|
|
||||||
testKeyRSA_2048_0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "one key id matches",
|
|
||||||
keys: []jose.JSONWebKey{
|
|
||||||
testKeyRSA_2048_0,
|
|
||||||
testKeyECDSA_256_0,
|
|
||||||
},
|
|
||||||
keyIDs: []string{
|
|
||||||
testKeyRSA_2048_0.KeyID,
|
|
||||||
testKeyRSA_2048_1.KeyID,
|
|
||||||
},
|
|
||||||
wantKeys: []jose.JSONWebKey{
|
|
||||||
testKeyRSA_2048_0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "no valid keys",
|
|
||||||
keys: []jose.JSONWebKey{
|
|
||||||
testKeyRSA_2048_1,
|
|
||||||
testKeyECDSA_256_0,
|
|
||||||
},
|
|
||||||
keyIDs: []string{
|
|
||||||
testKeyRSA_2048_0.KeyID,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
t0 := time.Now()
|
|
||||||
now := func() time.Time { return t0 }
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
func() {
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
server := httptest.NewServer(newKeyServer(test.keys...))
|
|
||||||
defer server.Close()
|
|
||||||
|
|
||||||
keySet := newRemoteKeySet(ctx, server.URL, now)
|
|
||||||
gotKeys, err := keySet.keysWithID(ctx, test.keyIDs)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("%s: %v", test.name, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(gotKeys, test.wantKeys) {
|
|
||||||
t.Errorf("%s: expected keys=%#v, got=%#v", test.name, test.wantKeys, gotKeys)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
|
21
vendor/github.com/coreos/go-oidc/oidc_test.go
generated
vendored
21
vendor/github.com/coreos/go-oidc/oidc_test.go
generated
vendored
|
@ -1,21 +0,0 @@
|
||||||
package oidc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestClientContext(t *testing.T) {
|
|
||||||
myClient := &http.Client{}
|
|
||||||
|
|
||||||
ctx := ClientContext(context.Background(), myClient)
|
|
||||||
|
|
||||||
gotClient := clientFromContext(ctx)
|
|
||||||
|
|
||||||
// Compare pointer values.
|
|
||||||
if gotClient != myClient {
|
|
||||||
t.Fatal("clientFromContext did not return the value set by ClientContext")
|
|
||||||
}
|
|
||||||
}
|
|
15
vendor/github.com/coreos/go-oidc/test
generated
vendored
15
vendor/github.com/coreos/go-oidc/test
generated
vendored
|
@ -1,15 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Filter out any files with a !golint build tag.
|
|
||||||
LINTABLE=$( go list -tags=golint -f '
|
|
||||||
{{- range $i, $file := .GoFiles -}}
|
|
||||||
{{ $file }} {{ end }}
|
|
||||||
{{ range $i, $file := .TestGoFiles -}}
|
|
||||||
{{ $file }} {{ end }}' github.com/coreos/go-oidc )
|
|
||||||
|
|
||||||
go test -v -i -race github.com/coreos/go-oidc/...
|
|
||||||
go test -v -race github.com/coreos/go-oidc/...
|
|
||||||
golint $LINTABLE
|
|
||||||
go vet github.com/coreos/go-oidc/...
|
|
265
vendor/github.com/coreos/go-oidc/verify_test.go
generated
vendored
265
vendor/github.com/coreos/go-oidc/verify_test.go
generated
vendored
|
@ -1,265 +0,0 @@
|
||||||
package oidc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/elliptic"
|
|
||||||
"crypto/rsa"
|
|
||||||
"encoding/json"
|
|
||||||
"net/http/httptest"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
jose "gopkg.in/square/go-jose.v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestVerify(t *testing.T) {
|
|
||||||
tests := []verificationTest{
|
|
||||||
{
|
|
||||||
name: "good token",
|
|
||||||
idToken: idToken{
|
|
||||||
Issuer: "https://foo",
|
|
||||||
},
|
|
||||||
config: verificationConfig{
|
|
||||||
issuer: "https://foo",
|
|
||||||
},
|
|
||||||
signKey: testKeyRSA_2048_0_Priv,
|
|
||||||
pubKeys: []jose.JSONWebKey{testKeyRSA_2048_0},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid signature",
|
|
||||||
idToken: idToken{
|
|
||||||
Issuer: "https://foo",
|
|
||||||
},
|
|
||||||
config: verificationConfig{
|
|
||||||
issuer: "https://foo",
|
|
||||||
},
|
|
||||||
signKey: testKeyRSA_2048_0_Priv,
|
|
||||||
pubKeys: []jose.JSONWebKey{testKeyRSA_2048_1},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid issuer",
|
|
||||||
idToken: idToken{
|
|
||||||
Issuer: "https://foo",
|
|
||||||
},
|
|
||||||
config: verificationConfig{
|
|
||||||
issuer: "https://bar",
|
|
||||||
},
|
|
||||||
signKey: testKeyRSA_2048_0_Priv,
|
|
||||||
pubKeys: []jose.JSONWebKey{testKeyRSA_2048_0},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
test.run(t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestVerifyAudience(t *testing.T) {
|
|
||||||
tests := []verificationTest{
|
|
||||||
{
|
|
||||||
name: "good audience",
|
|
||||||
idToken: idToken{
|
|
||||||
Issuer: "https://foo",
|
|
||||||
Audience: []string{"client1"},
|
|
||||||
},
|
|
||||||
config: verificationConfig{
|
|
||||||
issuer: "https://foo",
|
|
||||||
audience: "client1",
|
|
||||||
},
|
|
||||||
signKey: testKeyRSA_2048_0_Priv,
|
|
||||||
pubKeys: []jose.JSONWebKey{testKeyRSA_2048_0},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "mismatched audience",
|
|
||||||
idToken: idToken{
|
|
||||||
Issuer: "https://foo",
|
|
||||||
Audience: []string{"client2"},
|
|
||||||
},
|
|
||||||
config: verificationConfig{
|
|
||||||
issuer: "https://foo",
|
|
||||||
audience: "client1",
|
|
||||||
},
|
|
||||||
signKey: testKeyRSA_2048_0_Priv,
|
|
||||||
pubKeys: []jose.JSONWebKey{testKeyRSA_2048_0},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "multiple audiences, one matches",
|
|
||||||
idToken: idToken{
|
|
||||||
Issuer: "https://foo",
|
|
||||||
Audience: []string{"client2", "client1"},
|
|
||||||
},
|
|
||||||
config: verificationConfig{
|
|
||||||
issuer: "https://foo",
|
|
||||||
audience: "client1",
|
|
||||||
},
|
|
||||||
signKey: testKeyRSA_2048_0_Priv,
|
|
||||||
pubKeys: []jose.JSONWebKey{testKeyRSA_2048_0},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
test.run(t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestVerifySigningAlg(t *testing.T) {
|
|
||||||
tests := []verificationTest{
|
|
||||||
{
|
|
||||||
name: "default signing alg",
|
|
||||||
idToken: idToken{
|
|
||||||
Issuer: "https://foo",
|
|
||||||
},
|
|
||||||
config: verificationConfig{
|
|
||||||
issuer: "https://foo",
|
|
||||||
},
|
|
||||||
signKey: testKeyRSA_2048_0_Priv,
|
|
||||||
signAlg: RS256, // By default we only support RS256.
|
|
||||||
pubKeys: []jose.JSONWebKey{testKeyRSA_2048_0},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "bad signing alg",
|
|
||||||
idToken: idToken{
|
|
||||||
Issuer: "https://foo",
|
|
||||||
},
|
|
||||||
config: verificationConfig{
|
|
||||||
issuer: "https://foo",
|
|
||||||
},
|
|
||||||
signKey: testKeyRSA_2048_0_Priv,
|
|
||||||
signAlg: RS512,
|
|
||||||
pubKeys: []jose.JSONWebKey{testKeyRSA_2048_0},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "ecdsa signing",
|
|
||||||
idToken: idToken{
|
|
||||||
Issuer: "https://foo",
|
|
||||||
},
|
|
||||||
config: verificationConfig{
|
|
||||||
issuer: "https://foo",
|
|
||||||
requiredAlgs: []string{ES384},
|
|
||||||
},
|
|
||||||
signAlg: ES384,
|
|
||||||
signKey: testKeyECDSA_384_0_Priv,
|
|
||||||
pubKeys: []jose.JSONWebKey{testKeyECDSA_384_0},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "one of many supported",
|
|
||||||
idToken: idToken{
|
|
||||||
Issuer: "https://foo",
|
|
||||||
},
|
|
||||||
config: verificationConfig{
|
|
||||||
issuer: "https://foo",
|
|
||||||
requiredAlgs: []string{RS256, ES384},
|
|
||||||
},
|
|
||||||
signAlg: ES384,
|
|
||||||
signKey: testKeyECDSA_384_0_Priv,
|
|
||||||
pubKeys: []jose.JSONWebKey{testKeyECDSA_384_0},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "not in requiredAlgs",
|
|
||||||
idToken: idToken{
|
|
||||||
Issuer: "https://foo",
|
|
||||||
},
|
|
||||||
config: verificationConfig{
|
|
||||||
issuer: "https://foo",
|
|
||||||
requiredAlgs: []string{RS256, ES512},
|
|
||||||
},
|
|
||||||
signAlg: ES384,
|
|
||||||
signKey: testKeyECDSA_384_0_Priv,
|
|
||||||
pubKeys: []jose.JSONWebKey{testKeyECDSA_384_0},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
test.run(t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type verificationTest struct {
|
|
||||||
name string
|
|
||||||
|
|
||||||
// ID token claims and a signing key to create the JWT.
|
|
||||||
idToken idToken
|
|
||||||
signKey jose.JSONWebKey
|
|
||||||
// If supplied use this signing algorithm. If not, guess
|
|
||||||
// from the signingKey.
|
|
||||||
signAlg string
|
|
||||||
|
|
||||||
config verificationConfig
|
|
||||||
pubKeys []jose.JSONWebKey
|
|
||||||
|
|
||||||
wantErr bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func algForKey(t *testing.T, k jose.JSONWebKey) string {
|
|
||||||
switch key := k.Key.(type) {
|
|
||||||
case *rsa.PrivateKey:
|
|
||||||
return RS256
|
|
||||||
case *ecdsa.PrivateKey:
|
|
||||||
name := key.PublicKey.Params().Name
|
|
||||||
switch name {
|
|
||||||
case elliptic.P256().Params().Name:
|
|
||||||
return ES256
|
|
||||||
case elliptic.P384().Params().Name:
|
|
||||||
return ES384
|
|
||||||
case elliptic.P521().Params().Name:
|
|
||||||
return ES512
|
|
||||||
}
|
|
||||||
t.Fatalf("unsupported ecdsa curve: %s", name)
|
|
||||||
default:
|
|
||||||
t.Fatalf("unsupported key type %T", key)
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v verificationTest) run(t *testing.T) {
|
|
||||||
payload, err := json.Marshal(v.idToken)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
signingAlg := v.signAlg
|
|
||||||
if signingAlg == "" {
|
|
||||||
signingAlg = algForKey(t, v.signKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
signer, err := jose.NewSigner(jose.SigningKey{
|
|
||||||
Algorithm: jose.SignatureAlgorithm(signingAlg),
|
|
||||||
Key: &v.signKey,
|
|
||||||
}, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
jws, err := signer.Sign(payload)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
token, err := jws.CompactSerialize()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
t0 := time.Now()
|
|
||||||
now := func() time.Time { return t0 }
|
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
server := httptest.NewServer(newKeyServer(v.pubKeys...))
|
|
||||||
defer server.Close()
|
|
||||||
|
|
||||||
verifier := newVerifier(newRemoteKeySet(ctx, server.URL, now), &v.config)
|
|
||||||
|
|
||||||
if _, err := verifier.Verify(ctx, token); err != nil {
|
|
||||||
if !v.wantErr {
|
|
||||||
t.Errorf("%s: verify %v", v.name, err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if v.wantErr {
|
|
||||||
t.Errorf("%s: expected error", v.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
20
vendor/github.com/ghodss/yaml/.gitignore
generated
vendored
20
vendor/github.com/ghodss/yaml/.gitignore
generated
vendored
|
@ -1,20 +0,0 @@
|
||||||
# OSX leaves these everywhere on SMB shares
|
|
||||||
._*
|
|
||||||
|
|
||||||
# Eclipse files
|
|
||||||
.classpath
|
|
||||||
.project
|
|
||||||
.settings/**
|
|
||||||
|
|
||||||
# Emacs save files
|
|
||||||
*~
|
|
||||||
|
|
||||||
# Vim-related files
|
|
||||||
[._]*.s[a-w][a-z]
|
|
||||||
[._]s[a-w][a-z]
|
|
||||||
*.un~
|
|
||||||
Session.vim
|
|
||||||
.netrwhist
|
|
||||||
|
|
||||||
# Go test binaries
|
|
||||||
*.test
|
|
7
vendor/github.com/ghodss/yaml/.travis.yml
generated
vendored
7
vendor/github.com/ghodss/yaml/.travis.yml
generated
vendored
|
@ -1,7 +0,0 @@
|
||||||
language: go
|
|
||||||
go:
|
|
||||||
- 1.3
|
|
||||||
- 1.4
|
|
||||||
script:
|
|
||||||
- go test
|
|
||||||
- go build
|
|
120
vendor/github.com/ghodss/yaml/README.md
generated
vendored
120
vendor/github.com/ghodss/yaml/README.md
generated
vendored
|
@ -1,120 +0,0 @@
|
||||||
# YAML marshaling and unmarshaling support for Go
|
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/ghodss/yaml.svg)](https://travis-ci.org/ghodss/yaml)
|
|
||||||
|
|
||||||
## Introduction
|
|
||||||
|
|
||||||
A wrapper around [go-yaml](https://github.com/go-yaml/yaml) designed to enable a better way of handling YAML when marshaling to and from structs.
|
|
||||||
|
|
||||||
In short, this library first converts YAML to JSON using go-yaml and then uses `json.Marshal` and `json.Unmarshal` to convert to or from the struct. This means that it effectively reuses the JSON struct tags as well as the custom JSON methods `MarshalJSON` and `UnmarshalJSON` unlike go-yaml. For a detailed overview of the rationale behind this method, [see this blog post](http://ghodss.com/2014/the-right-way-to-handle-yaml-in-golang/).
|
|
||||||
|
|
||||||
## Compatibility
|
|
||||||
|
|
||||||
This package uses [go-yaml](https://github.com/go-yaml/yaml) and therefore supports [everything go-yaml supports](https://github.com/go-yaml/yaml#compatibility).
|
|
||||||
|
|
||||||
## Caveats
|
|
||||||
|
|
||||||
**Caveat #1:** When using `yaml.Marshal` and `yaml.Unmarshal`, binary data should NOT be preceded with the `!!binary` YAML tag. If you do, candiedyaml will convert the binary data from base64 to native binary data, which is not compatible with JSON. You can still use binary in your YAML files though - just store them without the `!!binary` tag and decode the base64 in your code (e.g. in the custom JSON methods `MarshalJSON` and `UnmarshalJSON`). This also has the benefit that your YAML and your JSON binary data will be decoded exactly the same way. As an example:
|
|
||||||
|
|
||||||
```
|
|
||||||
BAD:
|
|
||||||
exampleKey: !!binary gIGC
|
|
||||||
|
|
||||||
GOOD:
|
|
||||||
exampleKey: gIGC
|
|
||||||
... and decode the base64 data in your code.
|
|
||||||
```
|
|
||||||
|
|
||||||
**Caveat #2:** When using `YAMLToJSON` directly, maps with keys that are maps will result in an error since this is not supported by JSON. This error will occur in `Unmarshal` as well since you can't unmarshal map keys anyways since struct fields can't be keys.
|
|
||||||
|
|
||||||
## Installation and usage
|
|
||||||
|
|
||||||
To install, run:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ go get github.com/ghodss/yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
And import using:
|
|
||||||
|
|
||||||
```
|
|
||||||
import "github.com/ghodss/yaml"
|
|
||||||
```
|
|
||||||
|
|
||||||
Usage is very similar to the JSON library:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/ghodss/yaml"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Person struct {
|
|
||||||
Name string `json:"name"` // Affects YAML field names too.
|
|
||||||
Age int `json:"age"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// Marshal a Person struct to YAML.
|
|
||||||
p := Person{"John", 30}
|
|
||||||
y, err := yaml.Marshal(p)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("err: %v\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Println(string(y))
|
|
||||||
/* Output:
|
|
||||||
age: 30
|
|
||||||
name: John
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Unmarshal the YAML back into a Person struct.
|
|
||||||
var p2 Person
|
|
||||||
err = yaml.Unmarshal(y, &p2)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("err: %v\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Println(p2)
|
|
||||||
/* Output:
|
|
||||||
{John 30}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
`yaml.YAMLToJSON` and `yaml.JSONToYAML` methods are also available:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/ghodss/yaml"
|
|
||||||
)
|
|
||||||
func main() {
|
|
||||||
j := []byte(`{"name": "John", "age": 30}`)
|
|
||||||
y, err := yaml.JSONToYAML(j)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("err: %v\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Println(string(y))
|
|
||||||
/* Output:
|
|
||||||
name: John
|
|
||||||
age: 30
|
|
||||||
*/
|
|
||||||
j2, err := yaml.YAMLToJSON(y)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("err: %v\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Println(string(j2))
|
|
||||||
/* Output:
|
|
||||||
{"age":30,"name":"John"}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
```
|
|
271
vendor/github.com/ghodss/yaml/yaml_test.go
generated
vendored
271
vendor/github.com/ghodss/yaml/yaml_test.go
generated
vendored
|
@ -1,271 +0,0 @@
|
||||||
package yaml
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MarshalTest struct {
|
|
||||||
A string
|
|
||||||
B int64
|
|
||||||
// Would like to test float64, but it's not supported in go-yaml.
|
|
||||||
// (See https://github.com/go-yaml/yaml/issues/83.)
|
|
||||||
C float32
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMarshal(t *testing.T) {
|
|
||||||
f32String := strconv.FormatFloat(math.MaxFloat32, 'g', -1, 32)
|
|
||||||
s := MarshalTest{"a", math.MaxInt64, math.MaxFloat32}
|
|
||||||
e := []byte(fmt.Sprintf("A: a\nB: %d\nC: %s\n", math.MaxInt64, f32String))
|
|
||||||
|
|
||||||
y, err := Marshal(s)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("error marshaling YAML: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(y, e) {
|
|
||||||
t.Errorf("marshal YAML was unsuccessful, expected: %#v, got: %#v",
|
|
||||||
string(e), string(y))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type UnmarshalString struct {
|
|
||||||
A string
|
|
||||||
True string
|
|
||||||
}
|
|
||||||
|
|
||||||
type UnmarshalStringMap struct {
|
|
||||||
A map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
type UnmarshalNestedString struct {
|
|
||||||
A NestedString
|
|
||||||
}
|
|
||||||
|
|
||||||
type NestedString struct {
|
|
||||||
A string
|
|
||||||
}
|
|
||||||
|
|
||||||
type UnmarshalSlice struct {
|
|
||||||
A []NestedSlice
|
|
||||||
}
|
|
||||||
|
|
||||||
type NestedSlice struct {
|
|
||||||
B string
|
|
||||||
C *string
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnmarshal(t *testing.T) {
|
|
||||||
y := []byte("a: 1")
|
|
||||||
s1 := UnmarshalString{}
|
|
||||||
e1 := UnmarshalString{A: "1"}
|
|
||||||
unmarshal(t, y, &s1, &e1)
|
|
||||||
|
|
||||||
y = []byte("a: true")
|
|
||||||
s1 = UnmarshalString{}
|
|
||||||
e1 = UnmarshalString{A: "true"}
|
|
||||||
unmarshal(t, y, &s1, &e1)
|
|
||||||
|
|
||||||
y = []byte("true: 1")
|
|
||||||
s1 = UnmarshalString{}
|
|
||||||
e1 = UnmarshalString{True: "1"}
|
|
||||||
unmarshal(t, y, &s1, &e1)
|
|
||||||
|
|
||||||
y = []byte("a:\n a: 1")
|
|
||||||
s2 := UnmarshalNestedString{}
|
|
||||||
e2 := UnmarshalNestedString{NestedString{"1"}}
|
|
||||||
unmarshal(t, y, &s2, &e2)
|
|
||||||
|
|
||||||
y = []byte("a:\n - b: abc\n c: def\n - b: 123\n c: 456\n")
|
|
||||||
s3 := UnmarshalSlice{}
|
|
||||||
e3 := UnmarshalSlice{[]NestedSlice{NestedSlice{"abc", strPtr("def")}, NestedSlice{"123", strPtr("456")}}}
|
|
||||||
unmarshal(t, y, &s3, &e3)
|
|
||||||
|
|
||||||
y = []byte("a:\n b: 1")
|
|
||||||
s4 := UnmarshalStringMap{}
|
|
||||||
e4 := UnmarshalStringMap{map[string]string{"b": "1"}}
|
|
||||||
unmarshal(t, y, &s4, &e4)
|
|
||||||
}
|
|
||||||
|
|
||||||
func unmarshal(t *testing.T, y []byte, s, e interface{}) {
|
|
||||||
err := Unmarshal(y, s)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("error unmarshaling YAML: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(s, e) {
|
|
||||||
t.Errorf("unmarshal YAML was unsuccessful, expected: %+#v, got: %+#v",
|
|
||||||
e, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Case struct {
|
|
||||||
input string
|
|
||||||
output string
|
|
||||||
// By default we test that reversing the output == input. But if there is a
|
|
||||||
// difference in the reversed output, you can optionally specify it here.
|
|
||||||
reverse *string
|
|
||||||
}
|
|
||||||
|
|
||||||
type RunType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
RunTypeJSONToYAML RunType = iota
|
|
||||||
RunTypeYAMLToJSON
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestJSONToYAML(t *testing.T) {
|
|
||||||
cases := []Case{
|
|
||||||
{
|
|
||||||
`{"t":"a"}`,
|
|
||||||
"t: a\n",
|
|
||||||
nil,
|
|
||||||
}, {
|
|
||||||
`{"t":null}`,
|
|
||||||
"t: null\n",
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
runCases(t, RunTypeJSONToYAML, cases)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestYAMLToJSON(t *testing.T) {
|
|
||||||
cases := []Case{
|
|
||||||
{
|
|
||||||
"t: a\n",
|
|
||||||
`{"t":"a"}`,
|
|
||||||
nil,
|
|
||||||
}, {
|
|
||||||
"t: \n",
|
|
||||||
`{"t":null}`,
|
|
||||||
strPtr("t: null\n"),
|
|
||||||
}, {
|
|
||||||
"t: null\n",
|
|
||||||
`{"t":null}`,
|
|
||||||
nil,
|
|
||||||
}, {
|
|
||||||
"1: a\n",
|
|
||||||
`{"1":"a"}`,
|
|
||||||
strPtr("\"1\": a\n"),
|
|
||||||
}, {
|
|
||||||
"1000000000000000000000000000000000000: a\n",
|
|
||||||
`{"1e+36":"a"}`,
|
|
||||||
strPtr("\"1e+36\": a\n"),
|
|
||||||
}, {
|
|
||||||
"1e+36: a\n",
|
|
||||||
`{"1e+36":"a"}`,
|
|
||||||
strPtr("\"1e+36\": a\n"),
|
|
||||||
}, {
|
|
||||||
"\"1e+36\": a\n",
|
|
||||||
`{"1e+36":"a"}`,
|
|
||||||
nil,
|
|
||||||
}, {
|
|
||||||
"\"1.2\": a\n",
|
|
||||||
`{"1.2":"a"}`,
|
|
||||||
nil,
|
|
||||||
}, {
|
|
||||||
"- t: a\n",
|
|
||||||
`[{"t":"a"}]`,
|
|
||||||
nil,
|
|
||||||
}, {
|
|
||||||
"- t: a\n" +
|
|
||||||
"- t:\n" +
|
|
||||||
" b: 1\n" +
|
|
||||||
" c: 2\n",
|
|
||||||
`[{"t":"a"},{"t":{"b":1,"c":2}}]`,
|
|
||||||
nil,
|
|
||||||
}, {
|
|
||||||
`[{t: a}, {t: {b: 1, c: 2}}]`,
|
|
||||||
`[{"t":"a"},{"t":{"b":1,"c":2}}]`,
|
|
||||||
strPtr("- t: a\n" +
|
|
||||||
"- t:\n" +
|
|
||||||
" b: 1\n" +
|
|
||||||
" c: 2\n"),
|
|
||||||
}, {
|
|
||||||
"- t: \n",
|
|
||||||
`[{"t":null}]`,
|
|
||||||
strPtr("- t: null\n"),
|
|
||||||
}, {
|
|
||||||
"- t: null\n",
|
|
||||||
`[{"t":null}]`,
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cases that should produce errors.
|
|
||||||
_ = []Case{
|
|
||||||
{
|
|
||||||
"~: a",
|
|
||||||
`{"null":"a"}`,
|
|
||||||
nil,
|
|
||||||
}, {
|
|
||||||
"a: !!binary gIGC\n",
|
|
||||||
"{\"a\":\"\x80\x81\x82\"}",
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
runCases(t, RunTypeYAMLToJSON, cases)
|
|
||||||
}
|
|
||||||
|
|
||||||
func runCases(t *testing.T, runType RunType, cases []Case) {
|
|
||||||
var f func([]byte) ([]byte, error)
|
|
||||||
var invF func([]byte) ([]byte, error)
|
|
||||||
var msg string
|
|
||||||
var invMsg string
|
|
||||||
if runType == RunTypeJSONToYAML {
|
|
||||||
f = JSONToYAML
|
|
||||||
invF = YAMLToJSON
|
|
||||||
msg = "JSON to YAML"
|
|
||||||
invMsg = "YAML back to JSON"
|
|
||||||
} else {
|
|
||||||
f = YAMLToJSON
|
|
||||||
invF = JSONToYAML
|
|
||||||
msg = "YAML to JSON"
|
|
||||||
invMsg = "JSON back to YAML"
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range cases {
|
|
||||||
// Convert the string.
|
|
||||||
t.Logf("converting %s\n", c.input)
|
|
||||||
output, err := f([]byte(c.input))
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Failed to convert %s, input: `%s`, err: %v", msg, c.input, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check it against the expected output.
|
|
||||||
if string(output) != c.output {
|
|
||||||
t.Errorf("Failed to convert %s, input: `%s`, expected `%s`, got `%s`",
|
|
||||||
msg, c.input, c.output, string(output))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the string that we will compare the reversed output to.
|
|
||||||
reverse := c.input
|
|
||||||
// If a special reverse string was specified, use that instead.
|
|
||||||
if c.reverse != nil {
|
|
||||||
reverse = *c.reverse
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reverse the output.
|
|
||||||
input, err := invF(output)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Failed to convert %s, input: `%s`, err: %v", invMsg, string(output), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check the reverse is equal to the input (or to *c.reverse).
|
|
||||||
if string(input) != reverse {
|
|
||||||
t.Errorf("Failed to convert %s, input: `%s`, expected `%s`, got `%s`",
|
|
||||||
invMsg, string(output), reverse, string(input))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// To be able to easily fill in the *Case.reverse string above.
|
|
||||||
func strPtr(s string) *string {
|
|
||||||
return &s
|
|
||||||
}
|
|
8
vendor/github.com/go-sql-driver/mysql/.gitignore
generated
vendored
8
vendor/github.com/go-sql-driver/mysql/.gitignore
generated
vendored
|
@ -1,8 +0,0 @@
|
||||||
.DS_Store
|
|
||||||
.DS_Store?
|
|
||||||
._*
|
|
||||||
.Spotlight-V100
|
|
||||||
.Trashes
|
|
||||||
Icon?
|
|
||||||
ehthumbs.db
|
|
||||||
Thumbs.db
|
|
12
vendor/github.com/go-sql-driver/mysql/.travis.yml
generated
vendored
12
vendor/github.com/go-sql-driver/mysql/.travis.yml
generated
vendored
|
@ -1,12 +0,0 @@
|
||||||
sudo: false
|
|
||||||
language: go
|
|
||||||
go:
|
|
||||||
- 1.2
|
|
||||||
- 1.3
|
|
||||||
- 1.4
|
|
||||||
- 1.5
|
|
||||||
- 1.6
|
|
||||||
- tip
|
|
||||||
|
|
||||||
before_script:
|
|
||||||
- mysql -e 'create database gotest;'
|
|
52
vendor/github.com/go-sql-driver/mysql/AUTHORS
generated
vendored
52
vendor/github.com/go-sql-driver/mysql/AUTHORS
generated
vendored
|
@ -1,52 +0,0 @@
|
||||||
# This is the official list of Go-MySQL-Driver authors for copyright purposes.
|
|
||||||
|
|
||||||
# If you are submitting a patch, please add your name or the name of the
|
|
||||||
# organization which holds the copyright to this list in alphabetical order.
|
|
||||||
|
|
||||||
# Names should be added to this file as
|
|
||||||
# Name <email address>
|
|
||||||
# The email address is not required for organizations.
|
|
||||||
# Please keep the list sorted.
|
|
||||||
|
|
||||||
|
|
||||||
# Individual Persons
|
|
||||||
|
|
||||||
Aaron Hopkins <go-sql-driver at die.net>
|
|
||||||
Arne Hormann <arnehormann at gmail.com>
|
|
||||||
Carlos Nieto <jose.carlos at menteslibres.net>
|
|
||||||
Chris Moos <chris at tech9computers.com>
|
|
||||||
Daniel Nichter <nil at codenode.com>
|
|
||||||
Daniël van Eeden <git at myname.nl>
|
|
||||||
DisposaBoy <disposaboy at dby.me>
|
|
||||||
Frederick Mayle <frederickmayle at gmail.com>
|
|
||||||
Gustavo Kristic <gkristic at gmail.com>
|
|
||||||
Hanno Braun <mail at hannobraun.com>
|
|
||||||
Henri Yandell <flamefew at gmail.com>
|
|
||||||
Hirotaka Yamamoto <ymmt2005 at gmail.com>
|
|
||||||
INADA Naoki <songofacandy at gmail.com>
|
|
||||||
James Harr <james.harr at gmail.com>
|
|
||||||
Jian Zhen <zhenjl at gmail.com>
|
|
||||||
Joshua Prunier <joshua.prunier at gmail.com>
|
|
||||||
Julien Lefevre <julien.lefevr at gmail.com>
|
|
||||||
Julien Schmidt <go-sql-driver at julienschmidt.com>
|
|
||||||
Kamil Dziedzic <kamil at klecza.pl>
|
|
||||||
Kevin Malachowski <kevin at chowski.com>
|
|
||||||
Leonardo YongUk Kim <dalinaum at gmail.com>
|
|
||||||
Luca Looz <luca.looz92 at gmail.com>
|
|
||||||
Lucas Liu <extrafliu at gmail.com>
|
|
||||||
Luke Scott <luke at webconnex.com>
|
|
||||||
Michael Woolnough <michael.woolnough at gmail.com>
|
|
||||||
Nicola Peduzzi <thenikso at gmail.com>
|
|
||||||
Paul Bonser <misterpib at gmail.com>
|
|
||||||
Runrioter Wung <runrioter at gmail.com>
|
|
||||||
Soroush Pour <me at soroushjp.com>
|
|
||||||
Stan Putrya <root.vagner at gmail.com>
|
|
||||||
Stanley Gunawan <gunawan.stanley at gmail.com>
|
|
||||||
Xiaobing Jiang <s7v7nislands at gmail.com>
|
|
||||||
Xiuming Chen <cc at cxm.cc>
|
|
||||||
|
|
||||||
# Organizations
|
|
||||||
|
|
||||||
Barracuda Networks, Inc.
|
|
||||||
Google Inc.
|
|
||||||
Stripe Inc.
|
|
103
vendor/github.com/go-sql-driver/mysql/CHANGELOG.md
generated
vendored
103
vendor/github.com/go-sql-driver/mysql/CHANGELOG.md
generated
vendored
|
@ -1,103 +0,0 @@
|
||||||
## HEAD
|
|
||||||
|
|
||||||
Changes:
|
|
||||||
|
|
||||||
- Go 1.1 is no longer supported
|
|
||||||
- Use decimals field from MySQL to format time types (#249)
|
|
||||||
- Buffer optimizations (#269)
|
|
||||||
- TLS ServerName defaults to the host (#283)
|
|
||||||
|
|
||||||
Bugfixes:
|
|
||||||
|
|
||||||
- Enable microsecond resolution on TIME, DATETIME and TIMESTAMP (#249)
|
|
||||||
- Fixed handling of queries without columns and rows (#255)
|
|
||||||
- Fixed a panic when SetKeepAlive() failed (#298)
|
|
||||||
- Support receiving ERR packet while reading rows (#321)
|
|
||||||
- Fixed reading NULL length-encoded integers in MySQL 5.6+ (#349)
|
|
||||||
- Fixed absolute paths support in LOAD LOCAL DATA INFILE (#356)
|
|
||||||
- Actually zero out bytes in handshake response (#378)
|
|
||||||
- Fixed race condition in registering LOAD DATA INFILE handler (#383)
|
|
||||||
- Fixed tests with MySQL 5.7.9+ (#380)
|
|
||||||
- QueryUnescape TLS config names (#397)
|
|
||||||
- Fixed "broken pipe" error by writing to closed socket (#390)
|
|
||||||
|
|
||||||
New Features:
|
|
||||||
- Support for returning table alias on Columns() (#289, #359, #382)
|
|
||||||
- Placeholder interpolation, can be actived with the DSN parameter `interpolateParams=true` (#309, #318)
|
|
||||||
- Support for uint64 parameters with high bit set (#332, #345)
|
|
||||||
- Cleartext authentication plugin support (#327)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Version 1.2 (2014-06-03)
|
|
||||||
|
|
||||||
Changes:
|
|
||||||
|
|
||||||
- We switched back to a "rolling release". `go get` installs the current master branch again
|
|
||||||
- Version v1 of the driver will not be maintained anymore. Go 1.0 is no longer supported by this driver
|
|
||||||
- Exported errors to allow easy checking from application code
|
|
||||||
- Enabled TCP Keepalives on TCP connections
|
|
||||||
- Optimized INFILE handling (better buffer size calculation, lazy init, ...)
|
|
||||||
- The DSN parser also checks for a missing separating slash
|
|
||||||
- Faster binary date / datetime to string formatting
|
|
||||||
- Also exported the MySQLWarning type
|
|
||||||
- mysqlConn.Close returns the first error encountered instead of ignoring all errors
|
|
||||||
- writePacket() automatically writes the packet size to the header
|
|
||||||
- readPacket() uses an iterative approach instead of the recursive approach to merge splitted packets
|
|
||||||
|
|
||||||
New Features:
|
|
||||||
|
|
||||||
- `RegisterDial` allows the usage of a custom dial function to establish the network connection
|
|
||||||
- Setting the connection collation is possible with the `collation` DSN parameter. This parameter should be preferred over the `charset` parameter
|
|
||||||
- Logging of critical errors is configurable with `SetLogger`
|
|
||||||
- Google CloudSQL support
|
|
||||||
|
|
||||||
Bugfixes:
|
|
||||||
|
|
||||||
- Allow more than 32 parameters in prepared statements
|
|
||||||
- Various old_password fixes
|
|
||||||
- Fixed TestConcurrent test to pass Go's race detection
|
|
||||||
- Fixed appendLengthEncodedInteger for large numbers
|
|
||||||
- Renamed readLengthEnodedString to readLengthEncodedString and skipLengthEnodedString to skipLengthEncodedString (fixed typo)
|
|
||||||
|
|
||||||
|
|
||||||
## Version 1.1 (2013-11-02)
|
|
||||||
|
|
||||||
Changes:
|
|
||||||
|
|
||||||
- Go-MySQL-Driver now requires Go 1.1
|
|
||||||
- Connections now use the collation `utf8_general_ci` by default. Adding `&charset=UTF8` to the DSN should not be necessary anymore
|
|
||||||
- Made closing rows and connections error tolerant. This allows for example deferring rows.Close() without checking for errors
|
|
||||||
- `[]byte(nil)` is now treated as a NULL value. Before, it was treated like an empty string / `[]byte("")`
|
|
||||||
- DSN parameter values must now be url.QueryEscape'ed. This allows text values to contain special characters, such as '&'.
|
|
||||||
- Use the IO buffer also for writing. This results in zero allocations (by the driver) for most queries
|
|
||||||
- Optimized the buffer for reading
|
|
||||||
- stmt.Query now caches column metadata
|
|
||||||
- New Logo
|
|
||||||
- Changed the copyright header to include all contributors
|
|
||||||
- Improved the LOAD INFILE documentation
|
|
||||||
- The driver struct is now exported to make the driver directly accessible
|
|
||||||
- Refactored the driver tests
|
|
||||||
- Added more benchmarks and moved all to a separate file
|
|
||||||
- Other small refactoring
|
|
||||||
|
|
||||||
New Features:
|
|
||||||
|
|
||||||
- Added *old_passwords* support: Required in some cases, but must be enabled by adding `allowOldPasswords=true` to the DSN since it is insecure
|
|
||||||
- Added a `clientFoundRows` parameter: Return the number of matching rows instead of the number of rows changed on UPDATEs
|
|
||||||
- Added TLS/SSL support: Use a TLS/SSL encrypted connection to the server. Custom TLS configs can be registered and used
|
|
||||||
|
|
||||||
Bugfixes:
|
|
||||||
|
|
||||||
- Fixed MySQL 4.1 support: MySQL 4.1 sends packets with lengths which differ from the specification
|
|
||||||
- Convert to DB timezone when inserting `time.Time`
|
|
||||||
- Splitted packets (more than 16MB) are now merged correctly
|
|
||||||
- Fixed false positive `io.EOF` errors when the data was fully read
|
|
||||||
- Avoid panics on reuse of closed connections
|
|
||||||
- Fixed empty string producing false nil values
|
|
||||||
- Fixed sign byte for positive TIME fields
|
|
||||||
|
|
||||||
|
|
||||||
## Version 1.0 (2013-05-14)
|
|
||||||
|
|
||||||
Initial Release
|
|
23
vendor/github.com/go-sql-driver/mysql/CONTRIBUTING.md
generated
vendored
23
vendor/github.com/go-sql-driver/mysql/CONTRIBUTING.md
generated
vendored
|
@ -1,23 +0,0 @@
|
||||||
# Contributing Guidelines
|
|
||||||
|
|
||||||
## Reporting Issues
|
|
||||||
|
|
||||||
Before creating a new Issue, please check first if a similar Issue [already exists](https://github.com/go-sql-driver/mysql/issues?state=open) or was [recently closed](https://github.com/go-sql-driver/mysql/issues?direction=desc&page=1&sort=updated&state=closed).
|
|
||||||
|
|
||||||
## Contributing Code
|
|
||||||
|
|
||||||
By contributing to this project, you share your code under the Mozilla Public License 2, as specified in the LICENSE file.
|
|
||||||
Don't forget to add yourself to the AUTHORS file.
|
|
||||||
|
|
||||||
### Code Review
|
|
||||||
|
|
||||||
Everyone is invited to review and comment on pull requests.
|
|
||||||
If it looks fine to you, comment with "LGTM" (Looks good to me).
|
|
||||||
|
|
||||||
If changes are required, notice the reviewers with "PTAL" (Please take another look) after committing the fixes.
|
|
||||||
|
|
||||||
Before merging the Pull Request, at least one [team member](https://github.com/go-sql-driver?tab=members) must have commented with "LGTM".
|
|
||||||
|
|
||||||
## Development Ideas
|
|
||||||
|
|
||||||
If you are looking for ideas for code contributions, please check our [Development Ideas](https://github.com/go-sql-driver/mysql/wiki/Development-Ideas) Wiki page.
|
|
21
vendor/github.com/go-sql-driver/mysql/ISSUE_TEMPLATE.md
generated
vendored
21
vendor/github.com/go-sql-driver/mysql/ISSUE_TEMPLATE.md
generated
vendored
|
@ -1,21 +0,0 @@
|
||||||
### Issue description
|
|
||||||
Tell us what should happen and what happens instead
|
|
||||||
|
|
||||||
### Example code
|
|
||||||
```go
|
|
||||||
If possible, please enter some example code here to reproduce the issue.
|
|
||||||
```
|
|
||||||
|
|
||||||
### Error log
|
|
||||||
```
|
|
||||||
If you have an error log, please paste it here.
|
|
||||||
```
|
|
||||||
|
|
||||||
### Configuration
|
|
||||||
*Driver version (or git SHA):*
|
|
||||||
|
|
||||||
*Go version:* run `go version` in your console
|
|
||||||
|
|
||||||
*Server version:* E.g. MySQL 5.6, MariaDB 10.0.20
|
|
||||||
|
|
||||||
*Server OS:* E.g. Debian 8.1 (Jessie), Windows 10
|
|
9
vendor/github.com/go-sql-driver/mysql/PULL_REQUEST_TEMPLATE.md
generated
vendored
9
vendor/github.com/go-sql-driver/mysql/PULL_REQUEST_TEMPLATE.md
generated
vendored
|
@ -1,9 +0,0 @@
|
||||||
### Description
|
|
||||||
Please explain the changes you made here.
|
|
||||||
|
|
||||||
### Checklist
|
|
||||||
- [ ] Code compiles correctly
|
|
||||||
- [ ] Created tests which fail without the change (if possible)
|
|
||||||
- [ ] All tests passing
|
|
||||||
- [ ] Extended the README / documentation, if necessary
|
|
||||||
- [ ] Added myself / the copyright holder to the AUTHORS file
|
|
420
vendor/github.com/go-sql-driver/mysql/README.md
generated
vendored
420
vendor/github.com/go-sql-driver/mysql/README.md
generated
vendored
|
@ -1,420 +0,0 @@
|
||||||
# Go-MySQL-Driver
|
|
||||||
|
|
||||||
A MySQL-Driver for Go's [database/sql](http://golang.org/pkg/database/sql) package
|
|
||||||
|
|
||||||
![Go-MySQL-Driver logo](https://raw.github.com/wiki/go-sql-driver/mysql/gomysql_m.png "Golang Gopher holding the MySQL Dolphin")
|
|
||||||
|
|
||||||
**Latest stable Release:** [Version 1.2 (June 03, 2014)](https://github.com/go-sql-driver/mysql/releases)
|
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/go-sql-driver/mysql.png?branch=master)](https://travis-ci.org/go-sql-driver/mysql)
|
|
||||||
|
|
||||||
---------------------------------------
|
|
||||||
* [Features](#features)
|
|
||||||
* [Requirements](#requirements)
|
|
||||||
* [Installation](#installation)
|
|
||||||
* [Usage](#usage)
|
|
||||||
* [DSN (Data Source Name)](#dsn-data-source-name)
|
|
||||||
* [Password](#password)
|
|
||||||
* [Protocol](#protocol)
|
|
||||||
* [Address](#address)
|
|
||||||
* [Parameters](#parameters)
|
|
||||||
* [Examples](#examples)
|
|
||||||
* [LOAD DATA LOCAL INFILE support](#load-data-local-infile-support)
|
|
||||||
* [time.Time support](#timetime-support)
|
|
||||||
* [Unicode support](#unicode-support)
|
|
||||||
* [Testing / Development](#testing--development)
|
|
||||||
* [License](#license)
|
|
||||||
|
|
||||||
---------------------------------------
|
|
||||||
|
|
||||||
## Features
|
|
||||||
* Lightweight and [fast](https://github.com/go-sql-driver/sql-benchmark "golang MySQL-Driver performance")
|
|
||||||
* Native Go implementation. No C-bindings, just pure Go
|
|
||||||
* Connections over TCP/IPv4, TCP/IPv6, Unix domain sockets or [custom protocols](http://godoc.org/github.com/go-sql-driver/mysql#DialFunc)
|
|
||||||
* Automatic handling of broken connections
|
|
||||||
* Automatic Connection Pooling *(by database/sql package)*
|
|
||||||
* Supports queries larger than 16MB
|
|
||||||
* Full [`sql.RawBytes`](http://golang.org/pkg/database/sql/#RawBytes) support.
|
|
||||||
* Intelligent `LONG DATA` handling in prepared statements
|
|
||||||
* Secure `LOAD DATA LOCAL INFILE` support with file Whitelisting and `io.Reader` support
|
|
||||||
* Optional `time.Time` parsing
|
|
||||||
* Optional placeholder interpolation
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
* Go 1.2 or higher
|
|
||||||
* MySQL (4.1+), MariaDB, Percona Server, Google CloudSQL or Sphinx (2.2.3+)
|
|
||||||
|
|
||||||
---------------------------------------
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
Simple install the package to your [$GOPATH](http://code.google.com/p/go-wiki/wiki/GOPATH "GOPATH") with the [go tool](http://golang.org/cmd/go/ "go command") from shell:
|
|
||||||
```bash
|
|
||||||
$ go get github.com/go-sql-driver/mysql
|
|
||||||
```
|
|
||||||
Make sure [Git is installed](http://git-scm.com/downloads) on your machine and in your system's `PATH`.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
_Go MySQL Driver_ is an implementation of Go's `database/sql/driver` interface. You only need to import the driver and can use the full [`database/sql`](http://golang.org/pkg/database/sql) API then.
|
|
||||||
|
|
||||||
Use `mysql` as `driverName` and a valid [DSN](#dsn-data-source-name) as `dataSourceName`:
|
|
||||||
```go
|
|
||||||
import "database/sql"
|
|
||||||
import _ "github.com/go-sql-driver/mysql"
|
|
||||||
|
|
||||||
db, err := sql.Open("mysql", "user:password@/dbname")
|
|
||||||
```
|
|
||||||
|
|
||||||
[Examples are available in our Wiki](https://github.com/go-sql-driver/mysql/wiki/Examples "Go-MySQL-Driver Examples").
|
|
||||||
|
|
||||||
|
|
||||||
### DSN (Data Source Name)
|
|
||||||
|
|
||||||
The Data Source Name has a common format, like e.g. [PEAR DB](http://pear.php.net/manual/en/package.database.db.intro-dsn.php) uses it, but without type-prefix (optional parts marked by squared brackets):
|
|
||||||
```
|
|
||||||
[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]
|
|
||||||
```
|
|
||||||
|
|
||||||
A DSN in its fullest form:
|
|
||||||
```
|
|
||||||
username:password@protocol(address)/dbname?param=value
|
|
||||||
```
|
|
||||||
|
|
||||||
Except for the databasename, all values are optional. So the minimal DSN is:
|
|
||||||
```
|
|
||||||
/dbname
|
|
||||||
```
|
|
||||||
|
|
||||||
If you do not want to preselect a database, leave `dbname` empty:
|
|
||||||
```
|
|
||||||
/
|
|
||||||
```
|
|
||||||
This has the same effect as an empty DSN string:
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
Alternatively, [Config.FormatDSN](https://godoc.org/github.com/go-sql-driver/mysql#Config.FormatDSN) can be used to create a DSN string by filling a struct.
|
|
||||||
|
|
||||||
#### Password
|
|
||||||
Passwords can consist of any character. Escaping is **not** necessary.
|
|
||||||
|
|
||||||
#### Protocol
|
|
||||||
See [net.Dial](http://golang.org/pkg/net/#Dial) for more information which networks are available.
|
|
||||||
In general you should use an Unix domain socket if available and TCP otherwise for best performance.
|
|
||||||
|
|
||||||
#### Address
|
|
||||||
For TCP and UDP networks, addresses have the form `host:port`.
|
|
||||||
If `host` is a literal IPv6 address, it must be enclosed in square brackets.
|
|
||||||
The functions [net.JoinHostPort](http://golang.org/pkg/net/#JoinHostPort) and [net.SplitHostPort](http://golang.org/pkg/net/#SplitHostPort) manipulate addresses in this form.
|
|
||||||
|
|
||||||
For Unix domain sockets the address is the absolute path to the MySQL-Server-socket, e.g. `/var/run/mysqld/mysqld.sock` or `/tmp/mysql.sock`.
|
|
||||||
|
|
||||||
#### Parameters
|
|
||||||
*Parameters are case-sensitive!*
|
|
||||||
|
|
||||||
Notice that any of `true`, `TRUE`, `True` or `1` is accepted to stand for a true boolean value. Not surprisingly, false can be specified as any of: `false`, `FALSE`, `False` or `0`.
|
|
||||||
|
|
||||||
##### `allowAllFiles`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: bool
|
|
||||||
Valid Values: true, false
|
|
||||||
Default: false
|
|
||||||
```
|
|
||||||
|
|
||||||
`allowAllFiles=true` disables the file Whitelist for `LOAD DATA LOCAL INFILE` and allows *all* files.
|
|
||||||
[*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html)
|
|
||||||
|
|
||||||
##### `allowCleartextPasswords`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: bool
|
|
||||||
Valid Values: true, false
|
|
||||||
Default: false
|
|
||||||
```
|
|
||||||
|
|
||||||
`allowCleartextPasswords=true` allows using the [cleartext client side plugin](http://dev.mysql.com/doc/en/cleartext-authentication-plugin.html) if required by an account, such as one defined with the [PAM authentication plugin](http://dev.mysql.com/doc/en/pam-authentication-plugin.html). Sending passwords in clear text may be a security problem in some configurations. To avoid problems if there is any possibility that the password would be intercepted, clients should connect to MySQL Server using a method that protects the password. Possibilities include [TLS / SSL](#tls), IPsec, or a private network.
|
|
||||||
|
|
||||||
##### `allowOldPasswords`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: bool
|
|
||||||
Valid Values: true, false
|
|
||||||
Default: false
|
|
||||||
```
|
|
||||||
`allowOldPasswords=true` allows the usage of the insecure old password method. This should be avoided, but is necessary in some cases. See also [the old_passwords wiki page](https://github.com/go-sql-driver/mysql/wiki/old_passwords).
|
|
||||||
|
|
||||||
##### `charset`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: string
|
|
||||||
Valid Values: <name>
|
|
||||||
Default: none
|
|
||||||
```
|
|
||||||
|
|
||||||
Sets the charset used for client-server interaction (`"SET NAMES <value>"`). If multiple charsets are set (separated by a comma), the following charset is used if setting the charset failes. This enables for example support for `utf8mb4` ([introduced in MySQL 5.5.3](http://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html)) with fallback to `utf8` for older servers (`charset=utf8mb4,utf8`).
|
|
||||||
|
|
||||||
Usage of the `charset` parameter is discouraged because it issues additional queries to the server.
|
|
||||||
Unless you need the fallback behavior, please use `collation` instead.
|
|
||||||
|
|
||||||
##### `collation`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: string
|
|
||||||
Valid Values: <name>
|
|
||||||
Default: utf8_general_ci
|
|
||||||
```
|
|
||||||
|
|
||||||
Sets the collation used for client-server interaction on connection. In contrast to `charset`, `collation` does not issue additional queries. If the specified collation is unavailable on the target server, the connection will fail.
|
|
||||||
|
|
||||||
A list of valid charsets for a server is retrievable with `SHOW COLLATION`.
|
|
||||||
|
|
||||||
##### `clientFoundRows`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: bool
|
|
||||||
Valid Values: true, false
|
|
||||||
Default: false
|
|
||||||
```
|
|
||||||
|
|
||||||
`clientFoundRows=true` causes an UPDATE to return the number of matching rows instead of the number of rows changed.
|
|
||||||
|
|
||||||
##### `columnsWithAlias`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: bool
|
|
||||||
Valid Values: true, false
|
|
||||||
Default: false
|
|
||||||
```
|
|
||||||
|
|
||||||
When `columnsWithAlias` is true, calls to `sql.Rows.Columns()` will return the table alias and the column name separated by a dot. For example:
|
|
||||||
|
|
||||||
```
|
|
||||||
SELECT u.id FROM users as u
|
|
||||||
```
|
|
||||||
|
|
||||||
will return `u.id` instead of just `id` if `columnsWithAlias=true`.
|
|
||||||
|
|
||||||
##### `interpolateParams`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: bool
|
|
||||||
Valid Values: true, false
|
|
||||||
Default: false
|
|
||||||
```
|
|
||||||
|
|
||||||
If `interpolateParams` is true, placeholders (`?`) in calls to `db.Query()` and `db.Exec()` are interpolated into a single query string with given parameters. This reduces the number of roundtrips, since the driver has to prepare a statement, execute it with given parameters and close the statement again with `interpolateParams=false`.
|
|
||||||
|
|
||||||
*This can not be used together with the multibyte encodings BIG5, CP932, GB2312, GBK or SJIS. These are blacklisted as they may [introduce a SQL injection vulnerability](http://stackoverflow.com/a/12118602/3430118)!*
|
|
||||||
|
|
||||||
##### `loc`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: string
|
|
||||||
Valid Values: <escaped name>
|
|
||||||
Default: UTC
|
|
||||||
```
|
|
||||||
|
|
||||||
Sets the location for time.Time values (when using `parseTime=true`). *"Local"* sets the system's location. See [time.LoadLocation](http://golang.org/pkg/time/#LoadLocation) for details.
|
|
||||||
|
|
||||||
Note that this sets the location for time.Time values but does not change MySQL's [time_zone setting](https://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html). For that see the [time_zone system variable](#system-variables), which can also be set as a DSN parameter.
|
|
||||||
|
|
||||||
Please keep in mind, that param values must be [url.QueryEscape](http://golang.org/pkg/net/url/#QueryEscape)'ed. Alternatively you can manually replace the `/` with `%2F`. For example `US/Pacific` would be `loc=US%2FPacific`.
|
|
||||||
|
|
||||||
##### `multiStatements`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: bool
|
|
||||||
Valid Values: true, false
|
|
||||||
Default: false
|
|
||||||
```
|
|
||||||
|
|
||||||
Allow multiple statements in one query. While this allows batch queries, it also greatly increases the risk of SQL injections. Only the result of the first query is returned, all other results are silently discarded.
|
|
||||||
|
|
||||||
When `multiStatements` is used, `?` parameters must only be used in the first statement.
|
|
||||||
|
|
||||||
|
|
||||||
##### `parseTime`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: bool
|
|
||||||
Valid Values: true, false
|
|
||||||
Default: false
|
|
||||||
```
|
|
||||||
|
|
||||||
`parseTime=true` changes the output type of `DATE` and `DATETIME` values to `time.Time` instead of `[]byte` / `string`
|
|
||||||
|
|
||||||
|
|
||||||
##### `readTimeout`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: decimal number
|
|
||||||
Default: 0
|
|
||||||
```
|
|
||||||
|
|
||||||
I/O read timeout. The value must be a decimal number with an unit suffix ( *"ms"*, *"s"*, *"m"*, *"h"* ), such as *"30s"*, *"0.5m"* or *"1m30s"*.
|
|
||||||
|
|
||||||
|
|
||||||
##### `strict`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: bool
|
|
||||||
Valid Values: true, false
|
|
||||||
Default: false
|
|
||||||
```
|
|
||||||
|
|
||||||
`strict=true` enables the strict mode in which MySQL warnings are treated as errors.
|
|
||||||
|
|
||||||
By default MySQL also treats notes as warnings. Use [`sql_notes=false`](http://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_sql_notes) to ignore notes. See the [examples](#examples) for an DSN example.
|
|
||||||
|
|
||||||
|
|
||||||
##### `timeout`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: decimal number
|
|
||||||
Default: OS default
|
|
||||||
```
|
|
||||||
|
|
||||||
*Driver* side connection timeout. The value must be a decimal number with an unit suffix ( *"ms"*, *"s"*, *"m"*, *"h"* ), such as *"30s"*, *"0.5m"* or *"1m30s"*. To set a server side timeout, use the parameter [`wait_timeout`](http://dev.mysql.com/doc/refman/5.6/en/server-system-variables.html#sysvar_wait_timeout).
|
|
||||||
|
|
||||||
|
|
||||||
##### `tls`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: bool / string
|
|
||||||
Valid Values: true, false, skip-verify, <name>
|
|
||||||
Default: false
|
|
||||||
```
|
|
||||||
|
|
||||||
`tls=true` enables TLS / SSL encrypted connection to the server. Use `skip-verify` if you want to use a self-signed or invalid certificate (server side). Use a custom value registered with [`mysql.RegisterTLSConfig`](http://godoc.org/github.com/go-sql-driver/mysql#RegisterTLSConfig).
|
|
||||||
|
|
||||||
|
|
||||||
##### `writeTimeout`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: decimal number
|
|
||||||
Default: 0
|
|
||||||
```
|
|
||||||
|
|
||||||
I/O write timeout. The value must be a decimal number with an unit suffix ( *"ms"*, *"s"*, *"m"*, *"h"* ), such as *"30s"*, *"0.5m"* or *"1m30s"*.
|
|
||||||
|
|
||||||
|
|
||||||
##### System Variables
|
|
||||||
|
|
||||||
All other parameters are interpreted as system variables:
|
|
||||||
* `autocommit`: `"SET autocommit=<value>"`
|
|
||||||
* [`time_zone`](https://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html): `"SET time_zone=<value>"`
|
|
||||||
* [`tx_isolation`](https://dev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_tx_isolation): `"SET tx_isolation=<value>"`
|
|
||||||
* `param`: `"SET <param>=<value>"`
|
|
||||||
|
|
||||||
*The values must be [url.QueryEscape](http://golang.org/pkg/net/url/#QueryEscape)'ed!*
|
|
||||||
|
|
||||||
#### Examples
|
|
||||||
```
|
|
||||||
user@unix(/path/to/socket)/dbname
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
root:pw@unix(/tmp/mysql.sock)/myDatabase?loc=Local
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
user:password@tcp(localhost:5555)/dbname?tls=skip-verify&autocommit=true
|
|
||||||
```
|
|
||||||
|
|
||||||
Use the [strict mode](#strict) but ignore notes:
|
|
||||||
```
|
|
||||||
user:password@/dbname?strict=true&sql_notes=false
|
|
||||||
```
|
|
||||||
|
|
||||||
TCP via IPv6:
|
|
||||||
```
|
|
||||||
user:password@tcp([de:ad:be:ef::ca:fe]:80)/dbname?timeout=90s&collation=utf8mb4_unicode_ci
|
|
||||||
```
|
|
||||||
|
|
||||||
TCP on a remote host, e.g. Amazon RDS:
|
|
||||||
```
|
|
||||||
id:password@tcp(your-amazonaws-uri.com:3306)/dbname
|
|
||||||
```
|
|
||||||
|
|
||||||
Google Cloud SQL on App Engine:
|
|
||||||
```
|
|
||||||
user@cloudsql(project-id:instance-name)/dbname
|
|
||||||
```
|
|
||||||
|
|
||||||
TCP using default port (3306) on localhost:
|
|
||||||
```
|
|
||||||
user:password@tcp/dbname?charset=utf8mb4,utf8&sys_var=esc%40ped
|
|
||||||
```
|
|
||||||
|
|
||||||
Use the default protocol (tcp) and host (localhost:3306):
|
|
||||||
```
|
|
||||||
user:password@/dbname
|
|
||||||
```
|
|
||||||
|
|
||||||
No Database preselected:
|
|
||||||
```
|
|
||||||
user:password@/
|
|
||||||
```
|
|
||||||
|
|
||||||
### `LOAD DATA LOCAL INFILE` support
|
|
||||||
For this feature you need direct access to the package. Therefore you must change the import path (no `_`):
|
|
||||||
```go
|
|
||||||
import "github.com/go-sql-driver/mysql"
|
|
||||||
```
|
|
||||||
|
|
||||||
Files must be whitelisted by registering them with `mysql.RegisterLocalFile(filepath)` (recommended) or the Whitelist check must be deactivated by using the DSN parameter `allowAllFiles=true` ([*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html)).
|
|
||||||
|
|
||||||
To use a `io.Reader` a handler function must be registered with `mysql.RegisterReaderHandler(name, handler)` which returns a `io.Reader` or `io.ReadCloser`. The Reader is available with the filepath `Reader::<name>` then. Choose different names for different handlers and `DeregisterReaderHandler` when you don't need it anymore.
|
|
||||||
|
|
||||||
See the [godoc of Go-MySQL-Driver](http://godoc.org/github.com/go-sql-driver/mysql "golang mysql driver documentation") for details.
|
|
||||||
|
|
||||||
|
|
||||||
### `time.Time` support
|
|
||||||
The default internal output type of MySQL `DATE` and `DATETIME` values is `[]byte` which allows you to scan the value into a `[]byte`, `string` or `sql.RawBytes` variable in your programm.
|
|
||||||
|
|
||||||
However, many want to scan MySQL `DATE` and `DATETIME` values into `time.Time` variables, which is the logical opposite in Go to `DATE` and `DATETIME` in MySQL. You can do that by changing the internal output type from `[]byte` to `time.Time` with the DSN parameter `parseTime=true`. You can set the default [`time.Time` location](http://golang.org/pkg/time/#Location) with the `loc` DSN parameter.
|
|
||||||
|
|
||||||
**Caution:** As of Go 1.1, this makes `time.Time` the only variable type you can scan `DATE` and `DATETIME` values into. This breaks for example [`sql.RawBytes` support](https://github.com/go-sql-driver/mysql/wiki/Examples#rawbytes).
|
|
||||||
|
|
||||||
Alternatively you can use the [`NullTime`](http://godoc.org/github.com/go-sql-driver/mysql#NullTime) type as the scan destination, which works with both `time.Time` and `string` / `[]byte`.
|
|
||||||
|
|
||||||
|
|
||||||
### Unicode support
|
|
||||||
Since version 1.1 Go-MySQL-Driver automatically uses the collation `utf8_general_ci` by default.
|
|
||||||
|
|
||||||
Other collations / charsets can be set using the [`collation`](#collation) DSN parameter.
|
|
||||||
|
|
||||||
Version 1.0 of the driver recommended adding `&charset=utf8` (alias for `SET NAMES utf8`) to the DSN to enable proper UTF-8 support. This is not necessary anymore. The [`collation`](#collation) parameter should be preferred to set another collation / charset than the default.
|
|
||||||
|
|
||||||
See http://dev.mysql.com/doc/refman/5.7/en/charset-unicode.html for more details on MySQL's Unicode support.
|
|
||||||
|
|
||||||
|
|
||||||
## Testing / Development
|
|
||||||
To run the driver tests you may need to adjust the configuration. See the [Testing Wiki-Page](https://github.com/go-sql-driver/mysql/wiki/Testing "Testing") for details.
|
|
||||||
|
|
||||||
Go-MySQL-Driver is not feature-complete yet. Your help is very appreciated.
|
|
||||||
If you want to contribute, you can work on an [open issue](https://github.com/go-sql-driver/mysql/issues?state=open) or review a [pull request](https://github.com/go-sql-driver/mysql/pulls).
|
|
||||||
|
|
||||||
See the [Contribution Guidelines](https://github.com/go-sql-driver/mysql/blob/master/CONTRIBUTING.md) for details.
|
|
||||||
|
|
||||||
---------------------------------------
|
|
||||||
|
|
||||||
## License
|
|
||||||
Go-MySQL-Driver is licensed under the [Mozilla Public License Version 2.0](https://raw.github.com/go-sql-driver/mysql/master/LICENSE)
|
|
||||||
|
|
||||||
Mozilla summarizes the license scope as follows:
|
|
||||||
> MPL: The copyleft applies to any files containing MPLed code.
|
|
||||||
|
|
||||||
|
|
||||||
That means:
|
|
||||||
* You can **use** the **unchanged** source code both in private and commercially
|
|
||||||
* When distributing, you **must publish** the source code of any **changed files** licensed under the MPL 2.0 under a) the MPL 2.0 itself or b) a compatible license (e.g. GPL 3.0 or Apache License 2.0)
|
|
||||||
* You **needn't publish** the source code of your library as long as the files licensed under the MPL 2.0 are **unchanged**
|
|
||||||
|
|
||||||
Please read the [MPL 2.0 FAQ](http://www.mozilla.org/MPL/2.0/FAQ.html) if you have further questions regarding the license.
|
|
||||||
|
|
||||||
You can read the full terms here: [LICENSE](https://raw.github.com/go-sql-driver/mysql/master/LICENSE)
|
|
||||||
|
|
||||||
![Go Gopher and MySQL Dolphin](https://raw.github.com/wiki/go-sql-driver/mysql/go-mysql-driver_m.jpg "Golang Gopher transporting the MySQL Dolphin in a wheelbarrow")
|
|
||||||
|
|
246
vendor/github.com/go-sql-driver/mysql/benchmark_test.go
generated
vendored
246
vendor/github.com/go-sql-driver/mysql/benchmark_test.go
generated
vendored
|
@ -1,246 +0,0 @@
|
||||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
|
||||||
//
|
|
||||||
// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
|
|
||||||
//
|
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
package mysql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"database/sql"
|
|
||||||
"database/sql/driver"
|
|
||||||
"math"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TB testing.B
|
|
||||||
|
|
||||||
func (tb *TB) check(err error) {
|
|
||||||
if err != nil {
|
|
||||||
tb.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tb *TB) checkDB(db *sql.DB, err error) *sql.DB {
|
|
||||||
tb.check(err)
|
|
||||||
return db
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tb *TB) checkRows(rows *sql.Rows, err error) *sql.Rows {
|
|
||||||
tb.check(err)
|
|
||||||
return rows
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tb *TB) checkStmt(stmt *sql.Stmt, err error) *sql.Stmt {
|
|
||||||
tb.check(err)
|
|
||||||
return stmt
|
|
||||||
}
|
|
||||||
|
|
||||||
func initDB(b *testing.B, queries ...string) *sql.DB {
|
|
||||||
tb := (*TB)(b)
|
|
||||||
db := tb.checkDB(sql.Open("mysql", dsn))
|
|
||||||
for _, query := range queries {
|
|
||||||
if _, err := db.Exec(query); err != nil {
|
|
||||||
if w, ok := err.(MySQLWarnings); ok {
|
|
||||||
b.Logf("warning on %q: %v", query, w)
|
|
||||||
} else {
|
|
||||||
b.Fatalf("error on %q: %v", query, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return db
|
|
||||||
}
|
|
||||||
|
|
||||||
const concurrencyLevel = 10
|
|
||||||
|
|
||||||
func BenchmarkQuery(b *testing.B) {
|
|
||||||
tb := (*TB)(b)
|
|
||||||
b.StopTimer()
|
|
||||||
b.ReportAllocs()
|
|
||||||
db := initDB(b,
|
|
||||||
"DROP TABLE IF EXISTS foo",
|
|
||||||
"CREATE TABLE foo (id INT PRIMARY KEY, val CHAR(50))",
|
|
||||||
`INSERT INTO foo VALUES (1, "one")`,
|
|
||||||
`INSERT INTO foo VALUES (2, "two")`,
|
|
||||||
)
|
|
||||||
db.SetMaxIdleConns(concurrencyLevel)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
stmt := tb.checkStmt(db.Prepare("SELECT val FROM foo WHERE id=?"))
|
|
||||||
defer stmt.Close()
|
|
||||||
|
|
||||||
remain := int64(b.N)
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
wg.Add(concurrencyLevel)
|
|
||||||
defer wg.Wait()
|
|
||||||
b.StartTimer()
|
|
||||||
|
|
||||||
for i := 0; i < concurrencyLevel; i++ {
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
if atomic.AddInt64(&remain, -1) < 0 {
|
|
||||||
wg.Done()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var got string
|
|
||||||
tb.check(stmt.QueryRow(1).Scan(&got))
|
|
||||||
if got != "one" {
|
|
||||||
b.Errorf("query = %q; want one", got)
|
|
||||||
wg.Done()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkExec(b *testing.B) {
|
|
||||||
tb := (*TB)(b)
|
|
||||||
b.StopTimer()
|
|
||||||
b.ReportAllocs()
|
|
||||||
db := tb.checkDB(sql.Open("mysql", dsn))
|
|
||||||
db.SetMaxIdleConns(concurrencyLevel)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
stmt := tb.checkStmt(db.Prepare("DO 1"))
|
|
||||||
defer stmt.Close()
|
|
||||||
|
|
||||||
remain := int64(b.N)
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
wg.Add(concurrencyLevel)
|
|
||||||
defer wg.Wait()
|
|
||||||
b.StartTimer()
|
|
||||||
|
|
||||||
for i := 0; i < concurrencyLevel; i++ {
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
if atomic.AddInt64(&remain, -1) < 0 {
|
|
||||||
wg.Done()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := stmt.Exec(); err != nil {
|
|
||||||
b.Fatal(err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// data, but no db writes
|
|
||||||
var roundtripSample []byte
|
|
||||||
|
|
||||||
func initRoundtripBenchmarks() ([]byte, int, int) {
|
|
||||||
if roundtripSample == nil {
|
|
||||||
roundtripSample = []byte(strings.Repeat("0123456789abcdef", 1024*1024))
|
|
||||||
}
|
|
||||||
return roundtripSample, 16, len(roundtripSample)
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkRoundtripTxt(b *testing.B) {
|
|
||||||
b.StopTimer()
|
|
||||||
sample, min, max := initRoundtripBenchmarks()
|
|
||||||
sampleString := string(sample)
|
|
||||||
b.ReportAllocs()
|
|
||||||
tb := (*TB)(b)
|
|
||||||
db := tb.checkDB(sql.Open("mysql", dsn))
|
|
||||||
defer db.Close()
|
|
||||||
b.StartTimer()
|
|
||||||
var result string
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
length := min + i
|
|
||||||
if length > max {
|
|
||||||
length = max
|
|
||||||
}
|
|
||||||
test := sampleString[0:length]
|
|
||||||
rows := tb.checkRows(db.Query(`SELECT "` + test + `"`))
|
|
||||||
if !rows.Next() {
|
|
||||||
rows.Close()
|
|
||||||
b.Fatalf("crashed")
|
|
||||||
}
|
|
||||||
err := rows.Scan(&result)
|
|
||||||
if err != nil {
|
|
||||||
rows.Close()
|
|
||||||
b.Fatalf("crashed")
|
|
||||||
}
|
|
||||||
if result != test {
|
|
||||||
rows.Close()
|
|
||||||
b.Errorf("mismatch")
|
|
||||||
}
|
|
||||||
rows.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkRoundtripBin(b *testing.B) {
|
|
||||||
b.StopTimer()
|
|
||||||
sample, min, max := initRoundtripBenchmarks()
|
|
||||||
b.ReportAllocs()
|
|
||||||
tb := (*TB)(b)
|
|
||||||
db := tb.checkDB(sql.Open("mysql", dsn))
|
|
||||||
defer db.Close()
|
|
||||||
stmt := tb.checkStmt(db.Prepare("SELECT ?"))
|
|
||||||
defer stmt.Close()
|
|
||||||
b.StartTimer()
|
|
||||||
var result sql.RawBytes
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
length := min + i
|
|
||||||
if length > max {
|
|
||||||
length = max
|
|
||||||
}
|
|
||||||
test := sample[0:length]
|
|
||||||
rows := tb.checkRows(stmt.Query(test))
|
|
||||||
if !rows.Next() {
|
|
||||||
rows.Close()
|
|
||||||
b.Fatalf("crashed")
|
|
||||||
}
|
|
||||||
err := rows.Scan(&result)
|
|
||||||
if err != nil {
|
|
||||||
rows.Close()
|
|
||||||
b.Fatalf("crashed")
|
|
||||||
}
|
|
||||||
if !bytes.Equal(result, test) {
|
|
||||||
rows.Close()
|
|
||||||
b.Errorf("mismatch")
|
|
||||||
}
|
|
||||||
rows.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkInterpolation(b *testing.B) {
|
|
||||||
mc := &mysqlConn{
|
|
||||||
cfg: &Config{
|
|
||||||
InterpolateParams: true,
|
|
||||||
Loc: time.UTC,
|
|
||||||
},
|
|
||||||
maxPacketAllowed: maxPacketSize,
|
|
||||||
maxWriteSize: maxPacketSize - 1,
|
|
||||||
buf: newBuffer(nil),
|
|
||||||
}
|
|
||||||
|
|
||||||
args := []driver.Value{
|
|
||||||
int64(42424242),
|
|
||||||
float64(math.Pi),
|
|
||||||
false,
|
|
||||||
time.Unix(1423411542, 807015000),
|
|
||||||
[]byte("bytes containing special chars ' \" \a \x00"),
|
|
||||||
"string containing special chars ' \" \a \x00",
|
|
||||||
}
|
|
||||||
q := "SELECT ?, ?, ?, ?, ?, ?"
|
|
||||||
|
|
||||||
b.ReportAllocs()
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
_, err := mc.interpolateParams(q, args)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
1857
vendor/github.com/go-sql-driver/mysql/driver_test.go
generated
vendored
1857
vendor/github.com/go-sql-driver/mysql/driver_test.go
generated
vendored
File diff suppressed because it is too large
Load diff
231
vendor/github.com/go-sql-driver/mysql/dsn_test.go
generated
vendored
231
vendor/github.com/go-sql-driver/mysql/dsn_test.go
generated
vendored
|
@ -1,231 +0,0 @@
|
||||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
|
||||||
//
|
|
||||||
// Copyright 2016 The Go-MySQL-Driver Authors. All rights reserved.
|
|
||||||
//
|
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
package mysql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var testDSNs = []struct {
|
|
||||||
in string
|
|
||||||
out *Config
|
|
||||||
}{{
|
|
||||||
"username:password@protocol(address)/dbname?param=value",
|
|
||||||
&Config{User: "username", Passwd: "password", Net: "protocol", Addr: "address", DBName: "dbname", Params: map[string]string{"param": "value"}, Collation: "utf8_general_ci", Loc: time.UTC},
|
|
||||||
}, {
|
|
||||||
"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true",
|
|
||||||
&Config{User: "username", Passwd: "password", Net: "protocol", Addr: "address", DBName: "dbname", Params: map[string]string{"param": "value"}, Collation: "utf8_general_ci", Loc: time.UTC, ColumnsWithAlias: true},
|
|
||||||
}, {
|
|
||||||
"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true&multiStatements=true",
|
|
||||||
&Config{User: "username", Passwd: "password", Net: "protocol", Addr: "address", DBName: "dbname", Params: map[string]string{"param": "value"}, Collation: "utf8_general_ci", Loc: time.UTC, ColumnsWithAlias: true, MultiStatements: true},
|
|
||||||
}, {
|
|
||||||
"user@unix(/path/to/socket)/dbname?charset=utf8",
|
|
||||||
&Config{User: "user", Net: "unix", Addr: "/path/to/socket", DBName: "dbname", Params: map[string]string{"charset": "utf8"}, Collation: "utf8_general_ci", Loc: time.UTC},
|
|
||||||
}, {
|
|
||||||
"user:password@tcp(localhost:5555)/dbname?charset=utf8&tls=true",
|
|
||||||
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "localhost:5555", DBName: "dbname", Params: map[string]string{"charset": "utf8"}, Collation: "utf8_general_ci", Loc: time.UTC, TLSConfig: "true"},
|
|
||||||
}, {
|
|
||||||
"user:password@tcp(localhost:5555)/dbname?charset=utf8mb4,utf8&tls=skip-verify",
|
|
||||||
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "localhost:5555", DBName: "dbname", Params: map[string]string{"charset": "utf8mb4,utf8"}, Collation: "utf8_general_ci", Loc: time.UTC, TLSConfig: "skip-verify"},
|
|
||||||
}, {
|
|
||||||
"user:password@/dbname?loc=UTC&timeout=30s&readTimeout=1s&writeTimeout=1s&allowAllFiles=1&clientFoundRows=true&allowOldPasswords=TRUE&collation=utf8mb4_unicode_ci",
|
|
||||||
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Collation: "utf8mb4_unicode_ci", Loc: time.UTC, Timeout: 30 * time.Second, ReadTimeout: time.Second, WriteTimeout: time.Second, AllowAllFiles: true, AllowOldPasswords: true, ClientFoundRows: true},
|
|
||||||
}, {
|
|
||||||
"user:p@ss(word)@tcp([de:ad:be:ef::ca:fe]:80)/dbname?loc=Local",
|
|
||||||
&Config{User: "user", Passwd: "p@ss(word)", Net: "tcp", Addr: "[de:ad:be:ef::ca:fe]:80", DBName: "dbname", Collation: "utf8_general_ci", Loc: time.Local},
|
|
||||||
}, {
|
|
||||||
"/dbname",
|
|
||||||
&Config{Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Collation: "utf8_general_ci", Loc: time.UTC},
|
|
||||||
}, {
|
|
||||||
"@/",
|
|
||||||
&Config{Net: "tcp", Addr: "127.0.0.1:3306", Collation: "utf8_general_ci", Loc: time.UTC},
|
|
||||||
}, {
|
|
||||||
"/",
|
|
||||||
&Config{Net: "tcp", Addr: "127.0.0.1:3306", Collation: "utf8_general_ci", Loc: time.UTC},
|
|
||||||
}, {
|
|
||||||
"",
|
|
||||||
&Config{Net: "tcp", Addr: "127.0.0.1:3306", Collation: "utf8_general_ci", Loc: time.UTC},
|
|
||||||
}, {
|
|
||||||
"user:p@/ssword@/",
|
|
||||||
&Config{User: "user", Passwd: "p@/ssword", Net: "tcp", Addr: "127.0.0.1:3306", Collation: "utf8_general_ci", Loc: time.UTC},
|
|
||||||
}, {
|
|
||||||
"unix/?arg=%2Fsome%2Fpath.ext",
|
|
||||||
&Config{Net: "unix", Addr: "/tmp/mysql.sock", Params: map[string]string{"arg": "/some/path.ext"}, Collation: "utf8_general_ci", Loc: time.UTC},
|
|
||||||
}}
|
|
||||||
|
|
||||||
func TestDSNParser(t *testing.T) {
|
|
||||||
for i, tst := range testDSNs {
|
|
||||||
cfg, err := ParseDSN(tst.in)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// pointer not static
|
|
||||||
cfg.tls = nil
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(cfg, tst.out) {
|
|
||||||
t.Errorf("%d. ParseDSN(%q) mismatch:\ngot %+v\nwant %+v", i, tst.in, cfg, tst.out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDSNParserInvalid(t *testing.T) {
|
|
||||||
var invalidDSNs = []string{
|
|
||||||
"@net(addr/", // no closing brace
|
|
||||||
"@tcp(/", // no closing brace
|
|
||||||
"tcp(/", // no closing brace
|
|
||||||
"(/", // no closing brace
|
|
||||||
"net(addr)//", // unescaped
|
|
||||||
"User:pass@tcp(1.2.3.4:3306)", // no trailing slash
|
|
||||||
//"/dbname?arg=/some/unescaped/path",
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tst := range invalidDSNs {
|
|
||||||
if _, err := ParseDSN(tst); err == nil {
|
|
||||||
t.Errorf("invalid DSN #%d. (%s) didn't error!", i, tst)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDSNReformat(t *testing.T) {
|
|
||||||
for i, tst := range testDSNs {
|
|
||||||
dsn1 := tst.in
|
|
||||||
cfg1, err := ParseDSN(dsn1)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
cfg1.tls = nil // pointer not static
|
|
||||||
res1 := fmt.Sprintf("%+v", cfg1)
|
|
||||||
|
|
||||||
dsn2 := cfg1.FormatDSN()
|
|
||||||
cfg2, err := ParseDSN(dsn2)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
cfg2.tls = nil // pointer not static
|
|
||||||
res2 := fmt.Sprintf("%+v", cfg2)
|
|
||||||
|
|
||||||
if res1 != res2 {
|
|
||||||
t.Errorf("%d. %q does not match %q", i, res2, res1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDSNWithCustomTLS(t *testing.T) {
|
|
||||||
baseDSN := "User:password@tcp(localhost:5555)/dbname?tls="
|
|
||||||
tlsCfg := tls.Config{}
|
|
||||||
|
|
||||||
RegisterTLSConfig("utils_test", &tlsCfg)
|
|
||||||
|
|
||||||
// Custom TLS is missing
|
|
||||||
tst := baseDSN + "invalid_tls"
|
|
||||||
cfg, err := ParseDSN(tst)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("invalid custom TLS in DSN (%s) but did not error. Got config: %#v", tst, cfg)
|
|
||||||
}
|
|
||||||
|
|
||||||
tst = baseDSN + "utils_test"
|
|
||||||
|
|
||||||
// Custom TLS with a server name
|
|
||||||
name := "foohost"
|
|
||||||
tlsCfg.ServerName = name
|
|
||||||
cfg, err = ParseDSN(tst)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err.Error())
|
|
||||||
} else if cfg.tls.ServerName != name {
|
|
||||||
t.Errorf("did not get the correct TLS ServerName (%s) parsing DSN (%s).", name, tst)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Custom TLS without a server name
|
|
||||||
name = "localhost"
|
|
||||||
tlsCfg.ServerName = ""
|
|
||||||
cfg, err = ParseDSN(tst)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err.Error())
|
|
||||||
} else if cfg.tls.ServerName != name {
|
|
||||||
t.Errorf("did not get the correct ServerName (%s) parsing DSN (%s).", name, tst)
|
|
||||||
}
|
|
||||||
|
|
||||||
DeregisterTLSConfig("utils_test")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDSNWithCustomTLSQueryEscape(t *testing.T) {
|
|
||||||
const configKey = "&%!:"
|
|
||||||
dsn := "User:password@tcp(localhost:5555)/dbname?tls=" + url.QueryEscape(configKey)
|
|
||||||
name := "foohost"
|
|
||||||
tlsCfg := tls.Config{ServerName: name}
|
|
||||||
|
|
||||||
RegisterTLSConfig(configKey, &tlsCfg)
|
|
||||||
|
|
||||||
cfg, err := ParseDSN(dsn)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err.Error())
|
|
||||||
} else if cfg.tls.ServerName != name {
|
|
||||||
t.Errorf("did not get the correct TLS ServerName (%s) parsing DSN (%s).", name, dsn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDSNUnsafeCollation(t *testing.T) {
|
|
||||||
_, err := ParseDSN("/dbname?collation=gbk_chinese_ci&interpolateParams=true")
|
|
||||||
if err != errInvalidDSNUnsafeCollation {
|
|
||||||
t.Errorf("expected %v, got %v", errInvalidDSNUnsafeCollation, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = ParseDSN("/dbname?collation=gbk_chinese_ci&interpolateParams=false")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("expected %v, got %v", nil, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = ParseDSN("/dbname?collation=gbk_chinese_ci")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("expected %v, got %v", nil, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = ParseDSN("/dbname?collation=ascii_bin&interpolateParams=true")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("expected %v, got %v", nil, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = ParseDSN("/dbname?collation=latin1_german1_ci&interpolateParams=true")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("expected %v, got %v", nil, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = ParseDSN("/dbname?collation=utf8_general_ci&interpolateParams=true")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("expected %v, got %v", nil, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = ParseDSN("/dbname?collation=utf8mb4_general_ci&interpolateParams=true")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("expected %v, got %v", nil, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkParseDSN(b *testing.B) {
|
|
||||||
b.ReportAllocs()
|
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
for _, tst := range testDSNs {
|
|
||||||
if _, err := ParseDSN(tst.in); err != nil {
|
|
||||||
b.Error(err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
42
vendor/github.com/go-sql-driver/mysql/errors_test.go
generated
vendored
42
vendor/github.com/go-sql-driver/mysql/errors_test.go
generated
vendored
|
@ -1,42 +0,0 @@
|
||||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
|
||||||
//
|
|
||||||
// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
|
|
||||||
//
|
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
package mysql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"log"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestErrorsSetLogger(t *testing.T) {
|
|
||||||
previous := errLog
|
|
||||||
defer func() {
|
|
||||||
errLog = previous
|
|
||||||
}()
|
|
||||||
|
|
||||||
// set up logger
|
|
||||||
const expected = "prefix: test\n"
|
|
||||||
buffer := bytes.NewBuffer(make([]byte, 0, 64))
|
|
||||||
logger := log.New(buffer, "prefix: ", 0)
|
|
||||||
|
|
||||||
// print
|
|
||||||
SetLogger(logger)
|
|
||||||
errLog.Print("test")
|
|
||||||
|
|
||||||
// check result
|
|
||||||
if actual := buffer.String(); actual != expected {
|
|
||||||
t.Errorf("expected %q, got %q", expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestErrorsStrictIgnoreNotes(t *testing.T) {
|
|
||||||
runTests(t, dsn+"&sql_notes=false", func(dbt *DBTest) {
|
|
||||||
dbt.mustExec("DROP TABLE IF EXISTS does_not_exist")
|
|
||||||
})
|
|
||||||
}
|
|
197
vendor/github.com/go-sql-driver/mysql/utils_test.go
generated
vendored
197
vendor/github.com/go-sql-driver/mysql/utils_test.go
generated
vendored
|
@ -1,197 +0,0 @@
|
||||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
|
||||||
//
|
|
||||||
// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
|
|
||||||
//
|
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
package mysql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestScanNullTime(t *testing.T) {
|
|
||||||
var scanTests = []struct {
|
|
||||||
in interface{}
|
|
||||||
error bool
|
|
||||||
valid bool
|
|
||||||
time time.Time
|
|
||||||
}{
|
|
||||||
{tDate, false, true, tDate},
|
|
||||||
{sDate, false, true, tDate},
|
|
||||||
{[]byte(sDate), false, true, tDate},
|
|
||||||
{tDateTime, false, true, tDateTime},
|
|
||||||
{sDateTime, false, true, tDateTime},
|
|
||||||
{[]byte(sDateTime), false, true, tDateTime},
|
|
||||||
{tDate0, false, true, tDate0},
|
|
||||||
{sDate0, false, true, tDate0},
|
|
||||||
{[]byte(sDate0), false, true, tDate0},
|
|
||||||
{sDateTime0, false, true, tDate0},
|
|
||||||
{[]byte(sDateTime0), false, true, tDate0},
|
|
||||||
{"", true, false, tDate0},
|
|
||||||
{"1234", true, false, tDate0},
|
|
||||||
{0, true, false, tDate0},
|
|
||||||
}
|
|
||||||
|
|
||||||
var nt = NullTime{}
|
|
||||||
var err error
|
|
||||||
|
|
||||||
for _, tst := range scanTests {
|
|
||||||
err = nt.Scan(tst.in)
|
|
||||||
if (err != nil) != tst.error {
|
|
||||||
t.Errorf("%v: expected error status %t, got %t", tst.in, tst.error, (err != nil))
|
|
||||||
}
|
|
||||||
if nt.Valid != tst.valid {
|
|
||||||
t.Errorf("%v: expected valid status %t, got %t", tst.in, tst.valid, nt.Valid)
|
|
||||||
}
|
|
||||||
if nt.Time != tst.time {
|
|
||||||
t.Errorf("%v: expected time %v, got %v", tst.in, tst.time, nt.Time)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLengthEncodedInteger(t *testing.T) {
|
|
||||||
var integerTests = []struct {
|
|
||||||
num uint64
|
|
||||||
encoded []byte
|
|
||||||
}{
|
|
||||||
{0x0000000000000000, []byte{0x00}},
|
|
||||||
{0x0000000000000012, []byte{0x12}},
|
|
||||||
{0x00000000000000fa, []byte{0xfa}},
|
|
||||||
{0x0000000000000100, []byte{0xfc, 0x00, 0x01}},
|
|
||||||
{0x0000000000001234, []byte{0xfc, 0x34, 0x12}},
|
|
||||||
{0x000000000000ffff, []byte{0xfc, 0xff, 0xff}},
|
|
||||||
{0x0000000000010000, []byte{0xfd, 0x00, 0x00, 0x01}},
|
|
||||||
{0x0000000000123456, []byte{0xfd, 0x56, 0x34, 0x12}},
|
|
||||||
{0x0000000000ffffff, []byte{0xfd, 0xff, 0xff, 0xff}},
|
|
||||||
{0x0000000001000000, []byte{0xfe, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00}},
|
|
||||||
{0x123456789abcdef0, []byte{0xfe, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12}},
|
|
||||||
{0xffffffffffffffff, []byte{0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tst := range integerTests {
|
|
||||||
num, isNull, numLen := readLengthEncodedInteger(tst.encoded)
|
|
||||||
if isNull {
|
|
||||||
t.Errorf("%x: expected %d, got NULL", tst.encoded, tst.num)
|
|
||||||
}
|
|
||||||
if num != tst.num {
|
|
||||||
t.Errorf("%x: expected %d, got %d", tst.encoded, tst.num, num)
|
|
||||||
}
|
|
||||||
if numLen != len(tst.encoded) {
|
|
||||||
t.Errorf("%x: expected size %d, got %d", tst.encoded, len(tst.encoded), numLen)
|
|
||||||
}
|
|
||||||
encoded := appendLengthEncodedInteger(nil, num)
|
|
||||||
if !bytes.Equal(encoded, tst.encoded) {
|
|
||||||
t.Errorf("%v: expected %x, got %x", num, tst.encoded, encoded)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOldPass(t *testing.T) {
|
|
||||||
scramble := []byte{9, 8, 7, 6, 5, 4, 3, 2}
|
|
||||||
vectors := []struct {
|
|
||||||
pass string
|
|
||||||
out string
|
|
||||||
}{
|
|
||||||
{" pass", "47575c5a435b4251"},
|
|
||||||
{"pass ", "47575c5a435b4251"},
|
|
||||||
{"123\t456", "575c47505b5b5559"},
|
|
||||||
{"C0mpl!ca ted#PASS123", "5d5d554849584a45"},
|
|
||||||
}
|
|
||||||
for _, tuple := range vectors {
|
|
||||||
ours := scrambleOldPassword(scramble, []byte(tuple.pass))
|
|
||||||
if tuple.out != fmt.Sprintf("%x", ours) {
|
|
||||||
t.Errorf("Failed old password %q", tuple.pass)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFormatBinaryDateTime(t *testing.T) {
|
|
||||||
rawDate := [11]byte{}
|
|
||||||
binary.LittleEndian.PutUint16(rawDate[:2], 1978) // years
|
|
||||||
rawDate[2] = 12 // months
|
|
||||||
rawDate[3] = 30 // days
|
|
||||||
rawDate[4] = 15 // hours
|
|
||||||
rawDate[5] = 46 // minutes
|
|
||||||
rawDate[6] = 23 // seconds
|
|
||||||
binary.LittleEndian.PutUint32(rawDate[7:], 987654) // microseconds
|
|
||||||
expect := func(expected string, inlen, outlen uint8) {
|
|
||||||
actual, _ := formatBinaryDateTime(rawDate[:inlen], outlen, false)
|
|
||||||
bytes, ok := actual.([]byte)
|
|
||||||
if !ok {
|
|
||||||
t.Errorf("formatBinaryDateTime must return []byte, was %T", actual)
|
|
||||||
}
|
|
||||||
if string(bytes) != expected {
|
|
||||||
t.Errorf(
|
|
||||||
"expected %q, got %q for length in %d, out %d",
|
|
||||||
bytes, actual, inlen, outlen,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
expect("0000-00-00", 0, 10)
|
|
||||||
expect("0000-00-00 00:00:00", 0, 19)
|
|
||||||
expect("1978-12-30", 4, 10)
|
|
||||||
expect("1978-12-30 15:46:23", 7, 19)
|
|
||||||
expect("1978-12-30 15:46:23.987654", 11, 26)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEscapeBackslash(t *testing.T) {
|
|
||||||
expect := func(expected, value string) {
|
|
||||||
actual := string(escapeBytesBackslash([]byte{}, []byte(value)))
|
|
||||||
if actual != expected {
|
|
||||||
t.Errorf(
|
|
||||||
"expected %s, got %s",
|
|
||||||
expected, actual,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
actual = string(escapeStringBackslash([]byte{}, value))
|
|
||||||
if actual != expected {
|
|
||||||
t.Errorf(
|
|
||||||
"expected %s, got %s",
|
|
||||||
expected, actual,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
expect("foo\\0bar", "foo\x00bar")
|
|
||||||
expect("foo\\nbar", "foo\nbar")
|
|
||||||
expect("foo\\rbar", "foo\rbar")
|
|
||||||
expect("foo\\Zbar", "foo\x1abar")
|
|
||||||
expect("foo\\\"bar", "foo\"bar")
|
|
||||||
expect("foo\\\\bar", "foo\\bar")
|
|
||||||
expect("foo\\'bar", "foo'bar")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEscapeQuotes(t *testing.T) {
|
|
||||||
expect := func(expected, value string) {
|
|
||||||
actual := string(escapeBytesQuotes([]byte{}, []byte(value)))
|
|
||||||
if actual != expected {
|
|
||||||
t.Errorf(
|
|
||||||
"expected %s, got %s",
|
|
||||||
expected, actual,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
actual = string(escapeStringQuotes([]byte{}, value))
|
|
||||||
if actual != expected {
|
|
||||||
t.Errorf(
|
|
||||||
"expected %s, got %s",
|
|
||||||
expected, actual,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
expect("foo\x00bar", "foo\x00bar") // not affected
|
|
||||||
expect("foo\nbar", "foo\nbar") // not affected
|
|
||||||
expect("foo\rbar", "foo\rbar") // not affected
|
|
||||||
expect("foo\x1abar", "foo\x1abar") // not affected
|
|
||||||
expect("foo''bar", "foo'bar") // affected
|
|
||||||
expect("foo\"bar", "foo\"bar") // not affected
|
|
||||||
}
|
|
43
vendor/github.com/golang/protobuf/proto/Makefile
generated
vendored
43
vendor/github.com/golang/protobuf/proto/Makefile
generated
vendored
|
@ -1,43 +0,0 @@
|
||||||
# Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
#
|
|
||||||
# Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
# https://github.com/golang/protobuf
|
|
||||||
#
|
|
||||||
# Redistribution and use in source and binary forms, with or without
|
|
||||||
# modification, are permitted provided that the following conditions are
|
|
||||||
# met:
|
|
||||||
#
|
|
||||||
# * Redistributions of source code must retain the above copyright
|
|
||||||
# notice, this list of conditions and the following disclaimer.
|
|
||||||
# * Redistributions in binary form must reproduce the above
|
|
||||||
# copyright notice, this list of conditions and the following disclaimer
|
|
||||||
# in the documentation and/or other materials provided with the
|
|
||||||
# distribution.
|
|
||||||
# * Neither the name of Google Inc. nor the names of its
|
|
||||||
# contributors may be used to endorse or promote products derived from
|
|
||||||
# this software without specific prior written permission.
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
install:
|
|
||||||
go install
|
|
||||||
|
|
||||||
test: install generate-test-pbs
|
|
||||||
go test
|
|
||||||
|
|
||||||
|
|
||||||
generate-test-pbs:
|
|
||||||
make install
|
|
||||||
make -C testdata
|
|
||||||
protoc --go_out=Mtestdata/test.proto=github.com/golang/protobuf/proto/testdata,Mgoogle/protobuf/any.proto=github.com/golang/protobuf/ptypes/any:. proto3_proto/proto3.proto
|
|
||||||
make
|
|
2269
vendor/github.com/golang/protobuf/proto/all_test.go
generated
vendored
2269
vendor/github.com/golang/protobuf/proto/all_test.go
generated
vendored
File diff suppressed because it is too large
Load diff
272
vendor/github.com/golang/protobuf/proto/any_test.go
generated
vendored
272
vendor/github.com/golang/protobuf/proto/any_test.go
generated
vendored
|
@ -1,272 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2016 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
|
|
||||||
pb "github.com/golang/protobuf/proto/proto3_proto"
|
|
||||||
testpb "github.com/golang/protobuf/proto/testdata"
|
|
||||||
anypb "github.com/golang/protobuf/ptypes/any"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
expandedMarshaler = proto.TextMarshaler{ExpandAny: true}
|
|
||||||
expandedCompactMarshaler = proto.TextMarshaler{Compact: true, ExpandAny: true}
|
|
||||||
)
|
|
||||||
|
|
||||||
// anyEqual reports whether two messages which may be google.protobuf.Any or may
|
|
||||||
// contain google.protobuf.Any fields are equal. We can't use proto.Equal for
|
|
||||||
// comparison, because semantically equivalent messages may be marshaled to
|
|
||||||
// binary in different tag order. Instead, trust that TextMarshaler with
|
|
||||||
// ExpandAny option works and compare the text marshaling results.
|
|
||||||
func anyEqual(got, want proto.Message) bool {
|
|
||||||
// if messages are proto.Equal, no need to marshal.
|
|
||||||
if proto.Equal(got, want) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
g := expandedMarshaler.Text(got)
|
|
||||||
w := expandedMarshaler.Text(want)
|
|
||||||
return g == w
|
|
||||||
}
|
|
||||||
|
|
||||||
type golden struct {
|
|
||||||
m proto.Message
|
|
||||||
t, c string
|
|
||||||
}
|
|
||||||
|
|
||||||
var goldenMessages = makeGolden()
|
|
||||||
|
|
||||||
func makeGolden() []golden {
|
|
||||||
nested := &pb.Nested{Bunny: "Monty"}
|
|
||||||
nb, err := proto.Marshal(nested)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
m1 := &pb.Message{
|
|
||||||
Name: "David",
|
|
||||||
ResultCount: 47,
|
|
||||||
Anything: &anypb.Any{TypeUrl: "type.googleapis.com/" + proto.MessageName(nested), Value: nb},
|
|
||||||
}
|
|
||||||
m2 := &pb.Message{
|
|
||||||
Name: "David",
|
|
||||||
ResultCount: 47,
|
|
||||||
Anything: &anypb.Any{TypeUrl: "http://[::1]/type.googleapis.com/" + proto.MessageName(nested), Value: nb},
|
|
||||||
}
|
|
||||||
m3 := &pb.Message{
|
|
||||||
Name: "David",
|
|
||||||
ResultCount: 47,
|
|
||||||
Anything: &anypb.Any{TypeUrl: `type.googleapis.com/"/` + proto.MessageName(nested), Value: nb},
|
|
||||||
}
|
|
||||||
m4 := &pb.Message{
|
|
||||||
Name: "David",
|
|
||||||
ResultCount: 47,
|
|
||||||
Anything: &anypb.Any{TypeUrl: "type.googleapis.com/a/path/" + proto.MessageName(nested), Value: nb},
|
|
||||||
}
|
|
||||||
m5 := &anypb.Any{TypeUrl: "type.googleapis.com/" + proto.MessageName(nested), Value: nb}
|
|
||||||
|
|
||||||
any1 := &testpb.MyMessage{Count: proto.Int32(47), Name: proto.String("David")}
|
|
||||||
proto.SetExtension(any1, testpb.E_Ext_More, &testpb.Ext{Data: proto.String("foo")})
|
|
||||||
proto.SetExtension(any1, testpb.E_Ext_Text, proto.String("bar"))
|
|
||||||
any1b, err := proto.Marshal(any1)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
any2 := &testpb.MyMessage{Count: proto.Int32(42), Bikeshed: testpb.MyMessage_GREEN.Enum(), RepBytes: [][]byte{[]byte("roboto")}}
|
|
||||||
proto.SetExtension(any2, testpb.E_Ext_More, &testpb.Ext{Data: proto.String("baz")})
|
|
||||||
any2b, err := proto.Marshal(any2)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
m6 := &pb.Message{
|
|
||||||
Name: "David",
|
|
||||||
ResultCount: 47,
|
|
||||||
Anything: &anypb.Any{TypeUrl: "type.googleapis.com/" + proto.MessageName(any1), Value: any1b},
|
|
||||||
ManyThings: []*anypb.Any{
|
|
||||||
&anypb.Any{TypeUrl: "type.googleapis.com/" + proto.MessageName(any2), Value: any2b},
|
|
||||||
&anypb.Any{TypeUrl: "type.googleapis.com/" + proto.MessageName(any1), Value: any1b},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
m1Golden = `
|
|
||||||
name: "David"
|
|
||||||
result_count: 47
|
|
||||||
anything: <
|
|
||||||
[type.googleapis.com/proto3_proto.Nested]: <
|
|
||||||
bunny: "Monty"
|
|
||||||
>
|
|
||||||
>
|
|
||||||
`
|
|
||||||
m2Golden = `
|
|
||||||
name: "David"
|
|
||||||
result_count: 47
|
|
||||||
anything: <
|
|
||||||
["http://[::1]/type.googleapis.com/proto3_proto.Nested"]: <
|
|
||||||
bunny: "Monty"
|
|
||||||
>
|
|
||||||
>
|
|
||||||
`
|
|
||||||
m3Golden = `
|
|
||||||
name: "David"
|
|
||||||
result_count: 47
|
|
||||||
anything: <
|
|
||||||
["type.googleapis.com/\"/proto3_proto.Nested"]: <
|
|
||||||
bunny: "Monty"
|
|
||||||
>
|
|
||||||
>
|
|
||||||
`
|
|
||||||
m4Golden = `
|
|
||||||
name: "David"
|
|
||||||
result_count: 47
|
|
||||||
anything: <
|
|
||||||
[type.googleapis.com/a/path/proto3_proto.Nested]: <
|
|
||||||
bunny: "Monty"
|
|
||||||
>
|
|
||||||
>
|
|
||||||
`
|
|
||||||
m5Golden = `
|
|
||||||
[type.googleapis.com/proto3_proto.Nested]: <
|
|
||||||
bunny: "Monty"
|
|
||||||
>
|
|
||||||
`
|
|
||||||
m6Golden = `
|
|
||||||
name: "David"
|
|
||||||
result_count: 47
|
|
||||||
anything: <
|
|
||||||
[type.googleapis.com/testdata.MyMessage]: <
|
|
||||||
count: 47
|
|
||||||
name: "David"
|
|
||||||
[testdata.Ext.more]: <
|
|
||||||
data: "foo"
|
|
||||||
>
|
|
||||||
[testdata.Ext.text]: "bar"
|
|
||||||
>
|
|
||||||
>
|
|
||||||
many_things: <
|
|
||||||
[type.googleapis.com/testdata.MyMessage]: <
|
|
||||||
count: 42
|
|
||||||
bikeshed: GREEN
|
|
||||||
rep_bytes: "roboto"
|
|
||||||
[testdata.Ext.more]: <
|
|
||||||
data: "baz"
|
|
||||||
>
|
|
||||||
>
|
|
||||||
>
|
|
||||||
many_things: <
|
|
||||||
[type.googleapis.com/testdata.MyMessage]: <
|
|
||||||
count: 47
|
|
||||||
name: "David"
|
|
||||||
[testdata.Ext.more]: <
|
|
||||||
data: "foo"
|
|
||||||
>
|
|
||||||
[testdata.Ext.text]: "bar"
|
|
||||||
>
|
|
||||||
>
|
|
||||||
`
|
|
||||||
)
|
|
||||||
return []golden{
|
|
||||||
{m1, strings.TrimSpace(m1Golden) + "\n", strings.TrimSpace(compact(m1Golden)) + " "},
|
|
||||||
{m2, strings.TrimSpace(m2Golden) + "\n", strings.TrimSpace(compact(m2Golden)) + " "},
|
|
||||||
{m3, strings.TrimSpace(m3Golden) + "\n", strings.TrimSpace(compact(m3Golden)) + " "},
|
|
||||||
{m4, strings.TrimSpace(m4Golden) + "\n", strings.TrimSpace(compact(m4Golden)) + " "},
|
|
||||||
{m5, strings.TrimSpace(m5Golden) + "\n", strings.TrimSpace(compact(m5Golden)) + " "},
|
|
||||||
{m6, strings.TrimSpace(m6Golden) + "\n", strings.TrimSpace(compact(m6Golden)) + " "},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMarshalGolden(t *testing.T) {
|
|
||||||
for _, tt := range goldenMessages {
|
|
||||||
if got, want := expandedMarshaler.Text(tt.m), tt.t; got != want {
|
|
||||||
t.Errorf("message %v: got:\n%s\nwant:\n%s", tt.m, got, want)
|
|
||||||
}
|
|
||||||
if got, want := expandedCompactMarshaler.Text(tt.m), tt.c; got != want {
|
|
||||||
t.Errorf("message %v: got:\n`%s`\nwant:\n`%s`", tt.m, got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnmarshalGolden(t *testing.T) {
|
|
||||||
for _, tt := range goldenMessages {
|
|
||||||
want := tt.m
|
|
||||||
got := proto.Clone(tt.m)
|
|
||||||
got.Reset()
|
|
||||||
if err := proto.UnmarshalText(tt.t, got); err != nil {
|
|
||||||
t.Errorf("failed to unmarshal\n%s\nerror: %v", tt.t, err)
|
|
||||||
}
|
|
||||||
if !anyEqual(got, want) {
|
|
||||||
t.Errorf("message:\n%s\ngot:\n%s\nwant:\n%s", tt.t, got, want)
|
|
||||||
}
|
|
||||||
got.Reset()
|
|
||||||
if err := proto.UnmarshalText(tt.c, got); err != nil {
|
|
||||||
t.Errorf("failed to unmarshal\n%s\nerror: %v", tt.c, err)
|
|
||||||
}
|
|
||||||
if !anyEqual(got, want) {
|
|
||||||
t.Errorf("message:\n%s\ngot:\n%s\nwant:\n%s", tt.c, got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMarsahlUnknownAny(t *testing.T) {
|
|
||||||
m := &pb.Message{
|
|
||||||
Anything: &anypb.Any{
|
|
||||||
TypeUrl: "foo",
|
|
||||||
Value: []byte("bar"),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
want := `anything: <
|
|
||||||
type_url: "foo"
|
|
||||||
value: "bar"
|
|
||||||
>
|
|
||||||
`
|
|
||||||
got := expandedMarshaler.Text(m)
|
|
||||||
if got != want {
|
|
||||||
t.Errorf("got\n`%s`\nwant\n`%s`", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAmbiguousAny(t *testing.T) {
|
|
||||||
pb := &anypb.Any{}
|
|
||||||
err := proto.UnmarshalText(`
|
|
||||||
[type.googleapis.com/proto3_proto.Nested]: <
|
|
||||||
bunny: "Monty"
|
|
||||||
>
|
|
||||||
type_url: "ttt/proto3_proto.Nested"
|
|
||||||
`, pb)
|
|
||||||
t.Logf("result: %v (error: %v)", expandedMarshaler.Text(pb), err)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("failed to parse ambiguous Any message: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
300
vendor/github.com/golang/protobuf/proto/clone_test.go
generated
vendored
300
vendor/github.com/golang/protobuf/proto/clone_test.go
generated
vendored
|
@ -1,300 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2011 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
|
|
||||||
proto3pb "github.com/golang/protobuf/proto/proto3_proto"
|
|
||||||
pb "github.com/golang/protobuf/proto/testdata"
|
|
||||||
)
|
|
||||||
|
|
||||||
var cloneTestMessage = &pb.MyMessage{
|
|
||||||
Count: proto.Int32(42),
|
|
||||||
Name: proto.String("Dave"),
|
|
||||||
Pet: []string{"bunny", "kitty", "horsey"},
|
|
||||||
Inner: &pb.InnerMessage{
|
|
||||||
Host: proto.String("niles"),
|
|
||||||
Port: proto.Int32(9099),
|
|
||||||
Connected: proto.Bool(true),
|
|
||||||
},
|
|
||||||
Others: []*pb.OtherMessage{
|
|
||||||
{
|
|
||||||
Value: []byte("some bytes"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Somegroup: &pb.MyMessage_SomeGroup{
|
|
||||||
GroupField: proto.Int32(6),
|
|
||||||
},
|
|
||||||
RepBytes: [][]byte{[]byte("sham"), []byte("wow")},
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
ext := &pb.Ext{
|
|
||||||
Data: proto.String("extension"),
|
|
||||||
}
|
|
||||||
if err := proto.SetExtension(cloneTestMessage, pb.E_Ext_More, ext); err != nil {
|
|
||||||
panic("SetExtension: " + err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestClone(t *testing.T) {
|
|
||||||
m := proto.Clone(cloneTestMessage).(*pb.MyMessage)
|
|
||||||
if !proto.Equal(m, cloneTestMessage) {
|
|
||||||
t.Errorf("Clone(%v) = %v", cloneTestMessage, m)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify it was a deep copy.
|
|
||||||
*m.Inner.Port++
|
|
||||||
if proto.Equal(m, cloneTestMessage) {
|
|
||||||
t.Error("Mutating clone changed the original")
|
|
||||||
}
|
|
||||||
// Byte fields and repeated fields should be copied.
|
|
||||||
if &m.Pet[0] == &cloneTestMessage.Pet[0] {
|
|
||||||
t.Error("Pet: repeated field not copied")
|
|
||||||
}
|
|
||||||
if &m.Others[0] == &cloneTestMessage.Others[0] {
|
|
||||||
t.Error("Others: repeated field not copied")
|
|
||||||
}
|
|
||||||
if &m.Others[0].Value[0] == &cloneTestMessage.Others[0].Value[0] {
|
|
||||||
t.Error("Others[0].Value: bytes field not copied")
|
|
||||||
}
|
|
||||||
if &m.RepBytes[0] == &cloneTestMessage.RepBytes[0] {
|
|
||||||
t.Error("RepBytes: repeated field not copied")
|
|
||||||
}
|
|
||||||
if &m.RepBytes[0][0] == &cloneTestMessage.RepBytes[0][0] {
|
|
||||||
t.Error("RepBytes[0]: bytes field not copied")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCloneNil(t *testing.T) {
|
|
||||||
var m *pb.MyMessage
|
|
||||||
if c := proto.Clone(m); !proto.Equal(m, c) {
|
|
||||||
t.Errorf("Clone(%v) = %v", m, c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var mergeTests = []struct {
|
|
||||||
src, dst, want proto.Message
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
src: &pb.MyMessage{
|
|
||||||
Count: proto.Int32(42),
|
|
||||||
},
|
|
||||||
dst: &pb.MyMessage{
|
|
||||||
Name: proto.String("Dave"),
|
|
||||||
},
|
|
||||||
want: &pb.MyMessage{
|
|
||||||
Count: proto.Int32(42),
|
|
||||||
Name: proto.String("Dave"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
src: &pb.MyMessage{
|
|
||||||
Inner: &pb.InnerMessage{
|
|
||||||
Host: proto.String("hey"),
|
|
||||||
Connected: proto.Bool(true),
|
|
||||||
},
|
|
||||||
Pet: []string{"horsey"},
|
|
||||||
Others: []*pb.OtherMessage{
|
|
||||||
{
|
|
||||||
Value: []byte("some bytes"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dst: &pb.MyMessage{
|
|
||||||
Inner: &pb.InnerMessage{
|
|
||||||
Host: proto.String("niles"),
|
|
||||||
Port: proto.Int32(9099),
|
|
||||||
},
|
|
||||||
Pet: []string{"bunny", "kitty"},
|
|
||||||
Others: []*pb.OtherMessage{
|
|
||||||
{
|
|
||||||
Key: proto.Int64(31415926535),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Explicitly test a src=nil field
|
|
||||||
Inner: nil,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
want: &pb.MyMessage{
|
|
||||||
Inner: &pb.InnerMessage{
|
|
||||||
Host: proto.String("hey"),
|
|
||||||
Connected: proto.Bool(true),
|
|
||||||
Port: proto.Int32(9099),
|
|
||||||
},
|
|
||||||
Pet: []string{"bunny", "kitty", "horsey"},
|
|
||||||
Others: []*pb.OtherMessage{
|
|
||||||
{
|
|
||||||
Key: proto.Int64(31415926535),
|
|
||||||
},
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
Value: []byte("some bytes"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
src: &pb.MyMessage{
|
|
||||||
RepBytes: [][]byte{[]byte("wow")},
|
|
||||||
},
|
|
||||||
dst: &pb.MyMessage{
|
|
||||||
Somegroup: &pb.MyMessage_SomeGroup{
|
|
||||||
GroupField: proto.Int32(6),
|
|
||||||
},
|
|
||||||
RepBytes: [][]byte{[]byte("sham")},
|
|
||||||
},
|
|
||||||
want: &pb.MyMessage{
|
|
||||||
Somegroup: &pb.MyMessage_SomeGroup{
|
|
||||||
GroupField: proto.Int32(6),
|
|
||||||
},
|
|
||||||
RepBytes: [][]byte{[]byte("sham"), []byte("wow")},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Check that a scalar bytes field replaces rather than appends.
|
|
||||||
{
|
|
||||||
src: &pb.OtherMessage{Value: []byte("foo")},
|
|
||||||
dst: &pb.OtherMessage{Value: []byte("bar")},
|
|
||||||
want: &pb.OtherMessage{Value: []byte("foo")},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
src: &pb.MessageWithMap{
|
|
||||||
NameMapping: map[int32]string{6: "Nigel"},
|
|
||||||
MsgMapping: map[int64]*pb.FloatingPoint{
|
|
||||||
0x4001: &pb.FloatingPoint{F: proto.Float64(2.0)},
|
|
||||||
0x4002: &pb.FloatingPoint{
|
|
||||||
F: proto.Float64(2.0),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ByteMapping: map[bool][]byte{true: []byte("wowsa")},
|
|
||||||
},
|
|
||||||
dst: &pb.MessageWithMap{
|
|
||||||
NameMapping: map[int32]string{
|
|
||||||
6: "Bruce", // should be overwritten
|
|
||||||
7: "Andrew",
|
|
||||||
},
|
|
||||||
MsgMapping: map[int64]*pb.FloatingPoint{
|
|
||||||
0x4002: &pb.FloatingPoint{
|
|
||||||
F: proto.Float64(3.0),
|
|
||||||
Exact: proto.Bool(true),
|
|
||||||
}, // the entire message should be overwritten
|
|
||||||
},
|
|
||||||
},
|
|
||||||
want: &pb.MessageWithMap{
|
|
||||||
NameMapping: map[int32]string{
|
|
||||||
6: "Nigel",
|
|
||||||
7: "Andrew",
|
|
||||||
},
|
|
||||||
MsgMapping: map[int64]*pb.FloatingPoint{
|
|
||||||
0x4001: &pb.FloatingPoint{F: proto.Float64(2.0)},
|
|
||||||
0x4002: &pb.FloatingPoint{
|
|
||||||
F: proto.Float64(2.0),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ByteMapping: map[bool][]byte{true: []byte("wowsa")},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// proto3 shouldn't merge zero values,
|
|
||||||
// in the same way that proto2 shouldn't merge nils.
|
|
||||||
{
|
|
||||||
src: &proto3pb.Message{
|
|
||||||
Name: "Aaron",
|
|
||||||
Data: []byte(""), // zero value, but not nil
|
|
||||||
},
|
|
||||||
dst: &proto3pb.Message{
|
|
||||||
HeightInCm: 176,
|
|
||||||
Data: []byte("texas!"),
|
|
||||||
},
|
|
||||||
want: &proto3pb.Message{
|
|
||||||
Name: "Aaron",
|
|
||||||
HeightInCm: 176,
|
|
||||||
Data: []byte("texas!"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Oneof fields should merge by assignment.
|
|
||||||
{
|
|
||||||
src: &pb.Communique{
|
|
||||||
Union: &pb.Communique_Number{41},
|
|
||||||
},
|
|
||||||
dst: &pb.Communique{
|
|
||||||
Union: &pb.Communique_Name{"Bobby Tables"},
|
|
||||||
},
|
|
||||||
want: &pb.Communique{
|
|
||||||
Union: &pb.Communique_Number{41},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Oneof nil is the same as not set.
|
|
||||||
{
|
|
||||||
src: &pb.Communique{},
|
|
||||||
dst: &pb.Communique{
|
|
||||||
Union: &pb.Communique_Name{"Bobby Tables"},
|
|
||||||
},
|
|
||||||
want: &pb.Communique{
|
|
||||||
Union: &pb.Communique_Name{"Bobby Tables"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
src: &proto3pb.Message{
|
|
||||||
Terrain: map[string]*proto3pb.Nested{
|
|
||||||
"kay_a": &proto3pb.Nested{Cute: true}, // replace
|
|
||||||
"kay_b": &proto3pb.Nested{Bunny: "rabbit"}, // insert
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dst: &proto3pb.Message{
|
|
||||||
Terrain: map[string]*proto3pb.Nested{
|
|
||||||
"kay_a": &proto3pb.Nested{Bunny: "lost"}, // replaced
|
|
||||||
"kay_c": &proto3pb.Nested{Bunny: "bunny"}, // keep
|
|
||||||
},
|
|
||||||
},
|
|
||||||
want: &proto3pb.Message{
|
|
||||||
Terrain: map[string]*proto3pb.Nested{
|
|
||||||
"kay_a": &proto3pb.Nested{Cute: true},
|
|
||||||
"kay_b": &proto3pb.Nested{Bunny: "rabbit"},
|
|
||||||
"kay_c": &proto3pb.Nested{Bunny: "bunny"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMerge(t *testing.T) {
|
|
||||||
for _, m := range mergeTests {
|
|
||||||
got := proto.Clone(m.dst)
|
|
||||||
proto.Merge(got, m.src)
|
|
||||||
if !proto.Equal(got, m.want) {
|
|
||||||
t.Errorf("Merge(%v, %v)\n got %v\nwant %v\n", m.dst, m.src, got, m.want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
212
vendor/github.com/golang/protobuf/proto/equal_test.go
generated
vendored
212
vendor/github.com/golang/protobuf/proto/equal_test.go
generated
vendored
|
@ -1,212 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2011 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
. "github.com/golang/protobuf/proto"
|
|
||||||
proto3pb "github.com/golang/protobuf/proto/proto3_proto"
|
|
||||||
pb "github.com/golang/protobuf/proto/testdata"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Four identical base messages.
|
|
||||||
// The init function adds extensions to some of them.
|
|
||||||
var messageWithoutExtension = &pb.MyMessage{Count: Int32(7)}
|
|
||||||
var messageWithExtension1a = &pb.MyMessage{Count: Int32(7)}
|
|
||||||
var messageWithExtension1b = &pb.MyMessage{Count: Int32(7)}
|
|
||||||
var messageWithExtension2 = &pb.MyMessage{Count: Int32(7)}
|
|
||||||
|
|
||||||
// Two messages with non-message extensions.
|
|
||||||
var messageWithInt32Extension1 = &pb.MyMessage{Count: Int32(8)}
|
|
||||||
var messageWithInt32Extension2 = &pb.MyMessage{Count: Int32(8)}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
ext1 := &pb.Ext{Data: String("Kirk")}
|
|
||||||
ext2 := &pb.Ext{Data: String("Picard")}
|
|
||||||
|
|
||||||
// messageWithExtension1a has ext1, but never marshals it.
|
|
||||||
if err := SetExtension(messageWithExtension1a, pb.E_Ext_More, ext1); err != nil {
|
|
||||||
panic("SetExtension on 1a failed: " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// messageWithExtension1b is the unmarshaled form of messageWithExtension1a.
|
|
||||||
if err := SetExtension(messageWithExtension1b, pb.E_Ext_More, ext1); err != nil {
|
|
||||||
panic("SetExtension on 1b failed: " + err.Error())
|
|
||||||
}
|
|
||||||
buf, err := Marshal(messageWithExtension1b)
|
|
||||||
if err != nil {
|
|
||||||
panic("Marshal of 1b failed: " + err.Error())
|
|
||||||
}
|
|
||||||
messageWithExtension1b.Reset()
|
|
||||||
if err := Unmarshal(buf, messageWithExtension1b); err != nil {
|
|
||||||
panic("Unmarshal of 1b failed: " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// messageWithExtension2 has ext2.
|
|
||||||
if err := SetExtension(messageWithExtension2, pb.E_Ext_More, ext2); err != nil {
|
|
||||||
panic("SetExtension on 2 failed: " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := SetExtension(messageWithInt32Extension1, pb.E_Ext_Number, Int32(23)); err != nil {
|
|
||||||
panic("SetExtension on Int32-1 failed: " + err.Error())
|
|
||||||
}
|
|
||||||
if err := SetExtension(messageWithInt32Extension1, pb.E_Ext_Number, Int32(24)); err != nil {
|
|
||||||
panic("SetExtension on Int32-2 failed: " + err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var EqualTests = []struct {
|
|
||||||
desc string
|
|
||||||
a, b Message
|
|
||||||
exp bool
|
|
||||||
}{
|
|
||||||
{"different types", &pb.GoEnum{}, &pb.GoTestField{}, false},
|
|
||||||
{"equal empty", &pb.GoEnum{}, &pb.GoEnum{}, true},
|
|
||||||
{"nil vs nil", nil, nil, true},
|
|
||||||
{"typed nil vs typed nil", (*pb.GoEnum)(nil), (*pb.GoEnum)(nil), true},
|
|
||||||
{"typed nil vs empty", (*pb.GoEnum)(nil), &pb.GoEnum{}, false},
|
|
||||||
{"different typed nil", (*pb.GoEnum)(nil), (*pb.GoTestField)(nil), false},
|
|
||||||
|
|
||||||
{"one set field, one unset field", &pb.GoTestField{Label: String("foo")}, &pb.GoTestField{}, false},
|
|
||||||
{"one set field zero, one unset field", &pb.GoTest{Param: Int32(0)}, &pb.GoTest{}, false},
|
|
||||||
{"different set fields", &pb.GoTestField{Label: String("foo")}, &pb.GoTestField{Label: String("bar")}, false},
|
|
||||||
{"equal set", &pb.GoTestField{Label: String("foo")}, &pb.GoTestField{Label: String("foo")}, true},
|
|
||||||
|
|
||||||
{"repeated, one set", &pb.GoTest{F_Int32Repeated: []int32{2, 3}}, &pb.GoTest{}, false},
|
|
||||||
{"repeated, different length", &pb.GoTest{F_Int32Repeated: []int32{2, 3}}, &pb.GoTest{F_Int32Repeated: []int32{2}}, false},
|
|
||||||
{"repeated, different value", &pb.GoTest{F_Int32Repeated: []int32{2}}, &pb.GoTest{F_Int32Repeated: []int32{3}}, false},
|
|
||||||
{"repeated, equal", &pb.GoTest{F_Int32Repeated: []int32{2, 4}}, &pb.GoTest{F_Int32Repeated: []int32{2, 4}}, true},
|
|
||||||
{"repeated, nil equal nil", &pb.GoTest{F_Int32Repeated: nil}, &pb.GoTest{F_Int32Repeated: nil}, true},
|
|
||||||
{"repeated, nil equal empty", &pb.GoTest{F_Int32Repeated: nil}, &pb.GoTest{F_Int32Repeated: []int32{}}, true},
|
|
||||||
{"repeated, empty equal nil", &pb.GoTest{F_Int32Repeated: []int32{}}, &pb.GoTest{F_Int32Repeated: nil}, true},
|
|
||||||
|
|
||||||
{
|
|
||||||
"nested, different",
|
|
||||||
&pb.GoTest{RequiredField: &pb.GoTestField{Label: String("foo")}},
|
|
||||||
&pb.GoTest{RequiredField: &pb.GoTestField{Label: String("bar")}},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nested, equal",
|
|
||||||
&pb.GoTest{RequiredField: &pb.GoTestField{Label: String("wow")}},
|
|
||||||
&pb.GoTest{RequiredField: &pb.GoTestField{Label: String("wow")}},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
|
|
||||||
{"bytes", &pb.OtherMessage{Value: []byte("foo")}, &pb.OtherMessage{Value: []byte("foo")}, true},
|
|
||||||
{"bytes, empty", &pb.OtherMessage{Value: []byte{}}, &pb.OtherMessage{Value: []byte{}}, true},
|
|
||||||
{"bytes, empty vs nil", &pb.OtherMessage{Value: []byte{}}, &pb.OtherMessage{Value: nil}, false},
|
|
||||||
{
|
|
||||||
"repeated bytes",
|
|
||||||
&pb.MyMessage{RepBytes: [][]byte{[]byte("sham"), []byte("wow")}},
|
|
||||||
&pb.MyMessage{RepBytes: [][]byte{[]byte("sham"), []byte("wow")}},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
// In proto3, []byte{} and []byte(nil) are equal.
|
|
||||||
{"proto3 bytes, empty vs nil", &proto3pb.Message{Data: []byte{}}, &proto3pb.Message{Data: nil}, true},
|
|
||||||
|
|
||||||
{"extension vs. no extension", messageWithoutExtension, messageWithExtension1a, false},
|
|
||||||
{"extension vs. same extension", messageWithExtension1a, messageWithExtension1b, true},
|
|
||||||
{"extension vs. different extension", messageWithExtension1a, messageWithExtension2, false},
|
|
||||||
|
|
||||||
{"int32 extension vs. itself", messageWithInt32Extension1, messageWithInt32Extension1, true},
|
|
||||||
{"int32 extension vs. a different int32", messageWithInt32Extension1, messageWithInt32Extension2, false},
|
|
||||||
|
|
||||||
{
|
|
||||||
"message with group",
|
|
||||||
&pb.MyMessage{
|
|
||||||
Count: Int32(1),
|
|
||||||
Somegroup: &pb.MyMessage_SomeGroup{
|
|
||||||
GroupField: Int32(5),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&pb.MyMessage{
|
|
||||||
Count: Int32(1),
|
|
||||||
Somegroup: &pb.MyMessage_SomeGroup{
|
|
||||||
GroupField: Int32(5),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"map same",
|
|
||||||
&pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}},
|
|
||||||
&pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"map different entry",
|
|
||||||
&pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}},
|
|
||||||
&pb.MessageWithMap{NameMapping: map[int32]string{2: "Rob"}},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"map different key only",
|
|
||||||
&pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}},
|
|
||||||
&pb.MessageWithMap{NameMapping: map[int32]string{2: "Ken"}},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"map different value only",
|
|
||||||
&pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}},
|
|
||||||
&pb.MessageWithMap{NameMapping: map[int32]string{1: "Rob"}},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"oneof same",
|
|
||||||
&pb.Communique{Union: &pb.Communique_Number{41}},
|
|
||||||
&pb.Communique{Union: &pb.Communique_Number{41}},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"oneof one nil",
|
|
||||||
&pb.Communique{Union: &pb.Communique_Number{41}},
|
|
||||||
&pb.Communique{},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"oneof different",
|
|
||||||
&pb.Communique{Union: &pb.Communique_Number{41}},
|
|
||||||
&pb.Communique{Union: &pb.Communique_Name{"Bobby Tables"}},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEqual(t *testing.T) {
|
|
||||||
for _, tc := range EqualTests {
|
|
||||||
if res := Equal(tc.a, tc.b); res != tc.exp {
|
|
||||||
t.Errorf("%v: Equal(%v, %v) = %v, want %v", tc.desc, tc.a, tc.b, res, tc.exp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
504
vendor/github.com/golang/protobuf/proto/extensions_test.go
generated
vendored
504
vendor/github.com/golang/protobuf/proto/extensions_test.go
generated
vendored
|
@ -1,504 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2014 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
pb "github.com/golang/protobuf/proto/testdata"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestGetExtensionsWithMissingExtensions(t *testing.T) {
|
|
||||||
msg := &pb.MyMessage{}
|
|
||||||
ext1 := &pb.Ext{}
|
|
||||||
if err := proto.SetExtension(msg, pb.E_Ext_More, ext1); err != nil {
|
|
||||||
t.Fatalf("Could not set ext1: %s", err)
|
|
||||||
}
|
|
||||||
exts, err := proto.GetExtensions(msg, []*proto.ExtensionDesc{
|
|
||||||
pb.E_Ext_More,
|
|
||||||
pb.E_Ext_Text,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("GetExtensions() failed: %s", err)
|
|
||||||
}
|
|
||||||
if exts[0] != ext1 {
|
|
||||||
t.Errorf("ext1 not in returned extensions: %T %v", exts[0], exts[0])
|
|
||||||
}
|
|
||||||
if exts[1] != nil {
|
|
||||||
t.Errorf("ext2 in returned extensions: %T %v", exts[1], exts[1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExtensionDescsWithMissingExtensions(t *testing.T) {
|
|
||||||
msg := &pb.MyMessage{Count: proto.Int32(0)}
|
|
||||||
extdesc1 := pb.E_Ext_More
|
|
||||||
ext1 := &pb.Ext{}
|
|
||||||
if err := proto.SetExtension(msg, extdesc1, ext1); err != nil {
|
|
||||||
t.Fatalf("Could not set ext1: %s", err)
|
|
||||||
}
|
|
||||||
extdesc2 := &proto.ExtensionDesc{
|
|
||||||
ExtendedType: (*pb.MyMessage)(nil),
|
|
||||||
ExtensionType: (*bool)(nil),
|
|
||||||
Field: 123456789,
|
|
||||||
Name: "a.b",
|
|
||||||
Tag: "varint,123456789,opt",
|
|
||||||
}
|
|
||||||
ext2 := proto.Bool(false)
|
|
||||||
if err := proto.SetExtension(msg, extdesc2, ext2); err != nil {
|
|
||||||
t.Fatalf("Could not set ext2: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := proto.Marshal(msg)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Could not marshal msg: %v", err)
|
|
||||||
}
|
|
||||||
if err := proto.Unmarshal(b, msg); err != nil {
|
|
||||||
t.Fatalf("Could not unmarshal into msg: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
descs, err := proto.ExtensionDescs(msg)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("proto.ExtensionDescs: got error %v", err)
|
|
||||||
}
|
|
||||||
sortExtDescs(descs)
|
|
||||||
wantDescs := []*proto.ExtensionDesc{extdesc1, &proto.ExtensionDesc{Field: extdesc2.Field}}
|
|
||||||
if !reflect.DeepEqual(descs, wantDescs) {
|
|
||||||
t.Errorf("proto.ExtensionDescs(msg) sorted extension ids: got %+v, want %+v", descs, wantDescs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type ExtensionDescSlice []*proto.ExtensionDesc
|
|
||||||
|
|
||||||
func (s ExtensionDescSlice) Len() int { return len(s) }
|
|
||||||
func (s ExtensionDescSlice) Less(i, j int) bool { return s[i].Field < s[j].Field }
|
|
||||||
func (s ExtensionDescSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
|
||||||
|
|
||||||
func sortExtDescs(s []*proto.ExtensionDesc) {
|
|
||||||
sort.Sort(ExtensionDescSlice(s))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetExtensionStability(t *testing.T) {
|
|
||||||
check := func(m *pb.MyMessage) bool {
|
|
||||||
ext1, err := proto.GetExtension(m, pb.E_Ext_More)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("GetExtension() failed: %s", err)
|
|
||||||
}
|
|
||||||
ext2, err := proto.GetExtension(m, pb.E_Ext_More)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("GetExtension() failed: %s", err)
|
|
||||||
}
|
|
||||||
return ext1 == ext2
|
|
||||||
}
|
|
||||||
msg := &pb.MyMessage{Count: proto.Int32(4)}
|
|
||||||
ext0 := &pb.Ext{}
|
|
||||||
if err := proto.SetExtension(msg, pb.E_Ext_More, ext0); err != nil {
|
|
||||||
t.Fatalf("Could not set ext1: %s", ext0)
|
|
||||||
}
|
|
||||||
if !check(msg) {
|
|
||||||
t.Errorf("GetExtension() not stable before marshaling")
|
|
||||||
}
|
|
||||||
bb, err := proto.Marshal(msg)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Marshal() failed: %s", err)
|
|
||||||
}
|
|
||||||
msg1 := &pb.MyMessage{}
|
|
||||||
err = proto.Unmarshal(bb, msg1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unmarshal() failed: %s", err)
|
|
||||||
}
|
|
||||||
if !check(msg1) {
|
|
||||||
t.Errorf("GetExtension() not stable after unmarshaling")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetExtensionDefaults(t *testing.T) {
|
|
||||||
var setFloat64 float64 = 1
|
|
||||||
var setFloat32 float32 = 2
|
|
||||||
var setInt32 int32 = 3
|
|
||||||
var setInt64 int64 = 4
|
|
||||||
var setUint32 uint32 = 5
|
|
||||||
var setUint64 uint64 = 6
|
|
||||||
var setBool = true
|
|
||||||
var setBool2 = false
|
|
||||||
var setString = "Goodnight string"
|
|
||||||
var setBytes = []byte("Goodnight bytes")
|
|
||||||
var setEnum = pb.DefaultsMessage_TWO
|
|
||||||
|
|
||||||
type testcase struct {
|
|
||||||
ext *proto.ExtensionDesc // Extension we are testing.
|
|
||||||
want interface{} // Expected value of extension, or nil (meaning that GetExtension will fail).
|
|
||||||
def interface{} // Expected value of extension after ClearExtension().
|
|
||||||
}
|
|
||||||
tests := []testcase{
|
|
||||||
{pb.E_NoDefaultDouble, setFloat64, nil},
|
|
||||||
{pb.E_NoDefaultFloat, setFloat32, nil},
|
|
||||||
{pb.E_NoDefaultInt32, setInt32, nil},
|
|
||||||
{pb.E_NoDefaultInt64, setInt64, nil},
|
|
||||||
{pb.E_NoDefaultUint32, setUint32, nil},
|
|
||||||
{pb.E_NoDefaultUint64, setUint64, nil},
|
|
||||||
{pb.E_NoDefaultSint32, setInt32, nil},
|
|
||||||
{pb.E_NoDefaultSint64, setInt64, nil},
|
|
||||||
{pb.E_NoDefaultFixed32, setUint32, nil},
|
|
||||||
{pb.E_NoDefaultFixed64, setUint64, nil},
|
|
||||||
{pb.E_NoDefaultSfixed32, setInt32, nil},
|
|
||||||
{pb.E_NoDefaultSfixed64, setInt64, nil},
|
|
||||||
{pb.E_NoDefaultBool, setBool, nil},
|
|
||||||
{pb.E_NoDefaultBool, setBool2, nil},
|
|
||||||
{pb.E_NoDefaultString, setString, nil},
|
|
||||||
{pb.E_NoDefaultBytes, setBytes, nil},
|
|
||||||
{pb.E_NoDefaultEnum, setEnum, nil},
|
|
||||||
{pb.E_DefaultDouble, setFloat64, float64(3.1415)},
|
|
||||||
{pb.E_DefaultFloat, setFloat32, float32(3.14)},
|
|
||||||
{pb.E_DefaultInt32, setInt32, int32(42)},
|
|
||||||
{pb.E_DefaultInt64, setInt64, int64(43)},
|
|
||||||
{pb.E_DefaultUint32, setUint32, uint32(44)},
|
|
||||||
{pb.E_DefaultUint64, setUint64, uint64(45)},
|
|
||||||
{pb.E_DefaultSint32, setInt32, int32(46)},
|
|
||||||
{pb.E_DefaultSint64, setInt64, int64(47)},
|
|
||||||
{pb.E_DefaultFixed32, setUint32, uint32(48)},
|
|
||||||
{pb.E_DefaultFixed64, setUint64, uint64(49)},
|
|
||||||
{pb.E_DefaultSfixed32, setInt32, int32(50)},
|
|
||||||
{pb.E_DefaultSfixed64, setInt64, int64(51)},
|
|
||||||
{pb.E_DefaultBool, setBool, true},
|
|
||||||
{pb.E_DefaultBool, setBool2, true},
|
|
||||||
{pb.E_DefaultString, setString, "Hello, string"},
|
|
||||||
{pb.E_DefaultBytes, setBytes, []byte("Hello, bytes")},
|
|
||||||
{pb.E_DefaultEnum, setEnum, pb.DefaultsMessage_ONE},
|
|
||||||
}
|
|
||||||
|
|
||||||
checkVal := func(test testcase, msg *pb.DefaultsMessage, valWant interface{}) error {
|
|
||||||
val, err := proto.GetExtension(msg, test.ext)
|
|
||||||
if err != nil {
|
|
||||||
if valWant != nil {
|
|
||||||
return fmt.Errorf("GetExtension(): %s", err)
|
|
||||||
}
|
|
||||||
if want := proto.ErrMissingExtension; err != want {
|
|
||||||
return fmt.Errorf("Unexpected error: got %v, want %v", err, want)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// All proto2 extension values are either a pointer to a value or a slice of values.
|
|
||||||
ty := reflect.TypeOf(val)
|
|
||||||
tyWant := reflect.TypeOf(test.ext.ExtensionType)
|
|
||||||
if got, want := ty, tyWant; got != want {
|
|
||||||
return fmt.Errorf("unexpected reflect.TypeOf(): got %v want %v", got, want)
|
|
||||||
}
|
|
||||||
tye := ty.Elem()
|
|
||||||
tyeWant := tyWant.Elem()
|
|
||||||
if got, want := tye, tyeWant; got != want {
|
|
||||||
return fmt.Errorf("unexpected reflect.TypeOf().Elem(): got %v want %v", got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check the name of the type of the value.
|
|
||||||
// If it is an enum it will be type int32 with the name of the enum.
|
|
||||||
if got, want := tye.Name(), tye.Name(); got != want {
|
|
||||||
return fmt.Errorf("unexpected reflect.TypeOf().Elem().Name(): got %v want %v", got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that value is what we expect.
|
|
||||||
// If we have a pointer in val, get the value it points to.
|
|
||||||
valExp := val
|
|
||||||
if ty.Kind() == reflect.Ptr {
|
|
||||||
valExp = reflect.ValueOf(val).Elem().Interface()
|
|
||||||
}
|
|
||||||
if got, want := valExp, valWant; !reflect.DeepEqual(got, want) {
|
|
||||||
return fmt.Errorf("unexpected reflect.DeepEqual(): got %v want %v", got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
setTo := func(test testcase) interface{} {
|
|
||||||
setTo := reflect.ValueOf(test.want)
|
|
||||||
if typ := reflect.TypeOf(test.ext.ExtensionType); typ.Kind() == reflect.Ptr {
|
|
||||||
setTo = reflect.New(typ).Elem()
|
|
||||||
setTo.Set(reflect.New(setTo.Type().Elem()))
|
|
||||||
setTo.Elem().Set(reflect.ValueOf(test.want))
|
|
||||||
}
|
|
||||||
return setTo.Interface()
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
msg := &pb.DefaultsMessage{}
|
|
||||||
name := test.ext.Name
|
|
||||||
|
|
||||||
// Check the initial value.
|
|
||||||
if err := checkVal(test, msg, test.def); err != nil {
|
|
||||||
t.Errorf("%s: %v", name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the per-type value and check value.
|
|
||||||
name = fmt.Sprintf("%s (set to %T %v)", name, test.want, test.want)
|
|
||||||
if err := proto.SetExtension(msg, test.ext, setTo(test)); err != nil {
|
|
||||||
t.Errorf("%s: SetExtension(): %v", name, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := checkVal(test, msg, test.want); err != nil {
|
|
||||||
t.Errorf("%s: %v", name, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set and check the value.
|
|
||||||
name += " (cleared)"
|
|
||||||
proto.ClearExtension(msg, test.ext)
|
|
||||||
if err := checkVal(test, msg, test.def); err != nil {
|
|
||||||
t.Errorf("%s: %v", name, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExtensionsRoundTrip(t *testing.T) {
|
|
||||||
msg := &pb.MyMessage{}
|
|
||||||
ext1 := &pb.Ext{
|
|
||||||
Data: proto.String("hi"),
|
|
||||||
}
|
|
||||||
ext2 := &pb.Ext{
|
|
||||||
Data: proto.String("there"),
|
|
||||||
}
|
|
||||||
exists := proto.HasExtension(msg, pb.E_Ext_More)
|
|
||||||
if exists {
|
|
||||||
t.Error("Extension More present unexpectedly")
|
|
||||||
}
|
|
||||||
if err := proto.SetExtension(msg, pb.E_Ext_More, ext1); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
if err := proto.SetExtension(msg, pb.E_Ext_More, ext2); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
e, err := proto.GetExtension(msg, pb.E_Ext_More)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
x, ok := e.(*pb.Ext)
|
|
||||||
if !ok {
|
|
||||||
t.Errorf("e has type %T, expected testdata.Ext", e)
|
|
||||||
} else if *x.Data != "there" {
|
|
||||||
t.Errorf("SetExtension failed to overwrite, got %+v, not 'there'", x)
|
|
||||||
}
|
|
||||||
proto.ClearExtension(msg, pb.E_Ext_More)
|
|
||||||
if _, err = proto.GetExtension(msg, pb.E_Ext_More); err != proto.ErrMissingExtension {
|
|
||||||
t.Errorf("got %v, expected ErrMissingExtension", e)
|
|
||||||
}
|
|
||||||
if _, err := proto.GetExtension(msg, pb.E_X215); err == nil {
|
|
||||||
t.Error("expected bad extension error, got nil")
|
|
||||||
}
|
|
||||||
if err := proto.SetExtension(msg, pb.E_X215, 12); err == nil {
|
|
||||||
t.Error("expected extension err")
|
|
||||||
}
|
|
||||||
if err := proto.SetExtension(msg, pb.E_Ext_More, 12); err == nil {
|
|
||||||
t.Error("expected some sort of type mismatch error, got nil")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNilExtension(t *testing.T) {
|
|
||||||
msg := &pb.MyMessage{
|
|
||||||
Count: proto.Int32(1),
|
|
||||||
}
|
|
||||||
if err := proto.SetExtension(msg, pb.E_Ext_Text, proto.String("hello")); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := proto.SetExtension(msg, pb.E_Ext_More, (*pb.Ext)(nil)); err == nil {
|
|
||||||
t.Error("expected SetExtension to fail due to a nil extension")
|
|
||||||
} else if want := "proto: SetExtension called with nil value of type *testdata.Ext"; err.Error() != want {
|
|
||||||
t.Errorf("expected error %v, got %v", want, err)
|
|
||||||
}
|
|
||||||
// Note: if the behavior of Marshal is ever changed to ignore nil extensions, update
|
|
||||||
// this test to verify that E_Ext_Text is properly propagated through marshal->unmarshal.
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMarshalUnmarshalRepeatedExtension(t *testing.T) {
|
|
||||||
// Add a repeated extension to the result.
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
ext []*pb.ComplexExtension
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"two fields",
|
|
||||||
[]*pb.ComplexExtension{
|
|
||||||
{First: proto.Int32(7)},
|
|
||||||
{Second: proto.Int32(11)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"repeated field",
|
|
||||||
[]*pb.ComplexExtension{
|
|
||||||
{Third: []int32{1000}},
|
|
||||||
{Third: []int32{2000}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"two fields and repeated field",
|
|
||||||
[]*pb.ComplexExtension{
|
|
||||||
{Third: []int32{1000}},
|
|
||||||
{First: proto.Int32(9)},
|
|
||||||
{Second: proto.Int32(21)},
|
|
||||||
{Third: []int32{2000}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
// Marshal message with a repeated extension.
|
|
||||||
msg1 := new(pb.OtherMessage)
|
|
||||||
err := proto.SetExtension(msg1, pb.E_RComplex, test.ext)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("[%s] Error setting extension: %v", test.name, err)
|
|
||||||
}
|
|
||||||
b, err := proto.Marshal(msg1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("[%s] Error marshaling message: %v", test.name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmarshal and read the merged proto.
|
|
||||||
msg2 := new(pb.OtherMessage)
|
|
||||||
err = proto.Unmarshal(b, msg2)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("[%s] Error unmarshaling message: %v", test.name, err)
|
|
||||||
}
|
|
||||||
e, err := proto.GetExtension(msg2, pb.E_RComplex)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("[%s] Error getting extension: %v", test.name, err)
|
|
||||||
}
|
|
||||||
ext := e.([]*pb.ComplexExtension)
|
|
||||||
if ext == nil {
|
|
||||||
t.Fatalf("[%s] Invalid extension", test.name)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(ext, test.ext) {
|
|
||||||
t.Errorf("[%s] Wrong value for ComplexExtension: got: %v want: %v\n", test.name, ext, test.ext)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnmarshalRepeatingNonRepeatedExtension(t *testing.T) {
|
|
||||||
// We may see multiple instances of the same extension in the wire
|
|
||||||
// format. For example, the proto compiler may encode custom options in
|
|
||||||
// this way. Here, we verify that we merge the extensions together.
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
ext []*pb.ComplexExtension
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"two fields",
|
|
||||||
[]*pb.ComplexExtension{
|
|
||||||
{First: proto.Int32(7)},
|
|
||||||
{Second: proto.Int32(11)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"repeated field",
|
|
||||||
[]*pb.ComplexExtension{
|
|
||||||
{Third: []int32{1000}},
|
|
||||||
{Third: []int32{2000}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"two fields and repeated field",
|
|
||||||
[]*pb.ComplexExtension{
|
|
||||||
{Third: []int32{1000}},
|
|
||||||
{First: proto.Int32(9)},
|
|
||||||
{Second: proto.Int32(21)},
|
|
||||||
{Third: []int32{2000}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
var want pb.ComplexExtension
|
|
||||||
|
|
||||||
// Generate a serialized representation of a repeated extension
|
|
||||||
// by catenating bytes together.
|
|
||||||
for i, e := range test.ext {
|
|
||||||
// Merge to create the wanted proto.
|
|
||||||
proto.Merge(&want, e)
|
|
||||||
|
|
||||||
// serialize the message
|
|
||||||
msg := new(pb.OtherMessage)
|
|
||||||
err := proto.SetExtension(msg, pb.E_Complex, e)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("[%s] Error setting extension %d: %v", test.name, i, err)
|
|
||||||
}
|
|
||||||
b, err := proto.Marshal(msg)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("[%s] Error marshaling message %d: %v", test.name, i, err)
|
|
||||||
}
|
|
||||||
buf.Write(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmarshal and read the merged proto.
|
|
||||||
msg2 := new(pb.OtherMessage)
|
|
||||||
err := proto.Unmarshal(buf.Bytes(), msg2)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("[%s] Error unmarshaling message: %v", test.name, err)
|
|
||||||
}
|
|
||||||
e, err := proto.GetExtension(msg2, pb.E_Complex)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("[%s] Error getting extension: %v", test.name, err)
|
|
||||||
}
|
|
||||||
ext := e.(*pb.ComplexExtension)
|
|
||||||
if ext == nil {
|
|
||||||
t.Fatalf("[%s] Invalid extension", test.name)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(*ext, want) {
|
|
||||||
t.Errorf("[%s] Wrong value for ComplexExtension: got: %s want: %s\n", test.name, ext, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestClearAllExtensions(t *testing.T) {
|
|
||||||
// unregistered extension
|
|
||||||
desc := &proto.ExtensionDesc{
|
|
||||||
ExtendedType: (*pb.MyMessage)(nil),
|
|
||||||
ExtensionType: (*bool)(nil),
|
|
||||||
Field: 101010100,
|
|
||||||
Name: "emptyextension",
|
|
||||||
Tag: "varint,0,opt",
|
|
||||||
}
|
|
||||||
m := &pb.MyMessage{}
|
|
||||||
if proto.HasExtension(m, desc) {
|
|
||||||
t.Errorf("proto.HasExtension(%s): got true, want false", proto.MarshalTextString(m))
|
|
||||||
}
|
|
||||||
if err := proto.SetExtension(m, desc, proto.Bool(true)); err != nil {
|
|
||||||
t.Errorf("proto.SetExtension(m, desc, true): got error %q, want nil", err)
|
|
||||||
}
|
|
||||||
if !proto.HasExtension(m, desc) {
|
|
||||||
t.Errorf("proto.HasExtension(%s): got false, want true", proto.MarshalTextString(m))
|
|
||||||
}
|
|
||||||
proto.ClearAllExtensions(m)
|
|
||||||
if proto.HasExtension(m, desc) {
|
|
||||||
t.Errorf("proto.HasExtension(%s): got true, want false", proto.MarshalTextString(m))
|
|
||||||
}
|
|
||||||
}
|
|
125
vendor/github.com/golang/protobuf/proto/proto3_test.go
generated
vendored
125
vendor/github.com/golang/protobuf/proto/proto3_test.go
generated
vendored
|
@ -1,125 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2014 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
pb "github.com/golang/protobuf/proto/proto3_proto"
|
|
||||||
tpb "github.com/golang/protobuf/proto/testdata"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestProto3ZeroValues(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
desc string
|
|
||||||
m proto.Message
|
|
||||||
}{
|
|
||||||
{"zero message", &pb.Message{}},
|
|
||||||
{"empty bytes field", &pb.Message{Data: []byte{}}},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
b, err := proto.Marshal(test.m)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("%s: proto.Marshal: %v", test.desc, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if len(b) > 0 {
|
|
||||||
t.Errorf("%s: Encoding is non-empty: %q", test.desc, b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRoundTripProto3(t *testing.T) {
|
|
||||||
m := &pb.Message{
|
|
||||||
Name: "David", // (2 | 1<<3): 0x0a 0x05 "David"
|
|
||||||
Hilarity: pb.Message_PUNS, // (0 | 2<<3): 0x10 0x01
|
|
||||||
HeightInCm: 178, // (0 | 3<<3): 0x18 0xb2 0x01
|
|
||||||
Data: []byte("roboto"), // (2 | 4<<3): 0x20 0x06 "roboto"
|
|
||||||
ResultCount: 47, // (0 | 7<<3): 0x38 0x2f
|
|
||||||
TrueScotsman: true, // (0 | 8<<3): 0x40 0x01
|
|
||||||
Score: 8.1, // (5 | 9<<3): 0x4d <8.1>
|
|
||||||
|
|
||||||
Key: []uint64{1, 0xdeadbeef},
|
|
||||||
Nested: &pb.Nested{
|
|
||||||
Bunny: "Monty",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
t.Logf(" m: %v", m)
|
|
||||||
|
|
||||||
b, err := proto.Marshal(m)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("proto.Marshal: %v", err)
|
|
||||||
}
|
|
||||||
t.Logf(" b: %q", b)
|
|
||||||
|
|
||||||
m2 := new(pb.Message)
|
|
||||||
if err := proto.Unmarshal(b, m2); err != nil {
|
|
||||||
t.Fatalf("proto.Unmarshal: %v", err)
|
|
||||||
}
|
|
||||||
t.Logf("m2: %v", m2)
|
|
||||||
|
|
||||||
if !proto.Equal(m, m2) {
|
|
||||||
t.Errorf("proto.Equal returned false:\n m: %v\nm2: %v", m, m2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestProto3SetDefaults(t *testing.T) {
|
|
||||||
in := &pb.Message{
|
|
||||||
Terrain: map[string]*pb.Nested{
|
|
||||||
"meadow": new(pb.Nested),
|
|
||||||
},
|
|
||||||
Proto2Field: new(tpb.SubDefaults),
|
|
||||||
Proto2Value: map[string]*tpb.SubDefaults{
|
|
||||||
"badlands": new(tpb.SubDefaults),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
got := proto.Clone(in).(*pb.Message)
|
|
||||||
proto.SetDefaults(got)
|
|
||||||
|
|
||||||
// There are no defaults in proto3. Everything should be the zero value, but
|
|
||||||
// we need to remember to set defaults for nested proto2 messages.
|
|
||||||
want := &pb.Message{
|
|
||||||
Terrain: map[string]*pb.Nested{
|
|
||||||
"meadow": new(pb.Nested),
|
|
||||||
},
|
|
||||||
Proto2Field: &tpb.SubDefaults{N: proto.Int64(7)},
|
|
||||||
Proto2Value: map[string]*tpb.SubDefaults{
|
|
||||||
"badlands": &tpb.SubDefaults{N: proto.Int64(7)},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if !proto.Equal(got, want) {
|
|
||||||
t.Errorf("with in = %v\nproto.SetDefaults(in) =>\ngot %v\nwant %v", in, got, want)
|
|
||||||
}
|
|
||||||
}
|
|
164
vendor/github.com/golang/protobuf/proto/size_test.go
generated
vendored
164
vendor/github.com/golang/protobuf/proto/size_test.go
generated
vendored
|
@ -1,164 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2012 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
. "github.com/golang/protobuf/proto"
|
|
||||||
proto3pb "github.com/golang/protobuf/proto/proto3_proto"
|
|
||||||
pb "github.com/golang/protobuf/proto/testdata"
|
|
||||||
)
|
|
||||||
|
|
||||||
var messageWithExtension1 = &pb.MyMessage{Count: Int32(7)}
|
|
||||||
|
|
||||||
// messageWithExtension2 is in equal_test.go.
|
|
||||||
var messageWithExtension3 = &pb.MyMessage{Count: Int32(8)}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
if err := SetExtension(messageWithExtension1, pb.E_Ext_More, &pb.Ext{Data: String("Abbott")}); err != nil {
|
|
||||||
log.Panicf("SetExtension: %v", err)
|
|
||||||
}
|
|
||||||
if err := SetExtension(messageWithExtension3, pb.E_Ext_More, &pb.Ext{Data: String("Costello")}); err != nil {
|
|
||||||
log.Panicf("SetExtension: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Force messageWithExtension3 to have the extension encoded.
|
|
||||||
Marshal(messageWithExtension3)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
var SizeTests = []struct {
|
|
||||||
desc string
|
|
||||||
pb Message
|
|
||||||
}{
|
|
||||||
{"empty", &pb.OtherMessage{}},
|
|
||||||
// Basic types.
|
|
||||||
{"bool", &pb.Defaults{F_Bool: Bool(true)}},
|
|
||||||
{"int32", &pb.Defaults{F_Int32: Int32(12)}},
|
|
||||||
{"negative int32", &pb.Defaults{F_Int32: Int32(-1)}},
|
|
||||||
{"small int64", &pb.Defaults{F_Int64: Int64(1)}},
|
|
||||||
{"big int64", &pb.Defaults{F_Int64: Int64(1 << 20)}},
|
|
||||||
{"negative int64", &pb.Defaults{F_Int64: Int64(-1)}},
|
|
||||||
{"fixed32", &pb.Defaults{F_Fixed32: Uint32(71)}},
|
|
||||||
{"fixed64", &pb.Defaults{F_Fixed64: Uint64(72)}},
|
|
||||||
{"uint32", &pb.Defaults{F_Uint32: Uint32(123)}},
|
|
||||||
{"uint64", &pb.Defaults{F_Uint64: Uint64(124)}},
|
|
||||||
{"float", &pb.Defaults{F_Float: Float32(12.6)}},
|
|
||||||
{"double", &pb.Defaults{F_Double: Float64(13.9)}},
|
|
||||||
{"string", &pb.Defaults{F_String: String("niles")}},
|
|
||||||
{"bytes", &pb.Defaults{F_Bytes: []byte("wowsa")}},
|
|
||||||
{"bytes, empty", &pb.Defaults{F_Bytes: []byte{}}},
|
|
||||||
{"sint32", &pb.Defaults{F_Sint32: Int32(65)}},
|
|
||||||
{"sint64", &pb.Defaults{F_Sint64: Int64(67)}},
|
|
||||||
{"enum", &pb.Defaults{F_Enum: pb.Defaults_BLUE.Enum()}},
|
|
||||||
// Repeated.
|
|
||||||
{"empty repeated bool", &pb.MoreRepeated{Bools: []bool{}}},
|
|
||||||
{"repeated bool", &pb.MoreRepeated{Bools: []bool{false, true, true, false}}},
|
|
||||||
{"packed repeated bool", &pb.MoreRepeated{BoolsPacked: []bool{false, true, true, false, true, true, true}}},
|
|
||||||
{"repeated int32", &pb.MoreRepeated{Ints: []int32{1, 12203, 1729, -1}}},
|
|
||||||
{"repeated int32 packed", &pb.MoreRepeated{IntsPacked: []int32{1, 12203, 1729}}},
|
|
||||||
{"repeated int64 packed", &pb.MoreRepeated{Int64SPacked: []int64{
|
|
||||||
// Need enough large numbers to verify that the header is counting the number of bytes
|
|
||||||
// for the field, not the number of elements.
|
|
||||||
1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62,
|
|
||||||
1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62,
|
|
||||||
}}},
|
|
||||||
{"repeated string", &pb.MoreRepeated{Strings: []string{"r", "ken", "gri"}}},
|
|
||||||
{"repeated fixed", &pb.MoreRepeated{Fixeds: []uint32{1, 2, 3, 4}}},
|
|
||||||
// Nested.
|
|
||||||
{"nested", &pb.OldMessage{Nested: &pb.OldMessage_Nested{Name: String("whatever")}}},
|
|
||||||
{"group", &pb.GroupOld{G: &pb.GroupOld_G{X: Int32(12345)}}},
|
|
||||||
// Other things.
|
|
||||||
{"unrecognized", &pb.MoreRepeated{XXX_unrecognized: []byte{13<<3 | 0, 4}}},
|
|
||||||
{"extension (unencoded)", messageWithExtension1},
|
|
||||||
{"extension (encoded)", messageWithExtension3},
|
|
||||||
// proto3 message
|
|
||||||
{"proto3 empty", &proto3pb.Message{}},
|
|
||||||
{"proto3 bool", &proto3pb.Message{TrueScotsman: true}},
|
|
||||||
{"proto3 int64", &proto3pb.Message{ResultCount: 1}},
|
|
||||||
{"proto3 uint32", &proto3pb.Message{HeightInCm: 123}},
|
|
||||||
{"proto3 float", &proto3pb.Message{Score: 12.6}},
|
|
||||||
{"proto3 string", &proto3pb.Message{Name: "Snezana"}},
|
|
||||||
{"proto3 bytes", &proto3pb.Message{Data: []byte("wowsa")}},
|
|
||||||
{"proto3 bytes, empty", &proto3pb.Message{Data: []byte{}}},
|
|
||||||
{"proto3 enum", &proto3pb.Message{Hilarity: proto3pb.Message_PUNS}},
|
|
||||||
{"proto3 map field with empty bytes", &proto3pb.MessageWithMap{ByteMapping: map[bool][]byte{false: []byte{}}}},
|
|
||||||
|
|
||||||
{"map field", &pb.MessageWithMap{NameMapping: map[int32]string{1: "Rob", 7: "Andrew"}}},
|
|
||||||
{"map field with message", &pb.MessageWithMap{MsgMapping: map[int64]*pb.FloatingPoint{0x7001: &pb.FloatingPoint{F: Float64(2.0)}}}},
|
|
||||||
{"map field with bytes", &pb.MessageWithMap{ByteMapping: map[bool][]byte{true: []byte("this time for sure")}}},
|
|
||||||
{"map field with empty bytes", &pb.MessageWithMap{ByteMapping: map[bool][]byte{true: []byte{}}}},
|
|
||||||
|
|
||||||
{"map field with big entry", &pb.MessageWithMap{NameMapping: map[int32]string{8: strings.Repeat("x", 125)}}},
|
|
||||||
{"map field with big key and val", &pb.MessageWithMap{StrToStr: map[string]string{strings.Repeat("x", 70): strings.Repeat("y", 70)}}},
|
|
||||||
{"map field with big numeric key", &pb.MessageWithMap{NameMapping: map[int32]string{0xf00d: "om nom nom"}}},
|
|
||||||
|
|
||||||
{"oneof not set", &pb.Oneof{}},
|
|
||||||
{"oneof bool", &pb.Oneof{Union: &pb.Oneof_F_Bool{true}}},
|
|
||||||
{"oneof zero int32", &pb.Oneof{Union: &pb.Oneof_F_Int32{0}}},
|
|
||||||
{"oneof big int32", &pb.Oneof{Union: &pb.Oneof_F_Int32{1 << 20}}},
|
|
||||||
{"oneof int64", &pb.Oneof{Union: &pb.Oneof_F_Int64{42}}},
|
|
||||||
{"oneof fixed32", &pb.Oneof{Union: &pb.Oneof_F_Fixed32{43}}},
|
|
||||||
{"oneof fixed64", &pb.Oneof{Union: &pb.Oneof_F_Fixed64{44}}},
|
|
||||||
{"oneof uint32", &pb.Oneof{Union: &pb.Oneof_F_Uint32{45}}},
|
|
||||||
{"oneof uint64", &pb.Oneof{Union: &pb.Oneof_F_Uint64{46}}},
|
|
||||||
{"oneof float", &pb.Oneof{Union: &pb.Oneof_F_Float{47.1}}},
|
|
||||||
{"oneof double", &pb.Oneof{Union: &pb.Oneof_F_Double{48.9}}},
|
|
||||||
{"oneof string", &pb.Oneof{Union: &pb.Oneof_F_String{"Rhythmic Fman"}}},
|
|
||||||
{"oneof bytes", &pb.Oneof{Union: &pb.Oneof_F_Bytes{[]byte("let go")}}},
|
|
||||||
{"oneof sint32", &pb.Oneof{Union: &pb.Oneof_F_Sint32{50}}},
|
|
||||||
{"oneof sint64", &pb.Oneof{Union: &pb.Oneof_F_Sint64{51}}},
|
|
||||||
{"oneof enum", &pb.Oneof{Union: &pb.Oneof_F_Enum{pb.MyMessage_BLUE}}},
|
|
||||||
{"message for oneof", &pb.GoTestField{Label: String("k"), Type: String("v")}},
|
|
||||||
{"oneof message", &pb.Oneof{Union: &pb.Oneof_F_Message{&pb.GoTestField{Label: String("k"), Type: String("v")}}}},
|
|
||||||
{"oneof group", &pb.Oneof{Union: &pb.Oneof_FGroup{&pb.Oneof_F_Group{X: Int32(52)}}}},
|
|
||||||
{"oneof largest tag", &pb.Oneof{Union: &pb.Oneof_F_Largest_Tag{1}}},
|
|
||||||
{"multiple oneofs", &pb.Oneof{Union: &pb.Oneof_F_Int32{1}, Tormato: &pb.Oneof_Value{2}}},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSize(t *testing.T) {
|
|
||||||
for _, tc := range SizeTests {
|
|
||||||
size := Size(tc.pb)
|
|
||||||
b, err := Marshal(tc.pb)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("%v: Marshal failed: %v", tc.desc, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if size != len(b) {
|
|
||||||
t.Errorf("%v: Size(%v) = %d, want %d", tc.desc, tc.pb, size, len(b))
|
|
||||||
t.Logf("%v: bytes: %#v", tc.desc, b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
573
vendor/github.com/golang/protobuf/proto/text_parser_test.go
generated
vendored
573
vendor/github.com/golang/protobuf/proto/text_parser_test.go
generated
vendored
|
@ -1,573 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
. "github.com/golang/protobuf/proto"
|
|
||||||
proto3pb "github.com/golang/protobuf/proto/proto3_proto"
|
|
||||||
. "github.com/golang/protobuf/proto/testdata"
|
|
||||||
)
|
|
||||||
|
|
||||||
type UnmarshalTextTest struct {
|
|
||||||
in string
|
|
||||||
err string // if "", no error expected
|
|
||||||
out *MyMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildExtStructTest(text string) UnmarshalTextTest {
|
|
||||||
msg := &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
}
|
|
||||||
SetExtension(msg, E_Ext_More, &Ext{
|
|
||||||
Data: String("Hello, world!"),
|
|
||||||
})
|
|
||||||
return UnmarshalTextTest{in: text, out: msg}
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildExtDataTest(text string) UnmarshalTextTest {
|
|
||||||
msg := &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
}
|
|
||||||
SetExtension(msg, E_Ext_Text, String("Hello, world!"))
|
|
||||||
SetExtension(msg, E_Ext_Number, Int32(1729))
|
|
||||||
return UnmarshalTextTest{in: text, out: msg}
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildExtRepStringTest(text string) UnmarshalTextTest {
|
|
||||||
msg := &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
}
|
|
||||||
if err := SetExtension(msg, E_Greeting, []string{"bula", "hola"}); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return UnmarshalTextTest{in: text, out: msg}
|
|
||||||
}
|
|
||||||
|
|
||||||
var unMarshalTextTests = []UnmarshalTextTest{
|
|
||||||
// Basic
|
|
||||||
{
|
|
||||||
in: " count:42\n name:\"Dave\" ",
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Name: String("Dave"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Empty quoted string
|
|
||||||
{
|
|
||||||
in: `count:42 name:""`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Name: String(""),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Quoted string concatenation with double quotes
|
|
||||||
{
|
|
||||||
in: `count:42 name: "My name is "` + "\n" + `"elsewhere"`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Name: String("My name is elsewhere"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Quoted string concatenation with single quotes
|
|
||||||
{
|
|
||||||
in: "count:42 name: 'My name is '\n'elsewhere'",
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Name: String("My name is elsewhere"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Quoted string concatenations with mixed quotes
|
|
||||||
{
|
|
||||||
in: "count:42 name: 'My name is '\n\"elsewhere\"",
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Name: String("My name is elsewhere"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
in: "count:42 name: \"My name is \"\n'elsewhere'",
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Name: String("My name is elsewhere"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Quoted string with escaped apostrophe
|
|
||||||
{
|
|
||||||
in: `count:42 name: "HOLIDAY - New Year\'s Day"`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Name: String("HOLIDAY - New Year's Day"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Quoted string with single quote
|
|
||||||
{
|
|
||||||
in: `count:42 name: 'Roger "The Ramster" Ramjet'`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Name: String(`Roger "The Ramster" Ramjet`),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Quoted string with all the accepted special characters from the C++ test
|
|
||||||
{
|
|
||||||
in: `count:42 name: ` + "\"\\\"A string with \\' characters \\n and \\r newlines and \\t tabs and \\001 slashes \\\\ and multiple spaces\"",
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Name: String("\"A string with ' characters \n and \r newlines and \t tabs and \001 slashes \\ and multiple spaces"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Quoted string with quoted backslash
|
|
||||||
{
|
|
||||||
in: `count:42 name: "\\'xyz"`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Name: String(`\'xyz`),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Quoted string with UTF-8 bytes.
|
|
||||||
{
|
|
||||||
in: "count:42 name: '\303\277\302\201\xAB'",
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Name: String("\303\277\302\201\xAB"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Bad quoted string
|
|
||||||
{
|
|
||||||
in: `inner: < host: "\0" >` + "\n",
|
|
||||||
err: `line 1.15: invalid quoted string "\0": \0 requires 2 following digits`,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Number too large for int64
|
|
||||||
{
|
|
||||||
in: "count: 1 others { key: 123456789012345678901 }",
|
|
||||||
err: "line 1.23: invalid int64: 123456789012345678901",
|
|
||||||
},
|
|
||||||
|
|
||||||
// Number too large for int32
|
|
||||||
{
|
|
||||||
in: "count: 1234567890123",
|
|
||||||
err: "line 1.7: invalid int32: 1234567890123",
|
|
||||||
},
|
|
||||||
|
|
||||||
// Number in hexadecimal
|
|
||||||
{
|
|
||||||
in: "count: 0x2beef",
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(0x2beef),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Number in octal
|
|
||||||
{
|
|
||||||
in: "count: 024601",
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(024601),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Floating point number with "f" suffix
|
|
||||||
{
|
|
||||||
in: "count: 4 others:< weight: 17.0f >",
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(4),
|
|
||||||
Others: []*OtherMessage{
|
|
||||||
{
|
|
||||||
Weight: Float32(17),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Floating point positive infinity
|
|
||||||
{
|
|
||||||
in: "count: 4 bigfloat: inf",
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(4),
|
|
||||||
Bigfloat: Float64(math.Inf(1)),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Floating point negative infinity
|
|
||||||
{
|
|
||||||
in: "count: 4 bigfloat: -inf",
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(4),
|
|
||||||
Bigfloat: Float64(math.Inf(-1)),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Number too large for float32
|
|
||||||
{
|
|
||||||
in: "others:< weight: 12345678901234567890123456789012345678901234567890 >",
|
|
||||||
err: "line 1.17: invalid float32: 12345678901234567890123456789012345678901234567890",
|
|
||||||
},
|
|
||||||
|
|
||||||
// Number posing as a quoted string
|
|
||||||
{
|
|
||||||
in: `inner: < host: 12 >` + "\n",
|
|
||||||
err: `line 1.15: invalid string: 12`,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Quoted string posing as int32
|
|
||||||
{
|
|
||||||
in: `count: "12"`,
|
|
||||||
err: `line 1.7: invalid int32: "12"`,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Quoted string posing a float32
|
|
||||||
{
|
|
||||||
in: `others:< weight: "17.4" >`,
|
|
||||||
err: `line 1.17: invalid float32: "17.4"`,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Enum
|
|
||||||
{
|
|
||||||
in: `count:42 bikeshed: BLUE`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Bikeshed: MyMessage_BLUE.Enum(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Repeated field
|
|
||||||
{
|
|
||||||
in: `count:42 pet: "horsey" pet:"bunny"`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Pet: []string{"horsey", "bunny"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Repeated field with list notation
|
|
||||||
{
|
|
||||||
in: `count:42 pet: ["horsey", "bunny"]`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Pet: []string{"horsey", "bunny"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Repeated message with/without colon and <>/{}
|
|
||||||
{
|
|
||||||
in: `count:42 others:{} others{} others:<> others:{}`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Others: []*OtherMessage{
|
|
||||||
{},
|
|
||||||
{},
|
|
||||||
{},
|
|
||||||
{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Missing colon for inner message
|
|
||||||
{
|
|
||||||
in: `count:42 inner < host: "cauchy.syd" >`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Inner: &InnerMessage{
|
|
||||||
Host: String("cauchy.syd"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Missing colon for string field
|
|
||||||
{
|
|
||||||
in: `name "Dave"`,
|
|
||||||
err: `line 1.5: expected ':', found "\"Dave\""`,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Missing colon for int32 field
|
|
||||||
{
|
|
||||||
in: `count 42`,
|
|
||||||
err: `line 1.6: expected ':', found "42"`,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Missing required field
|
|
||||||
{
|
|
||||||
in: `name: "Pawel"`,
|
|
||||||
err: `proto: required field "testdata.MyMessage.count" not set`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Name: String("Pawel"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Missing required field in a required submessage
|
|
||||||
{
|
|
||||||
in: `count: 42 we_must_go_deeper < leo_finally_won_an_oscar <> >`,
|
|
||||||
err: `proto: required field "testdata.InnerMessage.host" not set`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
WeMustGoDeeper: &RequiredInnerMessage{LeoFinallyWonAnOscar: &InnerMessage{}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Repeated non-repeated field
|
|
||||||
{
|
|
||||||
in: `name: "Rob" name: "Russ"`,
|
|
||||||
err: `line 1.12: non-repeated field "name" was repeated`,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Group
|
|
||||||
{
|
|
||||||
in: `count: 17 SomeGroup { group_field: 12 }`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(17),
|
|
||||||
Somegroup: &MyMessage_SomeGroup{
|
|
||||||
GroupField: Int32(12),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Semicolon between fields
|
|
||||||
{
|
|
||||||
in: `count:3;name:"Calvin"`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(3),
|
|
||||||
Name: String("Calvin"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Comma between fields
|
|
||||||
{
|
|
||||||
in: `count:4,name:"Ezekiel"`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(4),
|
|
||||||
Name: String("Ezekiel"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Extension
|
|
||||||
buildExtStructTest(`count: 42 [testdata.Ext.more]:<data:"Hello, world!" >`),
|
|
||||||
buildExtStructTest(`count: 42 [testdata.Ext.more] {data:"Hello, world!"}`),
|
|
||||||
buildExtDataTest(`count: 42 [testdata.Ext.text]:"Hello, world!" [testdata.Ext.number]:1729`),
|
|
||||||
buildExtRepStringTest(`count: 42 [testdata.greeting]:"bula" [testdata.greeting]:"hola"`),
|
|
||||||
|
|
||||||
// Big all-in-one
|
|
||||||
{
|
|
||||||
in: "count:42 # Meaning\n" +
|
|
||||||
`name:"Dave" ` +
|
|
||||||
`quote:"\"I didn't want to go.\"" ` +
|
|
||||||
`pet:"bunny" ` +
|
|
||||||
`pet:"kitty" ` +
|
|
||||||
`pet:"horsey" ` +
|
|
||||||
`inner:<` +
|
|
||||||
` host:"footrest.syd" ` +
|
|
||||||
` port:7001 ` +
|
|
||||||
` connected:true ` +
|
|
||||||
`> ` +
|
|
||||||
`others:<` +
|
|
||||||
` key:3735928559 ` +
|
|
||||||
` value:"\x01A\a\f" ` +
|
|
||||||
`> ` +
|
|
||||||
`others:<` +
|
|
||||||
" weight:58.9 # Atomic weight of Co\n" +
|
|
||||||
` inner:<` +
|
|
||||||
` host:"lesha.mtv" ` +
|
|
||||||
` port:8002 ` +
|
|
||||||
` >` +
|
|
||||||
`>`,
|
|
||||||
out: &MyMessage{
|
|
||||||
Count: Int32(42),
|
|
||||||
Name: String("Dave"),
|
|
||||||
Quote: String(`"I didn't want to go."`),
|
|
||||||
Pet: []string{"bunny", "kitty", "horsey"},
|
|
||||||
Inner: &InnerMessage{
|
|
||||||
Host: String("footrest.syd"),
|
|
||||||
Port: Int32(7001),
|
|
||||||
Connected: Bool(true),
|
|
||||||
},
|
|
||||||
Others: []*OtherMessage{
|
|
||||||
{
|
|
||||||
Key: Int64(3735928559),
|
|
||||||
Value: []byte{0x1, 'A', '\a', '\f'},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Weight: Float32(58.9),
|
|
||||||
Inner: &InnerMessage{
|
|
||||||
Host: String("lesha.mtv"),
|
|
||||||
Port: Int32(8002),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnmarshalText(t *testing.T) {
|
|
||||||
for i, test := range unMarshalTextTests {
|
|
||||||
pb := new(MyMessage)
|
|
||||||
err := UnmarshalText(test.in, pb)
|
|
||||||
if test.err == "" {
|
|
||||||
// We don't expect failure.
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Test %d: Unexpected error: %v", i, err)
|
|
||||||
} else if !reflect.DeepEqual(pb, test.out) {
|
|
||||||
t.Errorf("Test %d: Incorrect populated \nHave: %v\nWant: %v",
|
|
||||||
i, pb, test.out)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// We do expect failure.
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Test %d: Didn't get expected error: %v", i, test.err)
|
|
||||||
} else if err.Error() != test.err {
|
|
||||||
t.Errorf("Test %d: Incorrect error.\nHave: %v\nWant: %v",
|
|
||||||
i, err.Error(), test.err)
|
|
||||||
} else if _, ok := err.(*RequiredNotSetError); ok && test.out != nil && !reflect.DeepEqual(pb, test.out) {
|
|
||||||
t.Errorf("Test %d: Incorrect populated \nHave: %v\nWant: %v",
|
|
||||||
i, pb, test.out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnmarshalTextCustomMessage(t *testing.T) {
|
|
||||||
msg := &textMessage{}
|
|
||||||
if err := UnmarshalText("custom", msg); err != nil {
|
|
||||||
t.Errorf("Unexpected error from custom unmarshal: %v", err)
|
|
||||||
}
|
|
||||||
if UnmarshalText("not custom", msg) == nil {
|
|
||||||
t.Errorf("Didn't get expected error from custom unmarshal")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Regression test; this caused a panic.
|
|
||||||
func TestRepeatedEnum(t *testing.T) {
|
|
||||||
pb := new(RepeatedEnum)
|
|
||||||
if err := UnmarshalText("color: RED", pb); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
exp := &RepeatedEnum{
|
|
||||||
Color: []RepeatedEnum_Color{RepeatedEnum_RED},
|
|
||||||
}
|
|
||||||
if !Equal(pb, exp) {
|
|
||||||
t.Errorf("Incorrect populated \nHave: %v\nWant: %v", pb, exp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestProto3TextParsing(t *testing.T) {
|
|
||||||
m := new(proto3pb.Message)
|
|
||||||
const in = `name: "Wallace" true_scotsman: true`
|
|
||||||
want := &proto3pb.Message{
|
|
||||||
Name: "Wallace",
|
|
||||||
TrueScotsman: true,
|
|
||||||
}
|
|
||||||
if err := UnmarshalText(in, m); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if !Equal(m, want) {
|
|
||||||
t.Errorf("\n got %v\nwant %v", m, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMapParsing(t *testing.T) {
|
|
||||||
m := new(MessageWithMap)
|
|
||||||
const in = `name_mapping:<key:1234 value:"Feist"> name_mapping:<key:1 value:"Beatles">` +
|
|
||||||
`msg_mapping:<key:-4, value:<f: 2.0>,>` + // separating commas are okay
|
|
||||||
`msg_mapping<key:-2 value<f: 4.0>>` + // no colon after "value"
|
|
||||||
`msg_mapping:<value:<f: 5.0>>` + // omitted key
|
|
||||||
`msg_mapping:<key:1>` + // omitted value
|
|
||||||
`byte_mapping:<key:true value:"so be it">` +
|
|
||||||
`byte_mapping:<>` // omitted key and value
|
|
||||||
want := &MessageWithMap{
|
|
||||||
NameMapping: map[int32]string{
|
|
||||||
1: "Beatles",
|
|
||||||
1234: "Feist",
|
|
||||||
},
|
|
||||||
MsgMapping: map[int64]*FloatingPoint{
|
|
||||||
-4: {F: Float64(2.0)},
|
|
||||||
-2: {F: Float64(4.0)},
|
|
||||||
0: {F: Float64(5.0)},
|
|
||||||
1: nil,
|
|
||||||
},
|
|
||||||
ByteMapping: map[bool][]byte{
|
|
||||||
false: nil,
|
|
||||||
true: []byte("so be it"),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if err := UnmarshalText(in, m); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if !Equal(m, want) {
|
|
||||||
t.Errorf("\n got %v\nwant %v", m, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOneofParsing(t *testing.T) {
|
|
||||||
const in = `name:"Shrek"`
|
|
||||||
m := new(Communique)
|
|
||||||
want := &Communique{Union: &Communique_Name{"Shrek"}}
|
|
||||||
if err := UnmarshalText(in, m); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if !Equal(m, want) {
|
|
||||||
t.Errorf("\n got %v\nwant %v", m, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var benchInput string
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
benchInput = "count: 4\n"
|
|
||||||
for i := 0; i < 1000; i++ {
|
|
||||||
benchInput += "pet: \"fido\"\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check it is valid input.
|
|
||||||
pb := new(MyMessage)
|
|
||||||
err := UnmarshalText(benchInput, pb)
|
|
||||||
if err != nil {
|
|
||||||
panic("Bad benchmark input: " + err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkUnmarshalText(b *testing.B) {
|
|
||||||
pb := new(MyMessage)
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
UnmarshalText(benchInput, pb)
|
|
||||||
}
|
|
||||||
b.SetBytes(int64(len(benchInput)))
|
|
||||||
}
|
|
474
vendor/github.com/golang/protobuf/proto/text_test.go
generated
vendored
474
vendor/github.com/golang/protobuf/proto/text_test.go
generated
vendored
|
@ -1,474 +0,0 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
|
||||||
//
|
|
||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// https://github.com/golang/protobuf
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
package proto_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"io/ioutil"
|
|
||||||
"math"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
|
|
||||||
proto3pb "github.com/golang/protobuf/proto/proto3_proto"
|
|
||||||
pb "github.com/golang/protobuf/proto/testdata"
|
|
||||||
)
|
|
||||||
|
|
||||||
// textMessage implements the methods that allow it to marshal and unmarshal
|
|
||||||
// itself as text.
|
|
||||||
type textMessage struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*textMessage) MarshalText() ([]byte, error) {
|
|
||||||
return []byte("custom"), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*textMessage) UnmarshalText(bytes []byte) error {
|
|
||||||
if string(bytes) != "custom" {
|
|
||||||
return errors.New("expected 'custom'")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*textMessage) Reset() {}
|
|
||||||
func (*textMessage) String() string { return "" }
|
|
||||||
func (*textMessage) ProtoMessage() {}
|
|
||||||
|
|
||||||
func newTestMessage() *pb.MyMessage {
|
|
||||||
msg := &pb.MyMessage{
|
|
||||||
Count: proto.Int32(42),
|
|
||||||
Name: proto.String("Dave"),
|
|
||||||
Quote: proto.String(`"I didn't want to go."`),
|
|
||||||
Pet: []string{"bunny", "kitty", "horsey"},
|
|
||||||
Inner: &pb.InnerMessage{
|
|
||||||
Host: proto.String("footrest.syd"),
|
|
||||||
Port: proto.Int32(7001),
|
|
||||||
Connected: proto.Bool(true),
|
|
||||||
},
|
|
||||||
Others: []*pb.OtherMessage{
|
|
||||||
{
|
|
||||||
Key: proto.Int64(0xdeadbeef),
|
|
||||||
Value: []byte{1, 65, 7, 12},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Weight: proto.Float32(6.022),
|
|
||||||
Inner: &pb.InnerMessage{
|
|
||||||
Host: proto.String("lesha.mtv"),
|
|
||||||
Port: proto.Int32(8002),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Bikeshed: pb.MyMessage_BLUE.Enum(),
|
|
||||||
Somegroup: &pb.MyMessage_SomeGroup{
|
|
||||||
GroupField: proto.Int32(8),
|
|
||||||
},
|
|
||||||
// One normally wouldn't do this.
|
|
||||||
// This is an undeclared tag 13, as a varint (wire type 0) with value 4.
|
|
||||||
XXX_unrecognized: []byte{13<<3 | 0, 4},
|
|
||||||
}
|
|
||||||
ext := &pb.Ext{
|
|
||||||
Data: proto.String("Big gobs for big rats"),
|
|
||||||
}
|
|
||||||
if err := proto.SetExtension(msg, pb.E_Ext_More, ext); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
greetings := []string{"adg", "easy", "cow"}
|
|
||||||
if err := proto.SetExtension(msg, pb.E_Greeting, greetings); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add an unknown extension. We marshal a pb.Ext, and fake the ID.
|
|
||||||
b, err := proto.Marshal(&pb.Ext{Data: proto.String("3G skiing")})
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
b = append(proto.EncodeVarint(201<<3|proto.WireBytes), b...)
|
|
||||||
proto.SetRawExtension(msg, 201, b)
|
|
||||||
|
|
||||||
// Extensions can be plain fields, too, so let's test that.
|
|
||||||
b = append(proto.EncodeVarint(202<<3|proto.WireVarint), 19)
|
|
||||||
proto.SetRawExtension(msg, 202, b)
|
|
||||||
|
|
||||||
return msg
|
|
||||||
}
|
|
||||||
|
|
||||||
const text = `count: 42
|
|
||||||
name: "Dave"
|
|
||||||
quote: "\"I didn't want to go.\""
|
|
||||||
pet: "bunny"
|
|
||||||
pet: "kitty"
|
|
||||||
pet: "horsey"
|
|
||||||
inner: <
|
|
||||||
host: "footrest.syd"
|
|
||||||
port: 7001
|
|
||||||
connected: true
|
|
||||||
>
|
|
||||||
others: <
|
|
||||||
key: 3735928559
|
|
||||||
value: "\001A\007\014"
|
|
||||||
>
|
|
||||||
others: <
|
|
||||||
weight: 6.022
|
|
||||||
inner: <
|
|
||||||
host: "lesha.mtv"
|
|
||||||
port: 8002
|
|
||||||
>
|
|
||||||
>
|
|
||||||
bikeshed: BLUE
|
|
||||||
SomeGroup {
|
|
||||||
group_field: 8
|
|
||||||
}
|
|
||||||
/* 2 unknown bytes */
|
|
||||||
13: 4
|
|
||||||
[testdata.Ext.more]: <
|
|
||||||
data: "Big gobs for big rats"
|
|
||||||
>
|
|
||||||
[testdata.greeting]: "adg"
|
|
||||||
[testdata.greeting]: "easy"
|
|
||||||
[testdata.greeting]: "cow"
|
|
||||||
/* 13 unknown bytes */
|
|
||||||
201: "\t3G skiing"
|
|
||||||
/* 3 unknown bytes */
|
|
||||||
202: 19
|
|
||||||
`
|
|
||||||
|
|
||||||
func TestMarshalText(t *testing.T) {
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
if err := proto.MarshalText(buf, newTestMessage()); err != nil {
|
|
||||||
t.Fatalf("proto.MarshalText: %v", err)
|
|
||||||
}
|
|
||||||
s := buf.String()
|
|
||||||
if s != text {
|
|
||||||
t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", s, text)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMarshalTextCustomMessage(t *testing.T) {
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
if err := proto.MarshalText(buf, &textMessage{}); err != nil {
|
|
||||||
t.Fatalf("proto.MarshalText: %v", err)
|
|
||||||
}
|
|
||||||
s := buf.String()
|
|
||||||
if s != "custom" {
|
|
||||||
t.Errorf("Got %q, expected %q", s, "custom")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func TestMarshalTextNil(t *testing.T) {
|
|
||||||
want := "<nil>"
|
|
||||||
tests := []proto.Message{nil, (*pb.MyMessage)(nil)}
|
|
||||||
for i, test := range tests {
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
if err := proto.MarshalText(buf, test); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if got := buf.String(); got != want {
|
|
||||||
t.Errorf("%d: got %q want %q", i, got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMarshalTextUnknownEnum(t *testing.T) {
|
|
||||||
// The Color enum only specifies values 0-2.
|
|
||||||
m := &pb.MyMessage{Bikeshed: pb.MyMessage_Color(3).Enum()}
|
|
||||||
got := m.String()
|
|
||||||
const want = `bikeshed:3 `
|
|
||||||
if got != want {
|
|
||||||
t.Errorf("\n got %q\nwant %q", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTextOneof(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
m proto.Message
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
// zero message
|
|
||||||
{&pb.Communique{}, ``},
|
|
||||||
// scalar field
|
|
||||||
{&pb.Communique{Union: &pb.Communique_Number{4}}, `number:4`},
|
|
||||||
// message field
|
|
||||||
{&pb.Communique{Union: &pb.Communique_Msg{
|
|
||||||
&pb.Strings{StringField: proto.String("why hello!")},
|
|
||||||
}}, `msg:<string_field:"why hello!" >`},
|
|
||||||
// bad oneof (should not panic)
|
|
||||||
{&pb.Communique{Union: &pb.Communique_Msg{nil}}, `msg:/* nil */`},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
got := strings.TrimSpace(test.m.String())
|
|
||||||
if got != test.want {
|
|
||||||
t.Errorf("\n got %s\nwant %s", got, test.want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkMarshalTextBuffered(b *testing.B) {
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
m := newTestMessage()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
buf.Reset()
|
|
||||||
proto.MarshalText(buf, m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkMarshalTextUnbuffered(b *testing.B) {
|
|
||||||
w := ioutil.Discard
|
|
||||||
m := newTestMessage()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
proto.MarshalText(w, m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func compact(src string) string {
|
|
||||||
// s/[ \n]+/ /g; s/ $//;
|
|
||||||
dst := make([]byte, len(src))
|
|
||||||
space, comment := false, false
|
|
||||||
j := 0
|
|
||||||
for i := 0; i < len(src); i++ {
|
|
||||||
if strings.HasPrefix(src[i:], "/*") {
|
|
||||||
comment = true
|
|
||||||
i++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if comment && strings.HasPrefix(src[i:], "*/") {
|
|
||||||
comment = false
|
|
||||||
i++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if comment {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
c := src[i]
|
|
||||||
if c == ' ' || c == '\n' {
|
|
||||||
space = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if j > 0 && (dst[j-1] == ':' || dst[j-1] == '<' || dst[j-1] == '{') {
|
|
||||||
space = false
|
|
||||||
}
|
|
||||||
if c == '{' {
|
|
||||||
space = false
|
|
||||||
}
|
|
||||||
if space {
|
|
||||||
dst[j] = ' '
|
|
||||||
j++
|
|
||||||
space = false
|
|
||||||
}
|
|
||||||
dst[j] = c
|
|
||||||
j++
|
|
||||||
}
|
|
||||||
if space {
|
|
||||||
dst[j] = ' '
|
|
||||||
j++
|
|
||||||
}
|
|
||||||
return string(dst[0:j])
|
|
||||||
}
|
|
||||||
|
|
||||||
var compactText = compact(text)
|
|
||||||
|
|
||||||
func TestCompactText(t *testing.T) {
|
|
||||||
s := proto.CompactTextString(newTestMessage())
|
|
||||||
if s != compactText {
|
|
||||||
t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v\n===\n", s, compactText)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStringEscaping(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
in *pb.Strings
|
|
||||||
out string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
// Test data from C++ test (TextFormatTest.StringEscape).
|
|
||||||
// Single divergence: we don't escape apostrophes.
|
|
||||||
&pb.Strings{StringField: proto.String("\"A string with ' characters \n and \r newlines and \t tabs and \001 slashes \\ and multiple spaces")},
|
|
||||||
"string_field: \"\\\"A string with ' characters \\n and \\r newlines and \\t tabs and \\001 slashes \\\\ and multiple spaces\"\n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Test data from the same C++ test.
|
|
||||||
&pb.Strings{StringField: proto.String("\350\260\267\346\255\214")},
|
|
||||||
"string_field: \"\\350\\260\\267\\346\\255\\214\"\n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Some UTF-8.
|
|
||||||
&pb.Strings{StringField: proto.String("\x00\x01\xff\x81")},
|
|
||||||
`string_field: "\000\001\377\201"` + "\n",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tc := range testCases {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
if err := proto.MarshalText(&buf, tc.in); err != nil {
|
|
||||||
t.Errorf("proto.MarsalText: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
s := buf.String()
|
|
||||||
if s != tc.out {
|
|
||||||
t.Errorf("#%d: Got:\n%s\nExpected:\n%s\n", i, s, tc.out)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check round-trip.
|
|
||||||
pb := new(pb.Strings)
|
|
||||||
if err := proto.UnmarshalText(s, pb); err != nil {
|
|
||||||
t.Errorf("#%d: UnmarshalText: %v", i, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !proto.Equal(pb, tc.in) {
|
|
||||||
t.Errorf("#%d: Round-trip failed:\nstart: %v\n end: %v", i, tc.in, pb)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A limitedWriter accepts some output before it fails.
|
|
||||||
// This is a proxy for something like a nearly-full or imminently-failing disk,
|
|
||||||
// or a network connection that is about to die.
|
|
||||||
type limitedWriter struct {
|
|
||||||
b bytes.Buffer
|
|
||||||
limit int
|
|
||||||
}
|
|
||||||
|
|
||||||
var outOfSpace = errors.New("proto: insufficient space")
|
|
||||||
|
|
||||||
func (w *limitedWriter) Write(p []byte) (n int, err error) {
|
|
||||||
var avail = w.limit - w.b.Len()
|
|
||||||
if avail <= 0 {
|
|
||||||
return 0, outOfSpace
|
|
||||||
}
|
|
||||||
if len(p) <= avail {
|
|
||||||
return w.b.Write(p)
|
|
||||||
}
|
|
||||||
n, _ = w.b.Write(p[:avail])
|
|
||||||
return n, outOfSpace
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMarshalTextFailing(t *testing.T) {
|
|
||||||
// Try lots of different sizes to exercise more error code-paths.
|
|
||||||
for lim := 0; lim < len(text); lim++ {
|
|
||||||
buf := new(limitedWriter)
|
|
||||||
buf.limit = lim
|
|
||||||
err := proto.MarshalText(buf, newTestMessage())
|
|
||||||
// We expect a certain error, but also some partial results in the buffer.
|
|
||||||
if err != outOfSpace {
|
|
||||||
t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", err, outOfSpace)
|
|
||||||
}
|
|
||||||
s := buf.b.String()
|
|
||||||
x := text[:buf.limit]
|
|
||||||
if s != x {
|
|
||||||
t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", s, x)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFloats(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
f float64
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
{0, "0"},
|
|
||||||
{4.7, "4.7"},
|
|
||||||
{math.Inf(1), "inf"},
|
|
||||||
{math.Inf(-1), "-inf"},
|
|
||||||
{math.NaN(), "nan"},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
msg := &pb.FloatingPoint{F: &test.f}
|
|
||||||
got := strings.TrimSpace(msg.String())
|
|
||||||
want := `f:` + test.want
|
|
||||||
if got != want {
|
|
||||||
t.Errorf("f=%f: got %q, want %q", test.f, got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRepeatedNilText(t *testing.T) {
|
|
||||||
m := &pb.MessageList{
|
|
||||||
Message: []*pb.MessageList_Message{
|
|
||||||
nil,
|
|
||||||
&pb.MessageList_Message{
|
|
||||||
Name: proto.String("Horse"),
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
want := `Message <nil>
|
|
||||||
Message {
|
|
||||||
name: "Horse"
|
|
||||||
}
|
|
||||||
Message <nil>
|
|
||||||
`
|
|
||||||
if s := proto.MarshalTextString(m); s != want {
|
|
||||||
t.Errorf(" got: %s\nwant: %s", s, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestProto3Text(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
m proto.Message
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
// zero message
|
|
||||||
{&proto3pb.Message{}, ``},
|
|
||||||
// zero message except for an empty byte slice
|
|
||||||
{&proto3pb.Message{Data: []byte{}}, ``},
|
|
||||||
// trivial case
|
|
||||||
{&proto3pb.Message{Name: "Rob", HeightInCm: 175}, `name:"Rob" height_in_cm:175`},
|
|
||||||
// empty map
|
|
||||||
{&pb.MessageWithMap{}, ``},
|
|
||||||
// non-empty map; map format is the same as a repeated struct,
|
|
||||||
// and they are sorted by key (numerically for numeric keys).
|
|
||||||
{
|
|
||||||
&pb.MessageWithMap{NameMapping: map[int32]string{
|
|
||||||
-1: "Negatory",
|
|
||||||
7: "Lucky",
|
|
||||||
1234: "Feist",
|
|
||||||
6345789: "Otis",
|
|
||||||
}},
|
|
||||||
`name_mapping:<key:-1 value:"Negatory" > ` +
|
|
||||||
`name_mapping:<key:7 value:"Lucky" > ` +
|
|
||||||
`name_mapping:<key:1234 value:"Feist" > ` +
|
|
||||||
`name_mapping:<key:6345789 value:"Otis" >`,
|
|
||||||
},
|
|
||||||
// map with nil value; not well-defined, but we shouldn't crash
|
|
||||||
{
|
|
||||||
&pb.MessageWithMap{MsgMapping: map[int64]*pb.FloatingPoint{7: nil}},
|
|
||||||
`msg_mapping:<key:7 >`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
got := strings.TrimSpace(test.m.String())
|
|
||||||
if got != test.want {
|
|
||||||
t.Errorf("\n got %s\nwant %s", got, test.want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
// Go support for Protocol Buffers - Google's data interchange format
|
||||||
//
|
//
|
||||||
// Copyright 2014 The Go Authors. All rights reserved.
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
// https://github.com/golang/protobuf
|
// https://github.com/golang/protobuf
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -29,38 +29,23 @@
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
package proto
|
/*
|
||||||
|
A plugin for the Google protocol buffer compiler to generate Go code.
|
||||||
|
Run it by building this program and putting it in your path with the name
|
||||||
|
protoc-gen-go
|
||||||
|
That word 'go' at the end becomes part of the option string set for the
|
||||||
|
protocol compiler, so once the protocol compiler (protoc) is installed
|
||||||
|
you can run
|
||||||
|
protoc --go_out=output_directory input_directory/file.proto
|
||||||
|
to generate Go bindings for the protocol defined by file.proto.
|
||||||
|
With that input, the output will be written to
|
||||||
|
output_directory/file.pb.go
|
||||||
|
|
||||||
import (
|
The generated code is documented in the package comment for
|
||||||
"bytes"
|
the library.
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestUnmarshalMessageSetWithDuplicate(t *testing.T) {
|
See the README and documentation for protocol buffers to learn more:
|
||||||
// Check that a repeated message set entry will be concatenated.
|
https://developers.google.com/protocol-buffers/
|
||||||
in := &messageSet{
|
|
||||||
Item: []*_MessageSet_Item{
|
|
||||||
{TypeId: Int32(12345), Message: []byte("hoo")},
|
|
||||||
{TypeId: Int32(12345), Message: []byte("hah")},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
b, err := Marshal(in)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Marshal: %v", err)
|
|
||||||
}
|
|
||||||
t.Logf("Marshaled bytes: %q", b)
|
|
||||||
|
|
||||||
var extensions XXX_InternalExtensions
|
*/
|
||||||
if err := UnmarshalMessageSet(b, &extensions); err != nil {
|
package documentation
|
||||||
t.Fatalf("UnmarshalMessageSet: %v", err)
|
|
||||||
}
|
|
||||||
ext, ok := extensions.p.extensionMap[12345]
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("Didn't retrieve extension 12345; map is %v", extensions.p.extensionMap)
|
|
||||||
}
|
|
||||||
// Skip wire type/field number and length varints.
|
|
||||||
got := skipVarint(skipVarint(ext.enc))
|
|
||||||
if want := []byte("hoohah"); !bytes.Equal(got, want) {
|
|
||||||
t.Errorf("Combined extension is %q, want %q", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Go support for Protocol Buffers - Google's data interchange format
|
// Go support for Protocol Buffers - Google's data interchange format
|
||||||
//
|
//
|
||||||
// Copyright 2012 The Go Authors. All rights reserved.
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
// https://github.com/golang/protobuf
|
// https://github.com/golang/protobuf
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -29,35 +29,6 @@
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
package proto
|
package main
|
||||||
|
|
||||||
import (
|
import _ "github.com/golang/protobuf/protoc-gen-go/grpc"
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// This is a separate file and package from size_test.go because that one uses
|
|
||||||
// generated messages and thus may not be in package proto without having a circular
|
|
||||||
// dependency, whereas this file tests unexported details of size.go.
|
|
||||||
|
|
||||||
func TestVarintSize(t *testing.T) {
|
|
||||||
// Check the edge cases carefully.
|
|
||||||
testCases := []struct {
|
|
||||||
n uint64
|
|
||||||
size int
|
|
||||||
}{
|
|
||||||
{0, 1},
|
|
||||||
{1, 1},
|
|
||||||
{127, 1},
|
|
||||||
{128, 2},
|
|
||||||
{16383, 2},
|
|
||||||
{16384, 3},
|
|
||||||
{1<<63 - 1, 9},
|
|
||||||
{1 << 63, 10},
|
|
||||||
}
|
|
||||||
for _, tc := range testCases {
|
|
||||||
size := sizeVarint(tc.n)
|
|
||||||
if size != tc.size {
|
|
||||||
t.Errorf("sizeVarint(%d) = %d, want %d", tc.n, size, tc.size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
98
vendor/github.com/golang/protobuf/protoc-gen-go/main.go
generated
vendored
Normal file
98
vendor/github.com/golang/protobuf/protoc-gen-go/main.go
generated
vendored
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
// Go support for Protocol Buffers - Google's data interchange format
|
||||||
|
//
|
||||||
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
// https://github.com/golang/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
// protoc-gen-go is a plugin for the Google protocol buffer compiler to generate
|
||||||
|
// Go code. Run it by building this program and putting it in your path with
|
||||||
|
// the name
|
||||||
|
// protoc-gen-go
|
||||||
|
// That word 'go' at the end becomes part of the option string set for the
|
||||||
|
// protocol compiler, so once the protocol compiler (protoc) is installed
|
||||||
|
// you can run
|
||||||
|
// protoc --go_out=output_directory input_directory/file.proto
|
||||||
|
// to generate Go bindings for the protocol defined by file.proto.
|
||||||
|
// With that input, the output will be written to
|
||||||
|
// output_directory/file.pb.go
|
||||||
|
//
|
||||||
|
// The generated code is documented in the package comment for
|
||||||
|
// the library.
|
||||||
|
//
|
||||||
|
// See the README and documentation for protocol buffers to learn more:
|
||||||
|
// https://developers.google.com/protocol-buffers/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
"github.com/golang/protobuf/protoc-gen-go/generator"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Begin by allocating a generator. The request and response structures are stored there
|
||||||
|
// so we can do error handling easily - the response structure contains the field to
|
||||||
|
// report failure.
|
||||||
|
g := generator.New()
|
||||||
|
|
||||||
|
data, err := ioutil.ReadAll(os.Stdin)
|
||||||
|
if err != nil {
|
||||||
|
g.Error(err, "reading input")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := proto.Unmarshal(data, g.Request); err != nil {
|
||||||
|
g.Error(err, "parsing input proto")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(g.Request.FileToGenerate) == 0 {
|
||||||
|
g.Fail("no files to generate")
|
||||||
|
}
|
||||||
|
|
||||||
|
g.CommandLineParameters(g.Request.GetParameter())
|
||||||
|
|
||||||
|
// Create a wrapped version of the Descriptors and EnumDescriptors that
|
||||||
|
// point to the file that defines them.
|
||||||
|
g.WrapTypes()
|
||||||
|
|
||||||
|
g.SetPackageNames()
|
||||||
|
g.BuildTypeNameMap()
|
||||||
|
|
||||||
|
g.GenerateAllFiles()
|
||||||
|
|
||||||
|
// Send back the results.
|
||||||
|
data, err = proto.Marshal(g.Response)
|
||||||
|
if err != nil {
|
||||||
|
g.Error(err, "failed to marshal output proto")
|
||||||
|
}
|
||||||
|
_, err = os.Stdout.Write(data)
|
||||||
|
if err != nil {
|
||||||
|
g.Error(err, "failed to write output proto")
|
||||||
|
}
|
||||||
|
}
|
18
vendor/github.com/gorilla/context/.travis.yml
generated
vendored
18
vendor/github.com/gorilla/context/.travis.yml
generated
vendored
|
@ -1,18 +0,0 @@
|
||||||
language: go
|
|
||||||
sudo: false
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- go: 1.3
|
|
||||||
- go: 1.4
|
|
||||||
- go: 1.5
|
|
||||||
- go: 1.6
|
|
||||||
- go: tip
|
|
||||||
allow_failures:
|
|
||||||
- go: tip
|
|
||||||
|
|
||||||
script:
|
|
||||||
- go get -t -v ./...
|
|
||||||
- diff -u <(echo -n) <(gofmt -d .)
|
|
||||||
- go vet $(go list ./... | grep -v /vendor/)
|
|
||||||
- go test -v -race ./...
|
|
7
vendor/github.com/gorilla/context/README.md
generated
vendored
7
vendor/github.com/gorilla/context/README.md
generated
vendored
|
@ -1,7 +0,0 @@
|
||||||
context
|
|
||||||
=======
|
|
||||||
[![Build Status](https://travis-ci.org/gorilla/context.png?branch=master)](https://travis-ci.org/gorilla/context)
|
|
||||||
|
|
||||||
gorilla/context is a general purpose registry for global request variables.
|
|
||||||
|
|
||||||
Read the full documentation here: http://www.gorillatoolkit.org/pkg/context
|
|
161
vendor/github.com/gorilla/context/context_test.go
generated
vendored
161
vendor/github.com/gorilla/context/context_test.go
generated
vendored
|
@ -1,161 +0,0 @@
|
||||||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package context
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
type keyType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
key1 keyType = iota
|
|
||||||
key2
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestContext(t *testing.T) {
|
|
||||||
assertEqual := func(val interface{}, exp interface{}) {
|
|
||||||
if val != exp {
|
|
||||||
t.Errorf("Expected %v, got %v.", exp, val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
r, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
|
|
||||||
emptyR, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
|
|
||||||
|
|
||||||
// Get()
|
|
||||||
assertEqual(Get(r, key1), nil)
|
|
||||||
|
|
||||||
// Set()
|
|
||||||
Set(r, key1, "1")
|
|
||||||
assertEqual(Get(r, key1), "1")
|
|
||||||
assertEqual(len(data[r]), 1)
|
|
||||||
|
|
||||||
Set(r, key2, "2")
|
|
||||||
assertEqual(Get(r, key2), "2")
|
|
||||||
assertEqual(len(data[r]), 2)
|
|
||||||
|
|
||||||
//GetOk
|
|
||||||
value, ok := GetOk(r, key1)
|
|
||||||
assertEqual(value, "1")
|
|
||||||
assertEqual(ok, true)
|
|
||||||
|
|
||||||
value, ok = GetOk(r, "not exists")
|
|
||||||
assertEqual(value, nil)
|
|
||||||
assertEqual(ok, false)
|
|
||||||
|
|
||||||
Set(r, "nil value", nil)
|
|
||||||
value, ok = GetOk(r, "nil value")
|
|
||||||
assertEqual(value, nil)
|
|
||||||
assertEqual(ok, true)
|
|
||||||
|
|
||||||
// GetAll()
|
|
||||||
values := GetAll(r)
|
|
||||||
assertEqual(len(values), 3)
|
|
||||||
|
|
||||||
// GetAll() for empty request
|
|
||||||
values = GetAll(emptyR)
|
|
||||||
if values != nil {
|
|
||||||
t.Error("GetAll didn't return nil value for invalid request")
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAllOk()
|
|
||||||
values, ok = GetAllOk(r)
|
|
||||||
assertEqual(len(values), 3)
|
|
||||||
assertEqual(ok, true)
|
|
||||||
|
|
||||||
// GetAllOk() for empty request
|
|
||||||
values, ok = GetAllOk(emptyR)
|
|
||||||
assertEqual(len(values), 0)
|
|
||||||
assertEqual(ok, false)
|
|
||||||
|
|
||||||
// Delete()
|
|
||||||
Delete(r, key1)
|
|
||||||
assertEqual(Get(r, key1), nil)
|
|
||||||
assertEqual(len(data[r]), 2)
|
|
||||||
|
|
||||||
Delete(r, key2)
|
|
||||||
assertEqual(Get(r, key2), nil)
|
|
||||||
assertEqual(len(data[r]), 1)
|
|
||||||
|
|
||||||
// Clear()
|
|
||||||
Clear(r)
|
|
||||||
assertEqual(len(data), 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parallelReader(r *http.Request, key string, iterations int, wait, done chan struct{}) {
|
|
||||||
<-wait
|
|
||||||
for i := 0; i < iterations; i++ {
|
|
||||||
Get(r, key)
|
|
||||||
}
|
|
||||||
done <- struct{}{}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func parallelWriter(r *http.Request, key, value string, iterations int, wait, done chan struct{}) {
|
|
||||||
<-wait
|
|
||||||
for i := 0; i < iterations; i++ {
|
|
||||||
Set(r, key, value)
|
|
||||||
}
|
|
||||||
done <- struct{}{}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func benchmarkMutex(b *testing.B, numReaders, numWriters, iterations int) {
|
|
||||||
|
|
||||||
b.StopTimer()
|
|
||||||
r, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
|
|
||||||
done := make(chan struct{})
|
|
||||||
b.StartTimer()
|
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
wait := make(chan struct{})
|
|
||||||
|
|
||||||
for i := 0; i < numReaders; i++ {
|
|
||||||
go parallelReader(r, "test", iterations, wait, done)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < numWriters; i++ {
|
|
||||||
go parallelWriter(r, "test", "123", iterations, wait, done)
|
|
||||||
}
|
|
||||||
|
|
||||||
close(wait)
|
|
||||||
|
|
||||||
for i := 0; i < numReaders+numWriters; i++ {
|
|
||||||
<-done
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkMutexSameReadWrite1(b *testing.B) {
|
|
||||||
benchmarkMutex(b, 1, 1, 32)
|
|
||||||
}
|
|
||||||
func BenchmarkMutexSameReadWrite2(b *testing.B) {
|
|
||||||
benchmarkMutex(b, 2, 2, 32)
|
|
||||||
}
|
|
||||||
func BenchmarkMutexSameReadWrite4(b *testing.B) {
|
|
||||||
benchmarkMutex(b, 4, 4, 32)
|
|
||||||
}
|
|
||||||
func BenchmarkMutex1(b *testing.B) {
|
|
||||||
benchmarkMutex(b, 2, 8, 32)
|
|
||||||
}
|
|
||||||
func BenchmarkMutex2(b *testing.B) {
|
|
||||||
benchmarkMutex(b, 16, 4, 64)
|
|
||||||
}
|
|
||||||
func BenchmarkMutex3(b *testing.B) {
|
|
||||||
benchmarkMutex(b, 1, 2, 128)
|
|
||||||
}
|
|
||||||
func BenchmarkMutex4(b *testing.B) {
|
|
||||||
benchmarkMutex(b, 128, 32, 256)
|
|
||||||
}
|
|
||||||
func BenchmarkMutex5(b *testing.B) {
|
|
||||||
benchmarkMutex(b, 1024, 2048, 64)
|
|
||||||
}
|
|
||||||
func BenchmarkMutex6(b *testing.B) {
|
|
||||||
benchmarkMutex(b, 2048, 1024, 512)
|
|
||||||
}
|
|
20
vendor/github.com/gorilla/mux/.travis.yml
generated
vendored
20
vendor/github.com/gorilla/mux/.travis.yml
generated
vendored
|
@ -1,20 +0,0 @@
|
||||||
language: go
|
|
||||||
sudo: false
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- go: 1.2
|
|
||||||
- go: 1.3
|
|
||||||
- go: 1.4
|
|
||||||
- go: 1.5
|
|
||||||
- go: 1.6
|
|
||||||
- go: tip
|
|
||||||
|
|
||||||
install:
|
|
||||||
- # Skip
|
|
||||||
|
|
||||||
script:
|
|
||||||
- go get -t -v ./...
|
|
||||||
- diff -u <(echo -n) <(gofmt -d .)
|
|
||||||
- go tool vet .
|
|
||||||
- go test -v -race ./...
|
|
242
vendor/github.com/gorilla/mux/README.md
generated
vendored
242
vendor/github.com/gorilla/mux/README.md
generated
vendored
|
@ -1,242 +0,0 @@
|
||||||
mux
|
|
||||||
===
|
|
||||||
[![GoDoc](https://godoc.org/github.com/gorilla/mux?status.svg)](https://godoc.org/github.com/gorilla/mux)
|
|
||||||
[![Build Status](https://travis-ci.org/gorilla/mux.svg?branch=master)](https://travis-ci.org/gorilla/mux)
|
|
||||||
|
|
||||||
http://www.gorillatoolkit.org/pkg/mux
|
|
||||||
|
|
||||||
Package `gorilla/mux` implements a request router and dispatcher.
|
|
||||||
|
|
||||||
The name mux stands for "HTTP request multiplexer". Like the standard `http.ServeMux`, `mux.Router` matches incoming requests against a list of registered routes and calls a handler for the route that matches the URL or other conditions. The main features are:
|
|
||||||
|
|
||||||
* Requests can be matched based on URL host, path, path prefix, schemes, header and query values, HTTP methods or using custom matchers.
|
|
||||||
* URL hosts and paths can have variables with an optional regular expression.
|
|
||||||
* Registered URLs can be built, or "reversed", which helps maintaining references to resources.
|
|
||||||
* Routes can be used as subrouters: nested routes are only tested if the parent route matches. This is useful to define groups of routes that share common conditions like a host, a path prefix or other repeated attributes. As a bonus, this optimizes request matching.
|
|
||||||
* It implements the `http.Handler` interface so it is compatible with the standard `http.ServeMux`.
|
|
||||||
|
|
||||||
Let's start registering a couple of URL paths and handlers:
|
|
||||||
|
|
||||||
```go
|
|
||||||
func main() {
|
|
||||||
r := mux.NewRouter()
|
|
||||||
r.HandleFunc("/", HomeHandler)
|
|
||||||
r.HandleFunc("/products", ProductsHandler)
|
|
||||||
r.HandleFunc("/articles", ArticlesHandler)
|
|
||||||
http.Handle("/", r)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Here we register three routes mapping URL paths to handlers. This is equivalent to how `http.HandleFunc()` works: if an incoming request URL matches one of the paths, the corresponding handler is called passing (`http.ResponseWriter`, `*http.Request`) as parameters.
|
|
||||||
|
|
||||||
Paths can have variables. They are defined using the format `{name}` or `{name:pattern}`. If a regular expression pattern is not defined, the matched variable will be anything until the next slash. For example:
|
|
||||||
|
|
||||||
```go
|
|
||||||
r := mux.NewRouter()
|
|
||||||
r.HandleFunc("/products/{key}", ProductHandler)
|
|
||||||
r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler)
|
|
||||||
r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)
|
|
||||||
```
|
|
||||||
|
|
||||||
The names are used to create a map of route variables which can be retrieved calling `mux.Vars()`:
|
|
||||||
|
|
||||||
```go
|
|
||||||
vars := mux.Vars(request)
|
|
||||||
category := vars["category"]
|
|
||||||
```
|
|
||||||
|
|
||||||
And this is all you need to know about the basic usage. More advanced options are explained below.
|
|
||||||
|
|
||||||
Routes can also be restricted to a domain or subdomain. Just define a host pattern to be matched. They can also have variables:
|
|
||||||
|
|
||||||
```go
|
|
||||||
r := mux.NewRouter()
|
|
||||||
// Only matches if domain is "www.example.com".
|
|
||||||
r.Host("www.example.com")
|
|
||||||
// Matches a dynamic subdomain.
|
|
||||||
r.Host("{subdomain:[a-z]+}.domain.com")
|
|
||||||
```
|
|
||||||
|
|
||||||
There are several other matchers that can be added. To match path prefixes:
|
|
||||||
|
|
||||||
```go
|
|
||||||
r.PathPrefix("/products/")
|
|
||||||
```
|
|
||||||
|
|
||||||
...or HTTP methods:
|
|
||||||
|
|
||||||
```go
|
|
||||||
r.Methods("GET", "POST")
|
|
||||||
```
|
|
||||||
|
|
||||||
...or URL schemes:
|
|
||||||
|
|
||||||
```go
|
|
||||||
r.Schemes("https")
|
|
||||||
```
|
|
||||||
|
|
||||||
...or header values:
|
|
||||||
|
|
||||||
```go
|
|
||||||
r.Headers("X-Requested-With", "XMLHttpRequest")
|
|
||||||
```
|
|
||||||
|
|
||||||
...or query values:
|
|
||||||
|
|
||||||
```go
|
|
||||||
r.Queries("key", "value")
|
|
||||||
```
|
|
||||||
|
|
||||||
...or to use a custom matcher function:
|
|
||||||
|
|
||||||
```go
|
|
||||||
r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool {
|
|
||||||
return r.ProtoMajor == 0
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
...and finally, it is possible to combine several matchers in a single route:
|
|
||||||
|
|
||||||
```go
|
|
||||||
r.HandleFunc("/products", ProductsHandler).
|
|
||||||
Host("www.example.com").
|
|
||||||
Methods("GET").
|
|
||||||
Schemes("http")
|
|
||||||
```
|
|
||||||
|
|
||||||
Setting the same matching conditions again and again can be boring, so we have a way to group several routes that share the same requirements. We call it "subrouting".
|
|
||||||
|
|
||||||
For example, let's say we have several URLs that should only match when the host is `www.example.com`. Create a route for that host and get a "subrouter" from it:
|
|
||||||
|
|
||||||
```go
|
|
||||||
r := mux.NewRouter()
|
|
||||||
s := r.Host("www.example.com").Subrouter()
|
|
||||||
```
|
|
||||||
|
|
||||||
Then register routes in the subrouter:
|
|
||||||
|
|
||||||
```go
|
|
||||||
s.HandleFunc("/products/", ProductsHandler)
|
|
||||||
s.HandleFunc("/products/{key}", ProductHandler)
|
|
||||||
s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
|
|
||||||
```
|
|
||||||
|
|
||||||
The three URL paths we registered above will only be tested if the domain is `www.example.com`, because the subrouter is tested first. This is not only convenient, but also optimizes request matching. You can create subrouters combining any attribute matchers accepted by a route.
|
|
||||||
|
|
||||||
Subrouters can be used to create domain or path "namespaces": you define subrouters in a central place and then parts of the app can register its paths relatively to a given subrouter.
|
|
||||||
|
|
||||||
There's one more thing about subroutes. When a subrouter has a path prefix, the inner routes use it as base for their paths:
|
|
||||||
|
|
||||||
```go
|
|
||||||
r := mux.NewRouter()
|
|
||||||
s := r.PathPrefix("/products").Subrouter()
|
|
||||||
// "/products/"
|
|
||||||
s.HandleFunc("/", ProductsHandler)
|
|
||||||
// "/products/{key}/"
|
|
||||||
s.HandleFunc("/{key}/", ProductHandler)
|
|
||||||
// "/products/{key}/details"
|
|
||||||
s.HandleFunc("/{key}/details", ProductDetailsHandler)
|
|
||||||
```
|
|
||||||
|
|
||||||
Now let's see how to build registered URLs.
|
|
||||||
|
|
||||||
Routes can be named. All routes that define a name can have their URLs built, or "reversed". We define a name calling `Name()` on a route. For example:
|
|
||||||
|
|
||||||
```go
|
|
||||||
r := mux.NewRouter()
|
|
||||||
r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
|
|
||||||
Name("article")
|
|
||||||
```
|
|
||||||
|
|
||||||
To build a URL, get the route and call the `URL()` method, passing a sequence of key/value pairs for the route variables. For the previous route, we would do:
|
|
||||||
|
|
||||||
```go
|
|
||||||
url, err := r.Get("article").URL("category", "technology", "id", "42")
|
|
||||||
```
|
|
||||||
|
|
||||||
...and the result will be a `url.URL` with the following path:
|
|
||||||
|
|
||||||
```
|
|
||||||
"/articles/technology/42"
|
|
||||||
```
|
|
||||||
|
|
||||||
This also works for host variables:
|
|
||||||
|
|
||||||
```go
|
|
||||||
r := mux.NewRouter()
|
|
||||||
r.Host("{subdomain}.domain.com").
|
|
||||||
Path("/articles/{category}/{id:[0-9]+}").
|
|
||||||
HandlerFunc(ArticleHandler).
|
|
||||||
Name("article")
|
|
||||||
|
|
||||||
// url.String() will be "http://news.domain.com/articles/technology/42"
|
|
||||||
url, err := r.Get("article").URL("subdomain", "news",
|
|
||||||
"category", "technology",
|
|
||||||
"id", "42")
|
|
||||||
```
|
|
||||||
|
|
||||||
All variables defined in the route are required, and their values must conform to the corresponding patterns. These requirements guarantee that a generated URL will always match a registered route -- the only exception is for explicitly defined "build-only" routes which never match.
|
|
||||||
|
|
||||||
Regex support also exists for matching Headers within a route. For example, we could do:
|
|
||||||
|
|
||||||
```go
|
|
||||||
r.HeadersRegexp("Content-Type", "application/(text|json)")
|
|
||||||
```
|
|
||||||
|
|
||||||
...and the route will match both requests with a Content-Type of `application/json` as well as `application/text`
|
|
||||||
|
|
||||||
There's also a way to build only the URL host or path for a route: use the methods `URLHost()` or `URLPath()` instead. For the previous route, we would do:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// "http://news.domain.com/"
|
|
||||||
host, err := r.Get("article").URLHost("subdomain", "news")
|
|
||||||
|
|
||||||
// "/articles/technology/42"
|
|
||||||
path, err := r.Get("article").URLPath("category", "technology", "id", "42")
|
|
||||||
```
|
|
||||||
|
|
||||||
And if you use subrouters, host and path defined separately can be built as well:
|
|
||||||
|
|
||||||
```go
|
|
||||||
r := mux.NewRouter()
|
|
||||||
s := r.Host("{subdomain}.domain.com").Subrouter()
|
|
||||||
s.Path("/articles/{category}/{id:[0-9]+}").
|
|
||||||
HandlerFunc(ArticleHandler).
|
|
||||||
Name("article")
|
|
||||||
|
|
||||||
// "http://news.domain.com/articles/technology/42"
|
|
||||||
url, err := r.Get("article").URL("subdomain", "news",
|
|
||||||
"category", "technology",
|
|
||||||
"id", "42")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Full Example
|
|
||||||
|
|
||||||
Here's a complete, runnable example of a small `mux` based server:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"log"
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
)
|
|
||||||
|
|
||||||
func YourHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.Write([]byte("Gorilla!\n"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
r := mux.NewRouter()
|
|
||||||
// Routes consist of a path and a handler function.
|
|
||||||
r.HandleFunc("/", YourHandler)
|
|
||||||
|
|
||||||
// Bind to a port and pass our router in
|
|
||||||
log.Fatal(http.ListenAndServe(":8000", r))
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
BSD licensed. See the LICENSE file for details.
|
|
49
vendor/github.com/gorilla/mux/bench_test.go
generated
vendored
49
vendor/github.com/gorilla/mux/bench_test.go
generated
vendored
|
@ -1,49 +0,0 @@
|
||||||
// Copyright 2012 The Gorilla Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package mux
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func BenchmarkMux(b *testing.B) {
|
|
||||||
router := new(Router)
|
|
||||||
handler := func(w http.ResponseWriter, r *http.Request) {}
|
|
||||||
router.HandleFunc("/v1/{v1}", handler)
|
|
||||||
|
|
||||||
request, _ := http.NewRequest("GET", "/v1/anything", nil)
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
router.ServeHTTP(nil, request)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkMuxAlternativeInRegexp(b *testing.B) {
|
|
||||||
router := new(Router)
|
|
||||||
handler := func(w http.ResponseWriter, r *http.Request) {}
|
|
||||||
router.HandleFunc("/v1/{v1:(a|b)}", handler)
|
|
||||||
|
|
||||||
requestA, _ := http.NewRequest("GET", "/v1/a", nil)
|
|
||||||
requestB, _ := http.NewRequest("GET", "/v1/b", nil)
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
router.ServeHTTP(nil, requestA)
|
|
||||||
router.ServeHTTP(nil, requestB)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkManyPathVariables(b *testing.B) {
|
|
||||||
router := new(Router)
|
|
||||||
handler := func(w http.ResponseWriter, r *http.Request) {}
|
|
||||||
router.HandleFunc("/v1/{v1}/{v2}/{v3}/{v4}/{v5}", handler)
|
|
||||||
|
|
||||||
matchingRequest, _ := http.NewRequest("GET", "/v1/1/2/3/4/5", nil)
|
|
||||||
notMatchingRequest, _ := http.NewRequest("GET", "/v1/1/2/3/4", nil)
|
|
||||||
recorder := httptest.NewRecorder()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
router.ServeHTTP(nil, matchingRequest)
|
|
||||||
router.ServeHTTP(recorder, notMatchingRequest)
|
|
||||||
}
|
|
||||||
}
|
|
40
vendor/github.com/gorilla/mux/context_gorilla_test.go
generated
vendored
40
vendor/github.com/gorilla/mux/context_gorilla_test.go
generated
vendored
|
@ -1,40 +0,0 @@
|
||||||
// +build !go1.7
|
|
||||||
|
|
||||||
package mux
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/gorilla/context"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Tests that the context is cleared or not cleared properly depending on
|
|
||||||
// the configuration of the router
|
|
||||||
func TestKeepContext(t *testing.T) {
|
|
||||||
func1 := func(w http.ResponseWriter, r *http.Request) {}
|
|
||||||
|
|
||||||
r := NewRouter()
|
|
||||||
r.HandleFunc("/", func1).Name("func1")
|
|
||||||
|
|
||||||
req, _ := http.NewRequest("GET", "http://localhost/", nil)
|
|
||||||
context.Set(req, "t", 1)
|
|
||||||
|
|
||||||
res := new(http.ResponseWriter)
|
|
||||||
r.ServeHTTP(*res, req)
|
|
||||||
|
|
||||||
if _, ok := context.GetOk(req, "t"); ok {
|
|
||||||
t.Error("Context should have been cleared at end of request")
|
|
||||||
}
|
|
||||||
|
|
||||||
r.KeepContext = true
|
|
||||||
|
|
||||||
req, _ = http.NewRequest("GET", "http://localhost/", nil)
|
|
||||||
context.Set(req, "t", 1)
|
|
||||||
|
|
||||||
r.ServeHTTP(*res, req)
|
|
||||||
if _, ok := context.GetOk(req, "t"); !ok {
|
|
||||||
t.Error("Context should NOT have been cleared at end of request")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
32
vendor/github.com/gorilla/mux/context_native_test.go
generated
vendored
32
vendor/github.com/gorilla/mux/context_native_test.go
generated
vendored
|
@ -1,32 +0,0 @@
|
||||||
// +build go1.7
|
|
||||||
|
|
||||||
package mux
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net/http"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNativeContextMiddleware(t *testing.T) {
|
|
||||||
withTimeout := func(h http.Handler) http.Handler {
|
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ctx, cancel := context.WithTimeout(r.Context(), time.Minute)
|
|
||||||
defer cancel()
|
|
||||||
h.ServeHTTP(w, r.WithContext(ctx))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
r := NewRouter()
|
|
||||||
r.Handle("/path/{foo}", withTimeout(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
vars := Vars(r)
|
|
||||||
if vars["foo"] != "bar" {
|
|
||||||
t.Fatal("Expected foo var to be set")
|
|
||||||
}
|
|
||||||
})))
|
|
||||||
|
|
||||||
rec := NewRecorder()
|
|
||||||
req := newRequest("GET", "/path/bar")
|
|
||||||
r.ServeHTTP(rec, req)
|
|
||||||
}
|
|
1439
vendor/github.com/gorilla/mux/mux_test.go
generated
vendored
1439
vendor/github.com/gorilla/mux/mux_test.go
generated
vendored
File diff suppressed because it is too large
Load diff
710
vendor/github.com/gorilla/mux/old_test.go
generated
vendored
710
vendor/github.com/gorilla/mux/old_test.go
generated
vendored
|
@ -1,710 +0,0 @@
|
||||||
// Old tests ported to Go1. This is a mess. Want to drop it one day.
|
|
||||||
|
|
||||||
// Copyright 2011 Gorilla Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package mux
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"net/http"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// ResponseRecorder
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// ResponseRecorder is an implementation of http.ResponseWriter that
|
|
||||||
// records its mutations for later inspection in tests.
|
|
||||||
type ResponseRecorder struct {
|
|
||||||
Code int // the HTTP response code from WriteHeader
|
|
||||||
HeaderMap http.Header // the HTTP response headers
|
|
||||||
Body *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to
|
|
||||||
Flushed bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewRecorder returns an initialized ResponseRecorder.
|
|
||||||
func NewRecorder() *ResponseRecorder {
|
|
||||||
return &ResponseRecorder{
|
|
||||||
HeaderMap: make(http.Header),
|
|
||||||
Body: new(bytes.Buffer),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Header returns the response headers.
|
|
||||||
func (rw *ResponseRecorder) Header() http.Header {
|
|
||||||
return rw.HeaderMap
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write always succeeds and writes to rw.Body, if not nil.
|
|
||||||
func (rw *ResponseRecorder) Write(buf []byte) (int, error) {
|
|
||||||
if rw.Body != nil {
|
|
||||||
rw.Body.Write(buf)
|
|
||||||
}
|
|
||||||
if rw.Code == 0 {
|
|
||||||
rw.Code = http.StatusOK
|
|
||||||
}
|
|
||||||
return len(buf), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteHeader sets rw.Code.
|
|
||||||
func (rw *ResponseRecorder) WriteHeader(code int) {
|
|
||||||
rw.Code = code
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flush sets rw.Flushed to true.
|
|
||||||
func (rw *ResponseRecorder) Flush() {
|
|
||||||
rw.Flushed = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
func TestRouteMatchers(t *testing.T) {
|
|
||||||
var scheme, host, path, query, method string
|
|
||||||
var headers map[string]string
|
|
||||||
var resultVars map[bool]map[string]string
|
|
||||||
|
|
||||||
router := NewRouter()
|
|
||||||
router.NewRoute().Host("{var1}.google.com").
|
|
||||||
Path("/{var2:[a-z]+}/{var3:[0-9]+}").
|
|
||||||
Queries("foo", "bar").
|
|
||||||
Methods("GET").
|
|
||||||
Schemes("https").
|
|
||||||
Headers("x-requested-with", "XMLHttpRequest")
|
|
||||||
router.NewRoute().Host("www.{var4}.com").
|
|
||||||
PathPrefix("/foo/{var5:[a-z]+}/{var6:[0-9]+}").
|
|
||||||
Queries("baz", "ding").
|
|
||||||
Methods("POST").
|
|
||||||
Schemes("http").
|
|
||||||
Headers("Content-Type", "application/json")
|
|
||||||
|
|
||||||
reset := func() {
|
|
||||||
// Everything match.
|
|
||||||
scheme = "https"
|
|
||||||
host = "www.google.com"
|
|
||||||
path = "/product/42"
|
|
||||||
query = "?foo=bar"
|
|
||||||
method = "GET"
|
|
||||||
headers = map[string]string{"X-Requested-With": "XMLHttpRequest"}
|
|
||||||
resultVars = map[bool]map[string]string{
|
|
||||||
true: {"var1": "www", "var2": "product", "var3": "42"},
|
|
||||||
false: {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
reset2 := func() {
|
|
||||||
// Everything match.
|
|
||||||
scheme = "http"
|
|
||||||
host = "www.google.com"
|
|
||||||
path = "/foo/product/42/path/that/is/ignored"
|
|
||||||
query = "?baz=ding"
|
|
||||||
method = "POST"
|
|
||||||
headers = map[string]string{"Content-Type": "application/json"}
|
|
||||||
resultVars = map[bool]map[string]string{
|
|
||||||
true: {"var4": "google", "var5": "product", "var6": "42"},
|
|
||||||
false: {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match := func(shouldMatch bool) {
|
|
||||||
url := scheme + "://" + host + path + query
|
|
||||||
request, _ := http.NewRequest(method, url, nil)
|
|
||||||
for key, value := range headers {
|
|
||||||
request.Header.Add(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
var routeMatch RouteMatch
|
|
||||||
matched := router.Match(request, &routeMatch)
|
|
||||||
if matched != shouldMatch {
|
|
||||||
// Need better messages. :)
|
|
||||||
if matched {
|
|
||||||
t.Errorf("Should match.")
|
|
||||||
} else {
|
|
||||||
t.Errorf("Should not match.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if matched {
|
|
||||||
currentRoute := routeMatch.Route
|
|
||||||
if currentRoute == nil {
|
|
||||||
t.Errorf("Expected a current route.")
|
|
||||||
}
|
|
||||||
vars := routeMatch.Vars
|
|
||||||
expectedVars := resultVars[shouldMatch]
|
|
||||||
if len(vars) != len(expectedVars) {
|
|
||||||
t.Errorf("Expected vars: %v Got: %v.", expectedVars, vars)
|
|
||||||
}
|
|
||||||
for name, value := range vars {
|
|
||||||
if expectedVars[name] != value {
|
|
||||||
t.Errorf("Expected vars: %v Got: %v.", expectedVars, vars)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1st route --------------------------------------------------------------
|
|
||||||
|
|
||||||
// Everything match.
|
|
||||||
reset()
|
|
||||||
match(true)
|
|
||||||
|
|
||||||
// Scheme doesn't match.
|
|
||||||
reset()
|
|
||||||
scheme = "http"
|
|
||||||
match(false)
|
|
||||||
|
|
||||||
// Host doesn't match.
|
|
||||||
reset()
|
|
||||||
host = "www.mygoogle.com"
|
|
||||||
match(false)
|
|
||||||
|
|
||||||
// Path doesn't match.
|
|
||||||
reset()
|
|
||||||
path = "/product/notdigits"
|
|
||||||
match(false)
|
|
||||||
|
|
||||||
// Query doesn't match.
|
|
||||||
reset()
|
|
||||||
query = "?foo=baz"
|
|
||||||
match(false)
|
|
||||||
|
|
||||||
// Method doesn't match.
|
|
||||||
reset()
|
|
||||||
method = "POST"
|
|
||||||
match(false)
|
|
||||||
|
|
||||||
// Header doesn't match.
|
|
||||||
reset()
|
|
||||||
headers = map[string]string{}
|
|
||||||
match(false)
|
|
||||||
|
|
||||||
// Everything match, again.
|
|
||||||
reset()
|
|
||||||
match(true)
|
|
||||||
|
|
||||||
// 2nd route --------------------------------------------------------------
|
|
||||||
|
|
||||||
// Everything match.
|
|
||||||
reset2()
|
|
||||||
match(true)
|
|
||||||
|
|
||||||
// Scheme doesn't match.
|
|
||||||
reset2()
|
|
||||||
scheme = "https"
|
|
||||||
match(false)
|
|
||||||
|
|
||||||
// Host doesn't match.
|
|
||||||
reset2()
|
|
||||||
host = "sub.google.com"
|
|
||||||
match(false)
|
|
||||||
|
|
||||||
// Path doesn't match.
|
|
||||||
reset2()
|
|
||||||
path = "/bar/product/42"
|
|
||||||
match(false)
|
|
||||||
|
|
||||||
// Query doesn't match.
|
|
||||||
reset2()
|
|
||||||
query = "?foo=baz"
|
|
||||||
match(false)
|
|
||||||
|
|
||||||
// Method doesn't match.
|
|
||||||
reset2()
|
|
||||||
method = "GET"
|
|
||||||
match(false)
|
|
||||||
|
|
||||||
// Header doesn't match.
|
|
||||||
reset2()
|
|
||||||
headers = map[string]string{}
|
|
||||||
match(false)
|
|
||||||
|
|
||||||
// Everything match, again.
|
|
||||||
reset2()
|
|
||||||
match(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
type headerMatcherTest struct {
|
|
||||||
matcher headerMatcher
|
|
||||||
headers map[string]string
|
|
||||||
result bool
|
|
||||||
}
|
|
||||||
|
|
||||||
var headerMatcherTests = []headerMatcherTest{
|
|
||||||
{
|
|
||||||
matcher: headerMatcher(map[string]string{"x-requested-with": "XMLHttpRequest"}),
|
|
||||||
headers: map[string]string{"X-Requested-With": "XMLHttpRequest"},
|
|
||||||
result: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
matcher: headerMatcher(map[string]string{"x-requested-with": ""}),
|
|
||||||
headers: map[string]string{"X-Requested-With": "anything"},
|
|
||||||
result: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
matcher: headerMatcher(map[string]string{"x-requested-with": "XMLHttpRequest"}),
|
|
||||||
headers: map[string]string{},
|
|
||||||
result: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
type hostMatcherTest struct {
|
|
||||||
matcher *Route
|
|
||||||
url string
|
|
||||||
vars map[string]string
|
|
||||||
result bool
|
|
||||||
}
|
|
||||||
|
|
||||||
var hostMatcherTests = []hostMatcherTest{
|
|
||||||
{
|
|
||||||
matcher: NewRouter().NewRoute().Host("{foo:[a-z][a-z][a-z]}.{bar:[a-z][a-z][a-z]}.{baz:[a-z][a-z][a-z]}"),
|
|
||||||
url: "http://abc.def.ghi/",
|
|
||||||
vars: map[string]string{"foo": "abc", "bar": "def", "baz": "ghi"},
|
|
||||||
result: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
matcher: NewRouter().NewRoute().Host("{foo:[a-z][a-z][a-z]}.{bar:[a-z][a-z][a-z]}.{baz:[a-z][a-z][a-z]}"),
|
|
||||||
url: "http://a.b.c/",
|
|
||||||
vars: map[string]string{"foo": "abc", "bar": "def", "baz": "ghi"},
|
|
||||||
result: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
type methodMatcherTest struct {
|
|
||||||
matcher methodMatcher
|
|
||||||
method string
|
|
||||||
result bool
|
|
||||||
}
|
|
||||||
|
|
||||||
var methodMatcherTests = []methodMatcherTest{
|
|
||||||
{
|
|
||||||
matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
|
|
||||||
method: "GET",
|
|
||||||
result: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
|
|
||||||
method: "POST",
|
|
||||||
result: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
|
|
||||||
method: "PUT",
|
|
||||||
result: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
matcher: methodMatcher([]string{"GET", "POST", "PUT"}),
|
|
||||||
method: "DELETE",
|
|
||||||
result: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
type pathMatcherTest struct {
|
|
||||||
matcher *Route
|
|
||||||
url string
|
|
||||||
vars map[string]string
|
|
||||||
result bool
|
|
||||||
}
|
|
||||||
|
|
||||||
var pathMatcherTests = []pathMatcherTest{
|
|
||||||
{
|
|
||||||
matcher: NewRouter().NewRoute().Path("/{foo:[0-9][0-9][0-9]}/{bar:[0-9][0-9][0-9]}/{baz:[0-9][0-9][0-9]}"),
|
|
||||||
url: "http://localhost:8080/123/456/789",
|
|
||||||
vars: map[string]string{"foo": "123", "bar": "456", "baz": "789"},
|
|
||||||
result: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
matcher: NewRouter().NewRoute().Path("/{foo:[0-9][0-9][0-9]}/{bar:[0-9][0-9][0-9]}/{baz:[0-9][0-9][0-9]}"),
|
|
||||||
url: "http://localhost:8080/1/2/3",
|
|
||||||
vars: map[string]string{"foo": "123", "bar": "456", "baz": "789"},
|
|
||||||
result: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
type schemeMatcherTest struct {
|
|
||||||
matcher schemeMatcher
|
|
||||||
url string
|
|
||||||
result bool
|
|
||||||
}
|
|
||||||
|
|
||||||
var schemeMatcherTests = []schemeMatcherTest{
|
|
||||||
{
|
|
||||||
matcher: schemeMatcher([]string{"http", "https"}),
|
|
||||||
url: "http://localhost:8080/",
|
|
||||||
result: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
matcher: schemeMatcher([]string{"http", "https"}),
|
|
||||||
url: "https://localhost:8080/",
|
|
||||||
result: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
matcher: schemeMatcher([]string{"https"}),
|
|
||||||
url: "http://localhost:8080/",
|
|
||||||
result: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
matcher: schemeMatcher([]string{"http"}),
|
|
||||||
url: "https://localhost:8080/",
|
|
||||||
result: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
type urlBuildingTest struct {
|
|
||||||
route *Route
|
|
||||||
vars []string
|
|
||||||
url string
|
|
||||||
}
|
|
||||||
|
|
||||||
var urlBuildingTests = []urlBuildingTest{
|
|
||||||
{
|
|
||||||
route: new(Route).Host("foo.domain.com"),
|
|
||||||
vars: []string{},
|
|
||||||
url: "http://foo.domain.com",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
route: new(Route).Host("{subdomain}.domain.com"),
|
|
||||||
vars: []string{"subdomain", "bar"},
|
|
||||||
url: "http://bar.domain.com",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
route: new(Route).Host("foo.domain.com").Path("/articles"),
|
|
||||||
vars: []string{},
|
|
||||||
url: "http://foo.domain.com/articles",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
route: new(Route).Path("/articles"),
|
|
||||||
vars: []string{},
|
|
||||||
url: "/articles",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
route: new(Route).Path("/articles/{category}/{id:[0-9]+}"),
|
|
||||||
vars: []string{"category", "technology", "id", "42"},
|
|
||||||
url: "/articles/technology/42",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
route: new(Route).Host("{subdomain}.domain.com").Path("/articles/{category}/{id:[0-9]+}"),
|
|
||||||
vars: []string{"subdomain", "foo", "category", "technology", "id", "42"},
|
|
||||||
url: "http://foo.domain.com/articles/technology/42",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHeaderMatcher(t *testing.T) {
|
|
||||||
for _, v := range headerMatcherTests {
|
|
||||||
request, _ := http.NewRequest("GET", "http://localhost:8080/", nil)
|
|
||||||
for key, value := range v.headers {
|
|
||||||
request.Header.Add(key, value)
|
|
||||||
}
|
|
||||||
var routeMatch RouteMatch
|
|
||||||
result := v.matcher.Match(request, &routeMatch)
|
|
||||||
if result != v.result {
|
|
||||||
if v.result {
|
|
||||||
t.Errorf("%#v: should match %v.", v.matcher, request.Header)
|
|
||||||
} else {
|
|
||||||
t.Errorf("%#v: should not match %v.", v.matcher, request.Header)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHostMatcher(t *testing.T) {
|
|
||||||
for _, v := range hostMatcherTests {
|
|
||||||
request, _ := http.NewRequest("GET", v.url, nil)
|
|
||||||
var routeMatch RouteMatch
|
|
||||||
result := v.matcher.Match(request, &routeMatch)
|
|
||||||
vars := routeMatch.Vars
|
|
||||||
if result != v.result {
|
|
||||||
if v.result {
|
|
||||||
t.Errorf("%#v: should match %v.", v.matcher, v.url)
|
|
||||||
} else {
|
|
||||||
t.Errorf("%#v: should not match %v.", v.matcher, v.url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if result {
|
|
||||||
if len(vars) != len(v.vars) {
|
|
||||||
t.Errorf("%#v: vars length should be %v, got %v.", v.matcher, len(v.vars), len(vars))
|
|
||||||
}
|
|
||||||
for name, value := range vars {
|
|
||||||
if v.vars[name] != value {
|
|
||||||
t.Errorf("%#v: expected value %v for key %v, got %v.", v.matcher, v.vars[name], name, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if len(vars) != 0 {
|
|
||||||
t.Errorf("%#v: vars length should be 0, got %v.", v.matcher, len(vars))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMethodMatcher(t *testing.T) {
|
|
||||||
for _, v := range methodMatcherTests {
|
|
||||||
request, _ := http.NewRequest(v.method, "http://localhost:8080/", nil)
|
|
||||||
var routeMatch RouteMatch
|
|
||||||
result := v.matcher.Match(request, &routeMatch)
|
|
||||||
if result != v.result {
|
|
||||||
if v.result {
|
|
||||||
t.Errorf("%#v: should match %v.", v.matcher, v.method)
|
|
||||||
} else {
|
|
||||||
t.Errorf("%#v: should not match %v.", v.matcher, v.method)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPathMatcher(t *testing.T) {
|
|
||||||
for _, v := range pathMatcherTests {
|
|
||||||
request, _ := http.NewRequest("GET", v.url, nil)
|
|
||||||
var routeMatch RouteMatch
|
|
||||||
result := v.matcher.Match(request, &routeMatch)
|
|
||||||
vars := routeMatch.Vars
|
|
||||||
if result != v.result {
|
|
||||||
if v.result {
|
|
||||||
t.Errorf("%#v: should match %v.", v.matcher, v.url)
|
|
||||||
} else {
|
|
||||||
t.Errorf("%#v: should not match %v.", v.matcher, v.url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if result {
|
|
||||||
if len(vars) != len(v.vars) {
|
|
||||||
t.Errorf("%#v: vars length should be %v, got %v.", v.matcher, len(v.vars), len(vars))
|
|
||||||
}
|
|
||||||
for name, value := range vars {
|
|
||||||
if v.vars[name] != value {
|
|
||||||
t.Errorf("%#v: expected value %v for key %v, got %v.", v.matcher, v.vars[name], name, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if len(vars) != 0 {
|
|
||||||
t.Errorf("%#v: vars length should be 0, got %v.", v.matcher, len(vars))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSchemeMatcher(t *testing.T) {
|
|
||||||
for _, v := range schemeMatcherTests {
|
|
||||||
request, _ := http.NewRequest("GET", v.url, nil)
|
|
||||||
var routeMatch RouteMatch
|
|
||||||
result := v.matcher.Match(request, &routeMatch)
|
|
||||||
if result != v.result {
|
|
||||||
if v.result {
|
|
||||||
t.Errorf("%#v: should match %v.", v.matcher, v.url)
|
|
||||||
} else {
|
|
||||||
t.Errorf("%#v: should not match %v.", v.matcher, v.url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUrlBuilding(t *testing.T) {
|
|
||||||
|
|
||||||
for _, v := range urlBuildingTests {
|
|
||||||
u, _ := v.route.URL(v.vars...)
|
|
||||||
url := u.String()
|
|
||||||
if url != v.url {
|
|
||||||
t.Errorf("expected %v, got %v", v.url, url)
|
|
||||||
/*
|
|
||||||
reversePath := ""
|
|
||||||
reverseHost := ""
|
|
||||||
if v.route.pathTemplate != nil {
|
|
||||||
reversePath = v.route.pathTemplate.Reverse
|
|
||||||
}
|
|
||||||
if v.route.hostTemplate != nil {
|
|
||||||
reverseHost = v.route.hostTemplate.Reverse
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Errorf("%#v:\nexpected: %q\ngot: %q\nreverse path: %q\nreverse host: %q", v.route, v.url, url, reversePath, reverseHost)
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ArticleHandler := func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
}
|
|
||||||
|
|
||||||
router := NewRouter()
|
|
||||||
router.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).Name("article")
|
|
||||||
|
|
||||||
url, _ := router.Get("article").URL("category", "technology", "id", "42")
|
|
||||||
expected := "/articles/technology/42"
|
|
||||||
if url.String() != expected {
|
|
||||||
t.Errorf("Expected %v, got %v", expected, url.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMatchedRouteName(t *testing.T) {
|
|
||||||
routeName := "stock"
|
|
||||||
router := NewRouter()
|
|
||||||
route := router.NewRoute().Path("/products/").Name(routeName)
|
|
||||||
|
|
||||||
url := "http://www.example.com/products/"
|
|
||||||
request, _ := http.NewRequest("GET", url, nil)
|
|
||||||
var rv RouteMatch
|
|
||||||
ok := router.Match(request, &rv)
|
|
||||||
|
|
||||||
if !ok || rv.Route != route {
|
|
||||||
t.Errorf("Expected same route, got %+v.", rv.Route)
|
|
||||||
}
|
|
||||||
|
|
||||||
retName := rv.Route.GetName()
|
|
||||||
if retName != routeName {
|
|
||||||
t.Errorf("Expected %q, got %q.", routeName, retName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubRouting(t *testing.T) {
|
|
||||||
// Example from docs.
|
|
||||||
router := NewRouter()
|
|
||||||
subrouter := router.NewRoute().Host("www.example.com").Subrouter()
|
|
||||||
route := subrouter.NewRoute().Path("/products/").Name("products")
|
|
||||||
|
|
||||||
url := "http://www.example.com/products/"
|
|
||||||
request, _ := http.NewRequest("GET", url, nil)
|
|
||||||
var rv RouteMatch
|
|
||||||
ok := router.Match(request, &rv)
|
|
||||||
|
|
||||||
if !ok || rv.Route != route {
|
|
||||||
t.Errorf("Expected same route, got %+v.", rv.Route)
|
|
||||||
}
|
|
||||||
|
|
||||||
u, _ := router.Get("products").URL()
|
|
||||||
builtURL := u.String()
|
|
||||||
// Yay, subroute aware of the domain when building!
|
|
||||||
if builtURL != url {
|
|
||||||
t.Errorf("Expected %q, got %q.", url, builtURL)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestVariableNames(t *testing.T) {
|
|
||||||
route := new(Route).Host("{arg1}.domain.com").Path("/{arg1}/{arg2:[0-9]+}")
|
|
||||||
if route.err == nil {
|
|
||||||
t.Errorf("Expected error for duplicated variable names")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRedirectSlash(t *testing.T) {
|
|
||||||
var route *Route
|
|
||||||
var routeMatch RouteMatch
|
|
||||||
r := NewRouter()
|
|
||||||
|
|
||||||
r.StrictSlash(false)
|
|
||||||
route = r.NewRoute()
|
|
||||||
if route.strictSlash != false {
|
|
||||||
t.Errorf("Expected false redirectSlash.")
|
|
||||||
}
|
|
||||||
|
|
||||||
r.StrictSlash(true)
|
|
||||||
route = r.NewRoute()
|
|
||||||
if route.strictSlash != true {
|
|
||||||
t.Errorf("Expected true redirectSlash.")
|
|
||||||
}
|
|
||||||
|
|
||||||
route = new(Route)
|
|
||||||
route.strictSlash = true
|
|
||||||
route.Path("/{arg1}/{arg2:[0-9]+}/")
|
|
||||||
request, _ := http.NewRequest("GET", "http://localhost/foo/123", nil)
|
|
||||||
routeMatch = RouteMatch{}
|
|
||||||
_ = route.Match(request, &routeMatch)
|
|
||||||
vars := routeMatch.Vars
|
|
||||||
if vars["arg1"] != "foo" {
|
|
||||||
t.Errorf("Expected foo.")
|
|
||||||
}
|
|
||||||
if vars["arg2"] != "123" {
|
|
||||||
t.Errorf("Expected 123.")
|
|
||||||
}
|
|
||||||
rsp := NewRecorder()
|
|
||||||
routeMatch.Handler.ServeHTTP(rsp, request)
|
|
||||||
if rsp.HeaderMap.Get("Location") != "http://localhost/foo/123/" {
|
|
||||||
t.Errorf("Expected redirect header.")
|
|
||||||
}
|
|
||||||
|
|
||||||
route = new(Route)
|
|
||||||
route.strictSlash = true
|
|
||||||
route.Path("/{arg1}/{arg2:[0-9]+}")
|
|
||||||
request, _ = http.NewRequest("GET", "http://localhost/foo/123/", nil)
|
|
||||||
routeMatch = RouteMatch{}
|
|
||||||
_ = route.Match(request, &routeMatch)
|
|
||||||
vars = routeMatch.Vars
|
|
||||||
if vars["arg1"] != "foo" {
|
|
||||||
t.Errorf("Expected foo.")
|
|
||||||
}
|
|
||||||
if vars["arg2"] != "123" {
|
|
||||||
t.Errorf("Expected 123.")
|
|
||||||
}
|
|
||||||
rsp = NewRecorder()
|
|
||||||
routeMatch.Handler.ServeHTTP(rsp, request)
|
|
||||||
if rsp.HeaderMap.Get("Location") != "http://localhost/foo/123" {
|
|
||||||
t.Errorf("Expected redirect header.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test for the new regexp library, still not available in stable Go.
|
|
||||||
func TestNewRegexp(t *testing.T) {
|
|
||||||
var p *routeRegexp
|
|
||||||
var matches []string
|
|
||||||
|
|
||||||
tests := map[string]map[string][]string{
|
|
||||||
"/{foo:a{2}}": {
|
|
||||||
"/a": nil,
|
|
||||||
"/aa": {"aa"},
|
|
||||||
"/aaa": nil,
|
|
||||||
"/aaaa": nil,
|
|
||||||
},
|
|
||||||
"/{foo:a{2,}}": {
|
|
||||||
"/a": nil,
|
|
||||||
"/aa": {"aa"},
|
|
||||||
"/aaa": {"aaa"},
|
|
||||||
"/aaaa": {"aaaa"},
|
|
||||||
},
|
|
||||||
"/{foo:a{2,3}}": {
|
|
||||||
"/a": nil,
|
|
||||||
"/aa": {"aa"},
|
|
||||||
"/aaa": {"aaa"},
|
|
||||||
"/aaaa": nil,
|
|
||||||
},
|
|
||||||
"/{foo:[a-z]{3}}/{bar:[a-z]{2}}": {
|
|
||||||
"/a": nil,
|
|
||||||
"/ab": nil,
|
|
||||||
"/abc": nil,
|
|
||||||
"/abcd": nil,
|
|
||||||
"/abc/ab": {"abc", "ab"},
|
|
||||||
"/abc/abc": nil,
|
|
||||||
"/abcd/ab": nil,
|
|
||||||
},
|
|
||||||
`/{foo:\w{3,}}/{bar:\d{2,}}`: {
|
|
||||||
"/a": nil,
|
|
||||||
"/ab": nil,
|
|
||||||
"/abc": nil,
|
|
||||||
"/abc/1": nil,
|
|
||||||
"/abc/12": {"abc", "12"},
|
|
||||||
"/abcd/12": {"abcd", "12"},
|
|
||||||
"/abcd/123": {"abcd", "123"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for pattern, paths := range tests {
|
|
||||||
p, _ = newRouteRegexp(pattern, false, false, false, false)
|
|
||||||
for path, result := range paths {
|
|
||||||
matches = p.regexp.FindStringSubmatch(path)
|
|
||||||
if result == nil {
|
|
||||||
if matches != nil {
|
|
||||||
t.Errorf("%v should not match %v.", pattern, path)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if len(matches) != len(result)+1 {
|
|
||||||
t.Errorf("Expected %v matches, got %v.", len(result)+1, len(matches))
|
|
||||||
} else {
|
|
||||||
for k, v := range result {
|
|
||||||
if matches[k+1] != v {
|
|
||||||
t.Errorf("Expected %v, got %v.", v, matches[k+1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
121
vendor/github.com/gtank/cryptopasta/README
generated
vendored
121
vendor/github.com/gtank/cryptopasta/README
generated
vendored
|
@ -1,121 +0,0 @@
|
||||||
TL;DR- Copy & paste your crypto code from here instead of Stack Overflow.
|
|
||||||
|
|
||||||
This library demonstrates a suite of basic cryptography from the Go standard
|
|
||||||
library. To the extent possible, it tries to hide complexity and help you avoid
|
|
||||||
common mistakes. The recommendations were chosen as a compromise between
|
|
||||||
cryptographic qualities, the Go standard lib, and my existing use cases.
|
|
||||||
|
|
||||||
Some particular design choices I've made:
|
|
||||||
|
|
||||||
1. SHA-512/256 has been chosen as the default hash for the examples. It's
|
|
||||||
faster on 64-bit machines and immune to length extension. If it doesn't work
|
|
||||||
in your case, replace instances of it with ordinary SHA-256.
|
|
||||||
|
|
||||||
2. The specific ECDSA parameters were chosen to be compatible with RFC7518[1]
|
|
||||||
while using the best implementation of ECDSA available. Go's P-256 is
|
|
||||||
constant-time (which prevents certain types of attacks) while its P-384 and
|
|
||||||
P-521 are not.
|
|
||||||
|
|
||||||
3. Key parameters are arrays rather than slices so the compiler can help you
|
|
||||||
avoid mixing up the arguments. The signing and marshaling functions use the
|
|
||||||
crypto/ecdsa key types directly for the same reason.
|
|
||||||
|
|
||||||
4. Public/private keypairs for signing are marshaled into and out of PEM
|
|
||||||
format, making them relatively portable to other crypto software you're
|
|
||||||
likely to use (openssl, cfssl, etc).
|
|
||||||
|
|
||||||
5. Key generation functions will panic if they can't read enough random bytes
|
|
||||||
to generate the key. Key generation is critical, and if crypto/rand fails at
|
|
||||||
that stage then you should stop doing cryptography on that machine immediately.
|
|
||||||
|
|
||||||
6. The license is a CC0 public domain dedication, with the intent that you can
|
|
||||||
just copy bits of this directly into your code and never be required to
|
|
||||||
acknowledge my copyright, provide source code, or do anything else commonly
|
|
||||||
associated with open licenses.
|
|
||||||
|
|
||||||
|
|
||||||
The specific recommendations are:
|
|
||||||
|
|
||||||
|
|
||||||
Encryption - 256-bit AES-GCM with random 96-bit nonces
|
|
||||||
|
|
||||||
Using AES-GCM (instead of AES-CBC, AES-CFB, or AES-CTR, all of which Go also
|
|
||||||
offers) provides authentication in addition to confidentiality. This means that
|
|
||||||
the content of your data is hidden and that any modification of the encrypted
|
|
||||||
data will result in a failure to decrypt. This rules out entire classes of
|
|
||||||
possible attacks. Randomized nonces remove the choices around nonce generation
|
|
||||||
and management, which are another common source of error in crypto
|
|
||||||
implementations.
|
|
||||||
|
|
||||||
The interfaces in this library allow only the use of 256-bit keys.
|
|
||||||
|
|
||||||
|
|
||||||
Hashing - HMAC-SHA512/256
|
|
||||||
|
|
||||||
Using hash functions directly is fraught with various perils – it's common for
|
|
||||||
developers to accidentally write code that is subject to easy collision or
|
|
||||||
length extension attacks. HMAC is a function built on top of hashes and it
|
|
||||||
doesn't have those problems. Using SHA-512/256 as the underlying hash function
|
|
||||||
means the process will be faster on 64-bit machines, but the output will be the
|
|
||||||
same length as the more familiar SHA-256.
|
|
||||||
|
|
||||||
This interface encourages you to scope your hashes with an English-language
|
|
||||||
string (a "tag") that describes the purpose of the hash. Tagged hashes are a
|
|
||||||
common "security hygiene" measure to ensure that hashing the same data for
|
|
||||||
different purposes will produce different outputs.
|
|
||||||
|
|
||||||
|
|
||||||
Password hashing - bcrypt with work factor 14
|
|
||||||
|
|
||||||
Use this to store users' passwords and check them for login (e.g. in a web
|
|
||||||
backend). While they both have "hashing" in the name, password hashing is an
|
|
||||||
entirely different situation from ordinary hashing and requires its own
|
|
||||||
specialized algorithm. bcrypt is a hash function designed for password storage.
|
|
||||||
It can be made selectively slower (based on a "work factor") to increase the
|
|
||||||
difficulty of brute-force password cracking attempts.
|
|
||||||
|
|
||||||
As of 2016, a work factor of 14 should be well on the side of future-proofing
|
|
||||||
over performance. If it turns out to be too slow for your needs, you can try
|
|
||||||
using 13 or even 12. You should not go below work factor 12.
|
|
||||||
|
|
||||||
|
|
||||||
Symmetric Signatures / Message Authentication - HMAC-SHA512/256
|
|
||||||
|
|
||||||
When two parties share a secret key, they can use message authentication to
|
|
||||||
make sure that a piece of data hasn't been altered. You can think of it as a
|
|
||||||
"symmetric signature" - it proves both that the data is unchanged and that
|
|
||||||
someone who knows the shared secret key generated it. Anyone who does not know
|
|
||||||
the secret key can neither validate the data nor make valid alterations.
|
|
||||||
|
|
||||||
This comes up most often in the context of web stuff, such as:
|
|
||||||
|
|
||||||
1. Authenticating requests to your API. The most widely known example is
|
|
||||||
probably the Amazon AWS API, which requires you to sign requests with
|
|
||||||
HMAC-SHA256. In this type of use, the "secret key" is a token that the API
|
|
||||||
provider issues to authorized API users.
|
|
||||||
|
|
||||||
2. Validating authenticated tokens (cookies, JWTs, etc) that are issued by a
|
|
||||||
service but are stored by a user. In this case, the service wants to ensure
|
|
||||||
that a user doesn't modify the data contained in the token.
|
|
||||||
|
|
||||||
As with encryption, you should always use a 256-bit random key to
|
|
||||||
authenticate messages.
|
|
||||||
|
|
||||||
|
|
||||||
Asymmetric Signatures - ECDSA on P-256 with SHA-256 message digests
|
|
||||||
|
|
||||||
These are the classic public/private keypair signatures that you probably think
|
|
||||||
of when you hear the word "signature". The holder of a private key can sign
|
|
||||||
data that anyone who has the corresponding public key can verify.
|
|
||||||
|
|
||||||
Go takes very good care of us here. In particular, the Go implementation of
|
|
||||||
P-256 is constant time to protect against side-channel attacks, and the Go
|
|
||||||
implementation of ECDSA generates safe nonces to protect against the type of
|
|
||||||
repeated-nonce attack that broke the PS3.
|
|
||||||
|
|
||||||
In terms of JWTs, this algorithm is called "ES256". The functions
|
|
||||||
"EncodeSignatureJWT" and "DecodeSignatureJWT" will convert the basic signature
|
|
||||||
format to and from the encoding specified by RFC7515[2]
|
|
||||||
|
|
||||||
[1] https://tools.ietf.org/html/rfc7518#section-3.1
|
|
||||||
[2] https://tools.ietf.org/html/rfc7515#appendix-A.3
|
|
104
vendor/github.com/gtank/cryptopasta/encrypt_test.go
generated
vendored
104
vendor/github.com/gtank/cryptopasta/encrypt_test.go
generated
vendored
|
@ -1,104 +0,0 @@
|
||||||
// cryptopasta - basic cryptography examples
|
|
||||||
//
|
|
||||||
// Written in 2015 by George Tankersley <george.tankersley@gmail.com>
|
|
||||||
//
|
|
||||||
// To the extent possible under law, the author(s) have dedicated all copyright
|
|
||||||
// and related and neighboring rights to this software to the public domain
|
|
||||||
// worldwide. This software is distributed without any warranty.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the CC0 Public Domain Dedication along
|
|
||||||
// with this software. If not, see // <http://creativecommons.org/publicdomain/zero/1.0/>.
|
|
||||||
|
|
||||||
package cryptopasta
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/rand"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/nacl/secretbox"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestEncryptDecryptGCM(t *testing.T) {
|
|
||||||
randomKey := &[32]byte{}
|
|
||||||
_, err := io.ReadFull(rand.Reader, randomKey[:])
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
gcmTests := []struct {
|
|
||||||
plaintext []byte
|
|
||||||
key *[32]byte
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
plaintext: []byte("Hello, world!"),
|
|
||||||
key: randomKey,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range gcmTests {
|
|
||||||
ciphertext, err := Encrypt(tt.plaintext, tt.key)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
plaintext, err := Decrypt(ciphertext, tt.key)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !bytes.Equal(plaintext, tt.plaintext) {
|
|
||||||
t.Errorf("plaintexts don't match")
|
|
||||||
}
|
|
||||||
|
|
||||||
ciphertext[0] ^= 0xff
|
|
||||||
plaintext, err = Decrypt(ciphertext, tt.key)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("gcmOpen should not have worked, but did")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkAESGCM(b *testing.B) {
|
|
||||||
randomKey := &[32]byte{}
|
|
||||||
_, err := io.ReadFull(rand.Reader, randomKey[:])
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := ioutil.ReadFile("testdata/big")
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
b.SetBytes(int64(len(data)))
|
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
Encrypt(data, randomKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkSecretbox(b *testing.B) {
|
|
||||||
randomKey := &[32]byte{}
|
|
||||||
_, err := io.ReadFull(rand.Reader, randomKey[:])
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
nonce := &[24]byte{}
|
|
||||||
_, err = io.ReadFull(rand.Reader, nonce[:])
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := ioutil.ReadFile("testdata/big")
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
b.SetBytes(int64(len(data)))
|
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
secretbox.Seal(nil, data, nonce, randomKey)
|
|
||||||
}
|
|
||||||
}
|
|
91
vendor/github.com/gtank/cryptopasta/hash_test.go
generated
vendored
91
vendor/github.com/gtank/cryptopasta/hash_test.go
generated
vendored
|
@ -1,91 +0,0 @@
|
||||||
// cryptopasta - basic cryptography examples
|
|
||||||
//
|
|
||||||
// Written in 2015 by George Tankersley <george.tankersley@gmail.com>
|
|
||||||
//
|
|
||||||
// To the extent possible under law, the author(s) have dedicated all copyright
|
|
||||||
// and related and neighboring rights to this software to the public domain
|
|
||||||
// worldwide. This software is distributed without any warranty.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the CC0 Public Domain Dedication along
|
|
||||||
// with this software. If not, see // <http://creativecommons.org/publicdomain/zero/1.0/>.
|
|
||||||
|
|
||||||
package cryptopasta
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/sha256"
|
|
||||||
"crypto/sha512"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPasswordHashing(t *testing.T) {
|
|
||||||
bcryptTests := []struct {
|
|
||||||
plaintext []byte
|
|
||||||
hash []byte
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
plaintext: []byte("password"),
|
|
||||||
hash: []byte("$2a$14$uALAQb/Lwl59oHVbuUa5m.xEFmQBc9ME/IiSgJK/VHtNJJXASCDoS"),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range bcryptTests {
|
|
||||||
hashed, err := HashPassword(tt.plaintext)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = CheckPasswordHash(hashed, tt.plaintext); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Benchmarks SHA256 on 16K of random data.
|
|
||||||
func BenchmarkSHA256(b *testing.B) {
|
|
||||||
data, err := ioutil.ReadFile("testdata/random")
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
b.SetBytes(int64(len(data)))
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
_ = sha256.Sum256(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Benchmarks SHA512/256 on 16K of random data.
|
|
||||||
func BenchmarkSHA512_256(b *testing.B) {
|
|
||||||
data, err := ioutil.ReadFile("testdata/random")
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
b.SetBytes(int64(len(data)))
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
_ = sha512.Sum512_256(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkBcrypt(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
_, err := HashPassword([]byte("thisisareallybadpassword"))
|
|
||||||
if err != nil {
|
|
||||||
b.Error(err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleHash() {
|
|
||||||
tag := "hashing file for lookup key"
|
|
||||||
contents, err := ioutil.ReadFile("testdata/random")
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("could not read file: %v\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
digest := Hash(tag, contents)
|
|
||||||
fmt.Println(hex.EncodeToString(digest))
|
|
||||||
// Output: 9f4c795d8ae5c207f19184ccebee6a606c1fdfe509c793614066d613580f03e1
|
|
||||||
}
|
|
133
vendor/github.com/gtank/cryptopasta/marshal_test.go
generated
vendored
133
vendor/github.com/gtank/cryptopasta/marshal_test.go
generated
vendored
|
@ -1,133 +0,0 @@
|
||||||
// cryptopasta - basic cryptography examples
|
|
||||||
//
|
|
||||||
// Written in 2015 by George Tankersley <george.tankersley@gmail.com>
|
|
||||||
//
|
|
||||||
// To the extent possible under law, the author(s) have dedicated all copyright
|
|
||||||
// and related and neighboring rights to this software to the public domain
|
|
||||||
// worldwide. This software is distributed without any warranty.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the CC0 Public Domain Dedication along
|
|
||||||
// with this software. If not, see // <http://creativecommons.org/publicdomain/zero/1.0/>.
|
|
||||||
|
|
||||||
package cryptopasta
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A keypair for NIST P-256 / secp256r1
|
|
||||||
// Generated using:
|
|
||||||
// openssl ecparam -genkey -name prime256v1 -outform PEM
|
|
||||||
var pemECPrivateKeyP256 = `-----BEGIN EC PARAMETERS-----
|
|
||||||
BggqhkjOPQMBBw==
|
|
||||||
-----END EC PARAMETERS-----
|
|
||||||
-----BEGIN EC PRIVATE KEY-----
|
|
||||||
MHcCAQEEIOI+EZsjyN3jvWJI/KDihFmqTuDpUe/if6f/pgGTBta/oAoGCCqGSM49
|
|
||||||
AwEHoUQDQgAEhhObKJ1r1PcUw+3REd/TbmSZnDvXnFUSTwqQFo5gbfIlP+gvEYba
|
|
||||||
+Rxj2hhqjfzqxIleRK40IRyEi3fJM/8Qhg==
|
|
||||||
-----END EC PRIVATE KEY-----
|
|
||||||
`
|
|
||||||
|
|
||||||
var pemECPublicKeyP256 = `-----BEGIN PUBLIC KEY-----
|
|
||||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEhhObKJ1r1PcUw+3REd/TbmSZnDvX
|
|
||||||
nFUSTwqQFo5gbfIlP+gvEYba+Rxj2hhqjfzqxIleRK40IRyEi3fJM/8Qhg==
|
|
||||||
-----END PUBLIC KEY-----
|
|
||||||
`
|
|
||||||
|
|
||||||
// A keypair for NIST P-384 / secp384r1
|
|
||||||
// Generated using:
|
|
||||||
// openssl ecparam -genkey -name secp384r1 -outform PEM
|
|
||||||
var pemECPrivateKeyP384 = `-----BEGIN EC PARAMETERS-----
|
|
||||||
BgUrgQQAIg==
|
|
||||||
-----END EC PARAMETERS-----
|
|
||||||
-----BEGIN EC PRIVATE KEY-----
|
|
||||||
MIGkAgEBBDAhA0YPVL1kimIy+FAqzUAtmR3It2Yjv2I++YpcC4oX7wGuEWcWKBYE
|
|
||||||
oOjj7wG/memgBwYFK4EEACKhZANiAAQub8xaaCTTW5rCHJCqUddIXpvq/TxdwViH
|
|
||||||
+tPEQQlJAJciXStM/aNLYA7Q1K1zMjYyzKSWz5kAh/+x4rXQ9Hlm3VAwCQDVVSjP
|
|
||||||
bfiNOXKOWfmyrGyQ7fQfs+ro1lmjLjs=
|
|
||||||
-----END EC PRIVATE KEY-----
|
|
||||||
`
|
|
||||||
|
|
||||||
var pemECPublicKeyP384 = `-----BEGIN PUBLIC KEY-----
|
|
||||||
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAELm/MWmgk01uawhyQqlHXSF6b6v08XcFY
|
|
||||||
h/rTxEEJSQCXIl0rTP2jS2AO0NStczI2Msykls+ZAIf/seK10PR5Zt1QMAkA1VUo
|
|
||||||
z234jTlyjln5sqxskO30H7Pq6NZZoy47
|
|
||||||
-----END PUBLIC KEY-----
|
|
||||||
`
|
|
||||||
|
|
||||||
var garbagePEM = `-----BEGIN GARBAGE-----
|
|
||||||
TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ=
|
|
||||||
-----END GARBAGE-----
|
|
||||||
`
|
|
||||||
|
|
||||||
func TestPublicKeyMarshaling(t *testing.T) {
|
|
||||||
ecKey, err := DecodePublicKey([]byte(pemECPublicKeyP256))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
pemBytes, _ := EncodePublicKey(ecKey)
|
|
||||||
if !bytes.Equal(pemBytes, []byte(pemECPublicKeyP256)) {
|
|
||||||
t.Fatal("public key encoding did not match")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPrivateKeyBadDecode(t *testing.T) {
|
|
||||||
_, err := DecodePrivateKey([]byte(garbagePEM))
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("decoded garbage data without complaint")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPrivateKeyMarshaling(t *testing.T) {
|
|
||||||
ecKey, err := DecodePrivateKey([]byte(pemECPrivateKeyP256))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
pemBytes, _ := EncodePrivateKey(ecKey)
|
|
||||||
if !strings.HasSuffix(pemECPrivateKeyP256, string(pemBytes)) {
|
|
||||||
t.Fatal("private key encoding did not match")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test vector from https://tools.ietf.org/html/rfc7515#appendix-A.3.1
|
|
||||||
var jwtTest = []struct {
|
|
||||||
sigBytes []byte
|
|
||||||
b64sig string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
sigBytes: []byte{14, 209, 33, 83, 121, 99, 108, 72, 60, 47, 127, 21,
|
|
||||||
88, 7, 212, 2, 163, 178, 40, 3, 58, 249, 124, 126, 23, 129, 154, 195, 22, 158,
|
|
||||||
166, 101, 197, 10, 7, 211, 140, 60, 112, 229, 216, 241, 45, 175,
|
|
||||||
8, 74, 84, 128, 166, 101, 144, 197, 242, 147, 80, 154, 143, 63, 127, 138, 131,
|
|
||||||
163, 84, 213},
|
|
||||||
b64sig: "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestJWTEncoding(t *testing.T) {
|
|
||||||
for _, tt := range jwtTest {
|
|
||||||
result := EncodeSignatureJWT(tt.sigBytes)
|
|
||||||
|
|
||||||
if strings.Compare(result, tt.b64sig) != 0 {
|
|
||||||
t.Fatalf("expected %s, got %s\n", tt.b64sig, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestJWTDecoding(t *testing.T) {
|
|
||||||
for _, tt := range jwtTest {
|
|
||||||
resultSig, err := DecodeSignatureJWT(tt.b64sig)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !bytes.Equal(resultSig, tt.sigBytes) {
|
|
||||||
t.Fatalf("decoded signature was incorrect")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
107
vendor/github.com/gtank/cryptopasta/sign_test.go
generated
vendored
107
vendor/github.com/gtank/cryptopasta/sign_test.go
generated
vendored
|
@ -1,107 +0,0 @@
|
||||||
// cryptopasta - basic cryptography examples
|
|
||||||
//
|
|
||||||
// Written in 2015 by George Tankersley <george.tankersley@gmail.com>
|
|
||||||
//
|
|
||||||
// To the extent possible under law, the author(s) have dedicated all copyright
|
|
||||||
// and related and neighboring rights to this software to the public domain
|
|
||||||
// worldwide. This software is distributed without any warranty.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the CC0 Public Domain Dedication along
|
|
||||||
// with this software. If not, see // <http://creativecommons.org/publicdomain/zero/1.0/>.
|
|
||||||
|
|
||||||
package cryptopasta
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/elliptic"
|
|
||||||
"crypto/rand"
|
|
||||||
"encoding/hex"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// https://groups.google.com/d/msg/sci.crypt/OolWgsgQD-8/jHciyWkaL0gJ
|
|
||||||
var hmacTests = []struct {
|
|
||||||
key string
|
|
||||||
data string
|
|
||||||
digest string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
key: "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
|
|
||||||
data: "4869205468657265", // "Hi There"
|
|
||||||
digest: "9f9126c3d9c3c330d760425ca8a217e31feae31bfe70196ff81642b868402eab",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "4a656665", // "Jefe"
|
|
||||||
data: "7768617420646f2079612077616e7420666f72206e6f7468696e673f", // "what do ya want for nothing?"
|
|
||||||
digest: "6df7b24630d5ccb2ee335407081a87188c221489768fa2020513b2d593359456",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHMAC(t *testing.T) {
|
|
||||||
for idx, tt := range hmacTests {
|
|
||||||
keySlice, _ := hex.DecodeString(tt.key)
|
|
||||||
dataBytes, _ := hex.DecodeString(tt.data)
|
|
||||||
expectedDigest, _ := hex.DecodeString(tt.digest)
|
|
||||||
|
|
||||||
keyBytes := &[32]byte{}
|
|
||||||
copy(keyBytes[:], keySlice)
|
|
||||||
|
|
||||||
macDigest := GenerateHMAC(dataBytes, keyBytes)
|
|
||||||
if !bytes.Equal(macDigest, expectedDigest) {
|
|
||||||
t.Errorf("test %d generated unexpected mac", idx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSign(t *testing.T) {
|
|
||||||
message := []byte("Hello, world!")
|
|
||||||
|
|
||||||
key, err := NewSigningKey()
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
signature, err := Sign(message, key)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !Verify(message, signature, &key.PublicKey) {
|
|
||||||
t.Error("signature was not correct")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
message[0] ^= 0xff
|
|
||||||
if Verify(message, signature, &key.PublicKey) {
|
|
||||||
t.Error("signature was good for altered message")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSignWithP384(t *testing.T) {
|
|
||||||
message := []byte("Hello, world!")
|
|
||||||
|
|
||||||
key, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
signature, err := Sign(message, key)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !Verify(message, signature, &key.PublicKey) {
|
|
||||||
t.Error("signature was not correct")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
message[0] ^= 0xff
|
|
||||||
if Verify(message, signature, &key.PublicKey) {
|
|
||||||
t.Error("signature was good for altered message")
|
|
||||||
}
|
|
||||||
}
|
|
38
vendor/github.com/gtank/cryptopasta/tls_test.go
generated
vendored
38
vendor/github.com/gtank/cryptopasta/tls_test.go
generated
vendored
|
@ -1,38 +0,0 @@
|
||||||
// cryptopasta - basic cryptography examples
|
|
||||||
//
|
|
||||||
// Written in 2016 by George Tankersley <george.tankersley@gmail.com>
|
|
||||||
//
|
|
||||||
// To the extent possible under law, the author(s) have dedicated all copyright
|
|
||||||
// and related and neighboring rights to this software to the public domain
|
|
||||||
// worldwide. This software is distributed without any warranty.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the CC0 Public Domain Dedication along
|
|
||||||
// with this software. If not, see // <http://creativecommons.org/publicdomain/zero/1.0/>.
|
|
||||||
|
|
||||||
// Provides a recommended TLS configuration.
|
|
||||||
package cryptopasta
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ExampleTLSServer() {
|
|
||||||
// Get recommended basic configuration
|
|
||||||
config := DefaultTLSConfig()
|
|
||||||
|
|
||||||
// Serve up some HTTP
|
|
||||||
http.HandleFunc("/", func(rw http.ResponseWriter, req *http.Request) {
|
|
||||||
rw.Write([]byte("Hello, world\n"))
|
|
||||||
})
|
|
||||||
|
|
||||||
server := &http.Server{
|
|
||||||
Addr: ":8080",
|
|
||||||
TLSConfig: config,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := server.ListenAndServeTLS("cert.pem", "key.pem")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
23
vendor/github.com/inconshreveable/mousetrap/README.md
generated
vendored
23
vendor/github.com/inconshreveable/mousetrap/README.md
generated
vendored
|
@ -1,23 +0,0 @@
|
||||||
# mousetrap
|
|
||||||
|
|
||||||
mousetrap is a tiny library that answers a single question.
|
|
||||||
|
|
||||||
On a Windows machine, was the process invoked by someone double clicking on
|
|
||||||
the executable file while browsing in explorer?
|
|
||||||
|
|
||||||
### Motivation
|
|
||||||
|
|
||||||
Windows developers unfamiliar with command line tools will often "double-click"
|
|
||||||
the executable for a tool. Because most CLI tools print the help and then exit
|
|
||||||
when invoked without arguments, this is often very frustrating for those users.
|
|
||||||
|
|
||||||
mousetrap provides a way to detect these invocations so that you can provide
|
|
||||||
more helpful behavior and instructions on how to run the CLI tool. To see what
|
|
||||||
this looks like, both from an organizational and a technical perspective, see
|
|
||||||
https://inconshreveable.com/09-09-2014/sweat-the-small-stuff/
|
|
||||||
|
|
||||||
### The interface
|
|
||||||
|
|
||||||
The library exposes a single interface:
|
|
||||||
|
|
||||||
func StartedByExplorer() (bool)
|
|
120
vendor/github.com/kylelemons/godebug/diff/diff_test.go
generated
vendored
120
vendor/github.com/kylelemons/godebug/diff/diff_test.go
generated
vendored
|
@ -1,120 +0,0 @@
|
||||||
// Copyright 2013 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 diff
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDiff(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
desc string
|
|
||||||
A, B []string
|
|
||||||
chunks []Chunk
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "constitution",
|
|
||||||
A: []string{
|
|
||||||
"We the People of the United States, in Order to form a more perfect Union,",
|
|
||||||
"establish Justice, insure domestic Tranquility, provide for the common defence,",
|
|
||||||
"and secure the Blessings of Liberty to ourselves",
|
|
||||||
"and our Posterity, do ordain and establish this Constitution for the United",
|
|
||||||
"States of America.",
|
|
||||||
},
|
|
||||||
B: []string{
|
|
||||||
"We the People of the United States, in Order to form a more perfect Union,",
|
|
||||||
"establish Justice, insure domestic Tranquility, provide for the common defence,",
|
|
||||||
"promote the general Welfare, and secure the Blessings of Liberty to ourselves",
|
|
||||||
"and our Posterity, do ordain and establish this Constitution for the United",
|
|
||||||
"States of America.",
|
|
||||||
},
|
|
||||||
chunks: []Chunk{
|
|
||||||
0: {
|
|
||||||
Equal: []string{
|
|
||||||
"We the People of the United States, in Order to form a more perfect Union,",
|
|
||||||
"establish Justice, insure domestic Tranquility, provide for the common defence,",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
1: {
|
|
||||||
Deleted: []string{
|
|
||||||
"and secure the Blessings of Liberty to ourselves",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
2: {
|
|
||||||
Added: []string{
|
|
||||||
"promote the general Welfare, and secure the Blessings of Liberty to ourselves",
|
|
||||||
},
|
|
||||||
Equal: []string{
|
|
||||||
"and our Posterity, do ordain and establish this Constitution for the United",
|
|
||||||
"States of America.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
got := DiffChunks(test.A, test.B)
|
|
||||||
if got, want := len(got), len(test.chunks); got != want {
|
|
||||||
t.Errorf("%s: edit distance = %v, want %v", test.desc, got-1, want-1)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for i := range got {
|
|
||||||
got, want := got[i], test.chunks[i]
|
|
||||||
if got, want := got.Added, want.Added; !reflect.DeepEqual(got, want) {
|
|
||||||
t.Errorf("%s[%d]: Added = %v, want %v", test.desc, i, got, want)
|
|
||||||
}
|
|
||||||
if got, want := got.Deleted, want.Deleted; !reflect.DeepEqual(got, want) {
|
|
||||||
t.Errorf("%s[%d]: Deleted = %v, want %v", test.desc, i, got, want)
|
|
||||||
}
|
|
||||||
if got, want := got.Equal, want.Equal; !reflect.DeepEqual(got, want) {
|
|
||||||
t.Errorf("%s[%d]: Equal = %v, want %v", test.desc, i, got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleDiff() {
|
|
||||||
constitution := strings.TrimSpace(`
|
|
||||||
We the People of the United States, in Order to form a more perfect Union,
|
|
||||||
establish Justice, insure domestic Tranquility, provide for the common defence,
|
|
||||||
promote the general Welfare, and secure the Blessings of Liberty to ourselves
|
|
||||||
and our Posterity, do ordain and establish this Constitution for the United
|
|
||||||
States of America.
|
|
||||||
`)
|
|
||||||
|
|
||||||
got := strings.TrimSpace(`
|
|
||||||
:wq
|
|
||||||
We the People of the United States, in Order to form a more perfect Union,
|
|
||||||
establish Justice, insure domestic Tranquility, provide for the common defence,
|
|
||||||
and secure the Blessings of Liberty to ourselves
|
|
||||||
and our Posterity, do ordain and establish this Constitution for the United
|
|
||||||
States of America.
|
|
||||||
`)
|
|
||||||
|
|
||||||
fmt.Println(Diff(got, constitution))
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// -:wq
|
|
||||||
// We the People of the United States, in Order to form a more perfect Union,
|
|
||||||
// establish Justice, insure domestic Tranquility, provide for the common defence,
|
|
||||||
// -and secure the Blessings of Liberty to ourselves
|
|
||||||
// +promote the general Welfare, and secure the Blessings of Liberty to ourselves
|
|
||||||
// and our Posterity, do ordain and establish this Constitution for the United
|
|
||||||
// States of America.
|
|
||||||
}
|
|
5
vendor/github.com/kylelemons/godebug/pretty/.gitignore
generated
vendored
5
vendor/github.com/kylelemons/godebug/pretty/.gitignore
generated
vendored
|
@ -1,5 +0,0 @@
|
||||||
*.test
|
|
||||||
*.bench
|
|
||||||
*.golden
|
|
||||||
*.txt
|
|
||||||
*.prof
|
|
281
vendor/github.com/kylelemons/godebug/pretty/examples_test.go
generated
vendored
281
vendor/github.com/kylelemons/godebug/pretty/examples_test.go
generated
vendored
|
@ -1,281 +0,0 @@
|
||||||
// Copyright 2013 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 pretty_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/kylelemons/godebug/pretty"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ExampleConfig_Sprint() {
|
|
||||||
type Pair [2]int
|
|
||||||
type Map struct {
|
|
||||||
Name string
|
|
||||||
Players map[string]Pair
|
|
||||||
Obstacles map[Pair]string
|
|
||||||
}
|
|
||||||
|
|
||||||
m := Map{
|
|
||||||
Name: "Rock Creek",
|
|
||||||
Players: map[string]Pair{
|
|
||||||
"player1": {1, 3},
|
|
||||||
"player2": {0, -1},
|
|
||||||
},
|
|
||||||
Obstacles: map[Pair]string{
|
|
||||||
Pair{0, 0}: "rock",
|
|
||||||
Pair{2, 1}: "pond",
|
|
||||||
Pair{1, 1}: "stream",
|
|
||||||
Pair{0, 1}: "stream",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Specific output formats
|
|
||||||
compact := &pretty.Config{
|
|
||||||
Compact: true,
|
|
||||||
}
|
|
||||||
diffable := &pretty.Config{
|
|
||||||
Diffable: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print out a summary
|
|
||||||
fmt.Printf("Players: %s\n", compact.Sprint(m.Players))
|
|
||||||
|
|
||||||
// Print diffable output
|
|
||||||
fmt.Printf("Map State:\n%s", diffable.Sprint(m))
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// Players: {player1:[1,3],player2:[0,-1]}
|
|
||||||
// Map State:
|
|
||||||
// {
|
|
||||||
// Name: "Rock Creek",
|
|
||||||
// Players: {
|
|
||||||
// player1: [
|
|
||||||
// 1,
|
|
||||||
// 3,
|
|
||||||
// ],
|
|
||||||
// player2: [
|
|
||||||
// 0,
|
|
||||||
// -1,
|
|
||||||
// ],
|
|
||||||
// },
|
|
||||||
// Obstacles: {
|
|
||||||
// [0,0]: "rock",
|
|
||||||
// [0,1]: "stream",
|
|
||||||
// [1,1]: "stream",
|
|
||||||
// [2,1]: "pond",
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExamplePrint() {
|
|
||||||
type ShipManifest struct {
|
|
||||||
Name string
|
|
||||||
Crew map[string]string
|
|
||||||
Androids int
|
|
||||||
Stolen bool
|
|
||||||
}
|
|
||||||
|
|
||||||
manifest := &ShipManifest{
|
|
||||||
Name: "Spaceship Heart of Gold",
|
|
||||||
Crew: map[string]string{
|
|
||||||
"Zaphod Beeblebrox": "Galactic President",
|
|
||||||
"Trillian": "Human",
|
|
||||||
"Ford Prefect": "A Hoopy Frood",
|
|
||||||
"Arthur Dent": "Along for the Ride",
|
|
||||||
},
|
|
||||||
Androids: 1,
|
|
||||||
Stolen: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
pretty.Print(manifest)
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// {Name: "Spaceship Heart of Gold",
|
|
||||||
// Crew: {Arthur Dent: "Along for the Ride",
|
|
||||||
// Ford Prefect: "A Hoopy Frood",
|
|
||||||
// Trillian: "Human",
|
|
||||||
// Zaphod Beeblebrox: "Galactic President"},
|
|
||||||
// Androids: 1,
|
|
||||||
// Stolen: true}
|
|
||||||
}
|
|
||||||
|
|
||||||
var t = struct {
|
|
||||||
Errorf func(string, ...interface{})
|
|
||||||
}{
|
|
||||||
Errorf: func(format string, args ...interface{}) {
|
|
||||||
fmt.Println(fmt.Sprintf(format, args...) + "\n")
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleCompare_testing() {
|
|
||||||
// Code under test:
|
|
||||||
|
|
||||||
type ShipManifest struct {
|
|
||||||
Name string
|
|
||||||
Crew map[string]string
|
|
||||||
Androids int
|
|
||||||
Stolen bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddCrew tries to add the given crewmember to the manifest.
|
|
||||||
AddCrew := func(m *ShipManifest, name, title string) {
|
|
||||||
if m.Crew == nil {
|
|
||||||
m.Crew = make(map[string]string)
|
|
||||||
}
|
|
||||||
m.Crew[title] = name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test function:
|
|
||||||
tests := []struct {
|
|
||||||
desc string
|
|
||||||
before *ShipManifest
|
|
||||||
name, title string
|
|
||||||
after *ShipManifest
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "add first",
|
|
||||||
before: &ShipManifest{},
|
|
||||||
name: "Zaphod Beeblebrox",
|
|
||||||
title: "Galactic President",
|
|
||||||
after: &ShipManifest{
|
|
||||||
Crew: map[string]string{
|
|
||||||
"Zaphod Beeblebrox": "Galactic President",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "add another",
|
|
||||||
before: &ShipManifest{
|
|
||||||
Crew: map[string]string{
|
|
||||||
"Zaphod Beeblebrox": "Galactic President",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
name: "Trillian",
|
|
||||||
title: "Human",
|
|
||||||
after: &ShipManifest{
|
|
||||||
Crew: map[string]string{
|
|
||||||
"Zaphod Beeblebrox": "Galactic President",
|
|
||||||
"Trillian": "Human",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "overwrite",
|
|
||||||
before: &ShipManifest{
|
|
||||||
Crew: map[string]string{
|
|
||||||
"Zaphod Beeblebrox": "Galactic President",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
name: "Zaphod Beeblebrox",
|
|
||||||
title: "Just this guy, you know?",
|
|
||||||
after: &ShipManifest{
|
|
||||||
Crew: map[string]string{
|
|
||||||
"Zaphod Beeblebrox": "Just this guy, you know?",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
AddCrew(test.before, test.name, test.title)
|
|
||||||
if diff := pretty.Compare(test.before, test.after); diff != "" {
|
|
||||||
t.Errorf("%s: post-AddCrew diff: (-got +want)\n%s", test.desc, diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output:
|
|
||||||
// add first: post-AddCrew diff: (-got +want)
|
|
||||||
// {
|
|
||||||
// Name: "",
|
|
||||||
// Crew: {
|
|
||||||
// - Galactic President: "Zaphod Beeblebrox",
|
|
||||||
// + Zaphod Beeblebrox: "Galactic President",
|
|
||||||
// },
|
|
||||||
// Androids: 0,
|
|
||||||
// Stolen: false,
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// add another: post-AddCrew diff: (-got +want)
|
|
||||||
// {
|
|
||||||
// Name: "",
|
|
||||||
// Crew: {
|
|
||||||
// - Human: "Trillian",
|
|
||||||
// + Trillian: "Human",
|
|
||||||
// Zaphod Beeblebrox: "Galactic President",
|
|
||||||
// },
|
|
||||||
// Androids: 0,
|
|
||||||
// Stolen: false,
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// overwrite: post-AddCrew diff: (-got +want)
|
|
||||||
// {
|
|
||||||
// Name: "",
|
|
||||||
// Crew: {
|
|
||||||
// - Just this guy, you know?: "Zaphod Beeblebrox",
|
|
||||||
// - Zaphod Beeblebrox: "Galactic President",
|
|
||||||
// + Zaphod Beeblebrox: "Just this guy, you know?",
|
|
||||||
// },
|
|
||||||
// Androids: 0,
|
|
||||||
// Stolen: false,
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleCompare_debugging() {
|
|
||||||
type ShipManifest struct {
|
|
||||||
Name string
|
|
||||||
Crew map[string]string
|
|
||||||
Androids int
|
|
||||||
Stolen bool
|
|
||||||
}
|
|
||||||
|
|
||||||
reported := &ShipManifest{
|
|
||||||
Name: "Spaceship Heart of Gold",
|
|
||||||
Crew: map[string]string{
|
|
||||||
"Zaphod Beeblebrox": "Galactic President",
|
|
||||||
"Trillian": "Human",
|
|
||||||
"Ford Prefect": "A Hoopy Frood",
|
|
||||||
"Arthur Dent": "Along for the Ride",
|
|
||||||
},
|
|
||||||
Androids: 1,
|
|
||||||
Stolen: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
expected := &ShipManifest{
|
|
||||||
Name: "Spaceship Heart of Gold",
|
|
||||||
Crew: map[string]string{
|
|
||||||
"Trillian": "Human",
|
|
||||||
"Rowan Artosok": "Captain",
|
|
||||||
},
|
|
||||||
Androids: 1,
|
|
||||||
Stolen: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(pretty.Compare(reported, expected))
|
|
||||||
// Output:
|
|
||||||
// {
|
|
||||||
// Name: "Spaceship Heart of Gold",
|
|
||||||
// Crew: {
|
|
||||||
// - Arthur Dent: "Along for the Ride",
|
|
||||||
// - Ford Prefect: "A Hoopy Frood",
|
|
||||||
// + Rowan Artosok: "Captain",
|
|
||||||
// Trillian: "Human",
|
|
||||||
// - Zaphod Beeblebrox: "Galactic President",
|
|
||||||
// },
|
|
||||||
// Androids: 1,
|
|
||||||
// - Stolen: true,
|
|
||||||
// + Stolen: false,
|
|
||||||
// }
|
|
||||||
}
|
|
128
vendor/github.com/kylelemons/godebug/pretty/public_test.go
generated
vendored
128
vendor/github.com/kylelemons/godebug/pretty/public_test.go
generated
vendored
|
@ -1,128 +0,0 @@
|
||||||
// Copyright 2013 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 pretty
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDiff(t *testing.T) {
|
|
||||||
type example struct {
|
|
||||||
Name string
|
|
||||||
Age int
|
|
||||||
Friends []string
|
|
||||||
}
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
desc string
|
|
||||||
got, want interface{}
|
|
||||||
diff string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "basic struct",
|
|
||||||
got: example{
|
|
||||||
Name: "Zaphd",
|
|
||||||
Age: 42,
|
|
||||||
Friends: []string{
|
|
||||||
"Ford Prefect",
|
|
||||||
"Trillian",
|
|
||||||
"Marvin",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
want: example{
|
|
||||||
Name: "Zaphod",
|
|
||||||
Age: 42,
|
|
||||||
Friends: []string{
|
|
||||||
"Ford Prefect",
|
|
||||||
"Trillian",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
diff: ` {
|
|
||||||
- Name: "Zaphd",
|
|
||||||
+ Name: "Zaphod",
|
|
||||||
Age: 42,
|
|
||||||
Friends: [
|
|
||||||
"Ford Prefect",
|
|
||||||
"Trillian",
|
|
||||||
- "Marvin",
|
|
||||||
],
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
if got, want := Compare(test.got, test.want), test.diff; got != want {
|
|
||||||
t.Errorf("%s:", test.desc)
|
|
||||||
t.Errorf(" got: %q", got)
|
|
||||||
t.Errorf(" want: %q", want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSkipZeroFields(t *testing.T) {
|
|
||||||
type example struct {
|
|
||||||
Name string
|
|
||||||
Species string
|
|
||||||
Age int
|
|
||||||
Friends []string
|
|
||||||
}
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
desc string
|
|
||||||
got, want interface{}
|
|
||||||
diff string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "basic struct",
|
|
||||||
got: example{
|
|
||||||
Name: "Zaphd",
|
|
||||||
Species: "Betelgeusian",
|
|
||||||
Age: 42,
|
|
||||||
},
|
|
||||||
want: example{
|
|
||||||
Name: "Zaphod",
|
|
||||||
Species: "Betelgeusian",
|
|
||||||
Age: 42,
|
|
||||||
Friends: []string{
|
|
||||||
"Ford Prefect",
|
|
||||||
"Trillian",
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
diff: ` {
|
|
||||||
- Name: "Zaphd",
|
|
||||||
+ Name: "Zaphod",
|
|
||||||
Species: "Betelgeusian",
|
|
||||||
Age: 42,
|
|
||||||
+ Friends: [
|
|
||||||
+ "Ford Prefect",
|
|
||||||
+ "Trillian",
|
|
||||||
+ "",
|
|
||||||
+ ],
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg := *CompareConfig
|
|
||||||
cfg.SkipZeroFields = true
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
if got, want := cfg.Compare(test.got, test.want), test.diff; got != want {
|
|
||||||
t.Errorf("%s:", test.desc)
|
|
||||||
t.Errorf(" got: %q", got)
|
|
||||||
t.Errorf(" want: %q", want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
168
vendor/github.com/kylelemons/godebug/pretty/reflect_test.go
generated
vendored
168
vendor/github.com/kylelemons/godebug/pretty/reflect_test.go
generated
vendored
|
@ -1,168 +0,0 @@
|
||||||
// Copyright 2013 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 pretty
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestVal2nodeDefault(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
desc string
|
|
||||||
raw interface{}
|
|
||||||
want node
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "nil",
|
|
||||||
raw: nil,
|
|
||||||
want: rawVal("nil"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "nil ptr",
|
|
||||||
raw: (*int)(nil),
|
|
||||||
want: rawVal("nil"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "nil slice",
|
|
||||||
raw: []string(nil),
|
|
||||||
want: list{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "nil map",
|
|
||||||
raw: map[string]string(nil),
|
|
||||||
want: keyvals{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "string",
|
|
||||||
raw: "zaphod",
|
|
||||||
want: stringVal("zaphod"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "slice",
|
|
||||||
raw: []string{"a", "b"},
|
|
||||||
want: list{stringVal("a"), stringVal("b")},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "map",
|
|
||||||
raw: map[string]string{
|
|
||||||
"zaphod": "beeblebrox",
|
|
||||||
"ford": "prefect",
|
|
||||||
},
|
|
||||||
want: keyvals{
|
|
||||||
{"ford", stringVal("prefect")},
|
|
||||||
{"zaphod", stringVal("beeblebrox")},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "map of [2]int",
|
|
||||||
raw: map[[2]int]string{
|
|
||||||
[2]int{-1, 2}: "school",
|
|
||||||
[2]int{0, 0}: "origin",
|
|
||||||
[2]int{1, 3}: "home",
|
|
||||||
},
|
|
||||||
want: keyvals{
|
|
||||||
{"[-1,2]", stringVal("school")},
|
|
||||||
{"[0,0]", stringVal("origin")},
|
|
||||||
{"[1,3]", stringVal("home")},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "struct",
|
|
||||||
raw: struct{ Zaphod, Ford string }{"beeblebrox", "prefect"},
|
|
||||||
want: keyvals{
|
|
||||||
{"Zaphod", stringVal("beeblebrox")},
|
|
||||||
{"Ford", stringVal("prefect")},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "int",
|
|
||||||
raw: 3,
|
|
||||||
want: rawVal("3"),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
if got, want := DefaultConfig.val2node(reflect.ValueOf(test.raw)), test.want; !reflect.DeepEqual(got, want) {
|
|
||||||
t.Errorf("%s: got %#v, want %#v", test.desc, got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestVal2node(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
desc string
|
|
||||||
raw interface{}
|
|
||||||
cfg *Config
|
|
||||||
want node
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "struct default",
|
|
||||||
raw: struct{ Zaphod, Ford, foo string }{"beeblebrox", "prefect", "BAD"},
|
|
||||||
cfg: DefaultConfig,
|
|
||||||
want: keyvals{
|
|
||||||
{"Zaphod", stringVal("beeblebrox")},
|
|
||||||
{"Ford", stringVal("prefect")},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "struct w/ IncludeUnexported",
|
|
||||||
raw: struct{ Zaphod, Ford, foo string }{"beeblebrox", "prefect", "GOOD"},
|
|
||||||
cfg: &Config{
|
|
||||||
IncludeUnexported: true,
|
|
||||||
},
|
|
||||||
want: keyvals{
|
|
||||||
{"Zaphod", stringVal("beeblebrox")},
|
|
||||||
{"Ford", stringVal("prefect")},
|
|
||||||
{"foo", stringVal("GOOD")},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "time default",
|
|
||||||
raw: struct{ Date time.Time }{time.Unix(1234567890, 0).UTC()},
|
|
||||||
cfg: DefaultConfig,
|
|
||||||
want: keyvals{
|
|
||||||
{"Date", keyvals{}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "time w/ PrintTextMarshalers",
|
|
||||||
raw: struct{ Date time.Time }{time.Unix(1234567890, 0).UTC()},
|
|
||||||
cfg: &Config{
|
|
||||||
PrintTextMarshalers: true,
|
|
||||||
},
|
|
||||||
want: keyvals{
|
|
||||||
{"Date", stringVal("2009-02-13T23:31:30Z")},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "time w/ PrintStringers",
|
|
||||||
raw: struct{ Date time.Time }{time.Unix(1234567890, 0).UTC()},
|
|
||||||
cfg: &Config{
|
|
||||||
PrintStringers: true,
|
|
||||||
},
|
|
||||||
want: keyvals{
|
|
||||||
{"Date", stringVal("2009-02-13 23:31:30 +0000 UTC")},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
if got, want := test.cfg.val2node(reflect.ValueOf(test.raw)), test.want; !reflect.DeepEqual(got, want) {
|
|
||||||
t.Errorf("%s: got %#v, want %#v", test.desc, got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
316
vendor/github.com/kylelemons/godebug/pretty/structure_test.go
generated
vendored
316
vendor/github.com/kylelemons/godebug/pretty/structure_test.go
generated
vendored
|
@ -1,316 +0,0 @@
|
||||||
// Copyright 2013 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 pretty
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestWriteTo(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
desc string
|
|
||||||
node node
|
|
||||||
|
|
||||||
// All strings have a leading newline trimmed before comparison:
|
|
||||||
normal string
|
|
||||||
diffable string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "string",
|
|
||||||
node: stringVal("zaphod"),
|
|
||||||
normal: `"zaphod"`,
|
|
||||||
diffable: `"zaphod"`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "raw",
|
|
||||||
node: rawVal("42"),
|
|
||||||
normal: `42`,
|
|
||||||
diffable: `42`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "keyvals",
|
|
||||||
node: keyvals{
|
|
||||||
{"name", stringVal("zaphod")},
|
|
||||||
{"age", rawVal("42")},
|
|
||||||
},
|
|
||||||
normal: `
|
|
||||||
{name: "zaphod",
|
|
||||||
age: 42}`,
|
|
||||||
diffable: `
|
|
||||||
{
|
|
||||||
name: "zaphod",
|
|
||||||
age: 42,
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "empty list",
|
|
||||||
node: list{},
|
|
||||||
normal: `
|
|
||||||
[]`,
|
|
||||||
diffable: `
|
|
||||||
[
|
|
||||||
]`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "empty nested list",
|
|
||||||
node: list{list{}},
|
|
||||||
normal: `
|
|
||||||
[[]]`,
|
|
||||||
diffable: `
|
|
||||||
[
|
|
||||||
[
|
|
||||||
],
|
|
||||||
]`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "list",
|
|
||||||
node: list{
|
|
||||||
stringVal("zaphod"),
|
|
||||||
rawVal("42"),
|
|
||||||
},
|
|
||||||
normal: `
|
|
||||||
["zaphod",
|
|
||||||
42]`,
|
|
||||||
diffable: `
|
|
||||||
[
|
|
||||||
"zaphod",
|
|
||||||
42,
|
|
||||||
]`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "empty keyvals",
|
|
||||||
node: keyvals{},
|
|
||||||
normal: `
|
|
||||||
{}`,
|
|
||||||
diffable: `
|
|
||||||
{
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "empty nested keyvals",
|
|
||||||
node: keyvals{{"k", keyvals{}}},
|
|
||||||
normal: `
|
|
||||||
{k: {}}`,
|
|
||||||
diffable: `
|
|
||||||
{
|
|
||||||
k: {
|
|
||||||
},
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "nested",
|
|
||||||
node: list{
|
|
||||||
stringVal("first"),
|
|
||||||
list{rawVal("1"), rawVal("2"), rawVal("3")},
|
|
||||||
keyvals{
|
|
||||||
{"trillian", keyvals{
|
|
||||||
{"race", stringVal("human")},
|
|
||||||
{"age", rawVal("36")},
|
|
||||||
}},
|
|
||||||
{"zaphod", keyvals{
|
|
||||||
{"occupation", stringVal("president of the galaxy")},
|
|
||||||
{"features", stringVal("two heads")},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
keyvals{},
|
|
||||||
},
|
|
||||||
normal: `
|
|
||||||
["first",
|
|
||||||
[1,
|
|
||||||
2,
|
|
||||||
3],
|
|
||||||
{trillian: {race: "human",
|
|
||||||
age: 36},
|
|
||||||
zaphod: {occupation: "president of the galaxy",
|
|
||||||
features: "two heads"}},
|
|
||||||
{}]`,
|
|
||||||
diffable: `
|
|
||||||
[
|
|
||||||
"first",
|
|
||||||
[
|
|
||||||
1,
|
|
||||||
2,
|
|
||||||
3,
|
|
||||||
],
|
|
||||||
{
|
|
||||||
trillian: {
|
|
||||||
race: "human",
|
|
||||||
age: 36,
|
|
||||||
},
|
|
||||||
zaphod: {
|
|
||||||
occupation: "president of the galaxy",
|
|
||||||
features: "two heads",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
},
|
|
||||||
]`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
// For readability, we have a newline that won't be there in the output
|
|
||||||
test.normal = strings.TrimPrefix(test.normal, "\n")
|
|
||||||
test.diffable = strings.TrimPrefix(test.diffable, "\n")
|
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
test.node.WriteTo(buf, "", &Config{})
|
|
||||||
if got, want := buf.String(), test.normal; got != want {
|
|
||||||
t.Errorf("%s: normal rendendered incorrectly\ngot:\n%s\nwant:\n%s", test.desc, got, want)
|
|
||||||
}
|
|
||||||
buf.Reset()
|
|
||||||
test.node.WriteTo(buf, "", &Config{Diffable: true})
|
|
||||||
if got, want := buf.String(), test.diffable; got != want {
|
|
||||||
t.Errorf("%s: diffable rendendered incorrectly\ngot:\n%s\nwant:\n%s", test.desc, got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCompactString(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
node
|
|
||||||
compact string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
stringVal("abc"),
|
|
||||||
"abc",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
rawVal("2"),
|
|
||||||
"2",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
list{
|
|
||||||
rawVal("2"),
|
|
||||||
rawVal("3"),
|
|
||||||
},
|
|
||||||
"[2,3]",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
keyvals{
|
|
||||||
{"name", stringVal("zaphod")},
|
|
||||||
{"age", rawVal("42")},
|
|
||||||
},
|
|
||||||
`{name:"zaphod",age:42}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
list{
|
|
||||||
list{
|
|
||||||
rawVal("0"),
|
|
||||||
rawVal("1"),
|
|
||||||
rawVal("2"),
|
|
||||||
rawVal("3"),
|
|
||||||
},
|
|
||||||
list{
|
|
||||||
rawVal("1"),
|
|
||||||
rawVal("2"),
|
|
||||||
rawVal("3"),
|
|
||||||
rawVal("0"),
|
|
||||||
},
|
|
||||||
list{
|
|
||||||
rawVal("2"),
|
|
||||||
rawVal("3"),
|
|
||||||
rawVal("0"),
|
|
||||||
rawVal("1"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
`[[0,1,2,3],[1,2,3,0],[2,3,0,1]]`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
if got, want := compactString(test.node), test.compact; got != want {
|
|
||||||
t.Errorf("%#v: compact = %q, want %q", test.node, got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestShortList(t *testing.T) {
|
|
||||||
cfg := &Config{
|
|
||||||
ShortList: 16,
|
|
||||||
}
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
node
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
list{
|
|
||||||
list{
|
|
||||||
rawVal("0"),
|
|
||||||
rawVal("1"),
|
|
||||||
rawVal("2"),
|
|
||||||
rawVal("3"),
|
|
||||||
},
|
|
||||||
list{
|
|
||||||
rawVal("1"),
|
|
||||||
rawVal("2"),
|
|
||||||
rawVal("3"),
|
|
||||||
rawVal("0"),
|
|
||||||
},
|
|
||||||
list{
|
|
||||||
rawVal("2"),
|
|
||||||
rawVal("3"),
|
|
||||||
rawVal("0"),
|
|
||||||
rawVal("1"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
`[[0,1,2,3],
|
|
||||||
[1,2,3,0],
|
|
||||||
[2,3,0,1]]`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
test.node.WriteTo(buf, "", cfg)
|
|
||||||
if got, want := buf.String(), test.want; got != want {
|
|
||||||
t.Errorf("%#v: got:\n%s\nwant:\n%s", test.node, got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var benchNode = keyvals{
|
|
||||||
{"list", list{
|
|
||||||
rawVal("0"),
|
|
||||||
rawVal("1"),
|
|
||||||
rawVal("2"),
|
|
||||||
rawVal("3"),
|
|
||||||
}},
|
|
||||||
{"keyvals", keyvals{
|
|
||||||
{"a", stringVal("b")},
|
|
||||||
{"c", stringVal("e")},
|
|
||||||
{"d", stringVal("f")},
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
|
|
||||||
func benchOpts(b *testing.B, cfg *Config) {
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
benchNode.WriteTo(buf, "", cfg)
|
|
||||||
b.SetBytes(int64(buf.Len()))
|
|
||||||
b.ResetTimer()
|
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
buf.Reset()
|
|
||||||
benchNode.WriteTo(buf, "", cfg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkWriteDefault(b *testing.B) { benchOpts(b, DefaultConfig) }
|
|
||||||
func BenchmarkWriteShortList(b *testing.B) { benchOpts(b, &Config{ShortList: 16}) }
|
|
||||||
func BenchmarkWriteCompact(b *testing.B) { benchOpts(b, &Config{Compact: true}) }
|
|
||||||
func BenchmarkWriteDiffable(b *testing.B) { benchOpts(b, &Config{Diffable: true}) }
|
|
4
vendor/github.com/lib/pq/.gitignore
generated
vendored
4
vendor/github.com/lib/pq/.gitignore
generated
vendored
|
@ -1,4 +0,0 @@
|
||||||
.db
|
|
||||||
*.test
|
|
||||||
*~
|
|
||||||
*.swp
|
|
73
vendor/github.com/lib/pq/.travis.sh
generated
vendored
73
vendor/github.com/lib/pq/.travis.sh
generated
vendored
|
@ -1,73 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -eu
|
|
||||||
|
|
||||||
client_configure() {
|
|
||||||
sudo chmod 600 $PQSSLCERTTEST_PATH/postgresql.key
|
|
||||||
}
|
|
||||||
|
|
||||||
pgdg_repository() {
|
|
||||||
local sourcelist='sources.list.d/postgresql.list'
|
|
||||||
|
|
||||||
curl -sS 'https://www.postgresql.org/media/keys/ACCC4CF8.asc' | sudo apt-key add -
|
|
||||||
echo deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main $PGVERSION | sudo tee "/etc/apt/$sourcelist"
|
|
||||||
sudo apt-get -o Dir::Etc::sourcelist="$sourcelist" -o Dir::Etc::sourceparts='-' -o APT::Get::List-Cleanup='0' update
|
|
||||||
}
|
|
||||||
|
|
||||||
postgresql_configure() {
|
|
||||||
sudo tee /etc/postgresql/$PGVERSION/main/pg_hba.conf > /dev/null <<-config
|
|
||||||
local all all trust
|
|
||||||
hostnossl all pqgossltest 127.0.0.1/32 reject
|
|
||||||
hostnossl all pqgosslcert 127.0.0.1/32 reject
|
|
||||||
hostssl all pqgossltest 127.0.0.1/32 trust
|
|
||||||
hostssl all pqgosslcert 127.0.0.1/32 cert
|
|
||||||
host all all 127.0.0.1/32 trust
|
|
||||||
hostnossl all pqgossltest ::1/128 reject
|
|
||||||
hostnossl all pqgosslcert ::1/128 reject
|
|
||||||
hostssl all pqgossltest ::1/128 trust
|
|
||||||
hostssl all pqgosslcert ::1/128 cert
|
|
||||||
host all all ::1/128 trust
|
|
||||||
config
|
|
||||||
|
|
||||||
xargs sudo install -o postgres -g postgres -m 600 -t /var/lib/postgresql/$PGVERSION/main/ <<-certificates
|
|
||||||
certs/root.crt
|
|
||||||
certs/server.crt
|
|
||||||
certs/server.key
|
|
||||||
certificates
|
|
||||||
|
|
||||||
sort -VCu <<-versions ||
|
|
||||||
$PGVERSION
|
|
||||||
9.2
|
|
||||||
versions
|
|
||||||
sudo tee -a /etc/postgresql/$PGVERSION/main/postgresql.conf > /dev/null <<-config
|
|
||||||
ssl_ca_file = 'root.crt'
|
|
||||||
ssl_cert_file = 'server.crt'
|
|
||||||
ssl_key_file = 'server.key'
|
|
||||||
config
|
|
||||||
|
|
||||||
echo 127.0.0.1 postgres | sudo tee -a /etc/hosts > /dev/null
|
|
||||||
|
|
||||||
sudo service postgresql restart
|
|
||||||
}
|
|
||||||
|
|
||||||
postgresql_install() {
|
|
||||||
xargs sudo apt-get -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confnew' install <<-packages
|
|
||||||
postgresql-$PGVERSION
|
|
||||||
postgresql-server-dev-$PGVERSION
|
|
||||||
postgresql-contrib-$PGVERSION
|
|
||||||
packages
|
|
||||||
}
|
|
||||||
|
|
||||||
postgresql_uninstall() {
|
|
||||||
sudo service postgresql stop
|
|
||||||
xargs sudo apt-get -y --purge remove <<-packages
|
|
||||||
libpq-dev
|
|
||||||
libpq5
|
|
||||||
postgresql
|
|
||||||
postgresql-client-common
|
|
||||||
postgresql-common
|
|
||||||
packages
|
|
||||||
sudo rm -rf /var/lib/postgresql
|
|
||||||
}
|
|
||||||
|
|
||||||
$1
|
|
43
vendor/github.com/lib/pq/.travis.yml
generated
vendored
43
vendor/github.com/lib/pq/.travis.yml
generated
vendored
|
@ -1,43 +0,0 @@
|
||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.5
|
|
||||||
- 1.6
|
|
||||||
- 1.7
|
|
||||||
- tip
|
|
||||||
|
|
||||||
sudo: true
|
|
||||||
|
|
||||||
env:
|
|
||||||
global:
|
|
||||||
- PGUSER=postgres
|
|
||||||
- PQGOSSLTESTS=1
|
|
||||||
- PQSSLCERTTEST_PATH=$PWD/certs
|
|
||||||
- PGHOST=127.0.0.1
|
|
||||||
matrix:
|
|
||||||
- PGVERSION=9.5
|
|
||||||
- PGVERSION=9.4
|
|
||||||
- PGVERSION=9.3
|
|
||||||
- PGVERSION=9.2
|
|
||||||
- PGVERSION=9.1
|
|
||||||
- PGVERSION=9.0
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- ./.travis.sh postgresql_uninstall
|
|
||||||
- ./.travis.sh pgdg_repository
|
|
||||||
- ./.travis.sh postgresql_install
|
|
||||||
- ./.travis.sh postgresql_configure
|
|
||||||
- ./.travis.sh client_configure
|
|
||||||
- go get golang.org/x/tools/cmd/goimports
|
|
||||||
|
|
||||||
before_script:
|
|
||||||
- createdb pqgotest
|
|
||||||
- createuser -DRS pqgossltest
|
|
||||||
- createuser -DRS pqgosslcert
|
|
||||||
|
|
||||||
script:
|
|
||||||
- >
|
|
||||||
goimports -d -e $(find -name '*.go') | awk '{ print } END { exit NR == 0 ? 0 : 1 }'
|
|
||||||
- go vet ./...
|
|
||||||
- PQTEST_BINARY_PARAMETERS=no go test -v ./...
|
|
||||||
- PQTEST_BINARY_PARAMETERS=yes go test -v ./...
|
|
29
vendor/github.com/lib/pq/CONTRIBUTING.md
generated
vendored
29
vendor/github.com/lib/pq/CONTRIBUTING.md
generated
vendored
|
@ -1,29 +0,0 @@
|
||||||
## Contributing to pq
|
|
||||||
|
|
||||||
`pq` has a backlog of pull requests, but contributions are still very
|
|
||||||
much welcome. You can help with patch review, submitting bug reports,
|
|
||||||
or adding new functionality. There is no formal style guide, but
|
|
||||||
please conform to the style of existing code and general Go formatting
|
|
||||||
conventions when submitting patches.
|
|
||||||
|
|
||||||
### Patch review
|
|
||||||
|
|
||||||
Help review existing open pull requests by commenting on the code or
|
|
||||||
proposed functionality.
|
|
||||||
|
|
||||||
### Bug reports
|
|
||||||
|
|
||||||
We appreciate any bug reports, but especially ones with self-contained
|
|
||||||
(doesn't depend on code outside of pq), minimal (can't be simplified
|
|
||||||
further) test cases. It's especially helpful if you can submit a pull
|
|
||||||
request with just the failing test case (you'll probably want to
|
|
||||||
pattern it after the tests in
|
|
||||||
[conn_test.go](https://github.com/lib/pq/blob/master/conn_test.go).
|
|
||||||
|
|
||||||
### New functionality
|
|
||||||
|
|
||||||
There are a number of pending patches for new functionality, so
|
|
||||||
additional feature patches will take a while to merge. Still, patches
|
|
||||||
are generally reviewed based on usefulness and complexity in addition
|
|
||||||
to time-in-queue, so if you have a knockout idea, take a shot. Feel
|
|
||||||
free to open an issue discussion your proposed patch beforehand.
|
|
105
vendor/github.com/lib/pq/README.md
generated
vendored
105
vendor/github.com/lib/pq/README.md
generated
vendored
|
@ -1,105 +0,0 @@
|
||||||
# pq - A pure Go postgres driver for Go's database/sql package
|
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/lib/pq.png?branch=master)](https://travis-ci.org/lib/pq)
|
|
||||||
|
|
||||||
## Install
|
|
||||||
|
|
||||||
go get github.com/lib/pq
|
|
||||||
|
|
||||||
## Docs
|
|
||||||
|
|
||||||
For detailed documentation and basic usage examples, please see the package
|
|
||||||
documentation at <http://godoc.org/github.com/lib/pq>.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
`go test` is used for testing. A running PostgreSQL server is
|
|
||||||
required, with the ability to log in. The default database to connect
|
|
||||||
to test with is "pqgotest," but it can be overridden using environment
|
|
||||||
variables.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
PGHOST=/run/postgresql go test github.com/lib/pq
|
|
||||||
|
|
||||||
Optionally, a benchmark suite can be run as part of the tests:
|
|
||||||
|
|
||||||
PGHOST=/run/postgresql go test -bench .
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
* SSL
|
|
||||||
* Handles bad connections for `database/sql`
|
|
||||||
* Scan `time.Time` correctly (i.e. `timestamp[tz]`, `time[tz]`, `date`)
|
|
||||||
* Scan binary blobs correctly (i.e. `bytea`)
|
|
||||||
* Package for `hstore` support
|
|
||||||
* COPY FROM support
|
|
||||||
* pq.ParseURL for converting urls to connection strings for sql.Open.
|
|
||||||
* Many libpq compatible environment variables
|
|
||||||
* Unix socket support
|
|
||||||
* Notifications: `LISTEN`/`NOTIFY`
|
|
||||||
* pgpass support
|
|
||||||
|
|
||||||
## Future / Things you can help with
|
|
||||||
|
|
||||||
* Better COPY FROM / COPY TO (see discussion in #181)
|
|
||||||
|
|
||||||
## Thank you (alphabetical)
|
|
||||||
|
|
||||||
Some of these contributors are from the original library `bmizerany/pq.go` whose
|
|
||||||
code still exists in here.
|
|
||||||
|
|
||||||
* Andy Balholm (andybalholm)
|
|
||||||
* Ben Berkert (benburkert)
|
|
||||||
* Benjamin Heatwole (bheatwole)
|
|
||||||
* Bill Mill (llimllib)
|
|
||||||
* Bjørn Madsen (aeons)
|
|
||||||
* Blake Gentry (bgentry)
|
|
||||||
* Brad Fitzpatrick (bradfitz)
|
|
||||||
* Charlie Melbye (cmelbye)
|
|
||||||
* Chris Bandy (cbandy)
|
|
||||||
* Chris Gilling (cgilling)
|
|
||||||
* Chris Walsh (cwds)
|
|
||||||
* Dan Sosedoff (sosedoff)
|
|
||||||
* Daniel Farina (fdr)
|
|
||||||
* Eric Chlebek (echlebek)
|
|
||||||
* Eric Garrido (minusnine)
|
|
||||||
* Eric Urban (hydrogen18)
|
|
||||||
* Everyone at The Go Team
|
|
||||||
* Evan Shaw (edsrzf)
|
|
||||||
* Ewan Chou (coocood)
|
|
||||||
* Fazal Majid (fazalmajid)
|
|
||||||
* Federico Romero (federomero)
|
|
||||||
* Fumin (fumin)
|
|
||||||
* Gary Burd (garyburd)
|
|
||||||
* Heroku (heroku)
|
|
||||||
* James Pozdena (jpoz)
|
|
||||||
* Jason McVetta (jmcvetta)
|
|
||||||
* Jeremy Jay (pbnjay)
|
|
||||||
* Joakim Sernbrant (serbaut)
|
|
||||||
* John Gallagher (jgallagher)
|
|
||||||
* Jonathan Rudenberg (titanous)
|
|
||||||
* Joël Stemmer (jstemmer)
|
|
||||||
* Kamil Kisiel (kisielk)
|
|
||||||
* Kelly Dunn (kellydunn)
|
|
||||||
* Keith Rarick (kr)
|
|
||||||
* Kir Shatrov (kirs)
|
|
||||||
* Lann Martin (lann)
|
|
||||||
* Maciek Sakrejda (uhoh-itsmaciek)
|
|
||||||
* Marc Brinkmann (mbr)
|
|
||||||
* Marko Tiikkaja (johto)
|
|
||||||
* Matt Newberry (MattNewberry)
|
|
||||||
* Matt Robenolt (mattrobenolt)
|
|
||||||
* Martin Olsen (martinolsen)
|
|
||||||
* Mike Lewis (mikelikespie)
|
|
||||||
* Nicolas Patry (Narsil)
|
|
||||||
* Oliver Tonnhofer (olt)
|
|
||||||
* Patrick Hayes (phayes)
|
|
||||||
* Paul Hammond (paulhammond)
|
|
||||||
* Ryan Smith (ryandotsmith)
|
|
||||||
* Samuel Stauffer (samuel)
|
|
||||||
* Timothée Peignier (cyberdelia)
|
|
||||||
* Travis Cline (tmc)
|
|
||||||
* TruongSinh Tran-Nguyen (truongsinh)
|
|
||||||
* Yaismel Miranda (ympons)
|
|
||||||
* notedit (notedit)
|
|
1153
vendor/github.com/lib/pq/array_test.go
generated
vendored
1153
vendor/github.com/lib/pq/array_test.go
generated
vendored
File diff suppressed because it is too large
Load diff
435
vendor/github.com/lib/pq/bench_test.go
generated
vendored
435
vendor/github.com/lib/pq/bench_test.go
generated
vendored
|
@ -1,435 +0,0 @@
|
||||||
// +build go1.1
|
|
||||||
|
|
||||||
package pq
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"database/sql"
|
|
||||||
"database/sql/driver"
|
|
||||||
"io"
|
|
||||||
"math/rand"
|
|
||||||
"net"
|
|
||||||
"runtime"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/lib/pq/oid"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
selectStringQuery = "SELECT '" + strings.Repeat("0123456789", 10) + "'"
|
|
||||||
selectSeriesQuery = "SELECT generate_series(1, 100)"
|
|
||||||
)
|
|
||||||
|
|
||||||
func BenchmarkSelectString(b *testing.B) {
|
|
||||||
var result string
|
|
||||||
benchQuery(b, selectStringQuery, &result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkSelectSeries(b *testing.B) {
|
|
||||||
var result int
|
|
||||||
benchQuery(b, selectSeriesQuery, &result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func benchQuery(b *testing.B, query string, result interface{}) {
|
|
||||||
b.StopTimer()
|
|
||||||
db := openTestConn(b)
|
|
||||||
defer db.Close()
|
|
||||||
b.StartTimer()
|
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
benchQueryLoop(b, db, query, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func benchQueryLoop(b *testing.B, db *sql.DB, query string, result interface{}) {
|
|
||||||
rows, err := db.Query(query)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
for rows.Next() {
|
|
||||||
err = rows.Scan(result)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal("failed to scan", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// reading from circularConn yields content[:prefixLen] once, followed by
|
|
||||||
// content[prefixLen:] over and over again. It never returns EOF.
|
|
||||||
type circularConn struct {
|
|
||||||
content string
|
|
||||||
prefixLen int
|
|
||||||
pos int
|
|
||||||
net.Conn // for all other net.Conn methods that will never be called
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *circularConn) Read(b []byte) (n int, err error) {
|
|
||||||
n = copy(b, r.content[r.pos:])
|
|
||||||
r.pos += n
|
|
||||||
if r.pos >= len(r.content) {
|
|
||||||
r.pos = r.prefixLen
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *circularConn) Write(b []byte) (n int, err error) { return len(b), nil }
|
|
||||||
|
|
||||||
func (r *circularConn) Close() error { return nil }
|
|
||||||
|
|
||||||
func fakeConn(content string, prefixLen int) *conn {
|
|
||||||
c := &circularConn{content: content, prefixLen: prefixLen}
|
|
||||||
return &conn{buf: bufio.NewReader(c), c: c}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This benchmark is meant to be the same as BenchmarkSelectString, but takes
|
|
||||||
// out some of the factors this package can't control. The numbers are less noisy,
|
|
||||||
// but also the costs of network communication aren't accurately represented.
|
|
||||||
func BenchmarkMockSelectString(b *testing.B) {
|
|
||||||
b.StopTimer()
|
|
||||||
// taken from a recorded run of BenchmarkSelectString
|
|
||||||
// See: http://www.postgresql.org/docs/current/static/protocol-message-formats.html
|
|
||||||
const response = "1\x00\x00\x00\x04" +
|
|
||||||
"t\x00\x00\x00\x06\x00\x00" +
|
|
||||||
"T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" +
|
|
||||||
"Z\x00\x00\x00\x05I" +
|
|
||||||
"2\x00\x00\x00\x04" +
|
|
||||||
"D\x00\x00\x00n\x00\x01\x00\x00\x00d0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
|
|
||||||
"C\x00\x00\x00\rSELECT 1\x00" +
|
|
||||||
"Z\x00\x00\x00\x05I" +
|
|
||||||
"3\x00\x00\x00\x04" +
|
|
||||||
"Z\x00\x00\x00\x05I"
|
|
||||||
c := fakeConn(response, 0)
|
|
||||||
b.StartTimer()
|
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
benchMockQuery(b, c, selectStringQuery)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var seriesRowData = func() string {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
for i := 1; i <= 100; i++ {
|
|
||||||
digits := byte(2)
|
|
||||||
if i >= 100 {
|
|
||||||
digits = 3
|
|
||||||
} else if i < 10 {
|
|
||||||
digits = 1
|
|
||||||
}
|
|
||||||
buf.WriteString("D\x00\x00\x00")
|
|
||||||
buf.WriteByte(10 + digits)
|
|
||||||
buf.WriteString("\x00\x01\x00\x00\x00")
|
|
||||||
buf.WriteByte(digits)
|
|
||||||
buf.WriteString(strconv.Itoa(i))
|
|
||||||
}
|
|
||||||
return buf.String()
|
|
||||||
}()
|
|
||||||
|
|
||||||
func BenchmarkMockSelectSeries(b *testing.B) {
|
|
||||||
b.StopTimer()
|
|
||||||
var response = "1\x00\x00\x00\x04" +
|
|
||||||
"t\x00\x00\x00\x06\x00\x00" +
|
|
||||||
"T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" +
|
|
||||||
"Z\x00\x00\x00\x05I" +
|
|
||||||
"2\x00\x00\x00\x04" +
|
|
||||||
seriesRowData +
|
|
||||||
"C\x00\x00\x00\x0fSELECT 100\x00" +
|
|
||||||
"Z\x00\x00\x00\x05I" +
|
|
||||||
"3\x00\x00\x00\x04" +
|
|
||||||
"Z\x00\x00\x00\x05I"
|
|
||||||
c := fakeConn(response, 0)
|
|
||||||
b.StartTimer()
|
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
benchMockQuery(b, c, selectSeriesQuery)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func benchMockQuery(b *testing.B, c *conn, query string) {
|
|
||||||
stmt, err := c.Prepare(query)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
defer stmt.Close()
|
|
||||||
rows, err := stmt.Query(nil)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
var dest [1]driver.Value
|
|
||||||
for {
|
|
||||||
if err := rows.Next(dest[:]); err != nil {
|
|
||||||
if err == io.EOF {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkPreparedSelectString(b *testing.B) {
|
|
||||||
var result string
|
|
||||||
benchPreparedQuery(b, selectStringQuery, &result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkPreparedSelectSeries(b *testing.B) {
|
|
||||||
var result int
|
|
||||||
benchPreparedQuery(b, selectSeriesQuery, &result)
|
|
||||||
}
|
|
||||||
|
|
||||||
func benchPreparedQuery(b *testing.B, query string, result interface{}) {
|
|
||||||
b.StopTimer()
|
|
||||||
db := openTestConn(b)
|
|
||||||
defer db.Close()
|
|
||||||
stmt, err := db.Prepare(query)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
defer stmt.Close()
|
|
||||||
b.StartTimer()
|
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
benchPreparedQueryLoop(b, db, stmt, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func benchPreparedQueryLoop(b *testing.B, db *sql.DB, stmt *sql.Stmt, result interface{}) {
|
|
||||||
rows, err := stmt.Query()
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
if !rows.Next() {
|
|
||||||
rows.Close()
|
|
||||||
b.Fatal("no rows")
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
for rows.Next() {
|
|
||||||
err = rows.Scan(&result)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal("failed to scan")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// See the comment for BenchmarkMockSelectString.
|
|
||||||
func BenchmarkMockPreparedSelectString(b *testing.B) {
|
|
||||||
b.StopTimer()
|
|
||||||
const parseResponse = "1\x00\x00\x00\x04" +
|
|
||||||
"t\x00\x00\x00\x06\x00\x00" +
|
|
||||||
"T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" +
|
|
||||||
"Z\x00\x00\x00\x05I"
|
|
||||||
const responses = parseResponse +
|
|
||||||
"2\x00\x00\x00\x04" +
|
|
||||||
"D\x00\x00\x00n\x00\x01\x00\x00\x00d0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
|
|
||||||
"C\x00\x00\x00\rSELECT 1\x00" +
|
|
||||||
"Z\x00\x00\x00\x05I"
|
|
||||||
c := fakeConn(responses, len(parseResponse))
|
|
||||||
|
|
||||||
stmt, err := c.Prepare(selectStringQuery)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
b.StartTimer()
|
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
benchPreparedMockQuery(b, c, stmt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkMockPreparedSelectSeries(b *testing.B) {
|
|
||||||
b.StopTimer()
|
|
||||||
const parseResponse = "1\x00\x00\x00\x04" +
|
|
||||||
"t\x00\x00\x00\x06\x00\x00" +
|
|
||||||
"T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" +
|
|
||||||
"Z\x00\x00\x00\x05I"
|
|
||||||
var responses = parseResponse +
|
|
||||||
"2\x00\x00\x00\x04" +
|
|
||||||
seriesRowData +
|
|
||||||
"C\x00\x00\x00\x0fSELECT 100\x00" +
|
|
||||||
"Z\x00\x00\x00\x05I"
|
|
||||||
c := fakeConn(responses, len(parseResponse))
|
|
||||||
|
|
||||||
stmt, err := c.Prepare(selectSeriesQuery)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
b.StartTimer()
|
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
benchPreparedMockQuery(b, c, stmt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func benchPreparedMockQuery(b *testing.B, c *conn, stmt driver.Stmt) {
|
|
||||||
rows, err := stmt.Query(nil)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
var dest [1]driver.Value
|
|
||||||
for {
|
|
||||||
if err := rows.Next(dest[:]); err != nil {
|
|
||||||
if err == io.EOF {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkEncodeInt64(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
encode(¶meterStatus{}, int64(1234), oid.T_int8)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkEncodeFloat64(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
encode(¶meterStatus{}, 3.14159, oid.T_float8)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var testByteString = []byte("abcdefghijklmnopqrstuvwxyz")
|
|
||||||
|
|
||||||
func BenchmarkEncodeByteaHex(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
encode(¶meterStatus{serverVersion: 90000}, testByteString, oid.T_bytea)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func BenchmarkEncodeByteaEscape(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
encode(¶meterStatus{serverVersion: 84000}, testByteString, oid.T_bytea)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkEncodeBool(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
encode(¶meterStatus{}, true, oid.T_bool)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var testTimestamptz = time.Date(2001, time.January, 1, 0, 0, 0, 0, time.Local)
|
|
||||||
|
|
||||||
func BenchmarkEncodeTimestamptz(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
encode(¶meterStatus{}, testTimestamptz, oid.T_timestamptz)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var testIntBytes = []byte("1234")
|
|
||||||
|
|
||||||
func BenchmarkDecodeInt64(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
decode(¶meterStatus{}, testIntBytes, oid.T_int8, formatText)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var testFloatBytes = []byte("3.14159")
|
|
||||||
|
|
||||||
func BenchmarkDecodeFloat64(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
decode(¶meterStatus{}, testFloatBytes, oid.T_float8, formatText)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var testBoolBytes = []byte{'t'}
|
|
||||||
|
|
||||||
func BenchmarkDecodeBool(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
decode(¶meterStatus{}, testBoolBytes, oid.T_bool, formatText)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDecodeBool(t *testing.T) {
|
|
||||||
db := openTestConn(t)
|
|
||||||
rows, err := db.Query("select true")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
rows.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
var testTimestamptzBytes = []byte("2013-09-17 22:15:32.360754-07")
|
|
||||||
|
|
||||||
func BenchmarkDecodeTimestamptz(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
decode(¶meterStatus{}, testTimestamptzBytes, oid.T_timestamptz, formatText)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkDecodeTimestamptzMultiThread(b *testing.B) {
|
|
||||||
oldProcs := runtime.GOMAXPROCS(0)
|
|
||||||
defer runtime.GOMAXPROCS(oldProcs)
|
|
||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
|
||||||
globalLocationCache = newLocationCache()
|
|
||||||
|
|
||||||
f := func(wg *sync.WaitGroup, loops int) {
|
|
||||||
defer wg.Done()
|
|
||||||
for i := 0; i < loops; i++ {
|
|
||||||
decode(¶meterStatus{}, testTimestamptzBytes, oid.T_timestamptz, formatText)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wg := &sync.WaitGroup{}
|
|
||||||
b.ResetTimer()
|
|
||||||
for j := 0; j < 10; j++ {
|
|
||||||
wg.Add(1)
|
|
||||||
go f(wg, b.N/10)
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkLocationCache(b *testing.B) {
|
|
||||||
globalLocationCache = newLocationCache()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
globalLocationCache.getLocation(rand.Intn(10000))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkLocationCacheMultiThread(b *testing.B) {
|
|
||||||
oldProcs := runtime.GOMAXPROCS(0)
|
|
||||||
defer runtime.GOMAXPROCS(oldProcs)
|
|
||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
|
||||||
globalLocationCache = newLocationCache()
|
|
||||||
|
|
||||||
f := func(wg *sync.WaitGroup, loops int) {
|
|
||||||
defer wg.Done()
|
|
||||||
for i := 0; i < loops; i++ {
|
|
||||||
globalLocationCache.getLocation(rand.Intn(10000))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wg := &sync.WaitGroup{}
|
|
||||||
b.ResetTimer()
|
|
||||||
for j := 0; j < 10; j++ {
|
|
||||||
wg.Add(1)
|
|
||||||
go f(wg, b.N/10)
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stress test the performance of parsing results from the wire.
|
|
||||||
func BenchmarkResultParsing(b *testing.B) {
|
|
||||||
b.StopTimer()
|
|
||||||
|
|
||||||
db := openTestConn(b)
|
|
||||||
defer db.Close()
|
|
||||||
_, err := db.Exec("BEGIN")
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
b.StartTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
res, err := db.Query("SELECT generate_series(1, 50000)")
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
res.Close()
|
|
||||||
}
|
|
||||||
}
|
|
1448
vendor/github.com/lib/pq/conn_test.go
generated
vendored
1448
vendor/github.com/lib/pq/conn_test.go
generated
vendored
File diff suppressed because it is too large
Load diff
465
vendor/github.com/lib/pq/copy_test.go
generated
vendored
465
vendor/github.com/lib/pq/copy_test.go
generated
vendored
|
@ -1,465 +0,0 @@
|
||||||
package pq
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"database/sql"
|
|
||||||
"database/sql/driver"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCopyInStmt(t *testing.T) {
|
|
||||||
var stmt string
|
|
||||||
stmt = CopyIn("table name")
|
|
||||||
if stmt != `COPY "table name" () FROM STDIN` {
|
|
||||||
t.Fatal(stmt)
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt = CopyIn("table name", "column 1", "column 2")
|
|
||||||
if stmt != `COPY "table name" ("column 1", "column 2") FROM STDIN` {
|
|
||||||
t.Fatal(stmt)
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt = CopyIn(`table " name """`, `co"lumn""`)
|
|
||||||
if stmt != `COPY "table "" name """"""" ("co""lumn""""") FROM STDIN` {
|
|
||||||
t.Fatal(stmt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCopyInSchemaStmt(t *testing.T) {
|
|
||||||
var stmt string
|
|
||||||
stmt = CopyInSchema("schema name", "table name")
|
|
||||||
if stmt != `COPY "schema name"."table name" () FROM STDIN` {
|
|
||||||
t.Fatal(stmt)
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt = CopyInSchema("schema name", "table name", "column 1", "column 2")
|
|
||||||
if stmt != `COPY "schema name"."table name" ("column 1", "column 2") FROM STDIN` {
|
|
||||||
t.Fatal(stmt)
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt = CopyInSchema(`schema " name """`, `table " name """`, `co"lumn""`)
|
|
||||||
if stmt != `COPY "schema "" name """"""".`+
|
|
||||||
`"table "" name """"""" ("co""lumn""""") FROM STDIN` {
|
|
||||||
t.Fatal(stmt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCopyInMultipleValues(t *testing.T) {
|
|
||||||
db := openTestConn(t)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
txn, err := db.Begin()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer txn.Rollback()
|
|
||||||
|
|
||||||
_, err = txn.Exec("CREATE TEMP TABLE temp (a int, b varchar)")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt, err := txn.Prepare(CopyIn("temp", "a", "b"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
longString := strings.Repeat("#", 500)
|
|
||||||
|
|
||||||
for i := 0; i < 500; i++ {
|
|
||||||
_, err = stmt.Exec(int64(i), longString)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = stmt.Exec()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = stmt.Close()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var num int
|
|
||||||
err = txn.QueryRow("SELECT COUNT(*) FROM temp").Scan(&num)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if num != 500 {
|
|
||||||
t.Fatalf("expected 500 items, not %d", num)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCopyInRaiseStmtTrigger(t *testing.T) {
|
|
||||||
db := openTestConn(t)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
if getServerVersion(t, db) < 90000 {
|
|
||||||
var exists int
|
|
||||||
err := db.QueryRow("SELECT 1 FROM pg_language WHERE lanname = 'plpgsql'").Scan(&exists)
|
|
||||||
if err == sql.ErrNoRows {
|
|
||||||
t.Skip("language PL/PgSQL does not exist; skipping TestCopyInRaiseStmtTrigger")
|
|
||||||
} else if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
txn, err := db.Begin()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer txn.Rollback()
|
|
||||||
|
|
||||||
_, err = txn.Exec("CREATE TEMP TABLE temp (a int, b varchar)")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = txn.Exec(`
|
|
||||||
CREATE OR REPLACE FUNCTION pg_temp.temptest()
|
|
||||||
RETURNS trigger AS
|
|
||||||
$BODY$ begin
|
|
||||||
raise notice 'Hello world';
|
|
||||||
return new;
|
|
||||||
end $BODY$
|
|
||||||
LANGUAGE plpgsql`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = txn.Exec(`
|
|
||||||
CREATE TRIGGER temptest_trigger
|
|
||||||
BEFORE INSERT
|
|
||||||
ON temp
|
|
||||||
FOR EACH ROW
|
|
||||||
EXECUTE PROCEDURE pg_temp.temptest()`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt, err := txn.Prepare(CopyIn("temp", "a", "b"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
longString := strings.Repeat("#", 500)
|
|
||||||
|
|
||||||
_, err = stmt.Exec(int64(1), longString)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = stmt.Exec()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = stmt.Close()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var num int
|
|
||||||
err = txn.QueryRow("SELECT COUNT(*) FROM temp").Scan(&num)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if num != 1 {
|
|
||||||
t.Fatalf("expected 1 items, not %d", num)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCopyInTypes(t *testing.T) {
|
|
||||||
db := openTestConn(t)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
txn, err := db.Begin()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer txn.Rollback()
|
|
||||||
|
|
||||||
_, err = txn.Exec("CREATE TEMP TABLE temp (num INTEGER, text VARCHAR, blob BYTEA, nothing VARCHAR)")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt, err := txn.Prepare(CopyIn("temp", "num", "text", "blob", "nothing"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = stmt.Exec(int64(1234567890), "Héllö\n ☃!\r\t\\", []byte{0, 255, 9, 10, 13}, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = stmt.Exec()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = stmt.Close()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var num int
|
|
||||||
var text string
|
|
||||||
var blob []byte
|
|
||||||
var nothing sql.NullString
|
|
||||||
|
|
||||||
err = txn.QueryRow("SELECT * FROM temp").Scan(&num, &text, &blob, ¬hing)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if num != 1234567890 {
|
|
||||||
t.Fatal("unexpected result", num)
|
|
||||||
}
|
|
||||||
if text != "Héllö\n ☃!\r\t\\" {
|
|
||||||
t.Fatal("unexpected result", text)
|
|
||||||
}
|
|
||||||
if bytes.Compare(blob, []byte{0, 255, 9, 10, 13}) != 0 {
|
|
||||||
t.Fatal("unexpected result", blob)
|
|
||||||
}
|
|
||||||
if nothing.Valid {
|
|
||||||
t.Fatal("unexpected result", nothing.String)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCopyInWrongType(t *testing.T) {
|
|
||||||
db := openTestConn(t)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
txn, err := db.Begin()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer txn.Rollback()
|
|
||||||
|
|
||||||
_, err = txn.Exec("CREATE TEMP TABLE temp (num INTEGER)")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt, err := txn.Prepare(CopyIn("temp", "num"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer stmt.Close()
|
|
||||||
|
|
||||||
_, err = stmt.Exec("Héllö\n ☃!\r\t\\")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = stmt.Exec()
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected error")
|
|
||||||
}
|
|
||||||
if pge := err.(*Error); pge.Code.Name() != "invalid_text_representation" {
|
|
||||||
t.Fatalf("expected 'invalid input syntax for integer' error, got %s (%+v)", pge.Code.Name(), pge)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCopyOutsideOfTxnError(t *testing.T) {
|
|
||||||
db := openTestConn(t)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
_, err := db.Prepare(CopyIn("temp", "num"))
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("COPY outside of transaction did not return an error")
|
|
||||||
}
|
|
||||||
if err != errCopyNotSupportedOutsideTxn {
|
|
||||||
t.Fatalf("expected %s, got %s", err, err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCopyInBinaryError(t *testing.T) {
|
|
||||||
db := openTestConn(t)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
txn, err := db.Begin()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer txn.Rollback()
|
|
||||||
|
|
||||||
_, err = txn.Exec("CREATE TEMP TABLE temp (num INTEGER)")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
_, err = txn.Prepare("COPY temp (num) FROM STDIN WITH binary")
|
|
||||||
if err != errBinaryCopyNotSupported {
|
|
||||||
t.Fatalf("expected %s, got %+v", errBinaryCopyNotSupported, err)
|
|
||||||
}
|
|
||||||
// check that the protocol is in a valid state
|
|
||||||
err = txn.Rollback()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCopyFromError(t *testing.T) {
|
|
||||||
db := openTestConn(t)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
txn, err := db.Begin()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer txn.Rollback()
|
|
||||||
|
|
||||||
_, err = txn.Exec("CREATE TEMP TABLE temp (num INTEGER)")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
_, err = txn.Prepare("COPY temp (num) TO STDOUT")
|
|
||||||
if err != errCopyToNotSupported {
|
|
||||||
t.Fatalf("expected %s, got %+v", errCopyToNotSupported, err)
|
|
||||||
}
|
|
||||||
// check that the protocol is in a valid state
|
|
||||||
err = txn.Rollback()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCopySyntaxError(t *testing.T) {
|
|
||||||
db := openTestConn(t)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
txn, err := db.Begin()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer txn.Rollback()
|
|
||||||
|
|
||||||
_, err = txn.Prepare("COPY ")
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected error")
|
|
||||||
}
|
|
||||||
if pge := err.(*Error); pge.Code.Name() != "syntax_error" {
|
|
||||||
t.Fatalf("expected syntax error, got %s (%+v)", pge.Code.Name(), pge)
|
|
||||||
}
|
|
||||||
// check that the protocol is in a valid state
|
|
||||||
err = txn.Rollback()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tests for connection errors in copyin.resploop()
|
|
||||||
func TestCopyRespLoopConnectionError(t *testing.T) {
|
|
||||||
db := openTestConn(t)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
txn, err := db.Begin()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer txn.Rollback()
|
|
||||||
|
|
||||||
var pid int
|
|
||||||
err = txn.QueryRow("SELECT pg_backend_pid()").Scan(&pid)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = txn.Exec("CREATE TEMP TABLE temp (a int)")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt, err := txn.Prepare(CopyIn("temp", "a"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer stmt.Close()
|
|
||||||
|
|
||||||
_, err = db.Exec("SELECT pg_terminate_backend($1)", pid)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if getServerVersion(t, db) < 90500 {
|
|
||||||
// We have to try and send something over, since postgres before
|
|
||||||
// version 9.5 won't process SIGTERMs while it's waiting for
|
|
||||||
// CopyData/CopyEnd messages; see tcop/postgres.c.
|
|
||||||
_, err = stmt.Exec(1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_, err = stmt.Exec()
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("expected error")
|
|
||||||
}
|
|
||||||
pge, ok := err.(*Error)
|
|
||||||
if !ok {
|
|
||||||
if err == driver.ErrBadConn {
|
|
||||||
// likely an EPIPE
|
|
||||||
} else {
|
|
||||||
t.Fatalf("expected *pq.Error or driver.ErrBadConn, got %+#v", err)
|
|
||||||
}
|
|
||||||
} else if pge.Code.Name() != "admin_shutdown" {
|
|
||||||
t.Fatalf("expected admin_shutdown, got %s", pge.Code.Name())
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = stmt.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkCopyIn(b *testing.B) {
|
|
||||||
db := openTestConn(b)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
txn, err := db.Begin()
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
defer txn.Rollback()
|
|
||||||
|
|
||||||
_, err = txn.Exec("CREATE TEMP TABLE temp (a int, b varchar)")
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt, err := txn.Prepare(CopyIn("temp", "a", "b"))
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
_, err = stmt.Exec(int64(i), "hello world!")
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = stmt.Exec()
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = stmt.Close()
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var num int
|
|
||||||
err = txn.QueryRow("SELECT COUNT(*) FROM temp").Scan(&num)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if num != b.N {
|
|
||||||
b.Fatalf("expected %d items, not %d", b.N, num)
|
|
||||||
}
|
|
||||||
}
|
|
738
vendor/github.com/lib/pq/encode_test.go
generated
vendored
738
vendor/github.com/lib/pq/encode_test.go
generated
vendored
|
@ -1,738 +0,0 @@
|
||||||
package pq
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"database/sql"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/lib/pq/oid"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestScanTimestamp(t *testing.T) {
|
|
||||||
var nt NullTime
|
|
||||||
tn := time.Now()
|
|
||||||
nt.Scan(tn)
|
|
||||||
if !nt.Valid {
|
|
||||||
t.Errorf("Expected Valid=false")
|
|
||||||
}
|
|
||||||
if nt.Time != tn {
|
|
||||||
t.Errorf("Time value mismatch")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestScanNilTimestamp(t *testing.T) {
|
|
||||||
var nt NullTime
|
|
||||||
nt.Scan(nil)
|
|
||||||
if nt.Valid {
|
|
||||||
t.Errorf("Expected Valid=false")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var timeTests = []struct {
|
|
||||||
str string
|
|
||||||
timeval time.Time
|
|
||||||
}{
|
|
||||||
{"22001-02-03", time.Date(22001, time.February, 3, 0, 0, 0, 0, time.FixedZone("", 0))},
|
|
||||||
{"2001-02-03", time.Date(2001, time.February, 3, 0, 0, 0, 0, time.FixedZone("", 0))},
|
|
||||||
{"2001-02-03 04:05:06", time.Date(2001, time.February, 3, 4, 5, 6, 0, time.FixedZone("", 0))},
|
|
||||||
{"2001-02-03 04:05:06.000001", time.Date(2001, time.February, 3, 4, 5, 6, 1000, time.FixedZone("", 0))},
|
|
||||||
{"2001-02-03 04:05:06.00001", time.Date(2001, time.February, 3, 4, 5, 6, 10000, time.FixedZone("", 0))},
|
|
||||||
{"2001-02-03 04:05:06.0001", time.Date(2001, time.February, 3, 4, 5, 6, 100000, time.FixedZone("", 0))},
|
|
||||||
{"2001-02-03 04:05:06.001", time.Date(2001, time.February, 3, 4, 5, 6, 1000000, time.FixedZone("", 0))},
|
|
||||||
{"2001-02-03 04:05:06.01", time.Date(2001, time.February, 3, 4, 5, 6, 10000000, time.FixedZone("", 0))},
|
|
||||||
{"2001-02-03 04:05:06.1", time.Date(2001, time.February, 3, 4, 5, 6, 100000000, time.FixedZone("", 0))},
|
|
||||||
{"2001-02-03 04:05:06.12", time.Date(2001, time.February, 3, 4, 5, 6, 120000000, time.FixedZone("", 0))},
|
|
||||||
{"2001-02-03 04:05:06.123", time.Date(2001, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))},
|
|
||||||
{"2001-02-03 04:05:06.1234", time.Date(2001, time.February, 3, 4, 5, 6, 123400000, time.FixedZone("", 0))},
|
|
||||||
{"2001-02-03 04:05:06.12345", time.Date(2001, time.February, 3, 4, 5, 6, 123450000, time.FixedZone("", 0))},
|
|
||||||
{"2001-02-03 04:05:06.123456", time.Date(2001, time.February, 3, 4, 5, 6, 123456000, time.FixedZone("", 0))},
|
|
||||||
{"2001-02-03 04:05:06.123-07", time.Date(2001, time.February, 3, 4, 5, 6, 123000000,
|
|
||||||
time.FixedZone("", -7*60*60))},
|
|
||||||
{"2001-02-03 04:05:06-07", time.Date(2001, time.February, 3, 4, 5, 6, 0,
|
|
||||||
time.FixedZone("", -7*60*60))},
|
|
||||||
{"2001-02-03 04:05:06-07:42", time.Date(2001, time.February, 3, 4, 5, 6, 0,
|
|
||||||
time.FixedZone("", -(7*60*60+42*60)))},
|
|
||||||
{"2001-02-03 04:05:06-07:30:09", time.Date(2001, time.February, 3, 4, 5, 6, 0,
|
|
||||||
time.FixedZone("", -(7*60*60+30*60+9)))},
|
|
||||||
{"2001-02-03 04:05:06+07", time.Date(2001, time.February, 3, 4, 5, 6, 0,
|
|
||||||
time.FixedZone("", 7*60*60))},
|
|
||||||
{"0011-02-03 04:05:06 BC", time.Date(-10, time.February, 3, 4, 5, 6, 0, time.FixedZone("", 0))},
|
|
||||||
{"0011-02-03 04:05:06.123 BC", time.Date(-10, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))},
|
|
||||||
{"0011-02-03 04:05:06.123-07 BC", time.Date(-10, time.February, 3, 4, 5, 6, 123000000,
|
|
||||||
time.FixedZone("", -7*60*60))},
|
|
||||||
{"0001-02-03 04:05:06.123", time.Date(1, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))},
|
|
||||||
{"0001-02-03 04:05:06.123 BC", time.Date(1, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0)).AddDate(-1, 0, 0)},
|
|
||||||
{"0001-02-03 04:05:06.123 BC", time.Date(0, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))},
|
|
||||||
{"0002-02-03 04:05:06.123 BC", time.Date(0, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0)).AddDate(-1, 0, 0)},
|
|
||||||
{"0002-02-03 04:05:06.123 BC", time.Date(-1, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))},
|
|
||||||
{"12345-02-03 04:05:06.1", time.Date(12345, time.February, 3, 4, 5, 6, 100000000, time.FixedZone("", 0))},
|
|
||||||
{"123456-02-03 04:05:06.1", time.Date(123456, time.February, 3, 4, 5, 6, 100000000, time.FixedZone("", 0))},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that parsing the string results in the expected value.
|
|
||||||
func TestParseTs(t *testing.T) {
|
|
||||||
for i, tt := range timeTests {
|
|
||||||
val, err := ParseTimestamp(nil, tt.str)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("%d: got error: %v", i, err)
|
|
||||||
} else if val.String() != tt.timeval.String() {
|
|
||||||
t.Errorf("%d: expected to parse %q into %q; got %q",
|
|
||||||
i, tt.str, tt.timeval, val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var timeErrorTests = []string{
|
|
||||||
"2001",
|
|
||||||
"2001-2-03",
|
|
||||||
"2001-02-3",
|
|
||||||
"2001-02-03 ",
|
|
||||||
"2001-02-03 04",
|
|
||||||
"2001-02-03 04:",
|
|
||||||
"2001-02-03 04:05",
|
|
||||||
"2001-02-03 04:05:",
|
|
||||||
"2001-02-03 04:05:6",
|
|
||||||
"2001-02-03 04:05:06.123 B",
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that parsing the string results in an error.
|
|
||||||
func TestParseTsErrors(t *testing.T) {
|
|
||||||
for i, tt := range timeErrorTests {
|
|
||||||
_, err := ParseTimestamp(nil, tt)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("%d: expected an error from parsing: %v", i, tt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now test that sending the value into the database and parsing it back
|
|
||||||
// returns the same time.Time value.
|
|
||||||
func TestEncodeAndParseTs(t *testing.T) {
|
|
||||||
db, err := openTestConnConninfo("timezone='Etc/UTC'")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
for i, tt := range timeTests {
|
|
||||||
var dbstr string
|
|
||||||
err = db.QueryRow("SELECT ($1::timestamptz)::text", tt.timeval).Scan(&dbstr)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("%d: could not send value %q to the database: %s", i, tt.timeval, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
val, err := ParseTimestamp(nil, dbstr)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("%d: could not parse value %q: %s", i, dbstr, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
val = val.In(tt.timeval.Location())
|
|
||||||
if val.String() != tt.timeval.String() {
|
|
||||||
t.Errorf("%d: expected to parse %q into %q; got %q", i, dbstr, tt.timeval, val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var formatTimeTests = []struct {
|
|
||||||
time time.Time
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{time.Time{}, "0001-01-01T00:00:00Z"},
|
|
||||||
{time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 0)), "2001-02-03T04:05:06.123456789Z"},
|
|
||||||
{time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 2*60*60)), "2001-02-03T04:05:06.123456789+02:00"},
|
|
||||||
{time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", -6*60*60)), "2001-02-03T04:05:06.123456789-06:00"},
|
|
||||||
{time.Date(2001, time.February, 3, 4, 5, 6, 0, time.FixedZone("", -(7*60*60+30*60+9))), "2001-02-03T04:05:06-07:30:09"},
|
|
||||||
|
|
||||||
{time.Date(1, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 0)), "0001-02-03T04:05:06.123456789Z"},
|
|
||||||
{time.Date(1, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 2*60*60)), "0001-02-03T04:05:06.123456789+02:00"},
|
|
||||||
{time.Date(1, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", -6*60*60)), "0001-02-03T04:05:06.123456789-06:00"},
|
|
||||||
|
|
||||||
{time.Date(0, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 0)), "0001-02-03T04:05:06.123456789Z BC"},
|
|
||||||
{time.Date(0, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 2*60*60)), "0001-02-03T04:05:06.123456789+02:00 BC"},
|
|
||||||
{time.Date(0, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", -6*60*60)), "0001-02-03T04:05:06.123456789-06:00 BC"},
|
|
||||||
|
|
||||||
{time.Date(1, time.February, 3, 4, 5, 6, 0, time.FixedZone("", -(7*60*60+30*60+9))), "0001-02-03T04:05:06-07:30:09"},
|
|
||||||
{time.Date(0, time.February, 3, 4, 5, 6, 0, time.FixedZone("", -(7*60*60+30*60+9))), "0001-02-03T04:05:06-07:30:09 BC"},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFormatTs(t *testing.T) {
|
|
||||||
for i, tt := range formatTimeTests {
|
|
||||||
val := string(formatTs(tt.time))
|
|
||||||
if val != tt.expected {
|
|
||||||
t.Errorf("%d: incorrect time format %q, want %q", i, val, tt.expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTimestampWithTimeZone(t *testing.T) {
|
|
||||||
db := openTestConn(t)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
tx, err := db.Begin()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer tx.Rollback()
|
|
||||||
|
|
||||||
// try several different locations, all included in Go's zoneinfo.zip
|
|
||||||
for _, locName := range []string{
|
|
||||||
"UTC",
|
|
||||||
"America/Chicago",
|
|
||||||
"America/New_York",
|
|
||||||
"Australia/Darwin",
|
|
||||||
"Australia/Perth",
|
|
||||||
} {
|
|
||||||
loc, err := time.LoadLocation(locName)
|
|
||||||
if err != nil {
|
|
||||||
t.Logf("Could not load time zone %s - skipping", locName)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Postgres timestamps have a resolution of 1 microsecond, so don't
|
|
||||||
// use the full range of the Nanosecond argument
|
|
||||||
refTime := time.Date(2012, 11, 6, 10, 23, 42, 123456000, loc)
|
|
||||||
|
|
||||||
for _, pgTimeZone := range []string{"US/Eastern", "Australia/Darwin"} {
|
|
||||||
// Switch Postgres's timezone to test different output timestamp formats
|
|
||||||
_, err = tx.Exec(fmt.Sprintf("set time zone '%s'", pgTimeZone))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var gotTime time.Time
|
|
||||||
row := tx.QueryRow("select $1::timestamp with time zone", refTime)
|
|
||||||
err = row.Scan(&gotTime)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !refTime.Equal(gotTime) {
|
|
||||||
t.Errorf("timestamps not equal: %s != %s", refTime, gotTime)
|
|
||||||
}
|
|
||||||
|
|
||||||
// check that the time zone is set correctly based on TimeZone
|
|
||||||
pgLoc, err := time.LoadLocation(pgTimeZone)
|
|
||||||
if err != nil {
|
|
||||||
t.Logf("Could not load time zone %s - skipping", pgLoc)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
translated := refTime.In(pgLoc)
|
|
||||||
if translated.String() != gotTime.String() {
|
|
||||||
t.Errorf("timestamps not equal: %s != %s", translated, gotTime)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTimestampWithOutTimezone(t *testing.T) {
|
|
||||||
db := openTestConn(t)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
test := func(ts, pgts string) {
|
|
||||||
r, err := db.Query("SELECT $1::timestamp", pgts)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Could not run query: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
n := r.Next()
|
|
||||||
|
|
||||||
if n != true {
|
|
||||||
t.Fatal("Expected at least one row")
|
|
||||||
}
|
|
||||||
|
|
||||||
var result time.Time
|
|
||||||
err = r.Scan(&result)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Did not expect error scanning row: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
expected, err := time.Parse(time.RFC3339, ts)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Could not parse test time literal: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !result.Equal(expected) {
|
|
||||||
t.Fatalf("Expected time to match %v: got mismatch %v",
|
|
||||||
expected, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
n = r.Next()
|
|
||||||
if n != false {
|
|
||||||
t.Fatal("Expected only one row")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
test("2000-01-01T00:00:00Z", "2000-01-01T00:00:00")
|
|
||||||
|
|
||||||
// Test higher precision time
|
|
||||||
test("2013-01-04T20:14:58.80033Z", "2013-01-04 20:14:58.80033")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInfinityTimestamp(t *testing.T) {
|
|
||||||
db := openTestConn(t)
|
|
||||||
defer db.Close()
|
|
||||||
var err error
|
|
||||||
var resultT time.Time
|
|
||||||
|
|
||||||
expectedErrorStrPrefix := `sql: Scan error on column index 0: unsupported`
|
|
||||||
type testCases []struct {
|
|
||||||
Query string
|
|
||||||
Param string
|
|
||||||
ExpectedErrStrPrefix string
|
|
||||||
ExpectedVal interface{}
|
|
||||||
}
|
|
||||||
tc := testCases{
|
|
||||||
{"SELECT $1::timestamp", "-infinity", expectedErrorStrPrefix, "-infinity"},
|
|
||||||
{"SELECT $1::timestamptz", "-infinity", expectedErrorStrPrefix, "-infinity"},
|
|
||||||
{"SELECT $1::timestamp", "infinity", expectedErrorStrPrefix, "infinity"},
|
|
||||||
{"SELECT $1::timestamptz", "infinity", expectedErrorStrPrefix, "infinity"},
|
|
||||||
}
|
|
||||||
// try to assert []byte to time.Time
|
|
||||||
for _, q := range tc {
|
|
||||||
err = db.QueryRow(q.Query, q.Param).Scan(&resultT)
|
|
||||||
if !strings.HasPrefix(err.Error(), q.ExpectedErrStrPrefix) {
|
|
||||||
t.Errorf("Scanning -/+infinity, expected error to have prefix %q, got %q", q.ExpectedErrStrPrefix, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// yield []byte
|
|
||||||
for _, q := range tc {
|
|
||||||
var resultI interface{}
|
|
||||||
err = db.QueryRow(q.Query, q.Param).Scan(&resultI)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Scanning -/+infinity, expected no error, got %q", err)
|
|
||||||
}
|
|
||||||
result, ok := resultI.([]byte)
|
|
||||||
if !ok {
|
|
||||||
t.Errorf("Scanning -/+infinity, expected []byte, got %#v", resultI)
|
|
||||||
}
|
|
||||||
if string(result) != q.ExpectedVal {
|
|
||||||
t.Errorf("Scanning -/+infinity, expected %q, got %q", q.ExpectedVal, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
y1500 := time.Date(1500, time.January, 1, 0, 0, 0, 0, time.UTC)
|
|
||||||
y2500 := time.Date(2500, time.January, 1, 0, 0, 0, 0, time.UTC)
|
|
||||||
EnableInfinityTs(y1500, y2500)
|
|
||||||
|
|
||||||
err = db.QueryRow("SELECT $1::timestamp", "infinity").Scan(&resultT)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Scanning infinity, expected no error, got %q", err)
|
|
||||||
}
|
|
||||||
if !resultT.Equal(y2500) {
|
|
||||||
t.Errorf("Scanning infinity, expected %q, got %q", y2500, resultT)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = db.QueryRow("SELECT $1::timestamptz", "infinity").Scan(&resultT)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Scanning infinity, expected no error, got %q", err)
|
|
||||||
}
|
|
||||||
if !resultT.Equal(y2500) {
|
|
||||||
t.Errorf("Scanning Infinity, expected time %q, got %q", y2500, resultT.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
err = db.QueryRow("SELECT $1::timestamp", "-infinity").Scan(&resultT)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Scanning -infinity, expected no error, got %q", err)
|
|
||||||
}
|
|
||||||
if !resultT.Equal(y1500) {
|
|
||||||
t.Errorf("Scanning -infinity, expected time %q, got %q", y1500, resultT.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
err = db.QueryRow("SELECT $1::timestamptz", "-infinity").Scan(&resultT)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Scanning -infinity, expected no error, got %q", err)
|
|
||||||
}
|
|
||||||
if !resultT.Equal(y1500) {
|
|
||||||
t.Errorf("Scanning -infinity, expected time %q, got %q", y1500, resultT.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
y_1500 := time.Date(-1500, time.January, 1, 0, 0, 0, 0, time.UTC)
|
|
||||||
y11500 := time.Date(11500, time.January, 1, 0, 0, 0, 0, time.UTC)
|
|
||||||
var s string
|
|
||||||
err = db.QueryRow("SELECT $1::timestamp::text", y_1500).Scan(&s)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Encoding -infinity, expected no error, got %q", err)
|
|
||||||
}
|
|
||||||
if s != "-infinity" {
|
|
||||||
t.Errorf("Encoding -infinity, expected %q, got %q", "-infinity", s)
|
|
||||||
}
|
|
||||||
err = db.QueryRow("SELECT $1::timestamptz::text", y_1500).Scan(&s)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Encoding -infinity, expected no error, got %q", err)
|
|
||||||
}
|
|
||||||
if s != "-infinity" {
|
|
||||||
t.Errorf("Encoding -infinity, expected %q, got %q", "-infinity", s)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = db.QueryRow("SELECT $1::timestamp::text", y11500).Scan(&s)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Encoding infinity, expected no error, got %q", err)
|
|
||||||
}
|
|
||||||
if s != "infinity" {
|
|
||||||
t.Errorf("Encoding infinity, expected %q, got %q", "infinity", s)
|
|
||||||
}
|
|
||||||
err = db.QueryRow("SELECT $1::timestamptz::text", y11500).Scan(&s)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Encoding infinity, expected no error, got %q", err)
|
|
||||||
}
|
|
||||||
if s != "infinity" {
|
|
||||||
t.Errorf("Encoding infinity, expected %q, got %q", "infinity", s)
|
|
||||||
}
|
|
||||||
|
|
||||||
disableInfinityTs()
|
|
||||||
|
|
||||||
var panicErrorString string
|
|
||||||
func() {
|
|
||||||
defer func() {
|
|
||||||
panicErrorString, _ = recover().(string)
|
|
||||||
}()
|
|
||||||
EnableInfinityTs(y2500, y1500)
|
|
||||||
}()
|
|
||||||
if panicErrorString != infinityTsNegativeMustBeSmaller {
|
|
||||||
t.Errorf("Expected error, %q, got %q", infinityTsNegativeMustBeSmaller, panicErrorString)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStringWithNul(t *testing.T) {
|
|
||||||
db := openTestConn(t)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
hello0world := string("hello\x00world")
|
|
||||||
_, err := db.Query("SELECT $1::text", &hello0world)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Postgres accepts a string with nul in it; " +
|
|
||||||
"injection attacks may be plausible")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestByteSliceToText(t *testing.T) {
|
|
||||||
db := openTestConn(t)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
b := []byte("hello world")
|
|
||||||
row := db.QueryRow("SELECT $1::text", b)
|
|
||||||
|
|
||||||
var result []byte
|
|
||||||
err := row.Scan(&result)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if string(result) != string(b) {
|
|
||||||
t.Fatalf("expected %v but got %v", b, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStringToBytea(t *testing.T) {
|
|
||||||
db := openTestConn(t)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
b := "hello world"
|
|
||||||
row := db.QueryRow("SELECT $1::bytea", b)
|
|
||||||
|
|
||||||
var result []byte
|
|
||||||
err := row.Scan(&result)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !bytes.Equal(result, []byte(b)) {
|
|
||||||
t.Fatalf("expected %v but got %v", b, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTextByteSliceToUUID(t *testing.T) {
|
|
||||||
db := openTestConn(t)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
b := []byte("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11")
|
|
||||||
row := db.QueryRow("SELECT $1::uuid", b)
|
|
||||||
|
|
||||||
var result string
|
|
||||||
err := row.Scan(&result)
|
|
||||||
if forceBinaryParameters() {
|
|
||||||
pqErr := err.(*Error)
|
|
||||||
if pqErr == nil {
|
|
||||||
t.Errorf("Expected to get error")
|
|
||||||
} else if pqErr.Code != "22P03" {
|
|
||||||
t.Fatalf("Expected to get invalid binary encoding error (22P03), got %s", pqErr.Code)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if result != string(b) {
|
|
||||||
t.Fatalf("expected %v but got %v", b, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBinaryByteSlicetoUUID(t *testing.T) {
|
|
||||||
db := openTestConn(t)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
b := []byte{'\xa0', '\xee', '\xbc', '\x99',
|
|
||||||
'\x9c', '\x0b',
|
|
||||||
'\x4e', '\xf8',
|
|
||||||
'\xbb', '\x00', '\x6b',
|
|
||||||
'\xb9', '\xbd', '\x38', '\x0a', '\x11'}
|
|
||||||
row := db.QueryRow("SELECT $1::uuid", b)
|
|
||||||
|
|
||||||
var result string
|
|
||||||
err := row.Scan(&result)
|
|
||||||
if forceBinaryParameters() {
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if result != string("a0eebc99-9c0b-4ef8-bb00-6bb9bd380a11") {
|
|
||||||
t.Fatalf("expected %v but got %v", b, result)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pqErr := err.(*Error)
|
|
||||||
if pqErr == nil {
|
|
||||||
t.Errorf("Expected to get error")
|
|
||||||
} else if pqErr.Code != "22021" {
|
|
||||||
t.Fatalf("Expected to get invalid byte sequence for encoding error (22021), got %s", pqErr.Code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStringToUUID(t *testing.T) {
|
|
||||||
db := openTestConn(t)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
s := "a0eebc99-9c0b-4ef8-bb00-6bb9bd380a11"
|
|
||||||
row := db.QueryRow("SELECT $1::uuid", s)
|
|
||||||
|
|
||||||
var result string
|
|
||||||
err := row.Scan(&result)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if result != s {
|
|
||||||
t.Fatalf("expected %v but got %v", s, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTextByteSliceToInt(t *testing.T) {
|
|
||||||
db := openTestConn(t)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
expected := 12345678
|
|
||||||
b := []byte(fmt.Sprintf("%d", expected))
|
|
||||||
row := db.QueryRow("SELECT $1::int", b)
|
|
||||||
|
|
||||||
var result int
|
|
||||||
err := row.Scan(&result)
|
|
||||||
if forceBinaryParameters() {
|
|
||||||
pqErr := err.(*Error)
|
|
||||||
if pqErr == nil {
|
|
||||||
t.Errorf("Expected to get error")
|
|
||||||
} else if pqErr.Code != "22P03" {
|
|
||||||
t.Fatalf("Expected to get invalid binary encoding error (22P03), got %s", pqErr.Code)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if result != expected {
|
|
||||||
t.Fatalf("expected %v but got %v", expected, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBinaryByteSliceToInt(t *testing.T) {
|
|
||||||
db := openTestConn(t)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
expected := 12345678
|
|
||||||
b := []byte{'\x00', '\xbc', '\x61', '\x4e'}
|
|
||||||
row := db.QueryRow("SELECT $1::int", b)
|
|
||||||
|
|
||||||
var result int
|
|
||||||
err := row.Scan(&result)
|
|
||||||
if forceBinaryParameters() {
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if result != expected {
|
|
||||||
t.Fatalf("expected %v but got %v", expected, result)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pqErr := err.(*Error)
|
|
||||||
if pqErr == nil {
|
|
||||||
t.Errorf("Expected to get error")
|
|
||||||
} else if pqErr.Code != "22021" {
|
|
||||||
t.Fatalf("Expected to get invalid byte sequence for encoding error (22021), got %s", pqErr.Code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTextDecodeIntoString(t *testing.T) {
|
|
||||||
input := []byte("hello world")
|
|
||||||
want := string(input)
|
|
||||||
for _, typ := range []oid.Oid{oid.T_char, oid.T_varchar, oid.T_text} {
|
|
||||||
got := decode(¶meterStatus{}, input, typ, formatText)
|
|
||||||
if got != want {
|
|
||||||
t.Errorf("invalid string decoding output for %T(%+v), got %v but expected %v", typ, typ, got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestByteaOutputFormatEncoding(t *testing.T) {
|
|
||||||
input := []byte("\\x\x00\x01\x02\xFF\xFEabcdefg0123")
|
|
||||||
want := []byte("\\x5c78000102fffe6162636465666730313233")
|
|
||||||
got := encode(¶meterStatus{serverVersion: 90000}, input, oid.T_bytea)
|
|
||||||
if !bytes.Equal(want, got) {
|
|
||||||
t.Errorf("invalid hex bytea output, got %v but expected %v", got, want)
|
|
||||||
}
|
|
||||||
|
|
||||||
want = []byte("\\\\x\\000\\001\\002\\377\\376abcdefg0123")
|
|
||||||
got = encode(¶meterStatus{serverVersion: 84000}, input, oid.T_bytea)
|
|
||||||
if !bytes.Equal(want, got) {
|
|
||||||
t.Errorf("invalid escape bytea output, got %v but expected %v", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestByteaOutputFormats(t *testing.T) {
|
|
||||||
db := openTestConn(t)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
if getServerVersion(t, db) < 90000 {
|
|
||||||
// skip
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
testByteaOutputFormat := func(f string, usePrepared bool) {
|
|
||||||
expectedData := []byte("\x5c\x78\x00\xff\x61\x62\x63\x01\x08")
|
|
||||||
sqlQuery := "SELECT decode('5c7800ff6162630108', 'hex')"
|
|
||||||
|
|
||||||
var data []byte
|
|
||||||
|
|
||||||
// use a txn to avoid relying on getting the same connection
|
|
||||||
txn, err := db.Begin()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer txn.Rollback()
|
|
||||||
|
|
||||||
_, err = txn.Exec("SET LOCAL bytea_output TO " + f)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
var rows *sql.Rows
|
|
||||||
var stmt *sql.Stmt
|
|
||||||
if usePrepared {
|
|
||||||
stmt, err = txn.Prepare(sqlQuery)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
rows, err = stmt.Query()
|
|
||||||
} else {
|
|
||||||
// use Query; QueryRow would hide the actual error
|
|
||||||
rows, err = txn.Query(sqlQuery)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if !rows.Next() {
|
|
||||||
if rows.Err() != nil {
|
|
||||||
t.Fatal(rows.Err())
|
|
||||||
}
|
|
||||||
t.Fatal("shouldn't happen")
|
|
||||||
}
|
|
||||||
err = rows.Scan(&data)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
err = rows.Close()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if stmt != nil {
|
|
||||||
err = stmt.Close()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !bytes.Equal(data, expectedData) {
|
|
||||||
t.Errorf("unexpected bytea value %v for format %s; expected %v", data, f, expectedData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
testByteaOutputFormat("hex", false)
|
|
||||||
testByteaOutputFormat("escape", false)
|
|
||||||
testByteaOutputFormat("hex", true)
|
|
||||||
testByteaOutputFormat("escape", true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAppendEncodedText(t *testing.T) {
|
|
||||||
var buf []byte
|
|
||||||
|
|
||||||
buf = appendEncodedText(¶meterStatus{serverVersion: 90000}, buf, int64(10))
|
|
||||||
buf = append(buf, '\t')
|
|
||||||
buf = appendEncodedText(¶meterStatus{serverVersion: 90000}, buf, 42.0000000001)
|
|
||||||
buf = append(buf, '\t')
|
|
||||||
buf = appendEncodedText(¶meterStatus{serverVersion: 90000}, buf, "hello\tworld")
|
|
||||||
buf = append(buf, '\t')
|
|
||||||
buf = appendEncodedText(¶meterStatus{serverVersion: 90000}, buf, []byte{0, 128, 255})
|
|
||||||
|
|
||||||
if string(buf) != "10\t42.0000000001\thello\\tworld\t\\\\x0080ff" {
|
|
||||||
t.Fatal(string(buf))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAppendEscapedText(t *testing.T) {
|
|
||||||
if esc := appendEscapedText(nil, "hallo\tescape"); string(esc) != "hallo\\tescape" {
|
|
||||||
t.Fatal(string(esc))
|
|
||||||
}
|
|
||||||
if esc := appendEscapedText(nil, "hallo\\tescape\n"); string(esc) != "hallo\\\\tescape\\n" {
|
|
||||||
t.Fatal(string(esc))
|
|
||||||
}
|
|
||||||
if esc := appendEscapedText(nil, "\n\r\t\f"); string(esc) != "\\n\\r\\t\f" {
|
|
||||||
t.Fatal(string(esc))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAppendEscapedTextExistingBuffer(t *testing.T) {
|
|
||||||
var buf []byte
|
|
||||||
buf = []byte("123\t")
|
|
||||||
if esc := appendEscapedText(buf, "hallo\tescape"); string(esc) != "123\thallo\\tescape" {
|
|
||||||
t.Fatal(string(esc))
|
|
||||||
}
|
|
||||||
buf = []byte("123\t")
|
|
||||||
if esc := appendEscapedText(buf, "hallo\\tescape\n"); string(esc) != "123\thallo\\\\tescape\\n" {
|
|
||||||
t.Fatal(string(esc))
|
|
||||||
}
|
|
||||||
buf = []byte("123\t")
|
|
||||||
if esc := appendEscapedText(buf, "\n\r\t\f"); string(esc) != "123\t\\n\\r\\t\f" {
|
|
||||||
t.Fatal(string(esc))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkAppendEscapedText(b *testing.B) {
|
|
||||||
longString := ""
|
|
||||||
for i := 0; i < 100; i++ {
|
|
||||||
longString += "123456789\n"
|
|
||||||
}
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
appendEscapedText(nil, longString)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkAppendEscapedTextNoEscape(b *testing.B) {
|
|
||||||
longString := ""
|
|
||||||
for i := 0; i < 100; i++ {
|
|
||||||
longString += "1234567890"
|
|
||||||
}
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
appendEscapedText(nil, longString)
|
|
||||||
}
|
|
||||||
}
|
|
574
vendor/github.com/lib/pq/notify_test.go
generated
vendored
574
vendor/github.com/lib/pq/notify_test.go
generated
vendored
|
@ -1,574 +0,0 @@
|
||||||
package pq
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var errNilNotification = errors.New("nil notification")
|
|
||||||
|
|
||||||
func expectNotification(t *testing.T, ch <-chan *Notification, relname string, extra string) error {
|
|
||||||
select {
|
|
||||||
case n := <-ch:
|
|
||||||
if n == nil {
|
|
||||||
return errNilNotification
|
|
||||||
}
|
|
||||||
if n.Channel != relname || n.Extra != extra {
|
|
||||||
return fmt.Errorf("unexpected notification %v", n)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
case <-time.After(1500 * time.Millisecond):
|
|
||||||
return fmt.Errorf("timeout")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func expectNoNotification(t *testing.T, ch <-chan *Notification) error {
|
|
||||||
select {
|
|
||||||
case n := <-ch:
|
|
||||||
return fmt.Errorf("unexpected notification %v", n)
|
|
||||||
case <-time.After(100 * time.Millisecond):
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func expectEvent(t *testing.T, eventch <-chan ListenerEventType, et ListenerEventType) error {
|
|
||||||
select {
|
|
||||||
case e := <-eventch:
|
|
||||||
if e != et {
|
|
||||||
return fmt.Errorf("unexpected event %v", e)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
case <-time.After(1500 * time.Millisecond):
|
|
||||||
panic("expectEvent timeout")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func expectNoEvent(t *testing.T, eventch <-chan ListenerEventType) error {
|
|
||||||
select {
|
|
||||||
case e := <-eventch:
|
|
||||||
return fmt.Errorf("unexpected event %v", e)
|
|
||||||
case <-time.After(100 * time.Millisecond):
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTestListenerConn(t *testing.T) (*ListenerConn, <-chan *Notification) {
|
|
||||||
datname := os.Getenv("PGDATABASE")
|
|
||||||
sslmode := os.Getenv("PGSSLMODE")
|
|
||||||
|
|
||||||
if datname == "" {
|
|
||||||
os.Setenv("PGDATABASE", "pqgotest")
|
|
||||||
}
|
|
||||||
|
|
||||||
if sslmode == "" {
|
|
||||||
os.Setenv("PGSSLMODE", "disable")
|
|
||||||
}
|
|
||||||
|
|
||||||
notificationChan := make(chan *Notification)
|
|
||||||
l, err := NewListenerConn("", notificationChan)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return l, notificationChan
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewListenerConn(t *testing.T) {
|
|
||||||
l, _ := newTestListenerConn(t)
|
|
||||||
|
|
||||||
defer l.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConnListen(t *testing.T) {
|
|
||||||
l, channel := newTestListenerConn(t)
|
|
||||||
|
|
||||||
defer l.Close()
|
|
||||||
|
|
||||||
db := openTestConn(t)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
ok, err := l.Listen("notify_test")
|
|
||||||
if !ok || err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec("NOTIFY notify_test")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = expectNotification(t, channel, "notify_test", "")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConnUnlisten(t *testing.T) {
|
|
||||||
l, channel := newTestListenerConn(t)
|
|
||||||
|
|
||||||
defer l.Close()
|
|
||||||
|
|
||||||
db := openTestConn(t)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
ok, err := l.Listen("notify_test")
|
|
||||||
if !ok || err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec("NOTIFY notify_test")
|
|
||||||
|
|
||||||
err = expectNotification(t, channel, "notify_test", "")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ok, err = l.Unlisten("notify_test")
|
|
||||||
if !ok || err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec("NOTIFY notify_test")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = expectNoNotification(t, channel)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConnUnlistenAll(t *testing.T) {
|
|
||||||
l, channel := newTestListenerConn(t)
|
|
||||||
|
|
||||||
defer l.Close()
|
|
||||||
|
|
||||||
db := openTestConn(t)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
ok, err := l.Listen("notify_test")
|
|
||||||
if !ok || err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec("NOTIFY notify_test")
|
|
||||||
|
|
||||||
err = expectNotification(t, channel, "notify_test", "")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ok, err = l.UnlistenAll()
|
|
||||||
if !ok || err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec("NOTIFY notify_test")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = expectNoNotification(t, channel)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConnClose(t *testing.T) {
|
|
||||||
l, _ := newTestListenerConn(t)
|
|
||||||
defer l.Close()
|
|
||||||
|
|
||||||
err := l.Close()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
err = l.Close()
|
|
||||||
if err != errListenerConnClosed {
|
|
||||||
t.Fatalf("expected errListenerConnClosed; got %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConnPing(t *testing.T) {
|
|
||||||
l, _ := newTestListenerConn(t)
|
|
||||||
defer l.Close()
|
|
||||||
err := l.Ping()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
err = l.Close()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
err = l.Ping()
|
|
||||||
if err != errListenerConnClosed {
|
|
||||||
t.Fatalf("expected errListenerConnClosed; got %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test for deadlock where a query fails while another one is queued
|
|
||||||
func TestConnExecDeadlock(t *testing.T) {
|
|
||||||
l, _ := newTestListenerConn(t)
|
|
||||||
defer l.Close()
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
wg.Add(2)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
l.ExecSimpleQuery("SELECT pg_sleep(60)")
|
|
||||||
wg.Done()
|
|
||||||
}()
|
|
||||||
runtime.Gosched()
|
|
||||||
go func() {
|
|
||||||
l.ExecSimpleQuery("SELECT 1")
|
|
||||||
wg.Done()
|
|
||||||
}()
|
|
||||||
// give the two goroutines some time to get into position
|
|
||||||
runtime.Gosched()
|
|
||||||
// calls Close on the net.Conn; equivalent to a network failure
|
|
||||||
l.Close()
|
|
||||||
|
|
||||||
var done int32 = 0
|
|
||||||
go func() {
|
|
||||||
time.Sleep(10 * time.Second)
|
|
||||||
if atomic.LoadInt32(&done) != 1 {
|
|
||||||
panic("timed out")
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
wg.Wait()
|
|
||||||
atomic.StoreInt32(&done, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test for ListenerConn being closed while a slow query is executing
|
|
||||||
func TestListenerConnCloseWhileQueryIsExecuting(t *testing.T) {
|
|
||||||
l, _ := newTestListenerConn(t)
|
|
||||||
defer l.Close()
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
wg.Add(1)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
sent, err := l.ExecSimpleQuery("SELECT pg_sleep(60)")
|
|
||||||
if sent {
|
|
||||||
panic("expected sent=false")
|
|
||||||
}
|
|
||||||
// could be any of a number of errors
|
|
||||||
if err == nil {
|
|
||||||
panic("expected error")
|
|
||||||
}
|
|
||||||
wg.Done()
|
|
||||||
}()
|
|
||||||
// give the above goroutine some time to get into position
|
|
||||||
runtime.Gosched()
|
|
||||||
err := l.Close()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
var done int32 = 0
|
|
||||||
go func() {
|
|
||||||
time.Sleep(10 * time.Second)
|
|
||||||
if atomic.LoadInt32(&done) != 1 {
|
|
||||||
panic("timed out")
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
wg.Wait()
|
|
||||||
atomic.StoreInt32(&done, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNotifyExtra(t *testing.T) {
|
|
||||||
db := openTestConn(t)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
if getServerVersion(t, db) < 90000 {
|
|
||||||
t.Skip("skipping NOTIFY payload test since the server does not appear to support it")
|
|
||||||
}
|
|
||||||
|
|
||||||
l, channel := newTestListenerConn(t)
|
|
||||||
defer l.Close()
|
|
||||||
|
|
||||||
ok, err := l.Listen("notify_test")
|
|
||||||
if !ok || err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec("NOTIFY notify_test, 'something'")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = expectNotification(t, channel, "notify_test", "something")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// create a new test listener and also set the timeouts
|
|
||||||
func newTestListenerTimeout(t *testing.T, min time.Duration, max time.Duration) (*Listener, <-chan ListenerEventType) {
|
|
||||||
datname := os.Getenv("PGDATABASE")
|
|
||||||
sslmode := os.Getenv("PGSSLMODE")
|
|
||||||
|
|
||||||
if datname == "" {
|
|
||||||
os.Setenv("PGDATABASE", "pqgotest")
|
|
||||||
}
|
|
||||||
|
|
||||||
if sslmode == "" {
|
|
||||||
os.Setenv("PGSSLMODE", "disable")
|
|
||||||
}
|
|
||||||
|
|
||||||
eventch := make(chan ListenerEventType, 16)
|
|
||||||
l := NewListener("", min, max, func(t ListenerEventType, err error) { eventch <- t })
|
|
||||||
err := expectEvent(t, eventch, ListenerEventConnected)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
return l, eventch
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTestListener(t *testing.T) (*Listener, <-chan ListenerEventType) {
|
|
||||||
return newTestListenerTimeout(t, time.Hour, time.Hour)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestListenerListen(t *testing.T) {
|
|
||||||
l, _ := newTestListener(t)
|
|
||||||
defer l.Close()
|
|
||||||
|
|
||||||
db := openTestConn(t)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
err := l.Listen("notify_listen_test")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec("NOTIFY notify_listen_test")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = expectNotification(t, l.Notify, "notify_listen_test", "")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestListenerUnlisten(t *testing.T) {
|
|
||||||
l, _ := newTestListener(t)
|
|
||||||
defer l.Close()
|
|
||||||
|
|
||||||
db := openTestConn(t)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
err := l.Listen("notify_listen_test")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec("NOTIFY notify_listen_test")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = l.Unlisten("notify_listen_test")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = expectNotification(t, l.Notify, "notify_listen_test", "")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec("NOTIFY notify_listen_test")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = expectNoNotification(t, l.Notify)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestListenerUnlistenAll(t *testing.T) {
|
|
||||||
l, _ := newTestListener(t)
|
|
||||||
defer l.Close()
|
|
||||||
|
|
||||||
db := openTestConn(t)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
err := l.Listen("notify_listen_test")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec("NOTIFY notify_listen_test")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = l.UnlistenAll()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = expectNotification(t, l.Notify, "notify_listen_test", "")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec("NOTIFY notify_listen_test")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = expectNoNotification(t, l.Notify)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestListenerFailedQuery(t *testing.T) {
|
|
||||||
l, eventch := newTestListener(t)
|
|
||||||
defer l.Close()
|
|
||||||
|
|
||||||
db := openTestConn(t)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
err := l.Listen("notify_listen_test")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec("NOTIFY notify_listen_test")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = expectNotification(t, l.Notify, "notify_listen_test", "")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// shouldn't cause a disconnect
|
|
||||||
ok, err := l.cn.ExecSimpleQuery("SELECT error")
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("could not send query to server: %v", err)
|
|
||||||
}
|
|
||||||
_, ok = err.(PGError)
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("unexpected error %v", err)
|
|
||||||
}
|
|
||||||
err = expectNoEvent(t, eventch)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// should still work
|
|
||||||
_, err = db.Exec("NOTIFY notify_listen_test")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = expectNotification(t, l.Notify, "notify_listen_test", "")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestListenerReconnect(t *testing.T) {
|
|
||||||
l, eventch := newTestListenerTimeout(t, 20*time.Millisecond, time.Hour)
|
|
||||||
defer l.Close()
|
|
||||||
|
|
||||||
db := openTestConn(t)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
err := l.Listen("notify_listen_test")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec("NOTIFY notify_listen_test")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = expectNotification(t, l.Notify, "notify_listen_test", "")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// kill the connection and make sure it comes back up
|
|
||||||
ok, err := l.cn.ExecSimpleQuery("SELECT pg_terminate_backend(pg_backend_pid())")
|
|
||||||
if ok {
|
|
||||||
t.Fatalf("could not kill the connection: %v", err)
|
|
||||||
}
|
|
||||||
if err != io.EOF {
|
|
||||||
t.Fatalf("unexpected error %v", err)
|
|
||||||
}
|
|
||||||
err = expectEvent(t, eventch, ListenerEventDisconnected)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
err = expectEvent(t, eventch, ListenerEventReconnected)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// should still work
|
|
||||||
_, err = db.Exec("NOTIFY notify_listen_test")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// should get nil after Reconnected
|
|
||||||
err = expectNotification(t, l.Notify, "", "")
|
|
||||||
if err != errNilNotification {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = expectNotification(t, l.Notify, "notify_listen_test", "")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestListenerClose(t *testing.T) {
|
|
||||||
l, _ := newTestListenerTimeout(t, 20*time.Millisecond, time.Hour)
|
|
||||||
defer l.Close()
|
|
||||||
|
|
||||||
err := l.Close()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
err = l.Close()
|
|
||||||
if err != errListenerClosed {
|
|
||||||
t.Fatalf("expected errListenerClosed; got %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestListenerPing(t *testing.T) {
|
|
||||||
l, _ := newTestListenerTimeout(t, 20*time.Millisecond, time.Hour)
|
|
||||||
defer l.Close()
|
|
||||||
|
|
||||||
err := l.Ping()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = l.Close()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = l.Ping()
|
|
||||||
if err != errListenerClosed {
|
|
||||||
t.Fatalf("expected errListenerClosed; got %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
269
vendor/github.com/lib/pq/ssl_test.go
generated
vendored
269
vendor/github.com/lib/pq/ssl_test.go
generated
vendored
|
@ -1,269 +0,0 @@
|
||||||
package pq
|
|
||||||
|
|
||||||
// This file contains SSL tests
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "crypto/sha256"
|
|
||||||
"crypto/x509"
|
|
||||||
"database/sql"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func maybeSkipSSLTests(t *testing.T) {
|
|
||||||
// Require some special variables for testing certificates
|
|
||||||
if os.Getenv("PQSSLCERTTEST_PATH") == "" {
|
|
||||||
t.Skip("PQSSLCERTTEST_PATH not set, skipping SSL tests")
|
|
||||||
}
|
|
||||||
|
|
||||||
value := os.Getenv("PQGOSSLTESTS")
|
|
||||||
if value == "" || value == "0" {
|
|
||||||
t.Skip("PQGOSSLTESTS not enabled, skipping SSL tests")
|
|
||||||
} else if value != "1" {
|
|
||||||
t.Fatalf("unexpected value %q for PQGOSSLTESTS", value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func openSSLConn(t *testing.T, conninfo string) (*sql.DB, error) {
|
|
||||||
db, err := openTestConnConninfo(conninfo)
|
|
||||||
if err != nil {
|
|
||||||
// should never fail
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
// Do something with the connection to see whether it's working or not.
|
|
||||||
tx, err := db.Begin()
|
|
||||||
if err == nil {
|
|
||||||
return db, tx.Rollback()
|
|
||||||
}
|
|
||||||
_ = db.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkSSLSetup(t *testing.T, conninfo string) {
|
|
||||||
db, err := openSSLConn(t, conninfo)
|
|
||||||
if err == nil {
|
|
||||||
db.Close()
|
|
||||||
t.Fatalf("expected error with conninfo=%q", conninfo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect over SSL and run a simple query to test the basics
|
|
||||||
func TestSSLConnection(t *testing.T) {
|
|
||||||
maybeSkipSSLTests(t)
|
|
||||||
// Environment sanity check: should fail without SSL
|
|
||||||
checkSSLSetup(t, "sslmode=disable user=pqgossltest")
|
|
||||||
|
|
||||||
db, err := openSSLConn(t, "sslmode=require user=pqgossltest")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
rows, err := db.Query("SELECT 1")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
rows.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test sslmode=verify-full
|
|
||||||
func TestSSLVerifyFull(t *testing.T) {
|
|
||||||
maybeSkipSSLTests(t)
|
|
||||||
// Environment sanity check: should fail without SSL
|
|
||||||
checkSSLSetup(t, "sslmode=disable user=pqgossltest")
|
|
||||||
|
|
||||||
// Not OK according to the system CA
|
|
||||||
_, err := openSSLConn(t, "host=postgres sslmode=verify-full user=pqgossltest")
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected error")
|
|
||||||
}
|
|
||||||
_, ok := err.(x509.UnknownAuthorityError)
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("expected x509.UnknownAuthorityError, got %#+v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
rootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "root.crt")
|
|
||||||
rootCert := "sslrootcert=" + rootCertPath + " "
|
|
||||||
// No match on Common Name
|
|
||||||
_, err = openSSLConn(t, rootCert+"host=127.0.0.1 sslmode=verify-full user=pqgossltest")
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected error")
|
|
||||||
}
|
|
||||||
_, ok = err.(x509.HostnameError)
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("expected x509.HostnameError, got %#+v", err)
|
|
||||||
}
|
|
||||||
// OK
|
|
||||||
_, err = openSSLConn(t, rootCert+"host=postgres sslmode=verify-full user=pqgossltest")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test sslmode=require sslrootcert=rootCertPath
|
|
||||||
func TestSSLRequireWithRootCert(t *testing.T) {
|
|
||||||
maybeSkipSSLTests(t)
|
|
||||||
// Environment sanity check: should fail without SSL
|
|
||||||
checkSSLSetup(t, "sslmode=disable user=pqgossltest")
|
|
||||||
|
|
||||||
bogusRootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "bogus_root.crt")
|
|
||||||
bogusRootCert := "sslrootcert=" + bogusRootCertPath + " "
|
|
||||||
|
|
||||||
// Not OK according to the bogus CA
|
|
||||||
_, err := openSSLConn(t, bogusRootCert+"host=postgres sslmode=require user=pqgossltest")
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected error")
|
|
||||||
}
|
|
||||||
_, ok := err.(x509.UnknownAuthorityError)
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("expected x509.UnknownAuthorityError, got %s, %#+v", err, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
nonExistentCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "non_existent.crt")
|
|
||||||
nonExistentCert := "sslrootcert=" + nonExistentCertPath + " "
|
|
||||||
|
|
||||||
// No match on Common Name, but that's OK because we're not validating anything.
|
|
||||||
_, err = openSSLConn(t, nonExistentCert+"host=127.0.0.1 sslmode=require user=pqgossltest")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
rootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "root.crt")
|
|
||||||
rootCert := "sslrootcert=" + rootCertPath + " "
|
|
||||||
|
|
||||||
// No match on Common Name, but that's OK because we're not validating the CN.
|
|
||||||
_, err = openSSLConn(t, rootCert+"host=127.0.0.1 sslmode=require user=pqgossltest")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
// Everything OK
|
|
||||||
_, err = openSSLConn(t, rootCert+"host=postgres sslmode=require user=pqgossltest")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test sslmode=verify-ca
|
|
||||||
func TestSSLVerifyCA(t *testing.T) {
|
|
||||||
maybeSkipSSLTests(t)
|
|
||||||
// Environment sanity check: should fail without SSL
|
|
||||||
checkSSLSetup(t, "sslmode=disable user=pqgossltest")
|
|
||||||
|
|
||||||
// Not OK according to the system CA
|
|
||||||
_, err := openSSLConn(t, "host=postgres sslmode=verify-ca user=pqgossltest")
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected error")
|
|
||||||
}
|
|
||||||
_, ok := err.(x509.UnknownAuthorityError)
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("expected x509.UnknownAuthorityError, got %#+v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
rootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "root.crt")
|
|
||||||
rootCert := "sslrootcert=" + rootCertPath + " "
|
|
||||||
// No match on Common Name, but that's OK
|
|
||||||
_, err = openSSLConn(t, rootCert+"host=127.0.0.1 sslmode=verify-ca user=pqgossltest")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
// Everything OK
|
|
||||||
_, err = openSSLConn(t, rootCert+"host=postgres sslmode=verify-ca user=pqgossltest")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCertConninfo(t *testing.T, source string) string {
|
|
||||||
var sslkey string
|
|
||||||
var sslcert string
|
|
||||||
|
|
||||||
certpath := os.Getenv("PQSSLCERTTEST_PATH")
|
|
||||||
|
|
||||||
switch source {
|
|
||||||
case "missingkey":
|
|
||||||
sslkey = "/tmp/filedoesnotexist"
|
|
||||||
sslcert = filepath.Join(certpath, "postgresql.crt")
|
|
||||||
case "missingcert":
|
|
||||||
sslkey = filepath.Join(certpath, "postgresql.key")
|
|
||||||
sslcert = "/tmp/filedoesnotexist"
|
|
||||||
case "certtwice":
|
|
||||||
sslkey = filepath.Join(certpath, "postgresql.crt")
|
|
||||||
sslcert = filepath.Join(certpath, "postgresql.crt")
|
|
||||||
case "valid":
|
|
||||||
sslkey = filepath.Join(certpath, "postgresql.key")
|
|
||||||
sslcert = filepath.Join(certpath, "postgresql.crt")
|
|
||||||
default:
|
|
||||||
t.Fatalf("invalid source %q", source)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("sslmode=require user=pqgosslcert sslkey=%s sslcert=%s", sslkey, sslcert)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Authenticate over SSL using client certificates
|
|
||||||
func TestSSLClientCertificates(t *testing.T) {
|
|
||||||
maybeSkipSSLTests(t)
|
|
||||||
// Environment sanity check: should fail without SSL
|
|
||||||
checkSSLSetup(t, "sslmode=disable user=pqgossltest")
|
|
||||||
|
|
||||||
// Should also fail without a valid certificate
|
|
||||||
db, err := openSSLConn(t, "sslmode=require user=pqgosslcert")
|
|
||||||
if err == nil {
|
|
||||||
db.Close()
|
|
||||||
t.Fatal("expected error")
|
|
||||||
}
|
|
||||||
pge, ok := err.(*Error)
|
|
||||||
if !ok {
|
|
||||||
t.Fatal("expected pq.Error")
|
|
||||||
}
|
|
||||||
if pge.Code.Name() != "invalid_authorization_specification" {
|
|
||||||
t.Fatalf("unexpected error code %q", pge.Code.Name())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Should work
|
|
||||||
db, err = openSSLConn(t, getCertConninfo(t, "valid"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
rows, err := db.Query("SELECT 1")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
rows.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test errors with ssl certificates
|
|
||||||
func TestSSLClientCertificatesMissingFiles(t *testing.T) {
|
|
||||||
maybeSkipSSLTests(t)
|
|
||||||
// Environment sanity check: should fail without SSL
|
|
||||||
checkSSLSetup(t, "sslmode=disable user=pqgossltest")
|
|
||||||
|
|
||||||
// Key missing, should fail
|
|
||||||
_, err := openSSLConn(t, getCertConninfo(t, "missingkey"))
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected error")
|
|
||||||
}
|
|
||||||
// should be a PathError
|
|
||||||
_, ok := err.(*os.PathError)
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("expected PathError, got %#+v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cert missing, should fail
|
|
||||||
_, err = openSSLConn(t, getCertConninfo(t, "missingcert"))
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected error")
|
|
||||||
}
|
|
||||||
// should be a PathError
|
|
||||||
_, ok = err.(*os.PathError)
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("expected PathError, got %#+v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Key has wrong permissions, should fail
|
|
||||||
_, err = openSSLConn(t, getCertConninfo(t, "certtwice"))
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected error")
|
|
||||||
}
|
|
||||||
if err != ErrSSLKeyHasWorldPermissions {
|
|
||||||
t.Fatalf("expected ErrSSLKeyHasWorldPermissions, got %#+v", err)
|
|
||||||
}
|
|
||||||
}
|
|
66
vendor/github.com/lib/pq/url_test.go
generated
vendored
66
vendor/github.com/lib/pq/url_test.go
generated
vendored
|
@ -1,66 +0,0 @@
|
||||||
package pq
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSimpleParseURL(t *testing.T) {
|
|
||||||
expected := "host=hostname.remote"
|
|
||||||
str, err := ParseURL("postgres://hostname.remote")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if str != expected {
|
|
||||||
t.Fatalf("unexpected result from ParseURL:\n+ %v\n- %v", str, expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIPv6LoopbackParseURL(t *testing.T) {
|
|
||||||
expected := "host=::1 port=1234"
|
|
||||||
str, err := ParseURL("postgres://[::1]:1234")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if str != expected {
|
|
||||||
t.Fatalf("unexpected result from ParseURL:\n+ %v\n- %v", str, expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFullParseURL(t *testing.T) {
|
|
||||||
expected := `dbname=database host=hostname.remote password=top\ secret port=1234 user=username`
|
|
||||||
str, err := ParseURL("postgres://username:top%20secret@hostname.remote:1234/database")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if str != expected {
|
|
||||||
t.Fatalf("unexpected result from ParseURL:\n+ %s\n- %s", str, expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInvalidProtocolParseURL(t *testing.T) {
|
|
||||||
_, err := ParseURL("http://hostname.remote")
|
|
||||||
switch err {
|
|
||||||
case nil:
|
|
||||||
t.Fatal("Expected an error from parsing invalid protocol")
|
|
||||||
default:
|
|
||||||
msg := "invalid connection protocol: http"
|
|
||||||
if err.Error() != msg {
|
|
||||||
t.Fatalf("Unexpected error message:\n+ %s\n- %s",
|
|
||||||
err.Error(), msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMinimalURL(t *testing.T) {
|
|
||||||
cs, err := ParseURL("postgres://")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if cs != "" {
|
|
||||||
t.Fatalf("expected blank connection string, got: %q", cs)
|
|
||||||
}
|
|
||||||
}
|
|
4
vendor/github.com/mattn/go-sqlite3/.gitignore
generated
vendored
4
vendor/github.com/mattn/go-sqlite3/.gitignore
generated
vendored
|
@ -1,4 +0,0 @@
|
||||||
*.db
|
|
||||||
*.exe
|
|
||||||
*.dll
|
|
||||||
*.o
|
|
13
vendor/github.com/mattn/go-sqlite3/.travis.yml
generated
vendored
13
vendor/github.com/mattn/go-sqlite3/.travis.yml
generated
vendored
|
@ -1,13 +0,0 @@
|
||||||
language: go
|
|
||||||
sudo: required
|
|
||||||
dist: trusty
|
|
||||||
go:
|
|
||||||
- 1.5
|
|
||||||
- 1.6
|
|
||||||
- tip
|
|
||||||
before_install:
|
|
||||||
- go get github.com/mattn/goveralls
|
|
||||||
- go get golang.org/x/tools/cmd/cover
|
|
||||||
script:
|
|
||||||
- $HOME/gopath/bin/goveralls -repotoken 3qJVUE0iQwqnCbmNcDsjYu1nh4J4KIFXx
|
|
||||||
- go test -v . -tags "libsqlite3"
|
|
81
vendor/github.com/mattn/go-sqlite3/README.md
generated
vendored
81
vendor/github.com/mattn/go-sqlite3/README.md
generated
vendored
|
@ -1,81 +0,0 @@
|
||||||
go-sqlite3
|
|
||||||
==========
|
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/mattn/go-sqlite3.svg?branch=master)](https://travis-ci.org/mattn/go-sqlite3)
|
|
||||||
[![Coverage Status](https://coveralls.io/repos/mattn/go-sqlite3/badge.svg?branch=master)](https://coveralls.io/r/mattn/go-sqlite3?branch=master)
|
|
||||||
[![GoDoc](https://godoc.org/github.com/mattn/go-sqlite3?status.svg)](http://godoc.org/github.com/mattn/go-sqlite3)
|
|
||||||
|
|
||||||
Description
|
|
||||||
-----------
|
|
||||||
|
|
||||||
sqlite3 driver conforming to the built-in database/sql interface
|
|
||||||
|
|
||||||
Installation
|
|
||||||
------------
|
|
||||||
|
|
||||||
This package can be installed with the go get command:
|
|
||||||
|
|
||||||
go get github.com/mattn/go-sqlite3
|
|
||||||
|
|
||||||
_go-sqlite3_ is *cgo* package.
|
|
||||||
If you want to build your app using go-sqlite3, you need gcc.
|
|
||||||
However, if you install _go-sqlite3_ with `go install github.com/mattn/go-sqlite3`, you don't need gcc to build your app anymore.
|
|
||||||
|
|
||||||
Documentation
|
|
||||||
-------------
|
|
||||||
|
|
||||||
API documentation can be found here: http://godoc.org/github.com/mattn/go-sqlite3
|
|
||||||
|
|
||||||
Examples can be found under the `./_example` directory
|
|
||||||
|
|
||||||
FAQ
|
|
||||||
---
|
|
||||||
|
|
||||||
* Want to build go-sqlite3 with libsqlite3 on my linux.
|
|
||||||
|
|
||||||
Use `go build --tags "libsqlite3 linux"`
|
|
||||||
|
|
||||||
* Want to build go-sqlite3 with libsqlite3 on OS X.
|
|
||||||
|
|
||||||
Install sqlite3 from homebrew: `brew install sqlite3`
|
|
||||||
|
|
||||||
Use `go build --tags "libsqlite3 darwin"`
|
|
||||||
|
|
||||||
* Want to build go-sqlite3 with icu extension.
|
|
||||||
|
|
||||||
Use `go build --tags "icu"`
|
|
||||||
|
|
||||||
* Can't build go-sqlite3 on windows 64bit.
|
|
||||||
|
|
||||||
> Probably, you are using go 1.0, go1.0 has a problem when it comes to compiling/linking on windows 64bit.
|
|
||||||
> See: https://github.com/mattn/go-sqlite3/issues/27
|
|
||||||
|
|
||||||
* Getting insert error while query is opened.
|
|
||||||
|
|
||||||
> You can pass some arguments into the connection string, for example, a URI.
|
|
||||||
> See: https://github.com/mattn/go-sqlite3/issues/39
|
|
||||||
|
|
||||||
* Do you want to cross compile? mingw on Linux or Mac?
|
|
||||||
|
|
||||||
> See: https://github.com/mattn/go-sqlite3/issues/106
|
|
||||||
> See also: http://www.limitlessfx.com/cross-compile-golang-app-for-windows-from-linux.html
|
|
||||||
|
|
||||||
* Want to get time.Time with current locale
|
|
||||||
|
|
||||||
Use `loc=auto` in SQLite3 filename schema like `file:foo.db?loc=auto`.
|
|
||||||
|
|
||||||
License
|
|
||||||
-------
|
|
||||||
|
|
||||||
MIT: http://mattn.mit-license.org/2012
|
|
||||||
|
|
||||||
sqlite3-binding.c, sqlite3-binding.h, sqlite3ext.h
|
|
||||||
|
|
||||||
The -binding suffix was added to avoid build failures under gccgo.
|
|
||||||
|
|
||||||
In this repository, those files are an amalgamation of code that was copied from SQLite3. The license of that code is the same as the license of SQLite3.
|
|
||||||
|
|
||||||
Author
|
|
||||||
------
|
|
||||||
|
|
||||||
Yasuhiro Matsumoto (a.k.a mattn)
|
|
247
vendor/github.com/mattn/go-sqlite3/backup_test.go
generated
vendored
247
vendor/github.com/mattn/go-sqlite3/backup_test.go
generated
vendored
|
@ -1,247 +0,0 @@
|
||||||
// Use of this source code is governed by an MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package sqlite3
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// The number of rows of test data to create in the source database.
|
|
||||||
// Can be used to control how many pages are available to be backed up.
|
|
||||||
const testRowCount = 100
|
|
||||||
|
|
||||||
// The maximum number of seconds after which the page-by-page backup is considered to have taken too long.
|
|
||||||
const usePagePerStepsTimeoutSeconds = 30
|
|
||||||
|
|
||||||
// Test the backup functionality.
|
|
||||||
func testBackup(t *testing.T, testRowCount int, usePerPageSteps bool) {
|
|
||||||
// This function will be called multiple times.
|
|
||||||
// It uses sql.Register(), which requires the name parameter value to be unique.
|
|
||||||
// There does not currently appear to be a way to unregister a registered driver, however.
|
|
||||||
// So generate a database driver name that will likely be unique.
|
|
||||||
var driverName = fmt.Sprintf("sqlite3_testBackup_%v_%v_%v", testRowCount, usePerPageSteps, time.Now().UnixNano())
|
|
||||||
|
|
||||||
// The driver's connection will be needed in order to perform the backup.
|
|
||||||
driverConns := []*SQLiteConn{}
|
|
||||||
sql.Register(driverName, &SQLiteDriver{
|
|
||||||
ConnectHook: func(conn *SQLiteConn) error {
|
|
||||||
driverConns = append(driverConns, conn)
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
// Connect to the source database.
|
|
||||||
srcTempFilename := TempFilename(t)
|
|
||||||
defer os.Remove(srcTempFilename)
|
|
||||||
srcDb, err := sql.Open(driverName, srcTempFilename)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Failed to open the source database:", err)
|
|
||||||
}
|
|
||||||
defer srcDb.Close()
|
|
||||||
err = srcDb.Ping()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Failed to connect to the source database:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect to the destination database.
|
|
||||||
destTempFilename := TempFilename(t)
|
|
||||||
defer os.Remove(destTempFilename)
|
|
||||||
destDb, err := sql.Open(driverName, destTempFilename)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Failed to open the destination database:", err)
|
|
||||||
}
|
|
||||||
defer destDb.Close()
|
|
||||||
err = destDb.Ping()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Failed to connect to the destination database:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check the driver connections.
|
|
||||||
if len(driverConns) != 2 {
|
|
||||||
t.Fatalf("Expected 2 driver connections, but found %v.", len(driverConns))
|
|
||||||
}
|
|
||||||
srcDbDriverConn := driverConns[0]
|
|
||||||
if srcDbDriverConn == nil {
|
|
||||||
t.Fatal("The source database driver connection is nil.")
|
|
||||||
}
|
|
||||||
destDbDriverConn := driverConns[1]
|
|
||||||
if destDbDriverConn == nil {
|
|
||||||
t.Fatal("The destination database driver connection is nil.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate some test data for the given ID.
|
|
||||||
var generateTestData = func(id int) string {
|
|
||||||
return fmt.Sprintf("test-%v", id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Populate the source database with a test table containing some test data.
|
|
||||||
tx, err := srcDb.Begin()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Failed to begin a transaction when populating the source database:", err)
|
|
||||||
}
|
|
||||||
_, err = srcDb.Exec("CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)")
|
|
||||||
if err != nil {
|
|
||||||
tx.Rollback()
|
|
||||||
t.Fatal("Failed to create the source database \"test\" table:", err)
|
|
||||||
}
|
|
||||||
for id := 0; id < testRowCount; id++ {
|
|
||||||
_, err = srcDb.Exec("INSERT INTO test (id, value) VALUES (?, ?)", id, generateTestData(id))
|
|
||||||
if err != nil {
|
|
||||||
tx.Rollback()
|
|
||||||
t.Fatal("Failed to insert a row into the source database \"test\" table:", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = tx.Commit()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Failed to populate the source database:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Confirm that the destination database is initially empty.
|
|
||||||
var destTableCount int
|
|
||||||
err = destDb.QueryRow("SELECT COUNT(*) FROM sqlite_master WHERE type = 'table'").Scan(&destTableCount)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Failed to check the destination table count:", err)
|
|
||||||
}
|
|
||||||
if destTableCount != 0 {
|
|
||||||
t.Fatalf("The destination database is not empty; %v table(s) found.", destTableCount)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare to perform the backup.
|
|
||||||
backup, err := destDbDriverConn.Backup("main", srcDbDriverConn, "main")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Failed to initialize the backup:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow the initial page count and remaining values to be retrieved.
|
|
||||||
// According to <https://www.sqlite.org/c3ref/backup_finish.html>, the page count and remaining values are "... only updated by sqlite3_backup_step()."
|
|
||||||
isDone, err := backup.Step(0)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Unable to perform an initial 0-page backup step:", err)
|
|
||||||
}
|
|
||||||
if isDone {
|
|
||||||
t.Fatal("Backup is unexpectedly done.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that the page count and remaining values are reasonable.
|
|
||||||
initialPageCount := backup.PageCount()
|
|
||||||
if initialPageCount <= 0 {
|
|
||||||
t.Fatalf("Unexpected initial page count value: %v", initialPageCount)
|
|
||||||
}
|
|
||||||
initialRemaining := backup.Remaining()
|
|
||||||
if initialRemaining <= 0 {
|
|
||||||
t.Fatalf("Unexpected initial remaining value: %v", initialRemaining)
|
|
||||||
}
|
|
||||||
if initialRemaining != initialPageCount {
|
|
||||||
t.Fatalf("Initial remaining value differs from the initial page count value; remaining: %v; page count: %v", initialRemaining, initialPageCount)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform the backup.
|
|
||||||
if usePerPageSteps {
|
|
||||||
var startTime = time.Now().Unix()
|
|
||||||
|
|
||||||
// Test backing-up using a page-by-page approach.
|
|
||||||
var latestRemaining = initialRemaining
|
|
||||||
for {
|
|
||||||
// Perform the backup step.
|
|
||||||
isDone, err = backup.Step(1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Failed to perform a backup step:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The page count should remain unchanged from its initial value.
|
|
||||||
currentPageCount := backup.PageCount()
|
|
||||||
if currentPageCount != initialPageCount {
|
|
||||||
t.Fatalf("Current page count differs from the initial page count; initial page count: %v; current page count: %v", initialPageCount, currentPageCount)
|
|
||||||
}
|
|
||||||
|
|
||||||
// There should now be one less page remaining.
|
|
||||||
currentRemaining := backup.Remaining()
|
|
||||||
expectedRemaining := latestRemaining - 1
|
|
||||||
if currentRemaining != expectedRemaining {
|
|
||||||
t.Fatalf("Unexpected remaining value; expected remaining value: %v; actual remaining value: %v", expectedRemaining, currentRemaining)
|
|
||||||
}
|
|
||||||
latestRemaining = currentRemaining
|
|
||||||
|
|
||||||
if isDone {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Limit the runtime of the backup attempt.
|
|
||||||
if (time.Now().Unix() - startTime) > usePagePerStepsTimeoutSeconds {
|
|
||||||
t.Fatal("Backup is taking longer than expected.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Test the copying of all remaining pages.
|
|
||||||
isDone, err = backup.Step(-1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Failed to perform a backup step:", err)
|
|
||||||
}
|
|
||||||
if !isDone {
|
|
||||||
t.Fatal("Backup is unexpectedly not done.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that the page count and remaining values are reasonable.
|
|
||||||
finalPageCount := backup.PageCount()
|
|
||||||
if finalPageCount != initialPageCount {
|
|
||||||
t.Fatalf("Final page count differs from the initial page count; initial page count: %v; final page count: %v", initialPageCount, finalPageCount)
|
|
||||||
}
|
|
||||||
finalRemaining := backup.Remaining()
|
|
||||||
if finalRemaining != 0 {
|
|
||||||
t.Fatalf("Unexpected remaining value: %v", finalRemaining)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finish the backup.
|
|
||||||
err = backup.Finish()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Failed to finish backup:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Confirm that the "test" table now exists in the destination database.
|
|
||||||
var doesTestTableExist bool
|
|
||||||
err = destDb.QueryRow("SELECT EXISTS (SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'test' LIMIT 1) AS test_table_exists").Scan(&doesTestTableExist)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Failed to check if the \"test\" table exists in the destination database:", err)
|
|
||||||
}
|
|
||||||
if !doesTestTableExist {
|
|
||||||
t.Fatal("The \"test\" table could not be found in the destination database.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Confirm that the number of rows in the destination database's "test" table matches that of the source table.
|
|
||||||
var actualTestTableRowCount int
|
|
||||||
err = destDb.QueryRow("SELECT COUNT(*) FROM test").Scan(&actualTestTableRowCount)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Failed to determine the rowcount of the \"test\" table in the destination database:", err)
|
|
||||||
}
|
|
||||||
if testRowCount != actualTestTableRowCount {
|
|
||||||
t.Fatalf("Unexpected destination \"test\" table row count; expected: %v; found: %v", testRowCount, actualTestTableRowCount)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check each of the rows in the destination database.
|
|
||||||
for id := 0; id < testRowCount; id++ {
|
|
||||||
var checkedValue string
|
|
||||||
err = destDb.QueryRow("SELECT value FROM test WHERE id = ?", id).Scan(&checkedValue)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Failed to query the \"test\" table in the destination database:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var expectedValue = generateTestData(id)
|
|
||||||
if checkedValue != expectedValue {
|
|
||||||
t.Fatalf("Unexpected value in the \"test\" table in the destination database; expected value: %v; actual value: %v", expectedValue, checkedValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBackupStepByStep(t *testing.T) {
|
|
||||||
testBackup(t, testRowCount, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBackupAllRemainingPages(t *testing.T) {
|
|
||||||
testBackup(t, testRowCount, false)
|
|
||||||
}
|
|
97
vendor/github.com/mattn/go-sqlite3/callback_test.go
generated
vendored
97
vendor/github.com/mattn/go-sqlite3/callback_test.go
generated
vendored
|
@ -1,97 +0,0 @@
|
||||||
package sqlite3
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"math"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCallbackArgCast(t *testing.T) {
|
|
||||||
intConv := callbackSyntheticForTests(reflect.ValueOf(int64(math.MaxInt64)), nil)
|
|
||||||
floatConv := callbackSyntheticForTests(reflect.ValueOf(float64(math.MaxFloat64)), nil)
|
|
||||||
errConv := callbackSyntheticForTests(reflect.Value{}, errors.New("test"))
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
f callbackArgConverter
|
|
||||||
o reflect.Value
|
|
||||||
}{
|
|
||||||
{intConv, reflect.ValueOf(int8(-1))},
|
|
||||||
{intConv, reflect.ValueOf(int16(-1))},
|
|
||||||
{intConv, reflect.ValueOf(int32(-1))},
|
|
||||||
{intConv, reflect.ValueOf(uint8(math.MaxUint8))},
|
|
||||||
{intConv, reflect.ValueOf(uint16(math.MaxUint16))},
|
|
||||||
{intConv, reflect.ValueOf(uint32(math.MaxUint32))},
|
|
||||||
// Special case, int64->uint64 is only 1<<63 - 1, not 1<<64 - 1
|
|
||||||
{intConv, reflect.ValueOf(uint64(math.MaxInt64))},
|
|
||||||
{floatConv, reflect.ValueOf(float32(math.Inf(1)))},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
conv := callbackArgCast{test.f, test.o.Type()}
|
|
||||||
val, err := conv.Run(nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Couldn't convert to %s: %s", test.o.Type(), err)
|
|
||||||
} else if !reflect.DeepEqual(val.Interface(), test.o.Interface()) {
|
|
||||||
t.Errorf("Unexpected result from converting to %s: got %v, want %v", test.o.Type(), val.Interface(), test.o.Interface())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
conv := callbackArgCast{errConv, reflect.TypeOf(int8(0))}
|
|
||||||
_, err := conv.Run(nil)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Expected error during callbackArgCast, but got none")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCallbackConverters(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
v interface{}
|
|
||||||
err bool
|
|
||||||
}{
|
|
||||||
// Unfortunately, we can't tell which converter was returned,
|
|
||||||
// but we can at least check which types can be converted.
|
|
||||||
{[]byte{0}, false},
|
|
||||||
{"text", false},
|
|
||||||
{true, false},
|
|
||||||
{int8(0), false},
|
|
||||||
{int16(0), false},
|
|
||||||
{int32(0), false},
|
|
||||||
{int64(0), false},
|
|
||||||
{uint8(0), false},
|
|
||||||
{uint16(0), false},
|
|
||||||
{uint32(0), false},
|
|
||||||
{uint64(0), false},
|
|
||||||
{int(0), false},
|
|
||||||
{uint(0), false},
|
|
||||||
{float64(0), false},
|
|
||||||
{float32(0), false},
|
|
||||||
|
|
||||||
{func() {}, true},
|
|
||||||
{complex64(complex(0, 0)), true},
|
|
||||||
{complex128(complex(0, 0)), true},
|
|
||||||
{struct{}{}, true},
|
|
||||||
{map[string]string{}, true},
|
|
||||||
{[]string{}, true},
|
|
||||||
{(*int8)(nil), true},
|
|
||||||
{make(chan int), true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
_, err := callbackArg(reflect.TypeOf(test.v))
|
|
||||||
if test.err && err == nil {
|
|
||||||
t.Errorf("Expected an error when converting %s, got no error", reflect.TypeOf(test.v))
|
|
||||||
} else if !test.err && err != nil {
|
|
||||||
t.Errorf("Expected converter when converting %s, got error: %s", reflect.TypeOf(test.v), err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
_, err := callbackRet(reflect.TypeOf(test.v))
|
|
||||||
if test.err && err == nil {
|
|
||||||
t.Errorf("Expected an error when converting %s, got no error", reflect.TypeOf(test.v))
|
|
||||||
} else if !test.err && err != nil {
|
|
||||||
t.Errorf("Expected converter when converting %s, got error: %s", reflect.TypeOf(test.v), err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
242
vendor/github.com/mattn/go-sqlite3/error_test.go
generated
vendored
242
vendor/github.com/mattn/go-sqlite3/error_test.go
generated
vendored
|
@ -1,242 +0,0 @@
|
||||||
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by an MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package sqlite3
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSimpleError(t *testing.T) {
|
|
||||||
e := ErrError.Error()
|
|
||||||
if e != "SQL logic error or missing database" {
|
|
||||||
t.Error("wrong error code:" + e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCorruptDbErrors(t *testing.T) {
|
|
||||||
dirName, err := ioutil.TempDir("", "sqlite3")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(dirName)
|
|
||||||
|
|
||||||
dbFileName := path.Join(dirName, "test.db")
|
|
||||||
f, err := os.Create(dbFileName)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
f.Write([]byte{1, 2, 3, 4, 5})
|
|
||||||
f.Close()
|
|
||||||
|
|
||||||
db, err := sql.Open("sqlite3", dbFileName)
|
|
||||||
if err == nil {
|
|
||||||
_, err = db.Exec("drop table foo")
|
|
||||||
}
|
|
||||||
|
|
||||||
sqliteErr := err.(Error)
|
|
||||||
if sqliteErr.Code != ErrNotADB {
|
|
||||||
t.Error("wrong error code for corrupted DB")
|
|
||||||
}
|
|
||||||
if err.Error() == "" {
|
|
||||||
t.Error("wrong error string for corrupted DB")
|
|
||||||
}
|
|
||||||
db.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSqlLogicErrors(t *testing.T) {
|
|
||||||
dirName, err := ioutil.TempDir("", "sqlite3")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(dirName)
|
|
||||||
|
|
||||||
dbFileName := path.Join(dirName, "test.db")
|
|
||||||
db, err := sql.Open("sqlite3", dbFileName)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
_, err = db.Exec("CREATE TABLE Foo (id INTEGER PRIMARY KEY)")
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
const expectedErr = "table Foo already exists"
|
|
||||||
_, err = db.Exec("CREATE TABLE Foo (id INTEGER PRIMARY KEY)")
|
|
||||||
if err.Error() != expectedErr {
|
|
||||||
t.Errorf("Unexpected error: %s, expected %s", err.Error(), expectedErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExtendedErrorCodes_ForeignKey(t *testing.T) {
|
|
||||||
dirName, err := ioutil.TempDir("", "sqlite3-err")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(dirName)
|
|
||||||
|
|
||||||
dbFileName := path.Join(dirName, "test.db")
|
|
||||||
db, err := sql.Open("sqlite3", dbFileName)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
_, err = db.Exec("PRAGMA foreign_keys=ON;")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("PRAGMA foreign_keys=ON: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec(`CREATE TABLE Foo (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
value INTEGER NOT NULL,
|
|
||||||
ref INTEGER NULL REFERENCES Foo (id),
|
|
||||||
UNIQUE(value)
|
|
||||||
);`)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec("INSERT INTO Foo (ref, value) VALUES (100, 100);")
|
|
||||||
if err == nil {
|
|
||||||
t.Error("No error!")
|
|
||||||
} else {
|
|
||||||
sqliteErr := err.(Error)
|
|
||||||
if sqliteErr.Code != ErrConstraint {
|
|
||||||
t.Errorf("Wrong basic error code: %d != %d",
|
|
||||||
sqliteErr.Code, ErrConstraint)
|
|
||||||
}
|
|
||||||
if sqliteErr.ExtendedCode != ErrConstraintForeignKey {
|
|
||||||
t.Errorf("Wrong extended error code: %d != %d",
|
|
||||||
sqliteErr.ExtendedCode, ErrConstraintForeignKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExtendedErrorCodes_NotNull(t *testing.T) {
|
|
||||||
dirName, err := ioutil.TempDir("", "sqlite3-err")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(dirName)
|
|
||||||
|
|
||||||
dbFileName := path.Join(dirName, "test.db")
|
|
||||||
db, err := sql.Open("sqlite3", dbFileName)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
_, err = db.Exec("PRAGMA foreign_keys=ON;")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("PRAGMA foreign_keys=ON: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec(`CREATE TABLE Foo (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
value INTEGER NOT NULL,
|
|
||||||
ref INTEGER NULL REFERENCES Foo (id),
|
|
||||||
UNIQUE(value)
|
|
||||||
);`)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := db.Exec("INSERT INTO Foo (value) VALUES (100);")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Creating first row: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
id, err := res.LastInsertId()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Retrieving last insert id: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec("INSERT INTO Foo (ref) VALUES (?);", id)
|
|
||||||
if err == nil {
|
|
||||||
t.Error("No error!")
|
|
||||||
} else {
|
|
||||||
sqliteErr := err.(Error)
|
|
||||||
if sqliteErr.Code != ErrConstraint {
|
|
||||||
t.Errorf("Wrong basic error code: %d != %d",
|
|
||||||
sqliteErr.Code, ErrConstraint)
|
|
||||||
}
|
|
||||||
if sqliteErr.ExtendedCode != ErrConstraintNotNull {
|
|
||||||
t.Errorf("Wrong extended error code: %d != %d",
|
|
||||||
sqliteErr.ExtendedCode, ErrConstraintNotNull)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExtendedErrorCodes_Unique(t *testing.T) {
|
|
||||||
dirName, err := ioutil.TempDir("", "sqlite3-err")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(dirName)
|
|
||||||
|
|
||||||
dbFileName := path.Join(dirName, "test.db")
|
|
||||||
db, err := sql.Open("sqlite3", dbFileName)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
_, err = db.Exec("PRAGMA foreign_keys=ON;")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("PRAGMA foreign_keys=ON: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec(`CREATE TABLE Foo (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
value INTEGER NOT NULL,
|
|
||||||
ref INTEGER NULL REFERENCES Foo (id),
|
|
||||||
UNIQUE(value)
|
|
||||||
);`)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := db.Exec("INSERT INTO Foo (value) VALUES (100);")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Creating first row: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
id, err := res.LastInsertId()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Retrieving last insert id: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec("INSERT INTO Foo (ref, value) VALUES (?, 100);", id)
|
|
||||||
if err == nil {
|
|
||||||
t.Error("No error!")
|
|
||||||
} else {
|
|
||||||
sqliteErr := err.(Error)
|
|
||||||
if sqliteErr.Code != ErrConstraint {
|
|
||||||
t.Errorf("Wrong basic error code: %d != %d",
|
|
||||||
sqliteErr.Code, ErrConstraint)
|
|
||||||
}
|
|
||||||
if sqliteErr.ExtendedCode != ErrConstraintUnique {
|
|
||||||
t.Errorf("Wrong extended error code: %d != %d",
|
|
||||||
sqliteErr.ExtendedCode, ErrConstraintUnique)
|
|
||||||
}
|
|
||||||
extended := sqliteErr.Code.Extend(3).Error()
|
|
||||||
expected := "constraint failed"
|
|
||||||
if extended != expected {
|
|
||||||
t.Errorf("Wrong basic error code: %q != %q",
|
|
||||||
extended, expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
130
vendor/github.com/mattn/go-sqlite3/sqlite3_fts3_test.go
generated
vendored
130
vendor/github.com/mattn/go-sqlite3/sqlite3_fts3_test.go
generated
vendored
|
@ -1,130 +0,0 @@
|
||||||
// Copyright (C) 2015 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
|
||||||
//
|
|
||||||
// Use of this source code is governed by an MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package sqlite3
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFTS3(t *testing.T) {
|
|
||||||
tempFilename := TempFilename(t)
|
|
||||||
defer os.Remove(tempFilename)
|
|
||||||
db, err := sql.Open("sqlite3", tempFilename)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Failed to open database:", err)
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
_, err = db.Exec("DROP TABLE foo")
|
|
||||||
_, err = db.Exec("CREATE VIRTUAL TABLE foo USING fts3(id INTEGER PRIMARY KEY, value TEXT)")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Failed to create table:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec("INSERT INTO foo(id, value) VALUES(?, ?)", 1, `今日の 晩御飯は 天麩羅よ`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Failed to insert value:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec("INSERT INTO foo(id, value) VALUES(?, ?)", 2, `今日は いい 天気だ`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Failed to insert value:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
rows, err := db.Query("SELECT id, value FROM foo WHERE value MATCH '今日* 天*'")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Unable to query foo table:", err)
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
for rows.Next() {
|
|
||||||
var id int
|
|
||||||
var value string
|
|
||||||
|
|
||||||
if err := rows.Scan(&id, &value); err != nil {
|
|
||||||
t.Error("Unable to scan results:", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if id == 1 && value != `今日の 晩御飯は 天麩羅よ` {
|
|
||||||
t.Error("Value for id 1 should be `今日の 晩御飯は 天麩羅よ`, but:", value)
|
|
||||||
} else if id == 2 && value != `今日は いい 天気だ` {
|
|
||||||
t.Error("Value for id 2 should be `今日は いい 天気だ`, but:", value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rows, err = db.Query("SELECT value FROM foo WHERE value MATCH '今日* 天麩羅*'")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Unable to query foo table:", err)
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
var value string
|
|
||||||
if !rows.Next() {
|
|
||||||
t.Fatal("Result should be only one")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := rows.Scan(&value); err != nil {
|
|
||||||
t.Fatal("Unable to scan results:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if value != `今日の 晩御飯は 天麩羅よ` {
|
|
||||||
t.Fatal("Value should be `今日の 晩御飯は 天麩羅よ`, but:", value)
|
|
||||||
}
|
|
||||||
|
|
||||||
if rows.Next() {
|
|
||||||
t.Fatal("Result should be only one")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFTS4(t *testing.T) {
|
|
||||||
tempFilename := TempFilename(t)
|
|
||||||
defer os.Remove(tempFilename)
|
|
||||||
db, err := sql.Open("sqlite3", tempFilename)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Failed to open database:", err)
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
_, err = db.Exec("DROP TABLE foo")
|
|
||||||
_, err = db.Exec("CREATE VIRTUAL TABLE foo USING fts4(tokenize=unicode61, id INTEGER PRIMARY KEY, value TEXT)")
|
|
||||||
switch {
|
|
||||||
case err != nil && err.Error() == "unknown tokenizer: unicode61":
|
|
||||||
t.Skip("FTS4 not supported")
|
|
||||||
case err != nil:
|
|
||||||
t.Fatal("Failed to create table:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec("INSERT INTO foo(id, value) VALUES(?, ?)", 1, `février`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Failed to insert value:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
rows, err := db.Query("SELECT value FROM foo WHERE value MATCH 'fevrier'")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Unable to query foo table:", err)
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
var value string
|
|
||||||
if !rows.Next() {
|
|
||||||
t.Fatal("Result should be only one")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := rows.Scan(&value); err != nil {
|
|
||||||
t.Fatal("Unable to scan results:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if value != `février` {
|
|
||||||
t.Fatal("Value should be `février`, but:", value)
|
|
||||||
}
|
|
||||||
|
|
||||||
if rows.Next() {
|
|
||||||
t.Fatal("Result should be only one")
|
|
||||||
}
|
|
||||||
}
|
|
1351
vendor/github.com/mattn/go-sqlite3/sqlite3_test.go
generated
vendored
1351
vendor/github.com/mattn/go-sqlite3/sqlite3_test.go
generated
vendored
File diff suppressed because it is too large
Load diff
10
vendor/github.com/pquerna/cachecontrol/.travis.yml
generated
vendored
10
vendor/github.com/pquerna/cachecontrol/.travis.yml
generated
vendored
|
@ -1,10 +0,0 @@
|
||||||
language: go
|
|
||||||
|
|
||||||
install:
|
|
||||||
- go get -d -v ./...
|
|
||||||
- go get -u github.com/stretchr/testify/require
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.5
|
|
||||||
- 1.6
|
|
||||||
- tip
|
|
108
vendor/github.com/pquerna/cachecontrol/README.md
generated
vendored
108
vendor/github.com/pquerna/cachecontrol/README.md
generated
vendored
|
@ -1,108 +0,0 @@
|
||||||
# cachecontrol: HTTP Caching Parser and Interpretation
|
|
||||||
|
|
||||||
[![GoDoc][1]][2][![Build Status](https://travis-ci.org/pquerna/cachecontrol.svg?branch=master)](https://travis-ci.org/pquerna/cachecontrol)
|
|
||||||
[1]: https://godoc.org/github.com/pquerna/cachecontrol?status.svg
|
|
||||||
[2]: https://godoc.org/github.com/pquerna/cachecontrol
|
|
||||||
|
|
||||||
|
|
||||||
`cachecontrol` implements [RFC 7234](http://tools.ietf.org/html/rfc7234) __Hypertext Transfer Protocol (HTTP/1.1): Caching__. It does this by parsing the `Cache-Control` and other headers, providing information about requests and responses -- but `cachecontrol` does not implement an actual cache backend, just the control plane to make decisions about if a particular response is cachable.
|
|
||||||
|
|
||||||
# Usage
|
|
||||||
|
|
||||||
`cachecontrol.CachableResponse` returns an array of [reasons](https://godoc.org/github.com/pquerna/cachecontrol/cacheobject#Reason) why a response should not be cached and when it expires. In the case that `len(reasons) == 0`, the response is cachable according to the RFC. However, some people want non-compliant caches for various business use cases, so each reason is specifically named, so if your cache wants to cache `POST` requests, it can easily do that, but still be RFC compliant in other situations.
|
|
||||||
|
|
||||||
# Examples
|
|
||||||
|
|
||||||
## Can you cache Example.com?
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/pquerna/cachecontrol"
|
|
||||||
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
req, _ := http.NewRequest("GET", "http://www.example.com/", nil)
|
|
||||||
|
|
||||||
res, _ := http.DefaultClient.Do(req)
|
|
||||||
_, _ = ioutil.ReadAll(res.Body)
|
|
||||||
|
|
||||||
reasons, expires, _ := cachecontrol.CachableResponse(req, res, cachecontrol.Options{})
|
|
||||||
|
|
||||||
fmt.Println("Reasons to not cache: ", reasons)
|
|
||||||
fmt.Println("Expiration: ", expires.String())
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Can I use this in a high performance caching server?
|
|
||||||
|
|
||||||
`cachecontrol` is divided into two packages: `cachecontrol` with a high level API, and a lower level `cacheobject` package. Use [Object](https://godoc.org/github.com/pquerna/cachecontrol/cacheobject#Object) in a high performance use case where you have previously parsed headers containing dates or would like to avoid memory allocations.
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/pquerna/cachecontrol/cacheobject"
|
|
||||||
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
req, _ := http.NewRequest("GET", "http://www.example.com/", nil)
|
|
||||||
|
|
||||||
res, _ := http.DefaultClient.Do(req)
|
|
||||||
_, _ = ioutil.ReadAll(res.Body)
|
|
||||||
|
|
||||||
reqDir, _ := cacheobject.ParseRequestCacheControl(req.Header.Get("Cache-Control"))
|
|
||||||
|
|
||||||
resDir, _ := cacheobject.ParseResponseCacheControl(res.Header.Get("Cache-Control"))
|
|
||||||
expiresHeader, _ := http.ParseTime(res.Header.Get("Expires"))
|
|
||||||
dateHeader, _ := http.ParseTime(res.Header.Get("Date"))
|
|
||||||
lastModifiedHeader, _ := http.ParseTime(res.Header.Get("Last-Modified"))
|
|
||||||
|
|
||||||
obj := cacheobject.Object{
|
|
||||||
RespDirectives: resDir,
|
|
||||||
RespHeaders: res.Header,
|
|
||||||
RespStatusCode: res.StatusCode,
|
|
||||||
RespExpiresHeader: expiresHeader,
|
|
||||||
RespDateHeader: dateHeader,
|
|
||||||
RespLastModifiedHeader: lastModifiedHeader,
|
|
||||||
|
|
||||||
ReqDirectives: reqDir,
|
|
||||||
ReqHeaders: req.Header,
|
|
||||||
ReqMethod: req.Method,
|
|
||||||
|
|
||||||
NowUTC: time.Now().UTC(),
|
|
||||||
}
|
|
||||||
rv := cacheobject.ObjectResults{}
|
|
||||||
|
|
||||||
cacheobject.CachableObject(&obj, &rv)
|
|
||||||
cacheobject.ExpirationObject(&obj, &rv)
|
|
||||||
|
|
||||||
fmt.Println("Errors: ", rv.OutErr)
|
|
||||||
fmt.Println("Reasons to not cache: ", rv.OutReasons)
|
|
||||||
fmt.Println("Warning headers to add: ", rv.OutWarnings)
|
|
||||||
fmt.Println("Expiration: ", rv.OutExpirationTime.String())
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Improvements, bugs, adding features, and taking cachecontrol new directions!
|
|
||||||
|
|
||||||
Please [open issues in Github](https://github.com/pquerna/cachecontrol/issues) for ideas, bugs, and general thoughts. Pull requests are of course preferred :)
|
|
||||||
|
|
||||||
# Credits
|
|
||||||
|
|
||||||
`cachecontrol` has recieved significant contributions from:
|
|
||||||
|
|
||||||
* [Paul Querna](https://github.com/pquerna)
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
`cachecontrol` is licensed under the [Apache License, Version 2.0](./LICENSE)
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue