From aaeb0828017918552295f071a1712284dda2b28a Mon Sep 17 00:00:00 2001 From: ValentinMontmirail Date: Fri, 5 Jan 2024 21:32:21 +0100 Subject: [PATCH 1/6] Implementing a recent logger and changing the severity level from Info to Debug when debugging logs were displayed --- ascii_over_tcp_client.go | 4 ++-- asciiclient.go | 4 ++-- client.go | 4 +++- cmd/modbus-cli/main.go | 38 ++++++++++++++++++++++---------- rtu_over_tcp_client.go | 4 ++-- rtuclient.go | 4 ++-- serial.go | 18 ++++++++++++--- tcpclient.go | 24 +++++++++++++++----- test/asciiclient_test.go | 4 ++-- test/rtu_over_tcp_client_test.go | 4 ++-- test/rtuclient_test.go | 4 ++-- test/tcpclient_test.go | 4 ++-- 12 files changed, 78 insertions(+), 38 deletions(-) diff --git a/ascii_over_tcp_client.go b/ascii_over_tcp_client.go index cc263e4..d45b008 100644 --- a/ascii_over_tcp_client.go +++ b/ascii_over_tcp_client.go @@ -55,7 +55,7 @@ func (mb *asciiTCPTransporter) Send(aduRequest []byte) (aduResponse []byte, err } // Send the request - mb.logf("modbus: send %q\n", aduRequest) + mb.Debug("modbus: send %q\n", aduRequest) if _, err = mb.conn.Write(aduRequest); err != nil { return } @@ -78,6 +78,6 @@ func (mb *asciiTCPTransporter) Send(aduRequest []byte) (aduResponse []byte, err } } aduResponse = data[:length] - mb.logf("modbus: recv %q\n", aduResponse) + mb.Debug("modbus: recv %q\n", aduResponse) return } diff --git a/asciiclient.go b/asciiclient.go index d860fb3..0deffea 100644 --- a/asciiclient.go +++ b/asciiclient.go @@ -181,7 +181,7 @@ func (mb *asciiSerialTransporter) Send(aduRequest []byte) (aduResponse []byte, e mb.startCloseTimer() // Send the request - mb.logf("modbus: send % x\n", aduRequest) + mb.Debug("modbus: send % x\n", aduRequest) if _, err = mb.port.Write(aduRequest); err != nil { return } @@ -204,7 +204,7 @@ func (mb *asciiSerialTransporter) Send(aduRequest []byte) (aduResponse []byte, e } } aduResponse = data[:length] - mb.logf("modbus: recv % x\n", aduResponse) + mb.Debug("modbus: recv % x\n", aduResponse) return } diff --git a/client.go b/client.go index 2535b72..002a8e7 100644 --- a/client.go +++ b/client.go @@ -11,7 +11,9 @@ import ( // logger is the interface to the required logging functions type logger interface { - Printf(format string, v ...interface{}) + Debug(format string, args ...interface{}) + Info(format string, args ...interface{}) + Error(format string, args ...interface{}) } // ClientHandler is the interface that groups the Packager and Transporter methods. diff --git a/cmd/modbus-cli/main.go b/cmd/modbus-cli/main.go index 7a4e8d9..6e842b1 100644 --- a/cmd/modbus-cli/main.go +++ b/cmd/modbus-cli/main.go @@ -7,7 +7,7 @@ import ( "fmt" "io" "io/ioutil" - "log" + "log/slog" "math" "net/url" "os" @@ -64,9 +64,12 @@ func main() { return } - logger := log.New(os.Stdout, "", 0) + logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) if *register > math.MaxUint16 || *register < 0 { - logger.Fatalf("invalid register value: %d", *register) + logger.Error("invalid register value", slog.Attr{ + Key: "registerValue", + Value: slog.IntValue(*register), + }) } startReg := uint16(*register) @@ -89,10 +92,12 @@ func main() { handler, err := newHandler(opt) if err != nil { - logger.Fatal(err) + logger.Error("%s", err) + os.Exit(-1) } if err := handler.Connect(); err != nil { - log.Fatal(err) + logger.Error("%s", err) + os.Exit(-1) } defer handler.Close() @@ -100,9 +105,10 @@ func main() { result, err := exec(client, eo, *writeParseOrder, *register, *fnCode, *writeValue, *eType, *quantity) if err != nil && strings.Contains(err.Error(), "crc") && *ignoreCRCError { - logger.Printf("ignoring crc error: %+v\n", err) + logger.Debug("ignoring crc error: %+v\n", err) } else if err != nil { - logger.Fatal(err) + logger.Error("%s", err) + os.Exit(-1) } var res string @@ -116,16 +122,22 @@ func main() { } if err != nil { - logger.Fatal(err) + logger.Error("%s", err) + os.Exit(-1) } - logger.Println(res) + logger.Info(res) if *filename != "" { if err := resultToFile([]byte(res), *filename); err != nil { - logger.Fatal(err) + logger.Error("%s", err) + os.Exit(-1) } - logger.Printf("%s successfully written\n", *filename) + + logger.Debug("file successfully written", slog.Attr{ + Key: "filename", + Value: slog.StringValue(*filename), + }) } } @@ -409,7 +421,9 @@ func resultToString(r []byte, order binary.ByteOrder, forcedOrder string, varTyp } type logger interface { - Printf(format string, v ...interface{}) + Debug(format string, args ...interface{}) + Info(format string, args ...interface{}) + Error(format string, args ...interface{}) } type option struct { diff --git a/rtu_over_tcp_client.go b/rtu_over_tcp_client.go index d9ec19d..45d5cb1 100644 --- a/rtu_over_tcp_client.go +++ b/rtu_over_tcp_client.go @@ -57,7 +57,7 @@ func (mb *rtuTCPTransporter) Send(aduRequest []byte) (aduResponse []byte, err er } // Send the request - mb.logf("modbus: send % x\n", aduRequest) + mb.Debug("modbus: send % x\n", aduRequest) if _, err = mb.conn.Write(aduRequest); err != nil { return } @@ -95,6 +95,6 @@ func (mb *rtuTCPTransporter) Send(aduRequest []byte) (aduResponse []byte, err er return } aduResponse = data[:n] - mb.logf("modbus: recv % x\n", aduResponse) + mb.Debug("modbus: recv % x\n", aduResponse) return } diff --git a/rtuclient.go b/rtuclient.go index 5d8d065..b4cf837 100644 --- a/rtuclient.go +++ b/rtuclient.go @@ -261,7 +261,7 @@ func (mb *rtuSerialTransporter) Send(aduRequest []byte) (aduResponse []byte, err mb.startCloseTimer() // Send the request - mb.logf("modbus: send % x\n", aduRequest) + mb.Debug("modbus: send % x\n", aduRequest) if _, err = mb.port.Write(aduRequest); err != nil { return } @@ -271,7 +271,7 @@ func (mb *rtuSerialTransporter) Send(aduRequest []byte) (aduResponse []byte, err time.Sleep(mb.calculateDelay(len(aduRequest) + bytesToRead)) data, err := readIncrementally(aduRequest[0], aduRequest[1], mb.port, time.Now().Add(mb.Config.Timeout)) - mb.logf("modbus: recv % x\n", data[:]) + mb.Debug("modbus: recv % x\n", data[:]) aduResponse = data return } diff --git a/serial.go b/serial.go index a910b95..01f0b03 100644 --- a/serial.go +++ b/serial.go @@ -69,9 +69,21 @@ func (mb *serialPort) close() (err error) { return } -func (mb *serialPort) logf(format string, v ...interface{}) { +func (mb *serialPort) Debug(format string, v ...interface{}) { if mb.Logger != nil { - mb.Logger.Printf(format, v...) + mb.Logger.Debug(format, v...) + } +} + +func (mb *serialPort) Info(format string, v ...interface{}) { + if mb.Logger != nil { + mb.Logger.Info(format, v...) + } +} + +func (mb *serialPort) Error(format string, v ...interface{}) { + if mb.Logger != nil { + mb.Logger.Error(format, v...) } } @@ -96,7 +108,7 @@ func (mb *serialPort) closeIdle() { } if idle := time.Since(mb.lastActivity); idle >= mb.IdleTimeout { - mb.logf("modbus: closing connection due to idle timeout: %v", idle) + mb.Debug("modbus: closing connection due to idle timeout: %v", idle) mb.close() } } diff --git a/tcpclient.go b/tcpclient.go index 57b3ffd..b199b39 100644 --- a/tcpclient.go +++ b/tcpclient.go @@ -183,7 +183,7 @@ func (mb *tcpTransporter) Send(aduRequest []byte) (aduResponse []byte, err error return } // Send data - mb.logf("modbus: send % x", aduRequest) + mb.Debug("modbus: send % x", aduRequest) if _, err = mb.conn.Write(aduRequest); err != nil { return } @@ -201,7 +201,7 @@ func (mb *tcpTransporter) Send(aduRequest []byte) (aduResponse []byte, err error continue } - mb.logf("modbus: close connection and retry, because of %v", err) + mb.Debug("modbus: close connection and retry, because of %v", err) mb.close() time.Sleep(mb.LinkRecoveryTimeout) @@ -216,7 +216,7 @@ func (mb *tcpTransporter) readResponse(aduRequest []byte, data []byte, recoveryD if err == nil { err = verify(aduRequest, aduResponse) if err == nil { - mb.logf("modbus: recv % x\n", aduResponse) + mb.Debug("modbus: recv % x\n", aduResponse) return // everything is OK } } @@ -382,9 +382,21 @@ func (mb *tcpTransporter) flush(b []byte) (err error) { return } -func (mb *tcpTransporter) logf(format string, v ...interface{}) { +func (mb *tcpTransporter) Debug(format string, v ...interface{}) { if mb.Logger != nil { - mb.Logger.Printf(format, v...) + mb.Logger.Debug(format, v...) + } +} + +func (mb *tcpTransporter) Info(format string, v ...interface{}) { + if mb.Logger != nil { + mb.Logger.Info(format, v...) + } +} + +func (mb *tcpTransporter) Error(format string, v ...interface{}) { + if mb.Logger != nil { + mb.Logger.Error(format, v...) } } @@ -407,7 +419,7 @@ func (mb *tcpTransporter) closeIdle() { } if idle := time.Since(mb.lastActivity); idle >= mb.IdleTimeout { - mb.logf("modbus: closing connection due to idle timeout: %v", idle) + mb.Debug("modbus: closing connection due to idle timeout: %v", idle) mb.close() } } diff --git a/test/asciiclient_test.go b/test/asciiclient_test.go index 45f4449..dbef4a2 100644 --- a/test/asciiclient_test.go +++ b/test/asciiclient_test.go @@ -5,7 +5,7 @@ package test import ( - "log" + "log/slog" "os" "testing" @@ -30,7 +30,7 @@ func TestASCIIClientAdvancedUsage(t *testing.T) { handler.Parity = "E" handler.StopBits = 1 handler.SlaveID = 12 - handler.Logger = log.New(os.Stdout, "ascii: ", log.LstdFlags) + handler.Logger = slog.New(slog.NewJSONHandler(os.Stdout, nil)) err := handler.Connect() if err != nil { t.Fatal(err) diff --git a/test/rtu_over_tcp_client_test.go b/test/rtu_over_tcp_client_test.go index 3615406..402c38b 100644 --- a/test/rtu_over_tcp_client_test.go +++ b/test/rtu_over_tcp_client_test.go @@ -5,7 +5,7 @@ package test import ( - "log" + "log/slog" "os" "testing" "time" @@ -28,7 +28,7 @@ func TestRTUOverTCPClientAdvancedUsage(t *testing.T) { handler := modbus.NewRTUOverTCPClientHandler(rtuOverTCPDevice) handler.Timeout = 5 * time.Second handler.SlaveID = 1 - handler.Logger = log.New(os.Stdout, "rtu over tcp: ", log.LstdFlags) + handler.Logger = slog.New(slog.NewJSONHandler(os.Stdout, nil)) handler.Connect() defer handler.Close() diff --git a/test/rtuclient_test.go b/test/rtuclient_test.go index 4722ee1..dddccb7 100644 --- a/test/rtuclient_test.go +++ b/test/rtuclient_test.go @@ -5,7 +5,7 @@ package test import ( - "log" + "log/slog" "os" "testing" @@ -30,7 +30,7 @@ func TestRTUClientAdvancedUsage(t *testing.T) { handler.Parity = "E" handler.StopBits = 1 handler.SlaveID = 11 - handler.Logger = log.New(os.Stdout, "rtu: ", log.LstdFlags) + handler.Logger = slog.New(slog.NewJSONHandler(os.Stdout, nil)) err := handler.Connect() if err != nil { t.Fatal(err) diff --git a/test/tcpclient_test.go b/test/tcpclient_test.go index 632814a..0363675 100644 --- a/test/tcpclient_test.go +++ b/test/tcpclient_test.go @@ -5,7 +5,7 @@ package test import ( - "log" + "log/slog" "os" "testing" "time" @@ -26,7 +26,7 @@ func TestTCPClientAdvancedUsage(t *testing.T) { handler := modbus.NewTCPClientHandler(tcpDevice) handler.Timeout = 5 * time.Second handler.SlaveID = 1 - handler.Logger = log.New(os.Stdout, "tcp: ", log.LstdFlags) + handler.Logger = slog.New(slog.NewJSONHandler(os.Stdout, nil)) handler.Connect() defer handler.Close() From 5e8be8a309bd7043a2882bede27baa2f810e0741 Mon Sep 17 00:00:00 2001 From: ValentinMontmirail Date: Fri, 5 Jan 2024 22:05:19 +0100 Subject: [PATCH 2/6] Cannot use slog, we are still in Go 1.13 --- cmd/modbus-cli/main.go | 78 ++++++++++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 29 deletions(-) diff --git a/cmd/modbus-cli/main.go b/cmd/modbus-cli/main.go index 6e842b1..aa66d68 100644 --- a/cmd/modbus-cli/main.go +++ b/cmd/modbus-cli/main.go @@ -7,7 +7,7 @@ import ( "fmt" "io" "io/ioutil" - "log/slog" + "log" "math" "net/url" "os" @@ -19,6 +19,44 @@ import ( "github.com/grid-x/serial" ) +type logger struct { +} + +func New(out io.Writer, prefix string, flag int) logger { + log.SetOutput(out) + log.SetPrefix(prefix) + log.SetFlags(flag) + return logger{} +} + +func (l logger) Printf(format string, v ...interface{}) { + log.Printf(format, v...) +} + +func (l logger) Println(format string, v ...interface{}) { + log.Printf(format, v...) +} + +func (l logger) Debug(format string, v ...interface{}) { + log.Printf("DEBUG: "+format, v...) +} + +func (l logger) Info(format string, v ...interface{}) { + log.Printf("INFO: "+format, v...) +} + +func (l logger) Error(format string, v ...interface{}) { + log.Printf("ERROR: "+format, v...) +} + +func (l logger) Fatalf(format string, v ...interface{}) { + log.Fatalf(format, v...) +} + +func (l logger) Fatal(v ...interface{}) { + log.Fatal(v...) +} + func main() { var opt option // general @@ -64,12 +102,9 @@ func main() { return } - logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) + logger := New(os.Stdout, "", 0) if *register > math.MaxUint16 || *register < 0 { - logger.Error("invalid register value", slog.Attr{ - Key: "registerValue", - Value: slog.IntValue(*register), - }) + logger.Fatalf("invalid register value: %d", *register) } startReg := uint16(*register) @@ -92,12 +127,10 @@ func main() { handler, err := newHandler(opt) if err != nil { - logger.Error("%s", err) - os.Exit(-1) + logger.Fatal(err) } if err := handler.Connect(); err != nil { - logger.Error("%s", err) - os.Exit(-1) + log.Fatal(err) } defer handler.Close() @@ -105,10 +138,9 @@ func main() { result, err := exec(client, eo, *writeParseOrder, *register, *fnCode, *writeValue, *eType, *quantity) if err != nil && strings.Contains(err.Error(), "crc") && *ignoreCRCError { - logger.Debug("ignoring crc error: %+v\n", err) + logger.Printf("ignoring crc error: %+v\n", err) } else if err != nil { - logger.Error("%s", err) - os.Exit(-1) + logger.Fatal(err) } var res string @@ -122,22 +154,16 @@ func main() { } if err != nil { - logger.Error("%s", err) - os.Exit(-1) + logger.Fatal(err) } - logger.Info(res) + logger.Println(res) if *filename != "" { if err := resultToFile([]byte(res), *filename); err != nil { - logger.Error("%s", err) - os.Exit(-1) + logger.Fatal(err) } - - logger.Debug("file successfully written", slog.Attr{ - Key: "filename", - Value: slog.StringValue(*filename), - }) + logger.Printf("%s successfully written\n", *filename) } } @@ -420,12 +446,6 @@ func resultToString(r []byte, order binary.ByteOrder, forcedOrder string, varTyp return "", fmt.Errorf("unsupported datatype: %s", varType) } -type logger interface { - Debug(format string, args ...interface{}) - Info(format string, args ...interface{}) - Error(format string, args ...interface{}) -} - type option struct { address string slaveID int From 6d14636270596ac133941d92630f83fc3ad34ec7 Mon Sep 17 00:00:00 2001 From: ValentinMontmirail Date: Fri, 5 Jan 2024 22:11:13 +0100 Subject: [PATCH 3/6] Make the New() function private. It is only related to logger so changing the name to newLogger --- cmd/modbus-cli/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/modbus-cli/main.go b/cmd/modbus-cli/main.go index aa66d68..db30290 100644 --- a/cmd/modbus-cli/main.go +++ b/cmd/modbus-cli/main.go @@ -22,7 +22,7 @@ import ( type logger struct { } -func New(out io.Writer, prefix string, flag int) logger { +func newLogger(out io.Writer, prefix string, flag int) logger { log.SetOutput(out) log.SetPrefix(prefix) log.SetFlags(flag) @@ -102,7 +102,7 @@ func main() { return } - logger := New(os.Stdout, "", 0) + logger := newLogger(os.Stdout, "", 0) if *register > math.MaxUint16 || *register < 0 { logger.Fatalf("invalid register value: %d", *register) } From 8eeb8b667dace57033149b0bff946db6e3806eea Mon Sep 17 00:00:00 2001 From: ValentinMontmirail Date: Sun, 21 Jan 2024 17:06:15 +0100 Subject: [PATCH 4/6] Adding a breaking point as accepted by frzifus --- client.go | 7 ----- cmd/modbus-cli/main.go | 70 ++++++++++++------------------------------ serial.go | 22 ++++++++++++- tcpclient.go | 22 ++++++++++++- 4 files changed, 62 insertions(+), 59 deletions(-) diff --git a/client.go b/client.go index 002a8e7..6d4c383 100644 --- a/client.go +++ b/client.go @@ -9,13 +9,6 @@ import ( "fmt" ) -// logger is the interface to the required logging functions -type logger interface { - Debug(format string, args ...interface{}) - Info(format string, args ...interface{}) - Error(format string, args ...interface{}) -} - // ClientHandler is the interface that groups the Packager and Transporter methods. type ClientHandler interface { Packager diff --git a/cmd/modbus-cli/main.go b/cmd/modbus-cli/main.go index db30290..b315703 100644 --- a/cmd/modbus-cli/main.go +++ b/cmd/modbus-cli/main.go @@ -6,11 +6,12 @@ import ( "flag" "fmt" "io" - "io/ioutil" "log" + "log/slog" "math" "net/url" "os" + "strconv" "strings" "text/tabwriter" "time" @@ -19,44 +20,6 @@ import ( "github.com/grid-x/serial" ) -type logger struct { -} - -func newLogger(out io.Writer, prefix string, flag int) logger { - log.SetOutput(out) - log.SetPrefix(prefix) - log.SetFlags(flag) - return logger{} -} - -func (l logger) Printf(format string, v ...interface{}) { - log.Printf(format, v...) -} - -func (l logger) Println(format string, v ...interface{}) { - log.Printf(format, v...) -} - -func (l logger) Debug(format string, v ...interface{}) { - log.Printf("DEBUG: "+format, v...) -} - -func (l logger) Info(format string, v ...interface{}) { - log.Printf("INFO: "+format, v...) -} - -func (l logger) Error(format string, v ...interface{}) { - log.Printf("ERROR: "+format, v...) -} - -func (l logger) Fatalf(format string, v ...interface{}) { - log.Fatalf(format, v...) -} - -func (l logger) Fatal(v ...interface{}) { - log.Fatal(v...) -} - func main() { var opt option // general @@ -102,9 +65,11 @@ func main() { return } - logger := newLogger(os.Stdout, "", 0) + logger := slog.Default() if *register > math.MaxUint16 || *register < 0 { - logger.Fatalf("invalid register value: %d", *register) + intRegister := *register + logger.Error("invalid register value: " + strconv.Itoa(intRegister)) + os.Exit(-1) } startReg := uint16(*register) @@ -127,7 +92,8 @@ func main() { handler, err := newHandler(opt) if err != nil { - logger.Fatal(err) + logger.Error(err.Error()) + os.Exit(-1) } if err := handler.Connect(); err != nil { log.Fatal(err) @@ -138,9 +104,10 @@ func main() { result, err := exec(client, eo, *writeParseOrder, *register, *fnCode, *writeValue, *eType, *quantity) if err != nil && strings.Contains(err.Error(), "crc") && *ignoreCRCError { - logger.Printf("ignoring crc error: %+v\n", err) + logger.Info("ignoring crc error: %+v\n", err) } else if err != nil { - logger.Fatal(err) + logger.Error(err.Error()) + os.Exit(-1) } var res string @@ -154,16 +121,19 @@ func main() { } if err != nil { - logger.Fatal(err) + logger.Error(err.Error()) + os.Exit(-1) } - logger.Println(res) + logger.Info(res) if *filename != "" { if err := resultToFile([]byte(res), *filename); err != nil { - logger.Fatal(err) + logger.Error(err.Error()) + os.Exit(-1) } - logger.Printf("%s successfully written\n", *filename) + fName := *filename + logger.Info(fName + " successfully written\n") } } @@ -268,7 +238,7 @@ func convertToBytes(eType string, order binary.ByteOrder, forcedOrder string, va } func resultToFile(r []byte, filename string) error { - return ioutil.WriteFile(filename, r, 0644) + return os.WriteFile(filename, r, 0644) } func resultToRawString(r []byte, startReg int) (string, error) { @@ -451,7 +421,7 @@ type option struct { slaveID int timeout time.Duration - logger logger + logger *slog.Logger rtu struct { baudrate int diff --git a/serial.go b/serial.go index 01f0b03..178c831 100644 --- a/serial.go +++ b/serial.go @@ -5,8 +5,10 @@ package modbus import ( + "context" "fmt" "io" + "log/slog" "sync" "time" @@ -24,7 +26,7 @@ type serialPort struct { // Serial port configuration. serial.Config - Logger logger + Logger *slog.Logger IdleTimeout time.Duration mu sync.Mutex @@ -87,6 +89,24 @@ func (mb *serialPort) Error(format string, v ...interface{}) { } } +func (mb *serialPort) DebugContext(ctx context.Context, format string, v ...interface{}) { + if mb.Logger != nil { + mb.Logger.DebugContext(ctx, format, v...) + } +} + +func (mb *serialPort) InfoContext(ctx context.Context, format string, v ...interface{}) { + if mb.Logger != nil { + mb.Logger.InfoContext(ctx, format, v...) + } +} + +func (mb *serialPort) ErrorContext(ctx context.Context, format string, v ...interface{}) { + if mb.Logger != nil { + mb.Logger.ErrorContext(ctx, format, v...) + } +} + func (mb *serialPort) startCloseTimer() { if mb.IdleTimeout <= 0 { return diff --git a/tcpclient.go b/tcpclient.go index b199b39..dcb872f 100644 --- a/tcpclient.go +++ b/tcpclient.go @@ -5,9 +5,11 @@ package modbus import ( + "context" "encoding/binary" "fmt" "io" + "log/slog" "net" "sync" "sync/atomic" @@ -136,7 +138,7 @@ type tcpTransporter struct { // Silent period after successful connection ConnectDelay time.Duration // Transmission logger - Logger logger + Logger *slog.Logger // TCP connection mu sync.Mutex @@ -400,6 +402,24 @@ func (mb *tcpTransporter) Error(format string, v ...interface{}) { } } +func (mb *tcpTransporter) DebugContext(ctx context.Context, format string, v ...interface{}) { + if mb.Logger != nil { + mb.Logger.DebugContext(ctx, format, v...) + } +} + +func (mb *tcpTransporter) InfoContext(ctx context.Context, format string, v ...interface{}) { + if mb.Logger != nil { + mb.Logger.InfoContext(ctx, format, v...) + } +} + +func (mb *tcpTransporter) ErrorContext(ctx context.Context, format string, v ...interface{}) { + if mb.Logger != nil { + mb.Logger.ErrorContext(ctx, format, v...) + } +} + // closeLocked closes current connection. Caller must hold the mutex before calling this method. func (mb *tcpTransporter) close() (err error) { if mb.conn != nil { From 59965a7855ce359564e769a4fed7ff6aace111d2 Mon Sep 17 00:00:00 2001 From: ValentinMontmirail Date: Sun, 21 Jan 2024 17:12:57 +0100 Subject: [PATCH 5/6] Update to Go 1.21 since slog is only available starting from 1.21 --- go.mod | 8 +- go.sum | 6 + vendor/github.com/google/go-cmp/.travis.yml | 26 - .../github.com/google/go-cmp/CONTRIBUTING.md | 23 - vendor/github.com/google/go-cmp/README.md | 44 - .../google/go-cmp/cmp/cmpopts/equate.go | 89 - .../google/go-cmp/cmp/cmpopts/ignore.go | 207 -- .../google/go-cmp/cmp/cmpopts/sort.go | 147 - .../go-cmp/cmp/cmpopts/struct_filter.go | 182 -- .../google/go-cmp/cmp/cmpopts/util_test.go | 1095 ------- .../google/go-cmp/cmp/cmpopts/xform.go | 35 - .../github.com/google/go-cmp/cmp/compare.go | 257 +- .../google/go-cmp/cmp/compare_test.go | 2829 ----------------- .../go-cmp/cmp/example_reporter_test.go | 59 - .../google/go-cmp/cmp/example_test.go | 376 --- vendor/github.com/google/go-cmp/cmp/export.go | 31 + .../google/go-cmp/cmp/export_panic.go | 15 - .../google/go-cmp/cmp/export_unsafe.go | 23 - .../go-cmp/cmp/internal/diff/debug_disable.go | 3 +- .../go-cmp/cmp/internal/diff/debug_enable.go | 3 +- .../google/go-cmp/cmp/internal/diff/diff.go | 88 +- .../go-cmp/cmp/internal/diff/diff_test.go | 444 --- .../google/go-cmp/cmp/internal/flags/flags.go | 2 +- .../cmp/internal/flags/toolchain_legacy.go | 10 - .../cmp/internal/flags/toolchain_recent.go | 10 - .../go-cmp/cmp/internal/function/func.go | 2 +- .../go-cmp/cmp/internal/function/func_test.go | 51 - .../go-cmp/cmp/internal/testprotos/protos.go | 116 - .../cmp/internal/teststructs/project1.go | 267 -- .../cmp/internal/teststructs/project2.go | 74 - .../cmp/internal/teststructs/project3.go | 82 - .../cmp/internal/teststructs/project4.go | 142 - .../cmp/internal/teststructs/structs.go | 197 -- .../google/go-cmp/cmp/internal/value/name.go | 164 + .../value/{pointer_unsafe.go => pointer.go} | 14 +- .../cmp/internal/value/pointer_purego.go | 23 - .../google/go-cmp/cmp/internal/value/sort.go | 2 +- .../go-cmp/cmp/internal/value/sort_test.go | 159 - .../google/go-cmp/cmp/internal/value/zero.go | 48 - .../go-cmp/cmp/internal/value/zero_test.go | 52 - .../github.com/google/go-cmp/cmp/options.go | 140 +- .../google/go-cmp/cmp/options_test.go | 216 -- vendor/github.com/google/go-cmp/cmp/path.go | 144 +- vendor/github.com/google/go-cmp/cmp/report.go | 7 +- .../google/go-cmp/cmp/report_compare.go | 215 +- .../google/go-cmp/cmp/report_references.go | 264 ++ .../google/go-cmp/cmp/report_reflect.go | 312 +- .../google/go-cmp/cmp/report_slices.go | 365 ++- .../google/go-cmp/cmp/report_text.go | 89 +- .../google/go-cmp/cmp/report_value.go | 2 +- vendor/github.com/google/go-cmp/go.mod | 3 - vendor/github.com/grid-x/serial/go.mod | 3 - vendor/github.com/grid-x/serial/serial_bsd.go | 2 +- .../grid-x/serial/serial_openbsd.go | 90 + .../github.com/grid-x/serial/serial_posix.go | 4 + .../github.com/grid-x/serial/termios_bsd.go | 2 +- .../grid-x/serial/termios_openbsd.go | 13 + vendor/modules.txt | 13 + .../rapid/.github/workflows/ci.yml | 32 - .../.github/workflows/codeql-analysis.yml | 71 - vendor/pgregory.net/rapid/.gitignore | 2 +- vendor/pgregory.net/rapid/README.md | 219 +- vendor/pgregory.net/rapid/TODO.md | 22 +- vendor/pgregory.net/rapid/collections.go | 233 +- .../rapid/collections_example_test.go | 139 - .../rapid/collections_external_test.go | 157 - vendor/pgregory.net/rapid/collections_test.go | 28 - vendor/pgregory.net/rapid/combinators.go | 288 +- .../rapid/combinators_example_test.go | 98 - .../rapid/combinators_external_test.go | 145 - vendor/pgregory.net/rapid/combinators_test.go | 26 - vendor/pgregory.net/rapid/data.go | 10 +- vendor/pgregory.net/rapid/data_test.go | 105 - vendor/pgregory.net/rapid/doc.go | 68 +- vendor/pgregory.net/rapid/engine.go | 360 ++- vendor/pgregory.net/rapid/engine_test.go | 84 - .../rapid/example_function_test.go | 66 - .../rapid/example_statemachine_test.go | 94 - .../rapid/failure_external_test.go | 173 - vendor/pgregory.net/rapid/floats.go | 87 +- .../rapid/floats_external_test.go | 145 - vendor/pgregory.net/rapid/floats_test.go | 120 - vendor/pgregory.net/rapid/generator.go | 94 +- vendor/pgregory.net/rapid/generator_test.go | 38 - vendor/pgregory.net/rapid/go.mod | 3 - vendor/pgregory.net/rapid/integers.go | 271 +- .../rapid/integers_external_test.go | 245 -- vendor/pgregory.net/rapid/make.go | 194 ++ vendor/pgregory.net/rapid/persist.go | 53 +- vendor/pgregory.net/rapid/persist_test.go | 45 - .../rapid/regexp_external_test.go | 1096 ------- vendor/pgregory.net/rapid/shrink.go | 11 +- vendor/pgregory.net/rapid/shrink_test.go | 232 -- vendor/pgregory.net/rapid/statemachine.go | 167 +- .../pgregory.net/rapid/statemachine_test.go | 270 -- vendor/pgregory.net/rapid/strings.go | 207 +- .../rapid/strings_example_test.go | 144 - .../rapid/strings_external_test.go | 101 - vendor/pgregory.net/rapid/utils.go | 12 +- vendor/pgregory.net/rapid/utils_test.go | 346 -- vendor/pgregory.net/rapid/vis_test.go | 74 - 101 files changed, 3106 insertions(+), 12558 deletions(-) delete mode 100644 vendor/github.com/google/go-cmp/.travis.yml delete mode 100644 vendor/github.com/google/go-cmp/CONTRIBUTING.md delete mode 100644 vendor/github.com/google/go-cmp/README.md delete mode 100644 vendor/github.com/google/go-cmp/cmp/cmpopts/equate.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/cmpopts/ignore.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/cmpopts/sort.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/cmpopts/struct_filter.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/cmpopts/util_test.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/cmpopts/xform.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/compare_test.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/example_reporter_test.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/example_test.go create mode 100644 vendor/github.com/google/go-cmp/cmp/export.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/export_panic.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/export_unsafe.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/internal/diff/diff_test.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_legacy.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_recent.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/internal/function/func_test.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/internal/testprotos/protos.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/internal/teststructs/project1.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/internal/teststructs/project2.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/internal/teststructs/project3.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/internal/teststructs/project4.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/internal/teststructs/structs.go create mode 100644 vendor/github.com/google/go-cmp/cmp/internal/value/name.go rename vendor/github.com/google/go-cmp/cmp/internal/value/{pointer_unsafe.go => pointer.go} (70%) delete mode 100644 vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/internal/value/sort_test.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/internal/value/zero.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/internal/value/zero_test.go delete mode 100644 vendor/github.com/google/go-cmp/cmp/options_test.go create mode 100644 vendor/github.com/google/go-cmp/cmp/report_references.go delete mode 100644 vendor/github.com/google/go-cmp/go.mod delete mode 100644 vendor/github.com/grid-x/serial/go.mod create mode 100644 vendor/github.com/grid-x/serial/serial_openbsd.go create mode 100644 vendor/github.com/grid-x/serial/termios_openbsd.go create mode 100644 vendor/modules.txt delete mode 100644 vendor/pgregory.net/rapid/.github/workflows/ci.yml delete mode 100644 vendor/pgregory.net/rapid/.github/workflows/codeql-analysis.yml delete mode 100644 vendor/pgregory.net/rapid/collections_example_test.go delete mode 100644 vendor/pgregory.net/rapid/collections_external_test.go delete mode 100644 vendor/pgregory.net/rapid/collections_test.go delete mode 100644 vendor/pgregory.net/rapid/combinators_example_test.go delete mode 100644 vendor/pgregory.net/rapid/combinators_external_test.go delete mode 100644 vendor/pgregory.net/rapid/combinators_test.go delete mode 100644 vendor/pgregory.net/rapid/data_test.go delete mode 100644 vendor/pgregory.net/rapid/engine_test.go delete mode 100644 vendor/pgregory.net/rapid/example_function_test.go delete mode 100644 vendor/pgregory.net/rapid/example_statemachine_test.go delete mode 100644 vendor/pgregory.net/rapid/failure_external_test.go delete mode 100644 vendor/pgregory.net/rapid/floats_external_test.go delete mode 100644 vendor/pgregory.net/rapid/floats_test.go delete mode 100644 vendor/pgregory.net/rapid/generator_test.go delete mode 100644 vendor/pgregory.net/rapid/go.mod delete mode 100644 vendor/pgregory.net/rapid/integers_external_test.go create mode 100644 vendor/pgregory.net/rapid/make.go delete mode 100644 vendor/pgregory.net/rapid/persist_test.go delete mode 100644 vendor/pgregory.net/rapid/regexp_external_test.go delete mode 100644 vendor/pgregory.net/rapid/shrink_test.go delete mode 100644 vendor/pgregory.net/rapid/statemachine_test.go delete mode 100644 vendor/pgregory.net/rapid/strings_example_test.go delete mode 100644 vendor/pgregory.net/rapid/strings_external_test.go delete mode 100644 vendor/pgregory.net/rapid/utils_test.go delete mode 100644 vendor/pgregory.net/rapid/vis_test.go diff --git a/go.mod b/go.mod index 7e1e99a..0e93002 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,9 @@ module github.com/grid-x/modbus -go 1.13 +go 1.21 require ( - github.com/google/go-cmp v0.5.6 - github.com/grid-x/serial v0.0.0-20191104121038-e24bc9bf6f08 - pgregory.net/rapid v0.4.7 + github.com/google/go-cmp v0.6.0 + github.com/grid-x/serial v0.0.0-20211107191517-583c7356b3aa + pgregory.net/rapid v1.1.0 ) diff --git a/go.sum b/go.sum index 249a3ad..9b3498f 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,14 @@ github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/grid-x/serial v0.0.0-20191104121038-e24bc9bf6f08 h1:syBxnRYnSPUDdkdo5U4sy2roxBPQDjNiw4od7xlsABQ= github.com/grid-x/serial v0.0.0-20191104121038-e24bc9bf6f08/go.mod h1:kdOd86/VGFWRrtkNwf1MPk0u1gIjc4Y7R2j7nhwc7Rk= +github.com/grid-x/serial v0.0.0-20211107191517-583c7356b3aa h1:Rsn6ARgNkXrsXJIzhkE4vQr5Gbx2LvtEMv4BJOK4LyU= +github.com/grid-x/serial v0.0.0-20211107191517-583c7356b3aa/go.mod h1:kdOd86/VGFWRrtkNwf1MPk0u1gIjc4Y7R2j7nhwc7Rk= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= pgregory.net/rapid v0.4.7 h1:MTNRktPuv5FNqOO151TM9mDTa+XHcX6ypYeISDVD14g= pgregory.net/rapid v0.4.7/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU= +pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= +pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= diff --git a/vendor/github.com/google/go-cmp/.travis.yml b/vendor/github.com/google/go-cmp/.travis.yml deleted file mode 100644 index ae1878d..0000000 --- a/vendor/github.com/google/go-cmp/.travis.yml +++ /dev/null @@ -1,26 +0,0 @@ -sudo: false -language: go -matrix: - include: - - go: 1.8.x - script: - - go test -v -race ./... - - go: 1.9.x - script: - - go test -v -race ./... - - go: 1.10.x - script: - - go test -v -race ./... - - go: 1.11.x - script: - - go test -v -race ./... - - go: 1.12.x - script: - - diff -u <(echo -n) <(gofmt -d .) - - go test -v -race ./... - - go: master - script: - - go test -v -race ./... - allow_failures: - - go: master - fast_finish: true diff --git a/vendor/github.com/google/go-cmp/CONTRIBUTING.md b/vendor/github.com/google/go-cmp/CONTRIBUTING.md deleted file mode 100644 index ae319c7..0000000 --- a/vendor/github.com/google/go-cmp/CONTRIBUTING.md +++ /dev/null @@ -1,23 +0,0 @@ -# How to Contribute - -We'd love to accept your patches and contributions to this project. There are -just a few small guidelines you need to follow. - -## Contributor License Agreement - -Contributions to this project must be accompanied by a Contributor License -Agreement. You (or your employer) retain the copyright to your contribution, -this simply gives us permission to use and redistribute your contributions as -part of the project. Head over to to see -your current agreements on file or to sign a new one. - -You generally only need to submit a CLA once, so if you've already submitted one -(even if it was for a different project), you probably don't need to do it -again. - -## Code reviews - -All submissions, including submissions by project members, require review. We -use GitHub pull requests for this purpose. Consult -[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more -information on using pull requests. diff --git a/vendor/github.com/google/go-cmp/README.md b/vendor/github.com/google/go-cmp/README.md deleted file mode 100644 index 61c9c4c..0000000 --- a/vendor/github.com/google/go-cmp/README.md +++ /dev/null @@ -1,44 +0,0 @@ -# Package for equality of Go values - -[![GoDoc](https://godoc.org/github.com/google/go-cmp/cmp?status.svg)][godoc] -[![Build Status](https://travis-ci.org/google/go-cmp.svg?branch=master)][travis] - -This package is intended to be a more powerful and safer alternative to -`reflect.DeepEqual` for comparing whether two values are semantically equal. - -The primary features of `cmp` are: - -* When the default behavior of equality does not suit the needs of the test, - custom equality functions can override the equality operation. - For example, an equality function may report floats as equal so long as they - are within some tolerance of each other. - -* Types that have an `Equal` method may use that method to determine equality. - This allows package authors to determine the equality operation for the types - that they define. - -* If no custom equality functions are used and no `Equal` method is defined, - equality is determined by recursively comparing the primitive kinds on both - values, much like `reflect.DeepEqual`. Unlike `reflect.DeepEqual`, unexported - fields are not compared by default; they result in panics unless suppressed - by using an `Ignore` option (see `cmpopts.IgnoreUnexported`) or explicitly - compared using the `AllowUnexported` option. - -See the [GoDoc documentation][godoc] for more information. - -This is not an official Google product. - -[godoc]: https://godoc.org/github.com/google/go-cmp/cmp -[travis]: https://travis-ci.org/google/go-cmp - -## Install - -``` -go get -u github.com/google/go-cmp/cmp -``` - -## License - -BSD - See [LICENSE][license] file - -[license]: https://github.com/google/go-cmp/blob/master/LICENSE diff --git a/vendor/github.com/google/go-cmp/cmp/cmpopts/equate.go b/vendor/github.com/google/go-cmp/cmp/cmpopts/equate.go deleted file mode 100644 index 41bbddc..0000000 --- a/vendor/github.com/google/go-cmp/cmp/cmpopts/equate.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2017, 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.md file. - -// Package cmpopts provides common options for the cmp package. -package cmpopts - -import ( - "math" - "reflect" - - "github.com/google/go-cmp/cmp" -) - -func equateAlways(_, _ interface{}) bool { return true } - -// EquateEmpty returns a Comparer option that determines all maps and slices -// with a length of zero to be equal, regardless of whether they are nil. -// -// EquateEmpty can be used in conjunction with SortSlices and SortMaps. -func EquateEmpty() cmp.Option { - return cmp.FilterValues(isEmpty, cmp.Comparer(equateAlways)) -} - -func isEmpty(x, y interface{}) bool { - vx, vy := reflect.ValueOf(x), reflect.ValueOf(y) - return (x != nil && y != nil && vx.Type() == vy.Type()) && - (vx.Kind() == reflect.Slice || vx.Kind() == reflect.Map) && - (vx.Len() == 0 && vy.Len() == 0) -} - -// EquateApprox returns a Comparer option that determines float32 or float64 -// values to be equal if they are within a relative fraction or absolute margin. -// This option is not used when either x or y is NaN or infinite. -// -// The fraction determines that the difference of two values must be within the -// smaller fraction of the two values, while the margin determines that the two -// values must be within some absolute margin. -// To express only a fraction or only a margin, use 0 for the other parameter. -// The fraction and margin must be non-negative. -// -// The mathematical expression used is equivalent to: -// |x-y| ≤ max(fraction*min(|x|, |y|), margin) -// -// EquateApprox can be used in conjunction with EquateNaNs. -func EquateApprox(fraction, margin float64) cmp.Option { - if margin < 0 || fraction < 0 || math.IsNaN(margin) || math.IsNaN(fraction) { - panic("margin or fraction must be a non-negative number") - } - a := approximator{fraction, margin} - return cmp.Options{ - cmp.FilterValues(areRealF64s, cmp.Comparer(a.compareF64)), - cmp.FilterValues(areRealF32s, cmp.Comparer(a.compareF32)), - } -} - -type approximator struct{ frac, marg float64 } - -func areRealF64s(x, y float64) bool { - return !math.IsNaN(x) && !math.IsNaN(y) && !math.IsInf(x, 0) && !math.IsInf(y, 0) -} -func areRealF32s(x, y float32) bool { - return areRealF64s(float64(x), float64(y)) -} -func (a approximator) compareF64(x, y float64) bool { - relMarg := a.frac * math.Min(math.Abs(x), math.Abs(y)) - return math.Abs(x-y) <= math.Max(a.marg, relMarg) -} -func (a approximator) compareF32(x, y float32) bool { - return a.compareF64(float64(x), float64(y)) -} - -// EquateNaNs returns a Comparer option that determines float32 and float64 -// NaN values to be equal. -// -// EquateNaNs can be used in conjunction with EquateApprox. -func EquateNaNs() cmp.Option { - return cmp.Options{ - cmp.FilterValues(areNaNsF64s, cmp.Comparer(equateAlways)), - cmp.FilterValues(areNaNsF32s, cmp.Comparer(equateAlways)), - } -} - -func areNaNsF64s(x, y float64) bool { - return math.IsNaN(x) && math.IsNaN(y) -} -func areNaNsF32s(x, y float32) bool { - return areNaNsF64s(float64(x), float64(y)) -} diff --git a/vendor/github.com/google/go-cmp/cmp/cmpopts/ignore.go b/vendor/github.com/google/go-cmp/cmp/cmpopts/ignore.go deleted file mode 100644 index ff8e785..0000000 --- a/vendor/github.com/google/go-cmp/cmp/cmpopts/ignore.go +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2017, 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.md file. - -package cmpopts - -import ( - "fmt" - "reflect" - "unicode" - "unicode/utf8" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/internal/function" -) - -// IgnoreFields returns an Option that ignores exported fields of the -// given names on a single struct type. -// The struct type is specified by passing in a value of that type. -// -// The name may be a dot-delimited string (e.g., "Foo.Bar") to ignore a -// specific sub-field that is embedded or nested within the parent struct. -// -// This does not handle unexported fields; use IgnoreUnexported instead. -func IgnoreFields(typ interface{}, names ...string) cmp.Option { - sf := newStructFilter(typ, names...) - return cmp.FilterPath(sf.filter, cmp.Ignore()) -} - -// IgnoreTypes returns an Option that ignores all values assignable to -// certain types, which are specified by passing in a value of each type. -func IgnoreTypes(typs ...interface{}) cmp.Option { - tf := newTypeFilter(typs...) - return cmp.FilterPath(tf.filter, cmp.Ignore()) -} - -type typeFilter []reflect.Type - -func newTypeFilter(typs ...interface{}) (tf typeFilter) { - for _, typ := range typs { - t := reflect.TypeOf(typ) - if t == nil { - // This occurs if someone tries to pass in sync.Locker(nil) - panic("cannot determine type; consider using IgnoreInterfaces") - } - tf = append(tf, t) - } - return tf -} -func (tf typeFilter) filter(p cmp.Path) bool { - if len(p) < 1 { - return false - } - t := p.Last().Type() - for _, ti := range tf { - if t.AssignableTo(ti) { - return true - } - } - return false -} - -// IgnoreInterfaces returns an Option that ignores all values or references of -// values assignable to certain interface types. These interfaces are specified -// by passing in an anonymous struct with the interface types embedded in it. -// For example, to ignore sync.Locker, pass in struct{sync.Locker}{}. -func IgnoreInterfaces(ifaces interface{}) cmp.Option { - tf := newIfaceFilter(ifaces) - return cmp.FilterPath(tf.filter, cmp.Ignore()) -} - -type ifaceFilter []reflect.Type - -func newIfaceFilter(ifaces interface{}) (tf ifaceFilter) { - t := reflect.TypeOf(ifaces) - if ifaces == nil || t.Name() != "" || t.Kind() != reflect.Struct { - panic("input must be an anonymous struct") - } - for i := 0; i < t.NumField(); i++ { - fi := t.Field(i) - switch { - case !fi.Anonymous: - panic("struct cannot have named fields") - case fi.Type.Kind() != reflect.Interface: - panic("embedded field must be an interface type") - case fi.Type.NumMethod() == 0: - // This matches everything; why would you ever want this? - panic("cannot ignore empty interface") - default: - tf = append(tf, fi.Type) - } - } - return tf -} -func (tf ifaceFilter) filter(p cmp.Path) bool { - if len(p) < 1 { - return false - } - t := p.Last().Type() - for _, ti := range tf { - if t.AssignableTo(ti) { - return true - } - if t.Kind() != reflect.Ptr && reflect.PtrTo(t).AssignableTo(ti) { - return true - } - } - return false -} - -// IgnoreUnexported returns an Option that only ignores the immediate unexported -// fields of a struct, including anonymous fields of unexported types. -// In particular, unexported fields within the struct's exported fields -// of struct types, including anonymous fields, will not be ignored unless the -// type of the field itself is also passed to IgnoreUnexported. -// -// Avoid ignoring unexported fields of a type which you do not control (i.e. a -// type from another repository), as changes to the implementation of such types -// may change how the comparison behaves. Prefer a custom Comparer instead. -func IgnoreUnexported(typs ...interface{}) cmp.Option { - ux := newUnexportedFilter(typs...) - return cmp.FilterPath(ux.filter, cmp.Ignore()) -} - -type unexportedFilter struct{ m map[reflect.Type]bool } - -func newUnexportedFilter(typs ...interface{}) unexportedFilter { - ux := unexportedFilter{m: make(map[reflect.Type]bool)} - for _, typ := range typs { - t := reflect.TypeOf(typ) - if t == nil || t.Kind() != reflect.Struct { - panic(fmt.Sprintf("invalid struct type: %T", typ)) - } - ux.m[t] = true - } - return ux -} -func (xf unexportedFilter) filter(p cmp.Path) bool { - sf, ok := p.Index(-1).(cmp.StructField) - if !ok { - return false - } - return xf.m[p.Index(-2).Type()] && !isExported(sf.Name()) -} - -// isExported reports whether the identifier is exported. -func isExported(id string) bool { - r, _ := utf8.DecodeRuneInString(id) - return unicode.IsUpper(r) -} - -// IgnoreSliceElements returns an Option that ignores elements of []V. -// The discard function must be of the form "func(T) bool" which is used to -// ignore slice elements of type V, where V is assignable to T. -// Elements are ignored if the function reports true. -func IgnoreSliceElements(discardFunc interface{}) cmp.Option { - vf := reflect.ValueOf(discardFunc) - if !function.IsType(vf.Type(), function.ValuePredicate) || vf.IsNil() { - panic(fmt.Sprintf("invalid discard function: %T", discardFunc)) - } - return cmp.FilterPath(func(p cmp.Path) bool { - si, ok := p.Index(-1).(cmp.SliceIndex) - if !ok { - return false - } - if !si.Type().AssignableTo(vf.Type().In(0)) { - return false - } - vx, vy := si.Values() - if vx.IsValid() && vf.Call([]reflect.Value{vx})[0].Bool() { - return true - } - if vy.IsValid() && vf.Call([]reflect.Value{vy})[0].Bool() { - return true - } - return false - }, cmp.Ignore()) -} - -// IgnoreMapEntries returns an Option that ignores entries of map[K]V. -// The discard function must be of the form "func(T, R) bool" which is used to -// ignore map entries of type K and V, where K and V are assignable to T and R. -// Entries are ignored if the function reports true. -func IgnoreMapEntries(discardFunc interface{}) cmp.Option { - vf := reflect.ValueOf(discardFunc) - if !function.IsType(vf.Type(), function.KeyValuePredicate) || vf.IsNil() { - panic(fmt.Sprintf("invalid discard function: %T", discardFunc)) - } - return cmp.FilterPath(func(p cmp.Path) bool { - mi, ok := p.Index(-1).(cmp.MapIndex) - if !ok { - return false - } - if !mi.Key().Type().AssignableTo(vf.Type().In(0)) || !mi.Type().AssignableTo(vf.Type().In(1)) { - return false - } - k := mi.Key() - vx, vy := mi.Values() - if vx.IsValid() && vf.Call([]reflect.Value{k, vx})[0].Bool() { - return true - } - if vy.IsValid() && vf.Call([]reflect.Value{k, vy})[0].Bool() { - return true - } - return false - }, cmp.Ignore()) -} diff --git a/vendor/github.com/google/go-cmp/cmp/cmpopts/sort.go b/vendor/github.com/google/go-cmp/cmp/cmpopts/sort.go deleted file mode 100644 index 3a48046..0000000 --- a/vendor/github.com/google/go-cmp/cmp/cmpopts/sort.go +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2017, 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.md file. - -package cmpopts - -import ( - "fmt" - "reflect" - "sort" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/internal/function" -) - -// SortSlices returns a Transformer option that sorts all []V. -// The less function must be of the form "func(T, T) bool" which is used to -// sort any slice with element type V that is assignable to T. -// -// The less function must be: -// • Deterministic: less(x, y) == less(x, y) -// • Irreflexive: !less(x, x) -// • Transitive: if !less(x, y) and !less(y, z), then !less(x, z) -// -// The less function does not have to be "total". That is, if !less(x, y) and -// !less(y, x) for two elements x and y, their relative order is maintained. -// -// SortSlices can be used in conjunction with EquateEmpty. -func SortSlices(lessFunc interface{}) cmp.Option { - vf := reflect.ValueOf(lessFunc) - if !function.IsType(vf.Type(), function.Less) || vf.IsNil() { - panic(fmt.Sprintf("invalid less function: %T", lessFunc)) - } - ss := sliceSorter{vf.Type().In(0), vf} - return cmp.FilterValues(ss.filter, cmp.Transformer("cmpopts.SortSlices", ss.sort)) -} - -type sliceSorter struct { - in reflect.Type // T - fnc reflect.Value // func(T, T) bool -} - -func (ss sliceSorter) filter(x, y interface{}) bool { - vx, vy := reflect.ValueOf(x), reflect.ValueOf(y) - if !(x != nil && y != nil && vx.Type() == vy.Type()) || - !(vx.Kind() == reflect.Slice && vx.Type().Elem().AssignableTo(ss.in)) || - (vx.Len() <= 1 && vy.Len() <= 1) { - return false - } - // Check whether the slices are already sorted to avoid an infinite - // recursion cycle applying the same transform to itself. - ok1 := sort.SliceIsSorted(x, func(i, j int) bool { return ss.less(vx, i, j) }) - ok2 := sort.SliceIsSorted(y, func(i, j int) bool { return ss.less(vy, i, j) }) - return !ok1 || !ok2 -} -func (ss sliceSorter) sort(x interface{}) interface{} { - src := reflect.ValueOf(x) - dst := reflect.MakeSlice(src.Type(), src.Len(), src.Len()) - for i := 0; i < src.Len(); i++ { - dst.Index(i).Set(src.Index(i)) - } - sort.SliceStable(dst.Interface(), func(i, j int) bool { return ss.less(dst, i, j) }) - ss.checkSort(dst) - return dst.Interface() -} -func (ss sliceSorter) checkSort(v reflect.Value) { - start := -1 // Start of a sequence of equal elements. - for i := 1; i < v.Len(); i++ { - if ss.less(v, i-1, i) { - // Check that first and last elements in v[start:i] are equal. - if start >= 0 && (ss.less(v, start, i-1) || ss.less(v, i-1, start)) { - panic(fmt.Sprintf("incomparable values detected: want equal elements: %v", v.Slice(start, i))) - } - start = -1 - } else if start == -1 { - start = i - } - } -} -func (ss sliceSorter) less(v reflect.Value, i, j int) bool { - vx, vy := v.Index(i), v.Index(j) - return ss.fnc.Call([]reflect.Value{vx, vy})[0].Bool() -} - -// SortMaps returns a Transformer option that flattens map[K]V types to be a -// sorted []struct{K, V}. The less function must be of the form -// "func(T, T) bool" which is used to sort any map with key K that is -// assignable to T. -// -// Flattening the map into a slice has the property that cmp.Equal is able to -// use Comparers on K or the K.Equal method if it exists. -// -// The less function must be: -// • Deterministic: less(x, y) == less(x, y) -// • Irreflexive: !less(x, x) -// • Transitive: if !less(x, y) and !less(y, z), then !less(x, z) -// • Total: if x != y, then either less(x, y) or less(y, x) -// -// SortMaps can be used in conjunction with EquateEmpty. -func SortMaps(lessFunc interface{}) cmp.Option { - vf := reflect.ValueOf(lessFunc) - if !function.IsType(vf.Type(), function.Less) || vf.IsNil() { - panic(fmt.Sprintf("invalid less function: %T", lessFunc)) - } - ms := mapSorter{vf.Type().In(0), vf} - return cmp.FilterValues(ms.filter, cmp.Transformer("cmpopts.SortMaps", ms.sort)) -} - -type mapSorter struct { - in reflect.Type // T - fnc reflect.Value // func(T, T) bool -} - -func (ms mapSorter) filter(x, y interface{}) bool { - vx, vy := reflect.ValueOf(x), reflect.ValueOf(y) - return (x != nil && y != nil && vx.Type() == vy.Type()) && - (vx.Kind() == reflect.Map && vx.Type().Key().AssignableTo(ms.in)) && - (vx.Len() != 0 || vy.Len() != 0) -} -func (ms mapSorter) sort(x interface{}) interface{} { - src := reflect.ValueOf(x) - outType := reflect.StructOf([]reflect.StructField{ - {Name: "K", Type: src.Type().Key()}, - {Name: "V", Type: src.Type().Elem()}, - }) - dst := reflect.MakeSlice(reflect.SliceOf(outType), src.Len(), src.Len()) - for i, k := range src.MapKeys() { - v := reflect.New(outType).Elem() - v.Field(0).Set(k) - v.Field(1).Set(src.MapIndex(k)) - dst.Index(i).Set(v) - } - sort.Slice(dst.Interface(), func(i, j int) bool { return ms.less(dst, i, j) }) - ms.checkSort(dst) - return dst.Interface() -} -func (ms mapSorter) checkSort(v reflect.Value) { - for i := 1; i < v.Len(); i++ { - if !ms.less(v, i-1, i) { - panic(fmt.Sprintf("partial order detected: want %v < %v", v.Index(i-1), v.Index(i))) - } - } -} -func (ms mapSorter) less(v reflect.Value, i, j int) bool { - vx, vy := v.Index(i).Field(0), v.Index(j).Field(0) - return ms.fnc.Call([]reflect.Value{vx, vy})[0].Bool() -} diff --git a/vendor/github.com/google/go-cmp/cmp/cmpopts/struct_filter.go b/vendor/github.com/google/go-cmp/cmp/cmpopts/struct_filter.go deleted file mode 100644 index 97f7079..0000000 --- a/vendor/github.com/google/go-cmp/cmp/cmpopts/struct_filter.go +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright 2017, 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.md file. - -package cmpopts - -import ( - "fmt" - "reflect" - "strings" - - "github.com/google/go-cmp/cmp" -) - -// filterField returns a new Option where opt is only evaluated on paths that -// include a specific exported field on a single struct type. -// The struct type is specified by passing in a value of that type. -// -// The name may be a dot-delimited string (e.g., "Foo.Bar") to select a -// specific sub-field that is embedded or nested within the parent struct. -func filterField(typ interface{}, name string, opt cmp.Option) cmp.Option { - // TODO: This is currently unexported over concerns of how helper filters - // can be composed together easily. - // TODO: Add tests for FilterField. - - sf := newStructFilter(typ, name) - return cmp.FilterPath(sf.filter, opt) -} - -type structFilter struct { - t reflect.Type // The root struct type to match on - ft fieldTree // Tree of fields to match on -} - -func newStructFilter(typ interface{}, names ...string) structFilter { - // TODO: Perhaps allow * as a special identifier to allow ignoring any - // number of path steps until the next field match? - // This could be useful when a concrete struct gets transformed into - // an anonymous struct where it is not possible to specify that by type, - // but the transformer happens to provide guarantees about the names of - // the transformed fields. - - t := reflect.TypeOf(typ) - if t == nil || t.Kind() != reflect.Struct { - panic(fmt.Sprintf("%T must be a struct", typ)) - } - var ft fieldTree - for _, name := range names { - cname, err := canonicalName(t, name) - if err != nil { - panic(fmt.Sprintf("%s: %v", strings.Join(cname, "."), err)) - } - ft.insert(cname) - } - return structFilter{t, ft} -} - -func (sf structFilter) filter(p cmp.Path) bool { - for i, ps := range p { - if ps.Type().AssignableTo(sf.t) && sf.ft.matchPrefix(p[i+1:]) { - return true - } - } - return false -} - -// fieldTree represents a set of dot-separated identifiers. -// -// For example, inserting the following selectors: -// Foo -// Foo.Bar.Baz -// Foo.Buzz -// Nuka.Cola.Quantum -// -// Results in a tree of the form: -// {sub: { -// "Foo": {ok: true, sub: { -// "Bar": {sub: { -// "Baz": {ok: true}, -// }}, -// "Buzz": {ok: true}, -// }}, -// "Nuka": {sub: { -// "Cola": {sub: { -// "Quantum": {ok: true}, -// }}, -// }}, -// }} -type fieldTree struct { - ok bool // Whether this is a specified node - sub map[string]fieldTree // The sub-tree of fields under this node -} - -// insert inserts a sequence of field accesses into the tree. -func (ft *fieldTree) insert(cname []string) { - if ft.sub == nil { - ft.sub = make(map[string]fieldTree) - } - if len(cname) == 0 { - ft.ok = true - return - } - sub := ft.sub[cname[0]] - sub.insert(cname[1:]) - ft.sub[cname[0]] = sub -} - -// matchPrefix reports whether any selector in the fieldTree matches -// the start of path p. -func (ft fieldTree) matchPrefix(p cmp.Path) bool { - for _, ps := range p { - switch ps := ps.(type) { - case cmp.StructField: - ft = ft.sub[ps.Name()] - if ft.ok { - return true - } - if len(ft.sub) == 0 { - return false - } - case cmp.Indirect: - default: - return false - } - } - return false -} - -// canonicalName returns a list of identifiers where any struct field access -// through an embedded field is expanded to include the names of the embedded -// types themselves. -// -// For example, suppose field "Foo" is not directly in the parent struct, -// but actually from an embedded struct of type "Bar". Then, the canonical name -// of "Foo" is actually "Bar.Foo". -// -// Suppose field "Foo" is not directly in the parent struct, but actually -// a field in two different embedded structs of types "Bar" and "Baz". -// Then the selector "Foo" causes a panic since it is ambiguous which one it -// refers to. The user must specify either "Bar.Foo" or "Baz.Foo". -func canonicalName(t reflect.Type, sel string) ([]string, error) { - var name string - sel = strings.TrimPrefix(sel, ".") - if sel == "" { - return nil, fmt.Errorf("name must not be empty") - } - if i := strings.IndexByte(sel, '.'); i < 0 { - name, sel = sel, "" - } else { - name, sel = sel[:i], sel[i:] - } - - // Type must be a struct or pointer to struct. - if t.Kind() == reflect.Ptr { - t = t.Elem() - } - if t.Kind() != reflect.Struct { - return nil, fmt.Errorf("%v must be a struct", t) - } - - // Find the canonical name for this current field name. - // If the field exists in an embedded struct, then it will be expanded. - if !isExported(name) { - // Disallow unexported fields: - // * To discourage people from actually touching unexported fields - // * FieldByName is buggy (https://golang.org/issue/4876) - return []string{name}, fmt.Errorf("name must be exported") - } - sf, ok := t.FieldByName(name) - if !ok { - return []string{name}, fmt.Errorf("does not exist") - } - var ss []string - for i := range sf.Index { - ss = append(ss, t.FieldByIndex(sf.Index[:i+1]).Name) - } - if sel == "" { - return ss, nil - } - ssPost, err := canonicalName(sf.Type, sel) - return append(ss, ssPost...), err -} diff --git a/vendor/github.com/google/go-cmp/cmp/cmpopts/util_test.go b/vendor/github.com/google/go-cmp/cmp/cmpopts/util_test.go deleted file mode 100644 index ed0fbb1..0000000 --- a/vendor/github.com/google/go-cmp/cmp/cmpopts/util_test.go +++ /dev/null @@ -1,1095 +0,0 @@ -// Copyright 2017, 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.md file. - -package cmpopts - -import ( - "bytes" - "fmt" - "io" - "math" - "reflect" - "strings" - "sync" - "testing" - "time" - - "github.com/google/go-cmp/cmp" -) - -type ( - MyInt int - MyInts []int - MyFloat float32 - MyString string - MyTime struct{ time.Time } - MyStruct struct { - A, B []int - C, D map[time.Time]string - } - - Foo1 struct{ Alpha, Bravo, Charlie int } - Foo2 struct{ *Foo1 } - Foo3 struct{ *Foo2 } - Bar1 struct{ Foo3 } - Bar2 struct { - Bar1 - *Foo3 - Bravo float32 - } - Bar3 struct { - Bar1 - Bravo *Bar2 - Delta struct{ Echo Foo1 } - *Foo3 - Alpha string - } - - privateStruct struct{ Public, private int } - PublicStruct struct{ Public, private int } - ParentStruct struct { - *privateStruct - *PublicStruct - Public int - private int - } - - Everything struct { - MyInt - MyFloat - MyTime - MyStruct - Bar3 - ParentStruct - } - - EmptyInterface interface{} -) - -func TestOptions(t *testing.T) { - createBar3X := func() *Bar3 { - return &Bar3{ - Bar1: Bar1{Foo3{&Foo2{&Foo1{Bravo: 2}}}}, - Bravo: &Bar2{ - Bar1: Bar1{Foo3{&Foo2{&Foo1{Charlie: 7}}}}, - Foo3: &Foo3{&Foo2{&Foo1{Bravo: 5}}}, - Bravo: 4, - }, - Delta: struct{ Echo Foo1 }{Foo1{Charlie: 3}}, - Foo3: &Foo3{&Foo2{&Foo1{Alpha: 1}}}, - Alpha: "alpha", - } - } - createBar3Y := func() *Bar3 { - return &Bar3{ - Bar1: Bar1{Foo3{&Foo2{&Foo1{Bravo: 3}}}}, - Bravo: &Bar2{ - Bar1: Bar1{Foo3{&Foo2{&Foo1{Charlie: 8}}}}, - Foo3: &Foo3{&Foo2{&Foo1{Bravo: 6}}}, - Bravo: 5, - }, - Delta: struct{ Echo Foo1 }{Foo1{Charlie: 4}}, - Foo3: &Foo3{&Foo2{&Foo1{Alpha: 2}}}, - Alpha: "ALPHA", - } - } - - tests := []struct { - label string // Test name - x, y interface{} // Input values to compare - opts []cmp.Option // Input options - wantEqual bool // Whether the inputs are equal - wantPanic bool // Whether Equal should panic - reason string // The reason for the expected outcome - }{{ - label: "EquateEmpty", - x: []int{}, - y: []int(nil), - wantEqual: false, - reason: "not equal because empty non-nil and nil slice differ", - }, { - label: "EquateEmpty", - x: []int{}, - y: []int(nil), - opts: []cmp.Option{EquateEmpty()}, - wantEqual: true, - reason: "equal because EquateEmpty equates empty slices", - }, { - label: "SortSlices", - x: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, - y: []int{1, 0, 5, 2, 8, 9, 4, 3, 6, 7}, - wantEqual: false, - reason: "not equal because element order differs", - }, { - label: "SortSlices", - x: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, - y: []int{1, 0, 5, 2, 8, 9, 4, 3, 6, 7}, - opts: []cmp.Option{SortSlices(func(x, y int) bool { return x < y })}, - wantEqual: true, - reason: "equal because SortSlices sorts the slices", - }, { - label: "SortSlices", - x: []MyInt{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, - y: []MyInt{1, 0, 5, 2, 8, 9, 4, 3, 6, 7}, - opts: []cmp.Option{SortSlices(func(x, y int) bool { return x < y })}, - wantEqual: false, - reason: "not equal because MyInt is not the same type as int", - }, { - label: "SortSlices", - x: []float64{0, 1, 1, 2, 2, 2}, - y: []float64{2, 0, 2, 1, 2, 1}, - opts: []cmp.Option{SortSlices(func(x, y float64) bool { return x < y })}, - wantEqual: true, - reason: "equal even when sorted with duplicate elements", - }, { - label: "SortSlices", - x: []float64{0, 1, 1, 2, 2, 2, math.NaN(), 3, 3, 3, 3, 4, 4, 4, 4}, - y: []float64{2, 0, 4, 4, 3, math.NaN(), 4, 1, 3, 2, 3, 3, 4, 1, 2}, - opts: []cmp.Option{SortSlices(func(x, y float64) bool { return x < y })}, - wantPanic: true, - reason: "panics because SortSlices used with non-transitive less function", - }, { - label: "SortSlices", - x: []float64{0, 1, 1, 2, 2, 2, math.NaN(), 3, 3, 3, 3, 4, 4, 4, 4}, - y: []float64{2, 0, 4, 4, 3, math.NaN(), 4, 1, 3, 2, 3, 3, 4, 1, 2}, - opts: []cmp.Option{SortSlices(func(x, y float64) bool { - return (!math.IsNaN(x) && math.IsNaN(y)) || x < y - })}, - wantEqual: false, - reason: "no panics because SortSlices used with valid less function; not equal because NaN != NaN", - }, { - label: "SortSlices+EquateNaNs", - x: []float64{0, 1, 1, 2, 2, 2, math.NaN(), 3, 3, 3, math.NaN(), 3, 4, 4, 4, 4}, - y: []float64{2, 0, 4, 4, 3, math.NaN(), 4, 1, 3, 2, 3, 3, 4, 1, math.NaN(), 2}, - opts: []cmp.Option{ - EquateNaNs(), - SortSlices(func(x, y float64) bool { - return (!math.IsNaN(x) && math.IsNaN(y)) || x < y - }), - }, - wantEqual: true, - reason: "no panics because SortSlices used with valid less function; equal because EquateNaNs is used", - }, { - label: "SortMaps", - x: map[time.Time]string{ - time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC): "0th birthday", - time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC): "1st birthday", - time.Date(2011, time.November, 10, 23, 0, 0, 0, time.UTC): "2nd birthday", - }, - y: map[time.Time]string{ - time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local): "0th birthday", - time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local): "1st birthday", - time.Date(2011, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local): "2nd birthday", - }, - wantEqual: false, - reason: "not equal because timezones differ", - }, { - label: "SortMaps", - x: map[time.Time]string{ - time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC): "0th birthday", - time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC): "1st birthday", - time.Date(2011, time.November, 10, 23, 0, 0, 0, time.UTC): "2nd birthday", - }, - y: map[time.Time]string{ - time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local): "0th birthday", - time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local): "1st birthday", - time.Date(2011, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local): "2nd birthday", - }, - opts: []cmp.Option{SortMaps(func(x, y time.Time) bool { return x.Before(y) })}, - wantEqual: true, - reason: "equal because SortMaps flattens to a slice where Time.Equal can be used", - }, { - label: "SortMaps", - x: map[MyTime]string{ - {time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)}: "0th birthday", - {time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC)}: "1st birthday", - {time.Date(2011, time.November, 10, 23, 0, 0, 0, time.UTC)}: "2nd birthday", - }, - y: map[MyTime]string{ - {time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local)}: "0th birthday", - {time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local)}: "1st birthday", - {time.Date(2011, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local)}: "2nd birthday", - }, - opts: []cmp.Option{SortMaps(func(x, y time.Time) bool { return x.Before(y) })}, - wantEqual: false, - reason: "not equal because MyTime is not assignable to time.Time", - }, { - label: "SortMaps", - x: map[int]string{-3: "", -2: "", -1: "", 0: "", 1: "", 2: "", 3: ""}, - // => {0, 1, 2, 3, -1, -2, -3}, - y: map[int]string{300: "", 200: "", 100: "", 0: "", 1: "", 2: "", 3: ""}, - // => {0, 1, 2, 3, 100, 200, 300}, - opts: []cmp.Option{SortMaps(func(a, b int) bool { - if -10 < a && a <= 0 { - a *= -100 - } - if -10 < b && b <= 0 { - b *= -100 - } - return a < b - })}, - wantEqual: false, - reason: "not equal because values differ even though SortMap provides valid ordering", - }, { - label: "SortMaps", - x: map[int]string{-3: "", -2: "", -1: "", 0: "", 1: "", 2: "", 3: ""}, - // => {0, 1, 2, 3, -1, -2, -3}, - y: map[int]string{300: "", 200: "", 100: "", 0: "", 1: "", 2: "", 3: ""}, - // => {0, 1, 2, 3, 100, 200, 300}, - opts: []cmp.Option{ - SortMaps(func(x, y int) bool { - if -10 < x && x <= 0 { - x *= -100 - } - if -10 < y && y <= 0 { - y *= -100 - } - return x < y - }), - cmp.Comparer(func(x, y int) bool { - if -10 < x && x <= 0 { - x *= -100 - } - if -10 < y && y <= 0 { - y *= -100 - } - return x == y - }), - }, - wantEqual: true, - reason: "equal because Comparer used to equate differences", - }, { - label: "SortMaps", - x: map[int]string{-3: "", -2: "", -1: "", 0: "", 1: "", 2: "", 3: ""}, - y: map[int]string{}, - opts: []cmp.Option{SortMaps(func(x, y int) bool { - return x < y && x >= 0 && y >= 0 - })}, - wantPanic: true, - reason: "panics because SortMaps used with non-transitive less function", - }, { - label: "SortMaps", - x: map[int]string{-3: "", -2: "", -1: "", 0: "", 1: "", 2: "", 3: ""}, - y: map[int]string{}, - opts: []cmp.Option{SortMaps(func(x, y int) bool { - return math.Abs(float64(x)) < math.Abs(float64(y)) - })}, - wantPanic: true, - reason: "panics because SortMaps used with partial less function", - }, { - label: "EquateEmpty+SortSlices+SortMaps", - x: MyStruct{ - A: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, - C: map[time.Time]string{ - time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC): "0th birthday", - time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC): "1st birthday", - }, - D: map[time.Time]string{}, - }, - y: MyStruct{ - A: []int{1, 0, 5, 2, 8, 9, 4, 3, 6, 7}, - B: []int{}, - C: map[time.Time]string{ - time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local): "0th birthday", - time.Date(2010, time.November, 10, 23, 0, 0, 0, time.UTC).In(time.Local): "1st birthday", - }, - }, - opts: []cmp.Option{ - EquateEmpty(), - SortSlices(func(x, y int) bool { return x < y }), - SortMaps(func(x, y time.Time) bool { return x.Before(y) }), - }, - wantEqual: true, - reason: "no panics because EquateEmpty should compose with the sort options", - }, { - label: "EquateApprox", - x: 3.09, - y: 3.10, - wantEqual: false, - reason: "not equal because floats do not exactly matches", - }, { - label: "EquateApprox", - x: 3.09, - y: 3.10, - opts: []cmp.Option{EquateApprox(0, 0)}, - wantEqual: false, - reason: "not equal because EquateApprox(0 ,0) is equivalent to using ==", - }, { - label: "EquateApprox", - x: 3.09, - y: 3.10, - opts: []cmp.Option{EquateApprox(0.003, 0.009)}, - wantEqual: false, - reason: "not equal because EquateApprox is too strict", - }, { - label: "EquateApprox", - x: 3.09, - y: 3.10, - opts: []cmp.Option{EquateApprox(0, 0.011)}, - wantEqual: true, - reason: "equal because margin is loose enough to match", - }, { - label: "EquateApprox", - x: 3.09, - y: 3.10, - opts: []cmp.Option{EquateApprox(0.004, 0)}, - wantEqual: true, - reason: "equal because fraction is loose enough to match", - }, { - label: "EquateApprox", - x: 3.09, - y: 3.10, - opts: []cmp.Option{EquateApprox(0.004, 0.011)}, - wantEqual: true, - reason: "equal because both the margin and fraction are loose enough to match", - }, { - label: "EquateApprox", - x: float32(3.09), - y: float64(3.10), - opts: []cmp.Option{EquateApprox(0.004, 0)}, - wantEqual: false, - reason: "not equal because the types differ", - }, { - label: "EquateApprox", - x: float32(3.09), - y: float32(3.10), - opts: []cmp.Option{EquateApprox(0.004, 0)}, - wantEqual: true, - reason: "equal because EquateApprox also applies on float32s", - }, { - label: "EquateApprox", - x: []float64{math.Inf(+1), math.Inf(-1)}, - y: []float64{math.Inf(+1), math.Inf(-1)}, - opts: []cmp.Option{EquateApprox(0, 1)}, - wantEqual: true, - reason: "equal because we fall back on == which matches Inf (EquateApprox does not apply on Inf) ", - }, { - label: "EquateApprox", - x: []float64{math.Inf(+1), -1e100}, - y: []float64{+1e100, math.Inf(-1)}, - opts: []cmp.Option{EquateApprox(0, 1)}, - wantEqual: false, - reason: "not equal because we fall back on == where Inf != 1e100 (EquateApprox does not apply on Inf)", - }, { - label: "EquateApprox", - x: float64(+1e100), - y: float64(-1e100), - opts: []cmp.Option{EquateApprox(math.Inf(+1), 0)}, - wantEqual: true, - reason: "equal because infinite fraction matches everything", - }, { - label: "EquateApprox", - x: float64(+1e100), - y: float64(-1e100), - opts: []cmp.Option{EquateApprox(0, math.Inf(+1))}, - wantEqual: true, - reason: "equal because infinite margin matches everything", - }, { - label: "EquateApprox", - x: math.Pi, - y: math.Pi, - opts: []cmp.Option{EquateApprox(0, 0)}, - wantEqual: true, - reason: "equal because EquateApprox(0, 0) is equivalent to ==", - }, { - label: "EquateApprox", - x: math.Pi, - y: math.Nextafter(math.Pi, math.Inf(+1)), - opts: []cmp.Option{EquateApprox(0, 0)}, - wantEqual: false, - reason: "not equal because EquateApprox(0, 0) is equivalent to ==", - }, { - label: "EquateNaNs", - x: []float64{1.0, math.NaN(), math.E, -0.0, +0.0, math.Inf(+1), math.Inf(-1)}, - y: []float64{1.0, math.NaN(), math.E, -0.0, +0.0, math.Inf(+1), math.Inf(-1)}, - wantEqual: false, - reason: "not equal because NaN != NaN", - }, { - label: "EquateNaNs", - x: []float64{1.0, math.NaN(), math.E, -0.0, +0.0, math.Inf(+1), math.Inf(-1)}, - y: []float64{1.0, math.NaN(), math.E, -0.0, +0.0, math.Inf(+1), math.Inf(-1)}, - opts: []cmp.Option{EquateNaNs()}, - wantEqual: true, - reason: "equal because EquateNaNs allows NaN == NaN", - }, { - label: "EquateNaNs", - x: []float32{1.0, float32(math.NaN()), math.E, -0.0, +0.0}, - y: []float32{1.0, float32(math.NaN()), math.E, -0.0, +0.0}, - opts: []cmp.Option{EquateNaNs()}, - wantEqual: true, - reason: "equal because EquateNaNs operates on float32", - }, { - label: "EquateApprox+EquateNaNs", - x: []float64{1.0, math.NaN(), math.E, -0.0, +0.0, math.Inf(+1), math.Inf(-1), 1.01, 5001}, - y: []float64{1.0, math.NaN(), math.E, -0.0, +0.0, math.Inf(+1), math.Inf(-1), 1.02, 5002}, - opts: []cmp.Option{ - EquateNaNs(), - EquateApprox(0.01, 0), - }, - wantEqual: true, - reason: "equal because EquateNaNs and EquateApprox compose together", - }, { - label: "EquateApprox+EquateNaNs", - x: []MyFloat{1.0, MyFloat(math.NaN()), MyFloat(math.E), -0.0, +0.0, MyFloat(math.Inf(+1)), MyFloat(math.Inf(-1)), 1.01, 5001}, - y: []MyFloat{1.0, MyFloat(math.NaN()), MyFloat(math.E), -0.0, +0.0, MyFloat(math.Inf(+1)), MyFloat(math.Inf(-1)), 1.02, 5002}, - opts: []cmp.Option{ - EquateNaNs(), - EquateApprox(0.01, 0), - }, - wantEqual: false, - reason: "not equal because EquateApprox and EquateNaNs do not apply on a named type", - }, { - label: "EquateApprox+EquateNaNs+Transform", - x: []MyFloat{1.0, MyFloat(math.NaN()), MyFloat(math.E), -0.0, +0.0, MyFloat(math.Inf(+1)), MyFloat(math.Inf(-1)), 1.01, 5001}, - y: []MyFloat{1.0, MyFloat(math.NaN()), MyFloat(math.E), -0.0, +0.0, MyFloat(math.Inf(+1)), MyFloat(math.Inf(-1)), 1.02, 5002}, - opts: []cmp.Option{ - cmp.Transformer("", func(x MyFloat) float64 { return float64(x) }), - EquateNaNs(), - EquateApprox(0.01, 0), - }, - wantEqual: true, - reason: "equal because named type is transformed to float64", - }, { - label: "IgnoreFields", - x: Bar1{Foo3{&Foo2{&Foo1{Alpha: 5}}}}, - y: Bar1{Foo3{&Foo2{&Foo1{Alpha: 6}}}}, - wantEqual: false, - reason: "not equal because values do not match in deeply embedded field", - }, { - label: "IgnoreFields", - x: Bar1{Foo3{&Foo2{&Foo1{Alpha: 5}}}}, - y: Bar1{Foo3{&Foo2{&Foo1{Alpha: 6}}}}, - opts: []cmp.Option{IgnoreFields(Bar1{}, "Alpha")}, - wantEqual: true, - reason: "equal because IgnoreField ignores deeply embedded field: Alpha", - }, { - label: "IgnoreFields", - x: Bar1{Foo3{&Foo2{&Foo1{Alpha: 5}}}}, - y: Bar1{Foo3{&Foo2{&Foo1{Alpha: 6}}}}, - opts: []cmp.Option{IgnoreFields(Bar1{}, "Foo1.Alpha")}, - wantEqual: true, - reason: "equal because IgnoreField ignores deeply embedded field: Foo1.Alpha", - }, { - label: "IgnoreFields", - x: Bar1{Foo3{&Foo2{&Foo1{Alpha: 5}}}}, - y: Bar1{Foo3{&Foo2{&Foo1{Alpha: 6}}}}, - opts: []cmp.Option{IgnoreFields(Bar1{}, "Foo2.Alpha")}, - wantEqual: true, - reason: "equal because IgnoreField ignores deeply embedded field: Foo2.Alpha", - }, { - label: "IgnoreFields", - x: Bar1{Foo3{&Foo2{&Foo1{Alpha: 5}}}}, - y: Bar1{Foo3{&Foo2{&Foo1{Alpha: 6}}}}, - opts: []cmp.Option{IgnoreFields(Bar1{}, "Foo3.Alpha")}, - wantEqual: true, - reason: "equal because IgnoreField ignores deeply embedded field: Foo3.Alpha", - }, { - label: "IgnoreFields", - x: Bar1{Foo3{&Foo2{&Foo1{Alpha: 5}}}}, - y: Bar1{Foo3{&Foo2{&Foo1{Alpha: 6}}}}, - opts: []cmp.Option{IgnoreFields(Bar1{}, "Foo3.Foo2.Alpha")}, - wantEqual: true, - reason: "equal because IgnoreField ignores deeply embedded field: Foo3.Foo2.Alpha", - }, { - label: "IgnoreFields", - x: createBar3X(), - y: createBar3Y(), - wantEqual: false, - reason: "not equal because many deeply nested or embedded fields differ", - }, { - label: "IgnoreFields", - x: createBar3X(), - y: createBar3Y(), - opts: []cmp.Option{IgnoreFields(Bar3{}, "Bar1", "Bravo", "Delta", "Foo3", "Alpha")}, - wantEqual: true, - reason: "equal because IgnoreFields ignores fields at the highest levels", - }, { - label: "IgnoreFields", - x: createBar3X(), - y: createBar3Y(), - opts: []cmp.Option{ - IgnoreFields(Bar3{}, - "Bar1.Foo3.Bravo", - "Bravo.Bar1.Foo3.Foo2.Foo1.Charlie", - "Bravo.Foo3.Foo2.Foo1.Bravo", - "Bravo.Bravo", - "Delta.Echo.Charlie", - "Foo3.Foo2.Foo1.Alpha", - "Alpha", - ), - }, - wantEqual: true, - reason: "equal because IgnoreFields ignores fields using fully-qualified field", - }, { - label: "IgnoreFields", - x: createBar3X(), - y: createBar3Y(), - opts: []cmp.Option{ - IgnoreFields(Bar3{}, - "Bar1.Foo3.Bravo", - "Bravo.Foo3.Foo2.Foo1.Bravo", - "Bravo.Bravo", - "Delta.Echo.Charlie", - "Foo3.Foo2.Foo1.Alpha", - "Alpha", - ), - }, - wantEqual: false, - reason: "not equal because one fully-qualified field is not ignored: Bravo.Bar1.Foo3.Foo2.Foo1.Charlie", - }, { - label: "IgnoreFields", - x: createBar3X(), - y: createBar3Y(), - opts: []cmp.Option{IgnoreFields(Bar3{}, "Bar1", "Bravo", "Delta", "Alpha")}, - wantEqual: false, - reason: "not equal because highest-level field is not ignored: Foo3", - }, { - label: "IgnoreTypes", - x: []interface{}{5, "same"}, - y: []interface{}{6, "same"}, - wantEqual: false, - reason: "not equal because 5 != 6", - }, { - label: "IgnoreTypes", - x: []interface{}{5, "same"}, - y: []interface{}{6, "same"}, - opts: []cmp.Option{IgnoreTypes(0)}, - wantEqual: true, - reason: "equal because ints are ignored", - }, { - label: "IgnoreTypes+IgnoreInterfaces", - x: []interface{}{5, "same", new(bytes.Buffer)}, - y: []interface{}{6, "same", new(bytes.Buffer)}, - opts: []cmp.Option{IgnoreTypes(0)}, - wantPanic: true, - reason: "panics because bytes.Buffer has unexported fields", - }, { - label: "IgnoreTypes+IgnoreInterfaces", - x: []interface{}{5, "same", new(bytes.Buffer)}, - y: []interface{}{6, "diff", new(bytes.Buffer)}, - opts: []cmp.Option{ - IgnoreTypes(0, ""), - IgnoreInterfaces(struct{ io.Reader }{}), - }, - wantEqual: true, - reason: "equal because bytes.Buffer is ignored by match on interface type", - }, { - label: "IgnoreTypes+IgnoreInterfaces", - x: []interface{}{5, "same", new(bytes.Buffer)}, - y: []interface{}{6, "same", new(bytes.Buffer)}, - opts: []cmp.Option{ - IgnoreTypes(0, ""), - IgnoreInterfaces(struct { - io.Reader - io.Writer - fmt.Stringer - }{}), - }, - wantEqual: true, - reason: "equal because bytes.Buffer is ignored by match on multiple interface types", - }, { - label: "IgnoreInterfaces", - x: struct{ mu sync.Mutex }{}, - y: struct{ mu sync.Mutex }{}, - wantPanic: true, - reason: "panics because sync.Mutex has unexported fields", - }, { - label: "IgnoreInterfaces", - x: struct{ mu sync.Mutex }{}, - y: struct{ mu sync.Mutex }{}, - opts: []cmp.Option{IgnoreInterfaces(struct{ sync.Locker }{})}, - wantEqual: true, - reason: "equal because IgnoreInterfaces applies on values (with pointer receiver)", - }, { - label: "IgnoreInterfaces", - x: struct{ mu *sync.Mutex }{}, - y: struct{ mu *sync.Mutex }{}, - opts: []cmp.Option{IgnoreInterfaces(struct{ sync.Locker }{})}, - wantEqual: true, - reason: "equal because IgnoreInterfaces applies on pointers", - }, { - label: "IgnoreUnexported", - x: ParentStruct{Public: 1, private: 2}, - y: ParentStruct{Public: 1, private: -2}, - opts: []cmp.Option{cmp.AllowUnexported(ParentStruct{})}, - wantEqual: false, - reason: "not equal because ParentStruct.private differs with AllowUnexported", - }, { - label: "IgnoreUnexported", - x: ParentStruct{Public: 1, private: 2}, - y: ParentStruct{Public: 1, private: -2}, - opts: []cmp.Option{IgnoreUnexported(ParentStruct{})}, - wantEqual: true, - reason: "equal because IgnoreUnexported ignored ParentStruct.private", - }, { - label: "IgnoreUnexported", - x: ParentStruct{Public: 1, private: 2, PublicStruct: &PublicStruct{Public: 3, private: 4}}, - y: ParentStruct{Public: 1, private: -2, PublicStruct: &PublicStruct{Public: 3, private: 4}}, - opts: []cmp.Option{ - cmp.AllowUnexported(PublicStruct{}), - IgnoreUnexported(ParentStruct{}), - }, - wantEqual: true, - reason: "equal because ParentStruct.private is ignored", - }, { - label: "IgnoreUnexported", - x: ParentStruct{Public: 1, private: 2, PublicStruct: &PublicStruct{Public: 3, private: 4}}, - y: ParentStruct{Public: 1, private: -2, PublicStruct: &PublicStruct{Public: 3, private: -4}}, - opts: []cmp.Option{ - cmp.AllowUnexported(PublicStruct{}), - IgnoreUnexported(ParentStruct{}), - }, - wantEqual: false, - reason: "not equal because ParentStruct.PublicStruct.private differs and not ignored by IgnoreUnexported(ParentStruct{})", - }, { - label: "IgnoreUnexported", - x: ParentStruct{Public: 1, private: 2, PublicStruct: &PublicStruct{Public: 3, private: 4}}, - y: ParentStruct{Public: 1, private: -2, PublicStruct: &PublicStruct{Public: 3, private: -4}}, - opts: []cmp.Option{ - IgnoreUnexported(ParentStruct{}, PublicStruct{}), - }, - wantEqual: true, - reason: "equal because both ParentStruct.PublicStruct and ParentStruct.PublicStruct.private are ignored", - }, { - label: "IgnoreUnexported", - x: ParentStruct{Public: 1, private: 2, privateStruct: &privateStruct{Public: 3, private: 4}}, - y: ParentStruct{Public: 1, private: 2, privateStruct: &privateStruct{Public: -3, private: -4}}, - opts: []cmp.Option{ - cmp.AllowUnexported(privateStruct{}, PublicStruct{}, ParentStruct{}), - }, - wantEqual: false, - reason: "not equal since ParentStruct.privateStruct differs", - }, { - label: "IgnoreUnexported", - x: ParentStruct{Public: 1, private: 2, privateStruct: &privateStruct{Public: 3, private: 4}}, - y: ParentStruct{Public: 1, private: 2, privateStruct: &privateStruct{Public: -3, private: -4}}, - opts: []cmp.Option{ - cmp.AllowUnexported(privateStruct{}, PublicStruct{}), - IgnoreUnexported(ParentStruct{}), - }, - wantEqual: true, - reason: "equal because ParentStruct.privateStruct ignored by IgnoreUnexported(ParentStruct{})", - }, { - label: "IgnoreUnexported", - x: ParentStruct{Public: 1, private: 2, privateStruct: &privateStruct{Public: 3, private: 4}}, - y: ParentStruct{Public: 1, private: 2, privateStruct: &privateStruct{Public: 3, private: -4}}, - opts: []cmp.Option{ - cmp.AllowUnexported(PublicStruct{}, ParentStruct{}), - IgnoreUnexported(privateStruct{}), - }, - wantEqual: true, - reason: "equal because privateStruct.private ignored by IgnoreUnexported(privateStruct{})", - }, { - label: "IgnoreUnexported", - x: ParentStruct{Public: 1, private: 2, privateStruct: &privateStruct{Public: 3, private: 4}}, - y: ParentStruct{Public: 1, private: 2, privateStruct: &privateStruct{Public: -3, private: -4}}, - opts: []cmp.Option{ - cmp.AllowUnexported(PublicStruct{}, ParentStruct{}), - IgnoreUnexported(privateStruct{}), - }, - wantEqual: false, - reason: "not equal because privateStruct.Public differs and not ignored by IgnoreUnexported(privateStruct{})", - }, { - label: "IgnoreFields+IgnoreTypes+IgnoreUnexported", - x: &Everything{ - MyInt: 5, - MyFloat: 3.3, - MyTime: MyTime{time.Now()}, - Bar3: *createBar3X(), - ParentStruct: ParentStruct{ - Public: 1, private: 2, PublicStruct: &PublicStruct{Public: 3, private: 4}, - }, - }, - y: &Everything{ - MyInt: -5, - MyFloat: 3.3, - MyTime: MyTime{time.Now()}, - Bar3: *createBar3Y(), - ParentStruct: ParentStruct{ - Public: 1, private: -2, PublicStruct: &PublicStruct{Public: -3, private: -4}, - }, - }, - opts: []cmp.Option{ - IgnoreFields(Everything{}, "MyTime", "Bar3.Foo3"), - IgnoreFields(Bar3{}, "Bar1", "Bravo", "Delta", "Alpha"), - IgnoreTypes(MyInt(0), PublicStruct{}), - IgnoreUnexported(ParentStruct{}), - }, - wantEqual: true, - reason: "equal because all Ignore options can be composed together", - }, { - label: "IgnoreSliceElements", - x: []int{1, 0, 2, 3, 0, 4, 0, 0}, - y: []int{0, 0, 0, 0, 1, 2, 3, 4}, - opts: []cmp.Option{ - IgnoreSliceElements(func(v int) bool { return v == 0 }), - }, - wantEqual: true, - reason: "equal because zero elements are ignored", - }, { - label: "IgnoreSliceElements", - x: []MyInt{1, 0, 2, 3, 0, 4, 0, 0}, - y: []MyInt{0, 0, 0, 0, 1, 2, 3, 4}, - opts: []cmp.Option{ - IgnoreSliceElements(func(v int) bool { return v == 0 }), - }, - wantEqual: false, - reason: "not equal because MyInt is not assignable to int", - }, { - label: "IgnoreSliceElements", - x: MyInts{1, 0, 2, 3, 0, 4, 0, 0}, - y: MyInts{0, 0, 0, 0, 1, 2, 3, 4}, - opts: []cmp.Option{ - IgnoreSliceElements(func(v int) bool { return v == 0 }), - }, - wantEqual: true, - reason: "equal because the element type of MyInts is assignable to int", - }, { - label: "IgnoreSliceElements+EquateEmpty", - x: []MyInt{}, - y: []MyInt{0, 0, 0, 0}, - opts: []cmp.Option{ - IgnoreSliceElements(func(v int) bool { return v == 0 }), - EquateEmpty(), - }, - wantEqual: false, - reason: "not equal because ignored elements does not imply empty slice", - }, { - label: "IgnoreMapEntries", - x: map[string]int{"one": 1, "TWO": 2, "three": 3, "FIVE": 5}, - y: map[string]int{"one": 1, "three": 3, "TEN": 10}, - opts: []cmp.Option{ - IgnoreMapEntries(func(k string, v int) bool { return strings.ToUpper(k) == k }), - }, - wantEqual: true, - reason: "equal because uppercase keys are ignored", - }, { - label: "IgnoreMapEntries", - x: map[MyString]int{"one": 1, "TWO": 2, "three": 3, "FIVE": 5}, - y: map[MyString]int{"one": 1, "three": 3, "TEN": 10}, - opts: []cmp.Option{ - IgnoreMapEntries(func(k string, v int) bool { return strings.ToUpper(k) == k }), - }, - wantEqual: false, - reason: "not equal because MyString is not assignable to string", - }, { - label: "IgnoreMapEntries", - x: map[string]MyInt{"one": 1, "TWO": 2, "three": 3, "FIVE": 5}, - y: map[string]MyInt{"one": 1, "three": 3, "TEN": 10}, - opts: []cmp.Option{ - IgnoreMapEntries(func(k string, v int) bool { return strings.ToUpper(k) == k }), - }, - wantEqual: false, - reason: "not equal because MyInt is not assignable to int", - }, { - label: "IgnoreMapEntries+EquateEmpty", - x: map[string]MyInt{"ONE": 1, "TWO": 2, "THREE": 3}, - y: nil, - opts: []cmp.Option{ - IgnoreMapEntries(func(k string, v int) bool { return strings.ToUpper(k) == k }), - EquateEmpty(), - }, - wantEqual: false, - reason: "not equal because ignored entries does not imply empty map", - }, { - label: "AcyclicTransformer", - x: "a\nb\nc\nd", - y: "a\nb\nd\nd", - opts: []cmp.Option{ - AcyclicTransformer("", func(s string) []string { return strings.Split(s, "\n") }), - }, - wantEqual: false, - reason: "not equal because 3rd line differs, but should not recurse infinitely", - }, { - label: "AcyclicTransformer", - x: []string{"foo", "Bar", "BAZ"}, - y: []string{"Foo", "BAR", "baz"}, - opts: []cmp.Option{ - AcyclicTransformer("", strings.ToUpper), - }, - wantEqual: true, - reason: "equal because of strings.ToUpper; AcyclicTransformer unnecessary, but check this still works", - }, { - label: "AcyclicTransformer", - x: "this is a sentence", - y: "this is a sentence", - opts: []cmp.Option{ - AcyclicTransformer("", strings.Fields), - }, - wantEqual: true, - reason: "equal because acyclic transformer splits on any contiguous whitespace", - }} - - for _, tt := range tests { - t.Run(tt.label, func(t *testing.T) { - var gotEqual bool - var gotPanic string - func() { - defer func() { - if ex := recover(); ex != nil { - gotPanic = fmt.Sprint(ex) - } - }() - gotEqual = cmp.Equal(tt.x, tt.y, tt.opts...) - }() - switch { - case tt.reason == "": - t.Errorf("reason must be provided") - case gotPanic == "" && tt.wantPanic: - t.Errorf("expected Equal panic\nreason: %s", tt.reason) - case gotPanic != "" && !tt.wantPanic: - t.Errorf("unexpected Equal panic: got %v\nreason: %v", gotPanic, tt.reason) - case gotEqual != tt.wantEqual: - t.Errorf("Equal = %v, want %v\nreason: %v", gotEqual, tt.wantEqual, tt.reason) - } - }) - } -} - -func TestPanic(t *testing.T) { - args := func(x ...interface{}) []interface{} { return x } - tests := []struct { - label string // Test name - fnc interface{} // Option function to call - args []interface{} // Arguments to pass in - wantPanic string // Expected panic message - reason string // The reason for the expected outcome - }{{ - label: "EquateApprox", - fnc: EquateApprox, - args: args(0.0, 0.0), - reason: "zero margin and fraction is equivalent to exact equality", - }, { - label: "EquateApprox", - fnc: EquateApprox, - args: args(-0.1, 0.0), - wantPanic: "margin or fraction must be a non-negative number", - reason: "negative inputs are invalid", - }, { - label: "EquateApprox", - fnc: EquateApprox, - args: args(0.0, -0.1), - wantPanic: "margin or fraction must be a non-negative number", - reason: "negative inputs are invalid", - }, { - label: "EquateApprox", - fnc: EquateApprox, - args: args(math.NaN(), 0.0), - wantPanic: "margin or fraction must be a non-negative number", - reason: "NaN inputs are invalid", - }, { - label: "EquateApprox", - fnc: EquateApprox, - args: args(1.0, 0.0), - reason: "fraction of 1.0 or greater is valid", - }, { - label: "EquateApprox", - fnc: EquateApprox, - args: args(0.0, math.Inf(+1)), - reason: "margin of infinity is valid", - }, { - label: "SortSlices", - fnc: SortSlices, - args: args(strings.Compare), - wantPanic: "invalid less function", - reason: "func(x, y string) int is wrong signature for less", - }, { - label: "SortSlices", - fnc: SortSlices, - args: args((func(_, _ int) bool)(nil)), - wantPanic: "invalid less function", - reason: "nil value is not valid", - }, { - label: "SortMaps", - fnc: SortMaps, - args: args(strings.Compare), - wantPanic: "invalid less function", - reason: "func(x, y string) int is wrong signature for less", - }, { - label: "SortMaps", - fnc: SortMaps, - args: args((func(_, _ int) bool)(nil)), - wantPanic: "invalid less function", - reason: "nil value is not valid", - }, { - label: "IgnoreFields", - fnc: IgnoreFields, - args: args(Foo1{}, ""), - wantPanic: "name must not be empty", - reason: "empty selector is invalid", - }, { - label: "IgnoreFields", - fnc: IgnoreFields, - args: args(Foo1{}, "."), - wantPanic: "name must not be empty", - reason: "single dot selector is invalid", - }, { - label: "IgnoreFields", - fnc: IgnoreFields, - args: args(Foo1{}, ".Alpha"), - reason: "dot-prefix is okay since Foo1.Alpha reads naturally", - }, { - label: "IgnoreFields", - fnc: IgnoreFields, - args: args(Foo1{}, "Alpha."), - wantPanic: "name must not be empty", - reason: "dot-suffix is invalid", - }, { - label: "IgnoreFields", - fnc: IgnoreFields, - args: args(Foo1{}, "Alpha "), - wantPanic: "does not exist", - reason: "identifiers must not have spaces", - }, { - label: "IgnoreFields", - fnc: IgnoreFields, - args: args(Foo1{}, "Zulu"), - wantPanic: "does not exist", - reason: "name of non-existent field is invalid", - }, { - label: "IgnoreFields", - fnc: IgnoreFields, - args: args(Foo1{}, "Alpha.NoExist"), - wantPanic: "must be a struct", - reason: "cannot select into a non-struct", - }, { - label: "IgnoreFields", - fnc: IgnoreFields, - args: args(&Foo1{}, "Alpha"), - wantPanic: "must be a struct", - reason: "the type must be a struct (not pointer to a struct)", - }, { - label: "IgnoreFields", - fnc: IgnoreFields, - args: args(Foo1{}, "unexported"), - wantPanic: "name must be exported", - reason: "unexported fields must not be specified", - }, { - label: "IgnoreTypes", - fnc: IgnoreTypes, - reason: "empty input is valid", - }, { - label: "IgnoreTypes", - fnc: IgnoreTypes, - args: args(nil), - wantPanic: "cannot determine type", - reason: "input must not be nil value", - }, { - label: "IgnoreTypes", - fnc: IgnoreTypes, - args: args(0, 0, 0), - reason: "duplicate inputs of the same type is valid", - }, { - label: "IgnoreInterfaces", - fnc: IgnoreInterfaces, - args: args(nil), - wantPanic: "input must be an anonymous struct", - reason: "input must not be nil value", - }, { - label: "IgnoreInterfaces", - fnc: IgnoreInterfaces, - args: args(Foo1{}), - wantPanic: "input must be an anonymous struct", - reason: "input must not be a named struct type", - }, { - label: "IgnoreInterfaces", - fnc: IgnoreInterfaces, - args: args(struct{ _ io.Reader }{}), - wantPanic: "struct cannot have named fields", - reason: "input must not have named fields", - }, { - label: "IgnoreInterfaces", - fnc: IgnoreInterfaces, - args: args(struct{ Foo1 }{}), - wantPanic: "embedded field must be an interface type", - reason: "field types must be interfaces", - }, { - label: "IgnoreInterfaces", - fnc: IgnoreInterfaces, - args: args(struct{ EmptyInterface }{}), - wantPanic: "cannot ignore empty interface", - reason: "field types must not be the empty interface", - }, { - label: "IgnoreInterfaces", - fnc: IgnoreInterfaces, - args: args(struct { - io.Reader - io.Writer - io.Closer - io.ReadWriteCloser - }{}), - reason: "multiple interfaces may be specified, even if they overlap", - }, { - label: "IgnoreUnexported", - fnc: IgnoreUnexported, - reason: "empty input is valid", - }, { - label: "IgnoreUnexported", - fnc: IgnoreUnexported, - args: args(nil), - wantPanic: "invalid struct type", - reason: "input must not be nil value", - }, { - label: "IgnoreUnexported", - fnc: IgnoreUnexported, - args: args(&Foo1{}), - wantPanic: "invalid struct type", - reason: "input must be a struct type (not a pointer to a struct)", - }, { - label: "IgnoreUnexported", - fnc: IgnoreUnexported, - args: args(Foo1{}, struct{ x, X int }{}), - reason: "input may be named or unnamed structs", - }, { - label: "AcyclicTransformer", - fnc: AcyclicTransformer, - args: args("", "not a func"), - wantPanic: "invalid transformer function", - reason: "AcyclicTransformer has same input requirements as Transformer", - }} - - for _, tt := range tests { - t.Run(tt.label, func(t *testing.T) { - // Prepare function arguments. - vf := reflect.ValueOf(tt.fnc) - var vargs []reflect.Value - for i, arg := range tt.args { - if arg == nil { - tf := vf.Type() - if i == tf.NumIn()-1 && tf.IsVariadic() { - vargs = append(vargs, reflect.Zero(tf.In(i).Elem())) - } else { - vargs = append(vargs, reflect.Zero(tf.In(i))) - } - } else { - vargs = append(vargs, reflect.ValueOf(arg)) - } - } - - // Call the function and capture any panics. - var gotPanic string - func() { - defer func() { - if ex := recover(); ex != nil { - if s, ok := ex.(string); ok { - gotPanic = s - } else { - panic(ex) - } - } - }() - vf.Call(vargs) - }() - - switch { - case tt.reason == "": - t.Errorf("reason must be provided") - case tt.wantPanic == "" && gotPanic != "": - t.Errorf("unexpected panic message: %s\nreason: %s", gotPanic, tt.reason) - case tt.wantPanic != "" && !strings.Contains(gotPanic, tt.wantPanic): - t.Errorf("panic message:\ngot: %s\nwant: %s\nreason: %s", gotPanic, tt.wantPanic, tt.reason) - } - }) - } -} diff --git a/vendor/github.com/google/go-cmp/cmp/cmpopts/xform.go b/vendor/github.com/google/go-cmp/cmp/cmpopts/xform.go deleted file mode 100644 index 9d65155..0000000 --- a/vendor/github.com/google/go-cmp/cmp/cmpopts/xform.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2018, 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.md file. - -package cmpopts - -import ( - "github.com/google/go-cmp/cmp" -) - -type xformFilter struct{ xform cmp.Option } - -func (xf xformFilter) filter(p cmp.Path) bool { - for _, ps := range p { - if t, ok := ps.(cmp.Transform); ok && t.Option() == xf.xform { - return false - } - } - return true -} - -// AcyclicTransformer returns a Transformer with a filter applied that ensures -// that the transformer cannot be recursively applied upon its own output. -// -// An example use case is a transformer that splits a string by lines: -// AcyclicTransformer("SplitLines", func(s string) []string{ -// return strings.Split(s, "\n") -// }) -// -// Had this been an unfiltered Transformer instead, this would result in an -// infinite cycle converting a string to []string to [][]string and so on. -func AcyclicTransformer(name string, xformFunc interface{}) cmp.Option { - xf := xformFilter{cmp.Transformer(name, xformFunc)} - return cmp.FilterPath(xf.filter, xf.xform) -} diff --git a/vendor/github.com/google/go-cmp/cmp/compare.go b/vendor/github.com/google/go-cmp/cmp/compare.go index 2133562..0f5b8a4 100644 --- a/vendor/github.com/google/go-cmp/cmp/compare.go +++ b/vendor/github.com/google/go-cmp/cmp/compare.go @@ -1,29 +1,34 @@ // Copyright 2017, 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.md file. +// license that can be found in the LICENSE file. // Package cmp determines equality of values. // // This package is intended to be a more powerful and safer alternative to -// reflect.DeepEqual for comparing whether two values are semantically equal. +// [reflect.DeepEqual] for comparing whether two values are semantically equal. +// It is intended to only be used in tests, as performance is not a goal and +// it may panic if it cannot compare the values. Its propensity towards +// panicking means that its unsuitable for production environments where a +// spurious panic may be fatal. // // The primary features of cmp are: // -// • When the default behavior of equality does not suit the needs of the test, -// custom equality functions can override the equality operation. -// For example, an equality function may report floats as equal so long as they -// are within some tolerance of each other. +// - When the default behavior of equality does not suit the test's needs, +// custom equality functions can override the equality operation. +// For example, an equality function may report floats as equal so long as +// they are within some tolerance of each other. // -// • Types that have an Equal method may use that method to determine equality. -// This allows package authors to determine the equality operation for the types -// that they define. +// - Types with an Equal method (e.g., [time.Time.Equal]) may use that method +// to determine equality. This allows package authors to determine +// the equality operation for the types that they define. // -// • If no custom equality functions are used and no Equal method is defined, -// equality is determined by recursively comparing the primitive kinds on both -// values, much like reflect.DeepEqual. Unlike reflect.DeepEqual, unexported -// fields are not compared by default; they result in panics unless suppressed -// by using an Ignore option (see cmpopts.IgnoreUnexported) or explicitly compared -// using the AllowUnexported option. +// - If no custom equality functions are used and no Equal method is defined, +// equality is determined by recursively comparing the primitive kinds on +// both values, much like [reflect.DeepEqual]. Unlike [reflect.DeepEqual], +// unexported fields are not compared by default; they result in panics +// unless suppressed by using an [Ignore] option +// (see [github.com/google/go-cmp/cmp/cmpopts.IgnoreUnexported]) +// or explicitly compared using the [Exporter] option. package cmp import ( @@ -32,55 +37,108 @@ import ( "strings" "github.com/google/go-cmp/cmp/internal/diff" - "github.com/google/go-cmp/cmp/internal/flags" "github.com/google/go-cmp/cmp/internal/function" "github.com/google/go-cmp/cmp/internal/value" ) +// TODO(≥go1.18): Use any instead of interface{}. + // Equal reports whether x and y are equal by recursively applying the // following rules in the given order to x and y and all of their sub-values: // -// • Let S be the set of all Ignore, Transformer, and Comparer options that -// remain after applying all path filters, value filters, and type filters. -// If at least one Ignore exists in S, then the comparison is ignored. -// If the number of Transformer and Comparer options in S is greater than one, -// then Equal panics because it is ambiguous which option to use. -// If S contains a single Transformer, then use that to transform the current -// values and recursively call Equal on the output values. -// If S contains a single Comparer, then use that to compare the current values. -// Otherwise, evaluation proceeds to the next rule. +// - Let S be the set of all [Ignore], [Transformer], and [Comparer] options that +// remain after applying all path filters, value filters, and type filters. +// If at least one [Ignore] exists in S, then the comparison is ignored. +// If the number of [Transformer] and [Comparer] options in S is non-zero, +// then Equal panics because it is ambiguous which option to use. +// If S contains a single [Transformer], then use that to transform +// the current values and recursively call Equal on the output values. +// If S contains a single [Comparer], then use that to compare the current values. +// Otherwise, evaluation proceeds to the next rule. // -// • If the values have an Equal method of the form "(T) Equal(T) bool" or -// "(T) Equal(I) bool" where T is assignable to I, then use the result of -// x.Equal(y) even if x or y is nil. Otherwise, no such method exists and -// evaluation proceeds to the next rule. +// - If the values have an Equal method of the form "(T) Equal(T) bool" or +// "(T) Equal(I) bool" where T is assignable to I, then use the result of +// x.Equal(y) even if x or y is nil. Otherwise, no such method exists and +// evaluation proceeds to the next rule. // -// • Lastly, try to compare x and y based on their basic kinds. -// Simple kinds like booleans, integers, floats, complex numbers, strings, and -// channels are compared using the equivalent of the == operator in Go. -// Functions are only equal if they are both nil, otherwise they are unequal. +// - Lastly, try to compare x and y based on their basic kinds. +// Simple kinds like booleans, integers, floats, complex numbers, strings, +// and channels are compared using the equivalent of the == operator in Go. +// Functions are only equal if they are both nil, otherwise they are unequal. // // Structs are equal if recursively calling Equal on all fields report equal. -// If a struct contains unexported fields, Equal panics unless an Ignore option -// (e.g., cmpopts.IgnoreUnexported) ignores that field or the AllowUnexported -// option explicitly permits comparing the unexported field. +// If a struct contains unexported fields, Equal panics unless an [Ignore] option +// (e.g., [github.com/google/go-cmp/cmp/cmpopts.IgnoreUnexported]) ignores that field +// or the [Exporter] option explicitly permits comparing the unexported field. // // Slices are equal if they are both nil or both non-nil, where recursively // calling Equal on all non-ignored slice or array elements report equal. // Empty non-nil slices and nil slices are not equal; to equate empty slices, -// consider using cmpopts.EquateEmpty. +// consider using [github.com/google/go-cmp/cmp/cmpopts.EquateEmpty]. // // Maps are equal if they are both nil or both non-nil, where recursively // calling Equal on all non-ignored map entries report equal. // Map keys are equal according to the == operator. -// To use custom comparisons for map keys, consider using cmpopts.SortMaps. +// To use custom comparisons for map keys, consider using +// [github.com/google/go-cmp/cmp/cmpopts.SortMaps]. // Empty non-nil maps and nil maps are not equal; to equate empty maps, -// consider using cmpopts.EquateEmpty. +// consider using [github.com/google/go-cmp/cmp/cmpopts.EquateEmpty]. // // Pointers and interfaces are equal if they are both nil or both non-nil, // where they have the same underlying concrete type and recursively // calling Equal on the underlying values reports equal. +// +// Before recursing into a pointer, slice element, or map, the current path +// is checked to detect whether the address has already been visited. +// If there is a cycle, then the pointed at values are considered equal +// only if both addresses were previously visited in the same path step. func Equal(x, y interface{}, opts ...Option) bool { + s := newState(opts) + s.compareAny(rootStep(x, y)) + return s.result.Equal() +} + +// Diff returns a human-readable report of the differences between two values: +// y - x. It returns an empty string if and only if Equal returns true for the +// same input values and options. +// +// The output is displayed as a literal in pseudo-Go syntax. +// At the start of each line, a "-" prefix indicates an element removed from x, +// a "+" prefix to indicates an element added from y, and the lack of a prefix +// indicates an element common to both x and y. If possible, the output +// uses fmt.Stringer.String or error.Error methods to produce more humanly +// readable outputs. In such cases, the string is prefixed with either an +// 's' or 'e' character, respectively, to indicate that the method was called. +// +// Do not depend on this output being stable. If you need the ability to +// programmatically interpret the difference, consider using a custom Reporter. +func Diff(x, y interface{}, opts ...Option) string { + s := newState(opts) + + // Optimization: If there are no other reporters, we can optimize for the + // common case where the result is equal (and thus no reported difference). + // This avoids the expensive construction of a difference tree. + if len(s.reporters) == 0 { + s.compareAny(rootStep(x, y)) + if s.result.Equal() { + return "" + } + s.result = diff.Result{} // Reset results + } + + r := new(defaultReporter) + s.reporters = append(s.reporters, reporter{r}) + s.compareAny(rootStep(x, y)) + d := r.String() + if (d == "") != s.result.Equal() { + panic("inconsistent difference and equality results") + } + return d +} + +// rootStep constructs the first path step. If x and y have differing types, +// then they are stored within an empty interface type. +func rootStep(x, y interface{}) PathStep { vx := reflect.ValueOf(x) vy := reflect.ValueOf(y) @@ -88,7 +146,7 @@ func Equal(x, y interface{}, opts ...Option) bool { // so that they have the same parent type. var t reflect.Type if !vx.IsValid() || !vy.IsValid() || vx.Type() != vy.Type() { - t = reflect.TypeOf((*interface{})(nil)).Elem() + t = anyType if vx.IsValid() { vvx := reflect.New(t).Elem() vvx.Set(vx) @@ -103,33 +161,7 @@ func Equal(x, y interface{}, opts ...Option) bool { t = vx.Type() } - s := newState(opts) - s.compareAny(&pathStep{t, vx, vy}) - return s.result.Equal() -} - -// Diff returns a human-readable report of the differences between two values. -// It returns an empty string if and only if Equal returns true for the same -// input values and options. -// -// The output is displayed as a literal in pseudo-Go syntax. -// At the start of each line, a "-" prefix indicates an element removed from x, -// a "+" prefix to indicates an element added to y, and the lack of a prefix -// indicates an element common to both x and y. If possible, the output -// uses fmt.Stringer.String or error.Error methods to produce more humanly -// readable outputs. In such cases, the string is prefixed with either an -// 's' or 'e' character, respectively, to indicate that the method was called. -// -// Do not depend on this output being stable. If you need the ability to -// programmatically interpret the difference, consider using a custom Reporter. -func Diff(x, y interface{}, opts ...Option) string { - r := new(defaultReporter) - eq := Equal(x, y, Options(opts), Reporter(r)) - d := r.String() - if (d == "") != eq { - panic("inconsistent difference and equality results") - } - return d + return &pathStep{t, vx, vy} } type state struct { @@ -137,6 +169,7 @@ type state struct { // Calling statelessCompare must not result in observable changes to these. result diff.Result // The current result of comparison curPath Path // The current path in the value tree + curPtrs pointerPath // The current set of visited pointers reporters []reporter // Optional reporters // recChecker checks for infinite cycles applying the same set of @@ -148,13 +181,14 @@ type state struct { dynChecker dynChecker // These fields, once set by processOption, will not change. - exporters map[reflect.Type]bool // Set of structs with unexported field visibility - opts Options // List of all fundamental and filter options + exporters []exporter // List of exporters for structs with unexported fields + opts Options // List of all fundamental and filter options } func newState(opts []Option) *state { // Always ensure a validator option exists to validate the inputs. s := &state{opts: Options{validator{}}} + s.curPtrs.Init() s.processOption(Options(opts)) return s } @@ -174,13 +208,8 @@ func (s *state) processOption(opt Option) { panic(fmt.Sprintf("cannot use an unfiltered option: %v", opt)) } s.opts = append(s.opts, opt) - case visibleStructs: - if s.exporters == nil { - s.exporters = make(map[reflect.Type]bool) - } - for t := range opt { - s.exporters[t] = true - } + case exporter: + s.exporters = append(s.exporters, opt) case reporter: s.reporters = append(s.reporters, opt) default: @@ -192,9 +221,9 @@ func (s *state) processOption(opt Option) { // This function is stateless in that it does not alter the current result, // or output to any registered reporters. func (s *state) statelessCompare(step PathStep) diff.Result { - // We do not save and restore the curPath because all of the compareX - // methods should properly push and pop from the path. - // It is an implementation bug if the contents of curPath differs from + // We do not save and restore curPath and curPtrs because all of the + // compareX methods should properly push and pop from them. + // It is an implementation bug if the contents of the paths differ from // when calling this function to when returning from it. oldResult, oldReporters := s.result, s.reporters @@ -216,9 +245,17 @@ func (s *state) compareAny(step PathStep) { } s.recChecker.Check(s.curPath) - // Obtain the current type and values. + // Cycle-detection for slice elements (see NOTE in compareSlice). t := step.Type() vx, vy := step.Values() + if si, ok := step.(SliceIndex); ok && si.isSlice && vx.IsValid() && vy.IsValid() { + px, py := vx.Addr(), vy.Addr() + if eq, visited := s.curPtrs.Push(px, py); visited { + s.report(eq, reportByCycle) + return + } + defer s.curPtrs.Pop(px, py) + } // Rule 1: Check whether an option applies on this node in the value tree. if s.tryOptions(t, vx, vy) { @@ -285,7 +322,6 @@ func (s *state) tryMethod(t reflect.Type, vx, vy reflect.Value) bool { } func (s *state) callTRFunc(f, v reflect.Value, step Transform) reflect.Value { - v = sanitizeValue(v, f.Type().In(0)) if !s.dynChecker.Next() { return f.Call([]reflect.Value{v})[0] } @@ -309,8 +345,6 @@ func (s *state) callTRFunc(f, v reflect.Value, step Transform) reflect.Value { } func (s *state) callTTBFunc(f, x, y reflect.Value) bool { - x = sanitizeValue(x, f.Type().In(0)) - y = sanitizeValue(y, f.Type().In(1)) if !s.dynChecker.Next() { return f.Call([]reflect.Value{x, y})[0].Bool() } @@ -338,22 +372,11 @@ func detectRaces(c chan<- reflect.Value, f reflect.Value, vs ...reflect.Value) { ret = f.Call(vs)[0] } -// sanitizeValue converts nil interfaces of type T to those of type R, -// assuming that T is assignable to R. -// Otherwise, it returns the input value as is. -func sanitizeValue(v reflect.Value, t reflect.Type) reflect.Value { - // TODO(dsnet): Workaround for reflect bug (https://golang.org/issue/22143). - if !flags.AtLeastGo110 { - if v.Kind() == reflect.Interface && v.IsNil() && v.Type() != t { - return reflect.New(t).Elem() - } - } - return v -} - func (s *state) compareStruct(t reflect.Type, vx, vy reflect.Value) { + var addr bool var vax, vay reflect.Value // Addressable versions of vx and vy + var mayForce, mayForceInit bool step := StructField{&structField{}} for i := 0; i < t.NumField(); i++ { step.typ = t.Field(i).Type @@ -372,10 +395,18 @@ func (s *state) compareStruct(t reflect.Type, vx, vy reflect.Value) { // For retrieveUnexportedField to work, the parent struct must // be addressable. Create a new copy of the values if // necessary to make them addressable. + addr = vx.CanAddr() || vy.CanAddr() vax = makeAddressable(vx) vay = makeAddressable(vy) } - step.mayForce = s.exporters[t] + if !mayForceInit { + for _, xf := range s.exporters { + mayForce = mayForce || xf(t) + } + mayForceInit = true + } + step.mayForce = mayForce + step.paddr = addr step.pvx = vax step.pvy = vay step.field = t.Field(i) @@ -391,9 +422,21 @@ func (s *state) compareSlice(t reflect.Type, vx, vy reflect.Value) { return } - // TODO: Support cyclic data structures. + // NOTE: It is incorrect to call curPtrs.Push on the slice header pointer + // since slices represents a list of pointers, rather than a single pointer. + // The pointer checking logic must be handled on a per-element basis + // in compareAny. + // + // A slice header (see reflect.SliceHeader) in Go is a tuple of a starting + // pointer P, a length N, and a capacity C. Supposing each slice element has + // a memory size of M, then the slice is equivalent to the list of pointers: + // [P+i*M for i in range(N)] + // + // For example, v[:0] and v[:1] are slices with the same starting pointer, + // but they are clearly different values. Using the slice pointer alone + // violates the assumption that equal pointers implies equal values. - step := SliceIndex{&sliceIndex{pathStep: pathStep{typ: t.Elem()}}} + step := SliceIndex{&sliceIndex{pathStep: pathStep{typ: t.Elem()}, isSlice: isSlice}} withIndexes := func(ix, iy int) SliceIndex { if ix >= 0 { step.vx, step.xkey = vx.Index(ix), ix @@ -470,7 +513,12 @@ func (s *state) compareMap(t reflect.Type, vx, vy reflect.Value) { return } - // TODO: Support cyclic data structures. + // Cycle-detection for maps. + if eq, visited := s.curPtrs.Push(vx, vy); visited { + s.report(eq, reportByCycle) + return + } + defer s.curPtrs.Pop(vx, vy) // We combine and sort the two map keys so that we can perform the // comparisons in a deterministic order. @@ -507,7 +555,12 @@ func (s *state) comparePtr(t reflect.Type, vx, vy reflect.Value) { return } - // TODO: Support cyclic data structures. + // Cycle-detection for pointers. + if eq, visited := s.curPtrs.Push(vx, vy); visited { + s.report(eq, reportByCycle) + return + } + defer s.curPtrs.Pop(vx, vy) vx, vy = vx.Elem(), vy.Elem() s.compareAny(Indirect{&indirect{pathStep{t.Elem(), vx, vy}}}) @@ -588,7 +641,9 @@ type dynChecker struct{ curr, next int } // Next increments the state and reports whether a check should be performed. // // Checks occur every Nth function call, where N is a triangular number: +// // 0 1 3 6 10 15 21 28 36 45 55 66 78 91 105 120 136 153 171 190 ... +// // See https://en.wikipedia.org/wiki/Triangular_number // // This sequence ensures that the cost of checks drops significantly as diff --git a/vendor/github.com/google/go-cmp/cmp/compare_test.go b/vendor/github.com/google/go-cmp/cmp/compare_test.go deleted file mode 100644 index ed46488..0000000 --- a/vendor/github.com/google/go-cmp/cmp/compare_test.go +++ /dev/null @@ -1,2829 +0,0 @@ -// Copyright 2017, 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.md file. - -package cmp_test - -import ( - "bytes" - "crypto/md5" - "encoding/json" - "fmt" - "io" - "math" - "math/rand" - "reflect" - "regexp" - "sort" - "strings" - "sync" - "testing" - "time" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - "github.com/google/go-cmp/cmp/internal/flags" - - pb "github.com/google/go-cmp/cmp/internal/testprotos" - ts "github.com/google/go-cmp/cmp/internal/teststructs" -) - -func init() { - flags.Deterministic = true -} - -var now = time.Date(2009, time.November, 10, 23, 00, 00, 00, time.UTC) - -func intPtr(n int) *int { return &n } - -type test struct { - label string // Test name - x, y interface{} // Input values to compare - opts []cmp.Option // Input options - wantDiff string // The exact difference string - wantPanic string // Sub-string of an expected panic message - reason string // The reason for the expected outcome -} - -func TestDiff(t *testing.T) { - var tests []test - tests = append(tests, comparerTests()...) - tests = append(tests, transformerTests()...) - tests = append(tests, embeddedTests()...) - tests = append(tests, methodTests()...) - tests = append(tests, project1Tests()...) - tests = append(tests, project2Tests()...) - tests = append(tests, project3Tests()...) - tests = append(tests, project4Tests()...) - - for _, tt := range tests { - tt := tt - t.Run(tt.label, func(t *testing.T) { - t.Parallel() - var gotDiff, gotPanic string - func() { - defer func() { - if ex := recover(); ex != nil { - if s, ok := ex.(string); ok { - gotPanic = s - } else { - panic(ex) - } - } - }() - gotDiff = cmp.Diff(tt.x, tt.y, tt.opts...) - }() - // TODO: Require every test case to provide a reason. - if tt.wantPanic == "" { - if gotPanic != "" { - t.Fatalf("unexpected panic message: %s\nreason: %v", gotPanic, tt.reason) - } - tt.wantDiff = strings.TrimPrefix(tt.wantDiff, "\n") - if gotDiff != tt.wantDiff { - t.Fatalf("difference message:\ngot:\n%s\nwant:\n%s\nreason: %v", gotDiff, tt.wantDiff, tt.reason) - } - } else { - if !strings.Contains(gotPanic, tt.wantPanic) { - t.Fatalf("panic message:\ngot: %s\nwant: %s\nreason: %v", gotPanic, tt.wantPanic, tt.reason) - } - } - }) - } -} - -func comparerTests() []test { - const label = "Comparer" - - type Iface1 interface { - Method() - } - type Iface2 interface { - Method() - } - - type tarHeader struct { - Name string - Mode int64 - Uid int - Gid int - Size int64 - ModTime time.Time - Typeflag byte - Linkname string - Uname string - Gname string - Devmajor int64 - Devminor int64 - AccessTime time.Time - ChangeTime time.Time - Xattrs map[string]string - } - - makeTarHeaders := func(tf byte) (hs []tarHeader) { - for i := 0; i < 5; i++ { - hs = append(hs, tarHeader{ - Name: fmt.Sprintf("some/dummy/test/file%d", i), - Mode: 0664, Uid: i * 1000, Gid: i * 1000, Size: 1 << uint(i), - ModTime: now.Add(time.Duration(i) * time.Hour), - Uname: "user", Gname: "group", - Typeflag: tf, - }) - } - return hs - } - - return []test{{ - label: label, - x: nil, - y: nil, - }, { - label: label, - x: 1, - y: 1, - }, { - label: label, - x: 1, - y: 1, - opts: []cmp.Option{cmp.Ignore()}, - wantPanic: "cannot use an unfiltered option", - }, { - label: label, - x: 1, - y: 1, - opts: []cmp.Option{cmp.Comparer(func(_, _ interface{}) bool { return true })}, - wantPanic: "cannot use an unfiltered option", - }, { - label: label, - x: 1, - y: 1, - opts: []cmp.Option{cmp.Transformer("λ", func(x interface{}) interface{} { return x })}, - wantPanic: "cannot use an unfiltered option", - }, { - label: label, - x: 1, - y: 1, - opts: []cmp.Option{ - cmp.Comparer(func(x, y int) bool { return true }), - cmp.Transformer("λ", func(x int) float64 { return float64(x) }), - }, - wantPanic: "ambiguous set of applicable options", - }, { - label: label, - x: 1, - y: 1, - opts: []cmp.Option{ - cmp.FilterPath(func(p cmp.Path) bool { - return len(p) > 0 && p[len(p)-1].Type().Kind() == reflect.Int - }, cmp.Options{cmp.Ignore(), cmp.Ignore(), cmp.Ignore()}), - cmp.Comparer(func(x, y int) bool { return true }), - cmp.Transformer("λ", func(x int) float64 { return float64(x) }), - }, - }, { - label: label, - opts: []cmp.Option{struct{ cmp.Option }{}}, - wantPanic: "unknown option", - }, { - label: label, - x: struct{ A, B, C int }{1, 2, 3}, - y: struct{ A, B, C int }{1, 2, 3}, - }, { - label: label, - x: struct{ A, B, C int }{1, 2, 3}, - y: struct{ A, B, C int }{1, 2, 4}, - wantDiff: ` - struct{ A int; B int; C int }{ - A: 1, - B: 2, -- C: 3, -+ C: 4, - } -`, - }, { - label: label, - x: struct{ a, b, c int }{1, 2, 3}, - y: struct{ a, b, c int }{1, 2, 4}, - wantPanic: "cannot handle unexported field", - }, { - label: label, - x: &struct{ A *int }{intPtr(4)}, - y: &struct{ A *int }{intPtr(4)}, - }, { - label: label, - x: &struct{ A *int }{intPtr(4)}, - y: &struct{ A *int }{intPtr(5)}, - wantDiff: ` - &struct{ A *int }{ -- A: &4, -+ A: &5, - } -`, - }, { - label: label, - x: &struct{ A *int }{intPtr(4)}, - y: &struct{ A *int }{intPtr(5)}, - opts: []cmp.Option{ - cmp.Comparer(func(x, y int) bool { return true }), - }, - }, { - label: label, - x: &struct{ A *int }{intPtr(4)}, - y: &struct{ A *int }{intPtr(5)}, - opts: []cmp.Option{ - cmp.Comparer(func(x, y *int) bool { return x != nil && y != nil }), - }, - }, { - label: label, - x: &struct{ R *bytes.Buffer }{}, - y: &struct{ R *bytes.Buffer }{}, - }, { - label: label, - x: &struct{ R *bytes.Buffer }{new(bytes.Buffer)}, - y: &struct{ R *bytes.Buffer }{}, - wantDiff: ` - &struct{ R *bytes.Buffer }{ -- R: s"", -+ R: nil, - } -`, - }, { - label: label, - x: &struct{ R *bytes.Buffer }{new(bytes.Buffer)}, - y: &struct{ R *bytes.Buffer }{}, - opts: []cmp.Option{ - cmp.Comparer(func(x, y io.Reader) bool { return true }), - }, - }, { - label: label, - x: &struct{ R bytes.Buffer }{}, - y: &struct{ R bytes.Buffer }{}, - wantPanic: "cannot handle unexported field", - }, { - label: label, - x: &struct{ R bytes.Buffer }{}, - y: &struct{ R bytes.Buffer }{}, - opts: []cmp.Option{ - cmp.Comparer(func(x, y io.Reader) bool { return true }), - }, - wantPanic: "cannot handle unexported field", - }, { - label: label, - x: &struct{ R bytes.Buffer }{}, - y: &struct{ R bytes.Buffer }{}, - opts: []cmp.Option{ - cmp.Transformer("Ref", func(x bytes.Buffer) *bytes.Buffer { return &x }), - cmp.Comparer(func(x, y io.Reader) bool { return true }), - }, - }, { - label: label, - x: []*regexp.Regexp{nil, regexp.MustCompile("a*b*c*")}, - y: []*regexp.Regexp{nil, regexp.MustCompile("a*b*c*")}, - wantPanic: "cannot handle unexported field", - }, { - label: label, - x: []*regexp.Regexp{nil, regexp.MustCompile("a*b*c*")}, - y: []*regexp.Regexp{nil, regexp.MustCompile("a*b*c*")}, - opts: []cmp.Option{cmp.Comparer(func(x, y *regexp.Regexp) bool { - if x == nil || y == nil { - return x == nil && y == nil - } - return x.String() == y.String() - })}, - }, { - label: label, - x: []*regexp.Regexp{nil, regexp.MustCompile("a*b*c*")}, - y: []*regexp.Regexp{nil, regexp.MustCompile("a*b*d*")}, - opts: []cmp.Option{cmp.Comparer(func(x, y *regexp.Regexp) bool { - if x == nil || y == nil { - return x == nil && y == nil - } - return x.String() == y.String() - })}, - wantDiff: ` - []*regexp.Regexp{ - nil, -- s"a*b*c*", -+ s"a*b*d*", - } -`, - }, { - label: label, - x: func() ***int { - a := 0 - b := &a - c := &b - return &c - }(), - y: func() ***int { - a := 0 - b := &a - c := &b - return &c - }(), - }, { - label: label, - x: func() ***int { - a := 0 - b := &a - c := &b - return &c - }(), - y: func() ***int { - a := 1 - b := &a - c := &b - return &c - }(), - wantDiff: ` - &&&int( -- 0, -+ 1, - ) -`, - }, { - label: label, - x: []int{1, 2, 3, 4, 5}[:3], - y: []int{1, 2, 3}, - }, { - label: label, - x: struct{ fmt.Stringer }{bytes.NewBufferString("hello")}, - y: struct{ fmt.Stringer }{regexp.MustCompile("hello")}, - opts: []cmp.Option{cmp.Comparer(func(x, y fmt.Stringer) bool { return x.String() == y.String() })}, - }, { - label: label, - x: struct{ fmt.Stringer }{bytes.NewBufferString("hello")}, - y: struct{ fmt.Stringer }{regexp.MustCompile("hello2")}, - opts: []cmp.Option{cmp.Comparer(func(x, y fmt.Stringer) bool { return x.String() == y.String() })}, - wantDiff: ` - struct{ fmt.Stringer }( -- s"hello", -+ s"hello2", - ) -`, - }, { - label: label, - x: md5.Sum([]byte{'a'}), - y: md5.Sum([]byte{'b'}), - wantDiff: ` - [16]uint8{ -- 0x0c, 0xc1, 0x75, 0xb9, 0xc0, 0xf1, 0xb6, 0xa8, 0x31, 0xc3, 0x99, 0xe2, 0x69, 0x77, 0x26, 0x61, -+ 0x92, 0xeb, 0x5f, 0xfe, 0xe6, 0xae, 0x2f, 0xec, 0x3a, 0xd7, 0x1c, 0x77, 0x75, 0x31, 0x57, 0x8f, - } -`, - }, { - label: label, - x: new(fmt.Stringer), - y: nil, - wantDiff: ` - interface{}( -- &fmt.Stringer(nil), - ) -`, - }, { - label: label, - x: makeTarHeaders('0'), - y: makeTarHeaders('\x00'), - wantDiff: ` - []cmp_test.tarHeader{ - { - ... // 4 identical fields - Size: 1, - ModTime: s"2009-11-10 23:00:00 +0000 UTC", -- Typeflag: 0x30, -+ Typeflag: 0x00, - Linkname: "", - Uname: "user", - ... // 6 identical fields - }, - { - ... // 4 identical fields - Size: 2, - ModTime: s"2009-11-11 00:00:00 +0000 UTC", -- Typeflag: 0x30, -+ Typeflag: 0x00, - Linkname: "", - Uname: "user", - ... // 6 identical fields - }, - { - ... // 4 identical fields - Size: 4, - ModTime: s"2009-11-11 01:00:00 +0000 UTC", -- Typeflag: 0x30, -+ Typeflag: 0x00, - Linkname: "", - Uname: "user", - ... // 6 identical fields - }, - { - ... // 4 identical fields - Size: 8, - ModTime: s"2009-11-11 02:00:00 +0000 UTC", -- Typeflag: 0x30, -+ Typeflag: 0x00, - Linkname: "", - Uname: "user", - ... // 6 identical fields - }, - { - ... // 4 identical fields - Size: 16, - ModTime: s"2009-11-11 03:00:00 +0000 UTC", -- Typeflag: 0x30, -+ Typeflag: 0x00, - Linkname: "", - Uname: "user", - ... // 6 identical fields - }, - } -`, - }, { - label: label, - x: make([]int, 1000), - y: make([]int, 1000), - opts: []cmp.Option{ - cmp.Comparer(func(_, _ int) bool { - return rand.Intn(2) == 0 - }), - }, - wantPanic: "non-deterministic or non-symmetric function detected", - }, { - label: label, - x: make([]int, 1000), - y: make([]int, 1000), - opts: []cmp.Option{ - cmp.FilterValues(func(_, _ int) bool { - return rand.Intn(2) == 0 - }, cmp.Ignore()), - }, - wantPanic: "non-deterministic or non-symmetric function detected", - }, { - label: label, - x: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, - y: []int{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, - opts: []cmp.Option{ - cmp.Comparer(func(x, y int) bool { - return x < y - }), - }, - wantPanic: "non-deterministic or non-symmetric function detected", - }, { - label: label, - x: make([]string, 1000), - y: make([]string, 1000), - opts: []cmp.Option{ - cmp.Transformer("λ", func(x string) int { - return rand.Int() - }), - }, - wantPanic: "non-deterministic function detected", - }, { - // Make sure the dynamic checks don't raise a false positive for - // non-reflexive comparisons. - label: label, - x: make([]int, 10), - y: make([]int, 10), - opts: []cmp.Option{ - cmp.Transformer("λ", func(x int) float64 { - return math.NaN() - }), - }, - wantDiff: ` - []int{ -- Inverse(λ, float64(NaN)), -+ Inverse(λ, float64(NaN)), -- Inverse(λ, float64(NaN)), -+ Inverse(λ, float64(NaN)), -- Inverse(λ, float64(NaN)), -+ Inverse(λ, float64(NaN)), -- Inverse(λ, float64(NaN)), -+ Inverse(λ, float64(NaN)), -- Inverse(λ, float64(NaN)), -+ Inverse(λ, float64(NaN)), -- Inverse(λ, float64(NaN)), -+ Inverse(λ, float64(NaN)), -- Inverse(λ, float64(NaN)), -+ Inverse(λ, float64(NaN)), -- Inverse(λ, float64(NaN)), -+ Inverse(λ, float64(NaN)), -- Inverse(λ, float64(NaN)), -+ Inverse(λ, float64(NaN)), -- Inverse(λ, float64(NaN)), -+ Inverse(λ, float64(NaN)), - } -`, - }, { - // Ensure reasonable Stringer formatting of map keys. - label: label, - x: map[*pb.Stringer]*pb.Stringer{{"hello"}: {"world"}}, - y: map[*pb.Stringer]*pb.Stringer(nil), - wantDiff: ` - map[*testprotos.Stringer]*testprotos.Stringer( -- {s"hello": s"world"}, -+ nil, - ) -`, - }, { - // Ensure Stringer avoids double-quote escaping if possible. - label: label, - x: []*pb.Stringer{{`multi\nline\nline\nline`}}, - wantDiff: strings.Replace(` - interface{}( -- []*testprotos.Stringer{s'multi\nline\nline\nline'}, - ) -`, "'", "`", -1), - }, { - label: label, - x: struct{ I Iface2 }{}, - y: struct{ I Iface2 }{}, - opts: []cmp.Option{ - cmp.Comparer(func(x, y Iface1) bool { - return x == nil && y == nil - }), - }, - }, { - label: label, - x: struct{ I Iface2 }{}, - y: struct{ I Iface2 }{}, - opts: []cmp.Option{ - cmp.Transformer("λ", func(v Iface1) bool { - return v == nil - }), - }, - }, { - label: label, - x: struct{ I Iface2 }{}, - y: struct{ I Iface2 }{}, - opts: []cmp.Option{ - cmp.FilterValues(func(x, y Iface1) bool { - return x == nil && y == nil - }, cmp.Ignore()), - }, - }, { - label: label, - x: []interface{}{map[string]interface{}{"avg": 0.278, "hr": 65, "name": "Mark McGwire"}, map[string]interface{}{"avg": 0.288, "hr": 63, "name": "Sammy Sosa"}}, - y: []interface{}{map[string]interface{}{"avg": 0.278, "hr": 65.0, "name": "Mark McGwire"}, map[string]interface{}{"avg": 0.288, "hr": 63.0, "name": "Sammy Sosa"}}, - wantDiff: ` - []interface{}{ - map[string]interface{}{ - "avg": float64(0.278), -- "hr": int(65), -+ "hr": float64(65), - "name": string("Mark McGwire"), - }, - map[string]interface{}{ - "avg": float64(0.288), -- "hr": int(63), -+ "hr": float64(63), - "name": string("Sammy Sosa"), - }, - } -`, - }, { - label: label, - x: map[*int]string{ - new(int): "hello", - }, - y: map[*int]string{ - new(int): "world", - }, - wantDiff: ` - map[*int]string{ -- ⟪0xdeadf00f⟫: "hello", -+ ⟪0xdeadf00f⟫: "world", - } -`, - }, { - label: label, - x: intPtr(0), - y: intPtr(0), - opts: []cmp.Option{ - cmp.Comparer(func(x, y *int) bool { return x == y }), - }, - // TODO: This output is unhelpful and should show the address. - wantDiff: ` - (*int)( -- &0, -+ &0, - ) -`, - }, { - label: label, - x: [2][]int{ - {0, 0, 0, 1, 2, 3, 0, 0, 4, 5, 6, 7, 8, 0, 9, 0, 0}, - {0, 1, 0, 0, 0, 20}, - }, - y: [2][]int{ - {1, 2, 3, 0, 4, 5, 6, 7, 0, 8, 9, 0, 0, 0}, - {0, 0, 1, 2, 0, 0, 0}, - }, - opts: []cmp.Option{ - cmp.FilterPath(func(p cmp.Path) bool { - vx, vy := p.Last().Values() - if vx.IsValid() && vx.Kind() == reflect.Int && vx.Int() == 0 { - return true - } - if vy.IsValid() && vy.Kind() == reflect.Int && vy.Int() == 0 { - return true - } - return false - }, cmp.Ignore()), - }, - wantDiff: ` - [2][]int{ - {..., 1, 2, 3, ..., 4, 5, 6, 7, ..., 8, ..., 9, ...}, - { - ... // 6 ignored and 1 identical elements -- 20, -+ 2, - ... // 3 ignored elements - }, - } -`, - reason: "all zero slice elements are ignored (even if missing)", - }, { - label: label, - x: [2]map[string]int{ - {"ignore1": 0, "ignore2": 0, "keep1": 1, "keep2": 2, "KEEP3": 3, "IGNORE3": 0}, - {"keep1": 1, "ignore1": 0}, - }, - y: [2]map[string]int{ - {"ignore1": 0, "ignore3": 0, "ignore4": 0, "keep1": 1, "keep2": 2, "KEEP3": 3}, - {"keep1": 1, "keep2": 2, "ignore2": 0}, - }, - opts: []cmp.Option{ - cmp.FilterPath(func(p cmp.Path) bool { - vx, vy := p.Last().Values() - if vx.IsValid() && vx.Kind() == reflect.Int && vx.Int() == 0 { - return true - } - if vy.IsValid() && vy.Kind() == reflect.Int && vy.Int() == 0 { - return true - } - return false - }, cmp.Ignore()), - }, - wantDiff: ` - [2]map[string]int{ - {"KEEP3": 3, "keep1": 1, "keep2": 2, ...}, - { - ... // 2 ignored entries - "keep1": 1, -+ "keep2": 2, - }, - } -`, - reason: "all zero map entries are ignored (even if missing)", - }} -} - -func transformerTests() []test { - type StringBytes struct { - String string - Bytes []byte - } - - const label = "Transformer" - - transformOnce := func(name string, f interface{}) cmp.Option { - xform := cmp.Transformer(name, f) - return cmp.FilterPath(func(p cmp.Path) bool { - for _, ps := range p { - if tr, ok := ps.(cmp.Transform); ok && tr.Option() == xform { - return false - } - } - return true - }, xform) - } - - return []test{{ - label: label, - x: uint8(0), - y: uint8(1), - opts: []cmp.Option{ - cmp.Transformer("λ", func(in uint8) uint16 { return uint16(in) }), - cmp.Transformer("λ", func(in uint16) uint32 { return uint32(in) }), - cmp.Transformer("λ", func(in uint32) uint64 { return uint64(in) }), - }, - wantDiff: ` - uint8(Inverse(λ, uint16(Inverse(λ, uint32(Inverse(λ, uint64( -- 0x00, -+ 0x01, - ))))))) -`, - }, { - label: label, - x: 0, - y: 1, - opts: []cmp.Option{ - cmp.Transformer("λ", func(in int) int { return in / 2 }), - cmp.Transformer("λ", func(in int) int { return in }), - }, - wantPanic: "ambiguous set of applicable options", - }, { - label: label, - x: []int{0, -5, 0, -1}, - y: []int{1, 3, 0, -5}, - opts: []cmp.Option{ - cmp.FilterValues( - func(x, y int) bool { return x+y >= 0 }, - cmp.Transformer("λ", func(in int) int64 { return int64(in / 2) }), - ), - cmp.FilterValues( - func(x, y int) bool { return x+y < 0 }, - cmp.Transformer("λ", func(in int) int64 { return int64(in) }), - ), - }, - wantDiff: ` - []int{ - Inverse(λ, int64(0)), -- Inverse(λ, int64(-5)), -+ Inverse(λ, int64(3)), - Inverse(λ, int64(0)), -- Inverse(λ, int64(-1)), -+ Inverse(λ, int64(-5)), - } -`, - }, { - label: label, - x: 0, - y: 1, - opts: []cmp.Option{ - cmp.Transformer("λ", func(in int) interface{} { - if in == 0 { - return "zero" - } - return float64(in) - }), - }, - wantDiff: ` - int(Inverse(λ, interface{}( -- string("zero"), -+ float64(1), - ))) -`, - }, { - label: label, - x: `{ - "firstName": "John", - "lastName": "Smith", - "age": 25, - "isAlive": true, - "address": { - "city": "Los Angeles", - "postalCode": "10021-3100", - "state": "CA", - "streetAddress": "21 2nd Street" - }, - "phoneNumbers": [{ - "type": "home", - "number": "212 555-4321" - },{ - "type": "office", - "number": "646 555-4567" - },{ - "number": "123 456-7890", - "type": "mobile" - }], - "children": [] - }`, - y: `{"firstName":"John","lastName":"Smith","isAlive":true,"age":25, - "address":{"streetAddress":"21 2nd Street","city":"New York", - "state":"NY","postalCode":"10021-3100"},"phoneNumbers":[{"type":"home", - "number":"212 555-1234"},{"type":"office","number":"646 555-4567"},{ - "type":"mobile","number":"123 456-7890"}],"children":[],"spouse":null}`, - opts: []cmp.Option{ - transformOnce("ParseJSON", func(s string) (m map[string]interface{}) { - if err := json.Unmarshal([]byte(s), &m); err != nil { - panic(err) - } - return m - }), - }, - wantDiff: ` - string(Inverse(ParseJSON, map[string]interface{}{ - "address": map[string]interface{}{ -- "city": string("Los Angeles"), -+ "city": string("New York"), - "postalCode": string("10021-3100"), -- "state": string("CA"), -+ "state": string("NY"), - "streetAddress": string("21 2nd Street"), - }, - "age": float64(25), - "children": []interface{}{}, - "firstName": string("John"), - "isAlive": bool(true), - "lastName": string("Smith"), - "phoneNumbers": []interface{}{ - map[string]interface{}{ -- "number": string("212 555-4321"), -+ "number": string("212 555-1234"), - "type": string("home"), - }, - map[string]interface{}{"number": string("646 555-4567"), "type": string("office")}, - map[string]interface{}{"number": string("123 456-7890"), "type": string("mobile")}, - }, -+ "spouse": nil, - })) -`, - }, { - label: label, - x: StringBytes{String: "some\nmulti\nLine\nstring", Bytes: []byte("some\nmulti\nline\nbytes")}, - y: StringBytes{String: "some\nmulti\nline\nstring", Bytes: []byte("some\nmulti\nline\nBytes")}, - opts: []cmp.Option{ - transformOnce("SplitString", func(s string) []string { return strings.Split(s, "\n") }), - transformOnce("SplitBytes", func(b []byte) [][]byte { return bytes.Split(b, []byte("\n")) }), - }, - wantDiff: ` - cmp_test.StringBytes{ - String: Inverse(SplitString, []string{ - "some", - "multi", -- "Line", -+ "line", - "string", - }), - Bytes: []uint8(Inverse(SplitBytes, [][]uint8{ - {0x73, 0x6f, 0x6d, 0x65}, - {0x6d, 0x75, 0x6c, 0x74, 0x69}, - {0x6c, 0x69, 0x6e, 0x65}, - { -- 0x62, -+ 0x42, - 0x79, - 0x74, - ... // 2 identical elements - }, - })), - } -`, - }, { - x: "a\nb\nc\n", - y: "a\nb\nc\n", - opts: []cmp.Option{ - cmp.Transformer("SplitLines", func(s string) []string { return strings.Split(s, "\n") }), - }, - wantPanic: "recursive set of Transformers detected", - }, { - x: complex64(0), - y: complex64(0), - opts: []cmp.Option{ - cmp.Transformer("T1", func(x complex64) complex128 { return complex128(x) }), - cmp.Transformer("T2", func(x complex128) [2]float64 { return [2]float64{real(x), imag(x)} }), - cmp.Transformer("T3", func(x float64) complex64 { return complex64(complex(x, 0)) }), - }, - wantPanic: "recursive set of Transformers detected", - }} -} - -func reporterTests() []test { - const label = "Reporter" - - type ( - MyString string - MyByte byte - MyBytes []byte - MyInt int8 - MyInts []int8 - MyUint int16 - MyUints []int16 - MyFloat float32 - MyFloats []float32 - MyComposite struct { - StringA string - StringB MyString - BytesA []byte - BytesB []MyByte - BytesC MyBytes - IntsA []int8 - IntsB []MyInt - IntsC MyInts - UintsA []uint16 - UintsB []MyUint - UintsC MyUints - FloatsA []float32 - FloatsB []MyFloat - FloatsC MyFloats - } - ) - - return []test{{ - label: label, - x: MyComposite{IntsA: []int8{11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}}, - y: MyComposite{IntsA: []int8{10, 11, 21, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}}, - wantDiff: ` - cmp_test.MyComposite{ - ... // 3 identical fields - BytesB: nil, - BytesC: nil, - IntsA: []int8{ -+ 10, - 11, -- 12, -+ 21, - 13, - 14, - ... // 15 identical elements - }, - IntsB: nil, - IntsC: nil, - ... // 6 identical fields - } -`, - reason: "unbatched diffing desired since few elements differ", - }, { - label: label, - x: MyComposite{IntsA: []int8{10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}}, - y: MyComposite{IntsA: []int8{12, 29, 13, 27, 22, 23, 17, 18, 19, 20, 21, 10, 26, 16, 25, 28, 11, 15, 24, 14}}, - wantDiff: ` - cmp_test.MyComposite{ - ... // 3 identical fields - BytesB: nil, - BytesC: nil, - IntsA: []int8{ -- 10, 11, 12, 13, 14, 15, 16, -+ 12, 29, 13, 27, 22, 23, - 17, 18, 19, 20, 21, -- 22, 23, 24, 25, 26, 27, 28, 29, -+ 10, 26, 16, 25, 28, 11, 15, 24, 14, - }, - IntsB: nil, - IntsC: nil, - ... // 6 identical fields - } -`, - reason: "batched diffing desired since many elements differ", - }, { - label: label, - x: MyComposite{ - BytesA: []byte{1, 2, 3}, - BytesB: []MyByte{4, 5, 6}, - BytesC: MyBytes{7, 8, 9}, - IntsA: []int8{-1, -2, -3}, - IntsB: []MyInt{-4, -5, -6}, - IntsC: MyInts{-7, -8, -9}, - UintsA: []uint16{1000, 2000, 3000}, - UintsB: []MyUint{4000, 5000, 6000}, - UintsC: MyUints{7000, 8000, 9000}, - FloatsA: []float32{1.5, 2.5, 3.5}, - FloatsB: []MyFloat{4.5, 5.5, 6.5}, - FloatsC: MyFloats{7.5, 8.5, 9.5}, - }, - y: MyComposite{ - BytesA: []byte{3, 2, 1}, - BytesB: []MyByte{6, 5, 4}, - BytesC: MyBytes{9, 8, 7}, - IntsA: []int8{-3, -2, -1}, - IntsB: []MyInt{-6, -5, -4}, - IntsC: MyInts{-9, -8, -7}, - UintsA: []uint16{3000, 2000, 1000}, - UintsB: []MyUint{6000, 5000, 4000}, - UintsC: MyUints{9000, 8000, 7000}, - FloatsA: []float32{3.5, 2.5, 1.5}, - FloatsB: []MyFloat{6.5, 5.5, 4.5}, - FloatsC: MyFloats{9.5, 8.5, 7.5}, - }, - wantDiff: ` - cmp_test.MyComposite{ - StringA: "", - StringB: "", - BytesA: []uint8{ -- 0x01, 0x02, 0x03, // -|...| -+ 0x03, 0x02, 0x01, // +|...| - }, - BytesB: []cmp_test.MyByte{ -- 0x04, 0x05, 0x06, -+ 0x06, 0x05, 0x04, - }, - BytesC: cmp_test.MyBytes{ -- 0x07, 0x08, 0x09, // -|...| -+ 0x09, 0x08, 0x07, // +|...| - }, - IntsA: []int8{ -- -1, -2, -3, -+ -3, -2, -1, - }, - IntsB: []cmp_test.MyInt{ -- -4, -5, -6, -+ -6, -5, -4, - }, - IntsC: cmp_test.MyInts{ -- -7, -8, -9, -+ -9, -8, -7, - }, - UintsA: []uint16{ -- 0x03e8, 0x07d0, 0x0bb8, -+ 0x0bb8, 0x07d0, 0x03e8, - }, - UintsB: []cmp_test.MyUint{ -- 4000, 5000, 6000, -+ 6000, 5000, 4000, - }, - UintsC: cmp_test.MyUints{ -- 7000, 8000, 9000, -+ 9000, 8000, 7000, - }, - FloatsA: []float32{ -- 1.5, 2.5, 3.5, -+ 3.5, 2.5, 1.5, - }, - FloatsB: []cmp_test.MyFloat{ -- 4.5, 5.5, 6.5, -+ 6.5, 5.5, 4.5, - }, - FloatsC: cmp_test.MyFloats{ -- 7.5, 8.5, 9.5, -+ 9.5, 8.5, 7.5, - }, - } -`, - reason: "batched diffing available for both named and unnamed slices", - }, { - label: label, - x: MyComposite{BytesA: []byte("\xf3\x0f\x8a\xa4\xd3\x12R\t$\xbeX\x95A\xfd$fX\x8byT\xac\r\xd8qwp\x20j\\s\u007f\x8c\x17U\xc04\xcen\xf7\xaaG\xee2\x9d\xc5\xca\x1eX\xaf\x8f'\xf3\x02J\x90\xedi.p2\xb4\xab0 \xb6\xbd\\b4\x17\xb0\x00\xbbO~'G\x06\xf4.f\xfdc\xd7\x04ݷ0\xb7\xd1U~{\xf6\xb3~\x1dWi \x9e\xbc\xdf\xe1M\xa9\xef\xa2\xd2\xed\xb4Gx\xc9\xc9'\xa4\xc6\xce\xecDp]")}, - y: MyComposite{BytesA: []byte("\xf3\x0f\x8a\xa4\xd3\x12R\t$\xbeT\xac\r\xd8qwp\x20j\\s\u007f\x8c\x17U\xc04\xcen\xf7\xaaG\xee2\x9d\xc5\xca\x1eX\xaf\x8f'\xf3\x02J\x90\xedi.p2\xb4\xab0 \xb6\xbd\\b4\x17\xb0\x00\xbbO~'G\x06\xf4.f\xfdc\xd7\x04ݷ0\xb7\xd1u-[]]\xf6\xb3haha~\x1dWI \x9e\xbc\xdf\xe1M\xa9\xef\xa2\xd2\xed\xb4Gx\xc9\xc9'\xa4\xc6\xce\xecDp]")}, - wantDiff: ` - cmp_test.MyComposite{ - StringA: "", - StringB: "", - BytesA: []uint8{ - 0xf3, 0x0f, 0x8a, 0xa4, 0xd3, 0x12, 0x52, 0x09, 0x24, 0xbe, // |......R.$.| -- 0x58, 0x95, 0x41, 0xfd, 0x24, 0x66, 0x58, 0x8b, 0x79, // -|X.A.$fX.y| - 0x54, 0xac, 0x0d, 0xd8, 0x71, 0x77, 0x70, 0x20, 0x6a, 0x5c, 0x73, 0x7f, 0x8c, 0x17, 0x55, 0xc0, // |T...qwp j\s...U.| - 0x34, 0xce, 0x6e, 0xf7, 0xaa, 0x47, 0xee, 0x32, 0x9d, 0xc5, 0xca, 0x1e, 0x58, 0xaf, 0x8f, 0x27, // |4.n..G.2....X..'| - 0xf3, 0x02, 0x4a, 0x90, 0xed, 0x69, 0x2e, 0x70, 0x32, 0xb4, 0xab, 0x30, 0x20, 0xb6, 0xbd, 0x5c, // |..J..i.p2..0 ..\| - 0x62, 0x34, 0x17, 0xb0, 0x00, 0xbb, 0x4f, 0x7e, 0x27, 0x47, 0x06, 0xf4, 0x2e, 0x66, 0xfd, 0x63, // |b4....O~'G...f.c| - 0xd7, 0x04, 0xdd, 0xb7, 0x30, 0xb7, 0xd1, // |....0..| -- 0x55, 0x7e, 0x7b, 0xf6, 0xb3, 0x7e, 0x1d, 0x57, 0x69, // -|U~{..~.Wi| -+ 0x75, 0x2d, 0x5b, 0x5d, 0x5d, 0xf6, 0xb3, 0x68, 0x61, 0x68, 0x61, 0x7e, 0x1d, 0x57, 0x49, // +|u-[]]..haha~.WI| - 0x20, 0x9e, 0xbc, 0xdf, 0xe1, 0x4d, 0xa9, 0xef, 0xa2, 0xd2, 0xed, 0xb4, 0x47, 0x78, 0xc9, 0xc9, // | ....M......Gx..| - 0x27, 0xa4, 0xc6, 0xce, 0xec, 0x44, 0x70, 0x5d, // |'....Dp]| - }, - BytesB: nil, - BytesC: nil, - ... // 9 identical fields - } -`, - reason: "binary diff in hexdump form since data is binary data", - }, { - label: label, - x: MyComposite{StringB: MyString("readme.txt\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000000600\x000000000\x000000000\x0000000000046\x0000000000000\x00011173\x00 0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00ustar\x0000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000000000\x000000000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")}, - y: MyComposite{StringB: MyString("gopher.txt\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000000600\x000000000\x000000000\x0000000000043\x0000000000000\x00011217\x00 0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00ustar\x0000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000000000\x000000000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")}, - wantDiff: ` - cmp_test.MyComposite{ - StringA: "", - StringB: cmp_test.MyString{ -- 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, // -|readme| -+ 0x67, 0x6f, 0x70, 0x68, 0x65, 0x72, // +|gopher| - 0x2e, 0x74, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // |.txt............| - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // |................| - ... // 64 identical bytes - 0x30, 0x30, 0x36, 0x30, 0x30, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x30, 0x30, // |00600.0000000.00| - 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x34, // |00000.0000000004| -- 0x36, // -|6| -+ 0x33, // +|3| - 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x30, 0x31, 0x31, // |.00000000000.011| -- 0x31, 0x37, 0x33, // -|173| -+ 0x32, 0x31, 0x37, // +|217| - 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // |. 0.............| - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // |................| - ... // 326 identical bytes - }, - BytesA: nil, - BytesB: nil, - ... // 10 identical fields - } -`, - reason: "binary diff desired since string looks like binary data", - }, { - label: label, - x: MyComposite{BytesA: []byte(`{"firstName":"John","lastName":"Smith","isAlive":true,"age":27,"address":{"streetAddress":"314 54th Avenue","city":"New York","state":"NY","postalCode":"10021-3100"},"phoneNumbers":[{"type":"home","number":"212 555-1234"},{"type":"office","number":"646 555-4567"},{"type":"mobile","number":"123 456-7890"}],"children":[],"spouse":null}`)}, - y: MyComposite{BytesA: []byte(`{"firstName":"John","lastName":"Smith","isAlive":true,"age":27,"address":{"streetAddress":"21 2nd Street","city":"New York","state":"NY","postalCode":"10021-3100"},"phoneNumbers":[{"type":"home","number":"212 555-1234"},{"type":"office","number":"646 555-4567"},{"type":"mobile","number":"123 456-7890"}],"children":[],"spouse":null}`)}, - wantDiff: strings.Replace(` - cmp_test.MyComposite{ - StringA: "", - StringB: "", - BytesA: bytes.Join({ - '{"firstName":"John","lastName":"Smith","isAlive":true,"age":27,"', - 'address":{"streetAddress":"', -- "314 54th Avenue", -+ "21 2nd Street", - '","city":"New York","state":"NY","postalCode":"10021-3100"},"pho', - 'neNumbers":[{"type":"home","number":"212 555-1234"},{"type":"off', - ... // 101 identical bytes - }, ""), - BytesB: nil, - BytesC: nil, - ... // 9 identical fields - } -`, "'", "`", -1), - reason: "batched textual diff desired since bytes looks like textual data", - }, { - label: label, - x: MyComposite{ - StringA: strings.TrimPrefix(` -Package cmp determines equality of values. - -This package is intended to be a more powerful and safer alternative to -reflect.DeepEqual for comparing whether two values are semantically equal. - -The primary features of cmp are: - -• When the default behavior of equality does not suit the needs of the test, -custom equality functions can override the equality operation. -For example, an equality function may report floats as equal so long as they -are within some tolerance of each other. - -• Types that have an Equal method may use that method to determine equality. -This allows package authors to determine the equality operation for the types -that they define. - -• If no custom equality functions are used and no Equal method is defined, -equality is determined by recursively comparing the primitive kinds on both -values, much like reflect.DeepEqual. Unlike reflect.DeepEqual, unexported -fields are not compared by default; they result in panics unless suppressed -by using an Ignore option (see cmpopts.IgnoreUnexported) or explicitly compared -using the AllowUnexported option. -`, "\n"), - }, - y: MyComposite{ - StringA: strings.TrimPrefix(` -Package cmp determines equality of value. - -This package is intended to be a more powerful and safer alternative to -reflect.DeepEqual for comparing whether two values are semantically equal. - -The primary features of cmp are: - -• When the default behavior of equality does not suit the needs of the test, -custom equality functions can override the equality operation. -For example, an equality function may report floats as equal so long as they -are within some tolerance of each other. - -• If no custom equality functions are used and no Equal method is defined, -equality is determined by recursively comparing the primitive kinds on both -values, much like reflect.DeepEqual. Unlike reflect.DeepEqual, unexported -fields are not compared by default; they result in panics unless suppressed -by using an Ignore option (see cmpopts.IgnoreUnexported) or explicitly compared -using the AllowUnexported option.`, "\n"), - }, - wantDiff: ` - cmp_test.MyComposite{ - StringA: strings.Join({ -- "Package cmp determines equality of values.", -+ "Package cmp determines equality of value.", - "", - "This package is intended to be a more powerful and safer alternative to", - ... // 6 identical lines - "For example, an equality function may report floats as equal so long as they", - "are within some tolerance of each other.", -- "", -- "• Types that have an Equal method may use that method to determine equality.", -- "This allows package authors to determine the equality operation for the types", -- "that they define.", - "", - "• If no custom equality functions are used and no Equal method is defined,", - ... // 3 identical lines - "by using an Ignore option (see cmpopts.IgnoreUnexported) or explicitly compared", - "using the AllowUnexported option.", -- "", - }, "\n"), - StringB: "", - BytesA: nil, - ... // 11 identical fields - } -`, - reason: "batched per-line diff desired since string looks like multi-line textual data", - }} -} - -func embeddedTests() []test { - const label = "EmbeddedStruct/" - - privateStruct := *new(ts.ParentStructA).PrivateStruct() - - createStructA := func(i int) ts.ParentStructA { - s := ts.ParentStructA{} - s.PrivateStruct().Public = 1 + i - s.PrivateStruct().SetPrivate(2 + i) - return s - } - - createStructB := func(i int) ts.ParentStructB { - s := ts.ParentStructB{} - s.PublicStruct.Public = 1 + i - s.PublicStruct.SetPrivate(2 + i) - return s - } - - createStructC := func(i int) ts.ParentStructC { - s := ts.ParentStructC{} - s.PrivateStruct().Public = 1 + i - s.PrivateStruct().SetPrivate(2 + i) - s.Public = 3 + i - s.SetPrivate(4 + i) - return s - } - - createStructD := func(i int) ts.ParentStructD { - s := ts.ParentStructD{} - s.PublicStruct.Public = 1 + i - s.PublicStruct.SetPrivate(2 + i) - s.Public = 3 + i - s.SetPrivate(4 + i) - return s - } - - createStructE := func(i int) ts.ParentStructE { - s := ts.ParentStructE{} - s.PrivateStruct().Public = 1 + i - s.PrivateStruct().SetPrivate(2 + i) - s.PublicStruct.Public = 3 + i - s.PublicStruct.SetPrivate(4 + i) - return s - } - - createStructF := func(i int) ts.ParentStructF { - s := ts.ParentStructF{} - s.PrivateStruct().Public = 1 + i - s.PrivateStruct().SetPrivate(2 + i) - s.PublicStruct.Public = 3 + i - s.PublicStruct.SetPrivate(4 + i) - s.Public = 5 + i - s.SetPrivate(6 + i) - return s - } - - createStructG := func(i int) *ts.ParentStructG { - s := ts.NewParentStructG() - s.PrivateStruct().Public = 1 + i - s.PrivateStruct().SetPrivate(2 + i) - return s - } - - createStructH := func(i int) *ts.ParentStructH { - s := ts.NewParentStructH() - s.PublicStruct.Public = 1 + i - s.PublicStruct.SetPrivate(2 + i) - return s - } - - createStructI := func(i int) *ts.ParentStructI { - s := ts.NewParentStructI() - s.PrivateStruct().Public = 1 + i - s.PrivateStruct().SetPrivate(2 + i) - s.PublicStruct.Public = 3 + i - s.PublicStruct.SetPrivate(4 + i) - return s - } - - createStructJ := func(i int) *ts.ParentStructJ { - s := ts.NewParentStructJ() - s.PrivateStruct().Public = 1 + i - s.PrivateStruct().SetPrivate(2 + i) - s.PublicStruct.Public = 3 + i - s.PublicStruct.SetPrivate(4 + i) - s.Private().Public = 5 + i - s.Private().SetPrivate(6 + i) - s.Public.Public = 7 + i - s.Public.SetPrivate(8 + i) - return s - } - - // TODO(dsnet): Workaround for reflect bug (https://golang.org/issue/21122). - wantPanicNotGo110 := func(s string) string { - if !flags.AtLeastGo110 { - return "" - } - return s - } - - return []test{{ - label: label + "ParentStructA", - x: ts.ParentStructA{}, - y: ts.ParentStructA{}, - wantPanic: "cannot handle unexported field", - }, { - label: label + "ParentStructA", - x: ts.ParentStructA{}, - y: ts.ParentStructA{}, - opts: []cmp.Option{ - cmpopts.IgnoreUnexported(ts.ParentStructA{}), - }, - }, { - label: label + "ParentStructA", - x: createStructA(0), - y: createStructA(0), - opts: []cmp.Option{ - cmp.AllowUnexported(ts.ParentStructA{}), - }, - wantPanic: "cannot handle unexported field", - }, { - label: label + "ParentStructA", - x: createStructA(0), - y: createStructA(0), - opts: []cmp.Option{ - cmp.AllowUnexported(ts.ParentStructA{}, privateStruct), - }, - }, { - label: label + "ParentStructA", - x: createStructA(0), - y: createStructA(1), - opts: []cmp.Option{ - cmp.AllowUnexported(ts.ParentStructA{}, privateStruct), - }, - wantDiff: ` - teststructs.ParentStructA{ - privateStruct: teststructs.privateStruct{ -- Public: 1, -+ Public: 2, -- private: 2, -+ private: 3, - }, - } -`, - }, { - label: label + "ParentStructB", - x: ts.ParentStructB{}, - y: ts.ParentStructB{}, - opts: []cmp.Option{ - cmpopts.IgnoreUnexported(ts.ParentStructB{}), - }, - wantPanic: "cannot handle unexported field", - }, { - label: label + "ParentStructB", - x: ts.ParentStructB{}, - y: ts.ParentStructB{}, - opts: []cmp.Option{ - cmpopts.IgnoreUnexported(ts.ParentStructB{}), - cmpopts.IgnoreUnexported(ts.PublicStruct{}), - }, - }, { - label: label + "ParentStructB", - x: createStructB(0), - y: createStructB(0), - opts: []cmp.Option{ - cmp.AllowUnexported(ts.ParentStructB{}), - }, - wantPanic: "cannot handle unexported field", - }, { - label: label + "ParentStructB", - x: createStructB(0), - y: createStructB(0), - opts: []cmp.Option{ - cmp.AllowUnexported(ts.ParentStructB{}, ts.PublicStruct{}), - }, - }, { - label: label + "ParentStructB", - x: createStructB(0), - y: createStructB(1), - opts: []cmp.Option{ - cmp.AllowUnexported(ts.ParentStructB{}, ts.PublicStruct{}), - }, - wantDiff: ` - teststructs.ParentStructB{ - PublicStruct: teststructs.PublicStruct{ -- Public: 1, -+ Public: 2, -- private: 2, -+ private: 3, - }, - } -`, - }, { - label: label + "ParentStructC", - x: ts.ParentStructC{}, - y: ts.ParentStructC{}, - wantPanic: "cannot handle unexported field", - }, { - label: label + "ParentStructC", - x: ts.ParentStructC{}, - y: ts.ParentStructC{}, - opts: []cmp.Option{ - cmpopts.IgnoreUnexported(ts.ParentStructC{}), - }, - }, { - label: label + "ParentStructC", - x: createStructC(0), - y: createStructC(0), - opts: []cmp.Option{ - cmp.AllowUnexported(ts.ParentStructC{}), - }, - wantPanic: "cannot handle unexported field", - }, { - label: label + "ParentStructC", - x: createStructC(0), - y: createStructC(0), - opts: []cmp.Option{ - cmp.AllowUnexported(ts.ParentStructC{}, privateStruct), - }, - }, { - label: label + "ParentStructC", - x: createStructC(0), - y: createStructC(1), - opts: []cmp.Option{ - cmp.AllowUnexported(ts.ParentStructC{}, privateStruct), - }, - wantDiff: ` - teststructs.ParentStructC{ - privateStruct: teststructs.privateStruct{ -- Public: 1, -+ Public: 2, -- private: 2, -+ private: 3, - }, -- Public: 3, -+ Public: 4, -- private: 4, -+ private: 5, - } -`, - }, { - label: label + "ParentStructD", - x: ts.ParentStructD{}, - y: ts.ParentStructD{}, - opts: []cmp.Option{ - cmpopts.IgnoreUnexported(ts.ParentStructD{}), - }, - wantPanic: "cannot handle unexported field", - }, { - label: label + "ParentStructD", - x: ts.ParentStructD{}, - y: ts.ParentStructD{}, - opts: []cmp.Option{ - cmpopts.IgnoreUnexported(ts.ParentStructD{}), - cmpopts.IgnoreUnexported(ts.PublicStruct{}), - }, - }, { - label: label + "ParentStructD", - x: createStructD(0), - y: createStructD(0), - opts: []cmp.Option{ - cmp.AllowUnexported(ts.ParentStructD{}), - }, - wantPanic: "cannot handle unexported field", - }, { - label: label + "ParentStructD", - x: createStructD(0), - y: createStructD(0), - opts: []cmp.Option{ - cmp.AllowUnexported(ts.ParentStructD{}, ts.PublicStruct{}), - }, - }, { - label: label + "ParentStructD", - x: createStructD(0), - y: createStructD(1), - opts: []cmp.Option{ - cmp.AllowUnexported(ts.ParentStructD{}, ts.PublicStruct{}), - }, - wantDiff: ` - teststructs.ParentStructD{ - PublicStruct: teststructs.PublicStruct{ -- Public: 1, -+ Public: 2, -- private: 2, -+ private: 3, - }, -- Public: 3, -+ Public: 4, -- private: 4, -+ private: 5, - } -`, - }, { - label: label + "ParentStructE", - x: ts.ParentStructE{}, - y: ts.ParentStructE{}, - opts: []cmp.Option{ - cmpopts.IgnoreUnexported(ts.ParentStructE{}), - }, - wantPanic: "cannot handle unexported field", - }, { - label: label + "ParentStructE", - x: ts.ParentStructE{}, - y: ts.ParentStructE{}, - opts: []cmp.Option{ - cmpopts.IgnoreUnexported(ts.ParentStructE{}), - cmpopts.IgnoreUnexported(ts.PublicStruct{}), - }, - }, { - label: label + "ParentStructE", - x: createStructE(0), - y: createStructE(0), - opts: []cmp.Option{ - cmp.AllowUnexported(ts.ParentStructE{}), - }, - wantPanic: "cannot handle unexported field", - }, { - label: label + "ParentStructE", - x: createStructE(0), - y: createStructE(0), - opts: []cmp.Option{ - cmp.AllowUnexported(ts.ParentStructE{}, ts.PublicStruct{}), - }, - wantPanic: "cannot handle unexported field", - }, { - label: label + "ParentStructE", - x: createStructE(0), - y: createStructE(0), - opts: []cmp.Option{ - cmp.AllowUnexported(ts.ParentStructE{}, ts.PublicStruct{}, privateStruct), - }, - }, { - label: label + "ParentStructE", - x: createStructE(0), - y: createStructE(1), - opts: []cmp.Option{ - cmp.AllowUnexported(ts.ParentStructE{}, ts.PublicStruct{}, privateStruct), - }, - wantDiff: ` - teststructs.ParentStructE{ - privateStruct: teststructs.privateStruct{ -- Public: 1, -+ Public: 2, -- private: 2, -+ private: 3, - }, - PublicStruct: teststructs.PublicStruct{ -- Public: 3, -+ Public: 4, -- private: 4, -+ private: 5, - }, - } -`, - }, { - label: label + "ParentStructF", - x: ts.ParentStructF{}, - y: ts.ParentStructF{}, - opts: []cmp.Option{ - cmpopts.IgnoreUnexported(ts.ParentStructF{}), - }, - wantPanic: "cannot handle unexported field", - }, { - label: label + "ParentStructF", - x: ts.ParentStructF{}, - y: ts.ParentStructF{}, - opts: []cmp.Option{ - cmpopts.IgnoreUnexported(ts.ParentStructF{}), - cmpopts.IgnoreUnexported(ts.PublicStruct{}), - }, - }, { - label: label + "ParentStructF", - x: createStructF(0), - y: createStructF(0), - opts: []cmp.Option{ - cmp.AllowUnexported(ts.ParentStructF{}), - }, - wantPanic: "cannot handle unexported field", - }, { - label: label + "ParentStructF", - x: createStructF(0), - y: createStructF(0), - opts: []cmp.Option{ - cmp.AllowUnexported(ts.ParentStructF{}, ts.PublicStruct{}), - }, - wantPanic: "cannot handle unexported field", - }, { - label: label + "ParentStructF", - x: createStructF(0), - y: createStructF(0), - opts: []cmp.Option{ - cmp.AllowUnexported(ts.ParentStructF{}, ts.PublicStruct{}, privateStruct), - }, - }, { - label: label + "ParentStructF", - x: createStructF(0), - y: createStructF(1), - opts: []cmp.Option{ - cmp.AllowUnexported(ts.ParentStructF{}, ts.PublicStruct{}, privateStruct), - }, - wantDiff: ` - teststructs.ParentStructF{ - privateStruct: teststructs.privateStruct{ -- Public: 1, -+ Public: 2, -- private: 2, -+ private: 3, - }, - PublicStruct: teststructs.PublicStruct{ -- Public: 3, -+ Public: 4, -- private: 4, -+ private: 5, - }, -- Public: 5, -+ Public: 6, -- private: 6, -+ private: 7, - } -`, - }, { - label: label + "ParentStructG", - x: ts.ParentStructG{}, - y: ts.ParentStructG{}, - wantPanic: wantPanicNotGo110("cannot handle unexported field"), - }, { - label: label + "ParentStructG", - x: ts.ParentStructG{}, - y: ts.ParentStructG{}, - opts: []cmp.Option{ - cmpopts.IgnoreUnexported(ts.ParentStructG{}), - }, - }, { - label: label + "ParentStructG", - x: createStructG(0), - y: createStructG(0), - opts: []cmp.Option{ - cmp.AllowUnexported(ts.ParentStructG{}), - }, - wantPanic: "cannot handle unexported field", - }, { - label: label + "ParentStructG", - x: createStructG(0), - y: createStructG(0), - opts: []cmp.Option{ - cmp.AllowUnexported(ts.ParentStructG{}, privateStruct), - }, - }, { - label: label + "ParentStructG", - x: createStructG(0), - y: createStructG(1), - opts: []cmp.Option{ - cmp.AllowUnexported(ts.ParentStructG{}, privateStruct), - }, - wantDiff: ` - &teststructs.ParentStructG{ - privateStruct: &teststructs.privateStruct{ -- Public: 1, -+ Public: 2, -- private: 2, -+ private: 3, - }, - } -`, - }, { - label: label + "ParentStructH", - x: ts.ParentStructH{}, - y: ts.ParentStructH{}, - }, { - label: label + "ParentStructH", - x: createStructH(0), - y: createStructH(0), - wantPanic: "cannot handle unexported field", - }, { - label: label + "ParentStructH", - x: ts.ParentStructH{}, - y: ts.ParentStructH{}, - opts: []cmp.Option{ - cmpopts.IgnoreUnexported(ts.ParentStructH{}), - }, - }, { - label: label + "ParentStructH", - x: createStructH(0), - y: createStructH(0), - opts: []cmp.Option{ - cmp.AllowUnexported(ts.ParentStructH{}), - }, - wantPanic: "cannot handle unexported field", - }, { - label: label + "ParentStructH", - x: createStructH(0), - y: createStructH(0), - opts: []cmp.Option{ - cmp.AllowUnexported(ts.ParentStructH{}, ts.PublicStruct{}), - }, - }, { - label: label + "ParentStructH", - x: createStructH(0), - y: createStructH(1), - opts: []cmp.Option{ - cmp.AllowUnexported(ts.ParentStructH{}, ts.PublicStruct{}), - }, - wantDiff: ` - &teststructs.ParentStructH{ - PublicStruct: &teststructs.PublicStruct{ -- Public: 1, -+ Public: 2, -- private: 2, -+ private: 3, - }, - } -`, - }, { - label: label + "ParentStructI", - x: ts.ParentStructI{}, - y: ts.ParentStructI{}, - wantPanic: wantPanicNotGo110("cannot handle unexported field"), - }, { - label: label + "ParentStructI", - x: ts.ParentStructI{}, - y: ts.ParentStructI{}, - opts: []cmp.Option{ - cmpopts.IgnoreUnexported(ts.ParentStructI{}), - }, - }, { - label: label + "ParentStructI", - x: createStructI(0), - y: createStructI(0), - opts: []cmp.Option{ - cmpopts.IgnoreUnexported(ts.ParentStructI{}), - }, - wantPanic: "cannot handle unexported field", - }, { - label: label + "ParentStructI", - x: createStructI(0), - y: createStructI(0), - opts: []cmp.Option{ - cmpopts.IgnoreUnexported(ts.ParentStructI{}, ts.PublicStruct{}), - }, - }, { - label: label + "ParentStructI", - x: createStructI(0), - y: createStructI(0), - opts: []cmp.Option{ - cmp.AllowUnexported(ts.ParentStructI{}), - }, - wantPanic: "cannot handle unexported field", - }, { - label: label + "ParentStructI", - x: createStructI(0), - y: createStructI(0), - opts: []cmp.Option{ - cmp.AllowUnexported(ts.ParentStructI{}, ts.PublicStruct{}, privateStruct), - }, - }, { - label: label + "ParentStructI", - x: createStructI(0), - y: createStructI(1), - opts: []cmp.Option{ - cmp.AllowUnexported(ts.ParentStructI{}, ts.PublicStruct{}, privateStruct), - }, - wantDiff: ` - &teststructs.ParentStructI{ - privateStruct: &teststructs.privateStruct{ -- Public: 1, -+ Public: 2, -- private: 2, -+ private: 3, - }, - PublicStruct: &teststructs.PublicStruct{ -- Public: 3, -+ Public: 4, -- private: 4, -+ private: 5, - }, - } -`, - }, { - label: label + "ParentStructJ", - x: ts.ParentStructJ{}, - y: ts.ParentStructJ{}, - wantPanic: "cannot handle unexported field", - }, { - label: label + "ParentStructJ", - x: ts.ParentStructJ{}, - y: ts.ParentStructJ{}, - opts: []cmp.Option{ - cmpopts.IgnoreUnexported(ts.ParentStructJ{}), - }, - wantPanic: "cannot handle unexported field", - }, { - label: label + "ParentStructJ", - x: ts.ParentStructJ{}, - y: ts.ParentStructJ{}, - opts: []cmp.Option{ - cmpopts.IgnoreUnexported(ts.ParentStructJ{}, ts.PublicStruct{}), - }, - }, { - label: label + "ParentStructJ", - x: createStructJ(0), - y: createStructJ(0), - opts: []cmp.Option{ - cmp.AllowUnexported(ts.ParentStructJ{}, ts.PublicStruct{}), - }, - wantPanic: "cannot handle unexported field", - }, { - label: label + "ParentStructJ", - x: createStructJ(0), - y: createStructJ(0), - opts: []cmp.Option{ - cmp.AllowUnexported(ts.ParentStructJ{}, ts.PublicStruct{}, privateStruct), - }, - }, { - label: label + "ParentStructJ", - x: createStructJ(0), - y: createStructJ(1), - opts: []cmp.Option{ - cmp.AllowUnexported(ts.ParentStructJ{}, ts.PublicStruct{}, privateStruct), - }, - wantDiff: ` - &teststructs.ParentStructJ{ - privateStruct: &teststructs.privateStruct{ -- Public: 1, -+ Public: 2, -- private: 2, -+ private: 3, - }, - PublicStruct: &teststructs.PublicStruct{ -- Public: 3, -+ Public: 4, -- private: 4, -+ private: 5, - }, - Public: teststructs.PublicStruct{ -- Public: 7, -+ Public: 8, -- private: 8, -+ private: 9, - }, - private: teststructs.privateStruct{ -- Public: 5, -+ Public: 6, -- private: 6, -+ private: 7, - }, - } -`, - }} -} - -func methodTests() []test { - const label = "EqualMethod/" - - // A common mistake that the Equal method is on a pointer receiver, - // but only a non-pointer value is present in the struct. - // A transform can be used to forcibly reference the value. - derefTransform := cmp.FilterPath(func(p cmp.Path) bool { - if len(p) == 0 { - return false - } - t := p[len(p)-1].Type() - if _, ok := t.MethodByName("Equal"); ok || t.Kind() == reflect.Ptr { - return false - } - if m, ok := reflect.PtrTo(t).MethodByName("Equal"); ok { - tf := m.Func.Type() - return !tf.IsVariadic() && tf.NumIn() == 2 && tf.NumOut() == 1 && - tf.In(0).AssignableTo(tf.In(1)) && tf.Out(0) == reflect.TypeOf(true) - } - return false - }, cmp.Transformer("Ref", func(x interface{}) interface{} { - v := reflect.ValueOf(x) - vp := reflect.New(v.Type()) - vp.Elem().Set(v) - return vp.Interface() - })) - - // For each of these types, there is an Equal method defined, which always - // returns true, while the underlying data are fundamentally different. - // Since the method should be called, these are expected to be equal. - return []test{{ - label: label + "StructA", - x: ts.StructA{X: "NotEqual"}, - y: ts.StructA{X: "not_equal"}, - }, { - label: label + "StructA", - x: &ts.StructA{X: "NotEqual"}, - y: &ts.StructA{X: "not_equal"}, - }, { - label: label + "StructB", - x: ts.StructB{X: "NotEqual"}, - y: ts.StructB{X: "not_equal"}, - wantDiff: ` - teststructs.StructB{ -- X: "NotEqual", -+ X: "not_equal", - } -`, - }, { - label: label + "StructB", - x: ts.StructB{X: "NotEqual"}, - y: ts.StructB{X: "not_equal"}, - opts: []cmp.Option{derefTransform}, - }, { - label: label + "StructB", - x: &ts.StructB{X: "NotEqual"}, - y: &ts.StructB{X: "not_equal"}, - }, { - label: label + "StructC", - x: ts.StructC{X: "NotEqual"}, - y: ts.StructC{X: "not_equal"}, - }, { - label: label + "StructC", - x: &ts.StructC{X: "NotEqual"}, - y: &ts.StructC{X: "not_equal"}, - }, { - label: label + "StructD", - x: ts.StructD{X: "NotEqual"}, - y: ts.StructD{X: "not_equal"}, - wantDiff: ` - teststructs.StructD{ -- X: "NotEqual", -+ X: "not_equal", - } -`, - }, { - label: label + "StructD", - x: ts.StructD{X: "NotEqual"}, - y: ts.StructD{X: "not_equal"}, - opts: []cmp.Option{derefTransform}, - }, { - label: label + "StructD", - x: &ts.StructD{X: "NotEqual"}, - y: &ts.StructD{X: "not_equal"}, - }, { - label: label + "StructE", - x: ts.StructE{X: "NotEqual"}, - y: ts.StructE{X: "not_equal"}, - wantDiff: ` - teststructs.StructE{ -- X: "NotEqual", -+ X: "not_equal", - } -`, - }, { - label: label + "StructE", - x: ts.StructE{X: "NotEqual"}, - y: ts.StructE{X: "not_equal"}, - opts: []cmp.Option{derefTransform}, - }, { - label: label + "StructE", - x: &ts.StructE{X: "NotEqual"}, - y: &ts.StructE{X: "not_equal"}, - }, { - label: label + "StructF", - x: ts.StructF{X: "NotEqual"}, - y: ts.StructF{X: "not_equal"}, - wantDiff: ` - teststructs.StructF{ -- X: "NotEqual", -+ X: "not_equal", - } -`, - }, { - label: label + "StructF", - x: &ts.StructF{X: "NotEqual"}, - y: &ts.StructF{X: "not_equal"}, - }, { - label: label + "StructA1", - x: ts.StructA1{StructA: ts.StructA{X: "NotEqual"}, X: "equal"}, - y: ts.StructA1{StructA: ts.StructA{X: "not_equal"}, X: "equal"}, - }, { - label: label + "StructA1", - x: ts.StructA1{StructA: ts.StructA{X: "NotEqual"}, X: "NotEqual"}, - y: ts.StructA1{StructA: ts.StructA{X: "not_equal"}, X: "not_equal"}, - wantDiff: ` - teststructs.StructA1{ - StructA: teststructs.StructA{X: "NotEqual"}, -- X: "NotEqual", -+ X: "not_equal", - } -`, - }, { - label: label + "StructA1", - x: &ts.StructA1{StructA: ts.StructA{X: "NotEqual"}, X: "equal"}, - y: &ts.StructA1{StructA: ts.StructA{X: "not_equal"}, X: "equal"}, - }, { - label: label + "StructA1", - x: &ts.StructA1{StructA: ts.StructA{X: "NotEqual"}, X: "NotEqual"}, - y: &ts.StructA1{StructA: ts.StructA{X: "not_equal"}, X: "not_equal"}, - wantDiff: ` - &teststructs.StructA1{ - StructA: teststructs.StructA{X: "NotEqual"}, -- X: "NotEqual", -+ X: "not_equal", - } -`, - }, { - label: label + "StructB1", - x: ts.StructB1{StructB: ts.StructB{X: "NotEqual"}, X: "equal"}, - y: ts.StructB1{StructB: ts.StructB{X: "not_equal"}, X: "equal"}, - opts: []cmp.Option{derefTransform}, - }, { - label: label + "StructB1", - x: ts.StructB1{StructB: ts.StructB{X: "NotEqual"}, X: "NotEqual"}, - y: ts.StructB1{StructB: ts.StructB{X: "not_equal"}, X: "not_equal"}, - opts: []cmp.Option{derefTransform}, - wantDiff: ` - teststructs.StructB1{ - StructB: teststructs.StructB(Inverse(Ref, &teststructs.StructB{X: "NotEqual"})), -- X: "NotEqual", -+ X: "not_equal", - } -`, - }, { - label: label + "StructB1", - x: &ts.StructB1{StructB: ts.StructB{X: "NotEqual"}, X: "equal"}, - y: &ts.StructB1{StructB: ts.StructB{X: "not_equal"}, X: "equal"}, - opts: []cmp.Option{derefTransform}, - }, { - label: label + "StructB1", - x: &ts.StructB1{StructB: ts.StructB{X: "NotEqual"}, X: "NotEqual"}, - y: &ts.StructB1{StructB: ts.StructB{X: "not_equal"}, X: "not_equal"}, - opts: []cmp.Option{derefTransform}, - wantDiff: ` - &teststructs.StructB1{ - StructB: teststructs.StructB(Inverse(Ref, &teststructs.StructB{X: "NotEqual"})), -- X: "NotEqual", -+ X: "not_equal", - } -`, - }, { - label: label + "StructC1", - x: ts.StructC1{StructC: ts.StructC{X: "NotEqual"}, X: "NotEqual"}, - y: ts.StructC1{StructC: ts.StructC{X: "not_equal"}, X: "not_equal"}, - }, { - label: label + "StructC1", - x: &ts.StructC1{StructC: ts.StructC{X: "NotEqual"}, X: "NotEqual"}, - y: &ts.StructC1{StructC: ts.StructC{X: "not_equal"}, X: "not_equal"}, - }, { - label: label + "StructD1", - x: ts.StructD1{StructD: ts.StructD{X: "NotEqual"}, X: "NotEqual"}, - y: ts.StructD1{StructD: ts.StructD{X: "not_equal"}, X: "not_equal"}, - wantDiff: ` - teststructs.StructD1{ -- StructD: teststructs.StructD{X: "NotEqual"}, -+ StructD: teststructs.StructD{X: "not_equal"}, -- X: "NotEqual", -+ X: "not_equal", - } -`, - }, { - label: label + "StructD1", - x: ts.StructD1{StructD: ts.StructD{X: "NotEqual"}, X: "NotEqual"}, - y: ts.StructD1{StructD: ts.StructD{X: "not_equal"}, X: "not_equal"}, - opts: []cmp.Option{derefTransform}, - }, { - label: label + "StructD1", - x: &ts.StructD1{StructD: ts.StructD{X: "NotEqual"}, X: "NotEqual"}, - y: &ts.StructD1{StructD: ts.StructD{X: "not_equal"}, X: "not_equal"}, - }, { - label: label + "StructE1", - x: ts.StructE1{StructE: ts.StructE{X: "NotEqual"}, X: "NotEqual"}, - y: ts.StructE1{StructE: ts.StructE{X: "not_equal"}, X: "not_equal"}, - wantDiff: ` - teststructs.StructE1{ -- StructE: teststructs.StructE{X: "NotEqual"}, -+ StructE: teststructs.StructE{X: "not_equal"}, -- X: "NotEqual", -+ X: "not_equal", - } -`, - }, { - label: label + "StructE1", - x: ts.StructE1{StructE: ts.StructE{X: "NotEqual"}, X: "NotEqual"}, - y: ts.StructE1{StructE: ts.StructE{X: "not_equal"}, X: "not_equal"}, - opts: []cmp.Option{derefTransform}, - }, { - label: label + "StructE1", - x: &ts.StructE1{StructE: ts.StructE{X: "NotEqual"}, X: "NotEqual"}, - y: &ts.StructE1{StructE: ts.StructE{X: "not_equal"}, X: "not_equal"}, - }, { - label: label + "StructF1", - x: ts.StructF1{StructF: ts.StructF{X: "NotEqual"}, X: "NotEqual"}, - y: ts.StructF1{StructF: ts.StructF{X: "not_equal"}, X: "not_equal"}, - wantDiff: ` - teststructs.StructF1{ -- StructF: teststructs.StructF{X: "NotEqual"}, -+ StructF: teststructs.StructF{X: "not_equal"}, -- X: "NotEqual", -+ X: "not_equal", - } -`, - }, { - label: label + "StructF1", - x: &ts.StructF1{StructF: ts.StructF{X: "NotEqual"}, X: "NotEqual"}, - y: &ts.StructF1{StructF: ts.StructF{X: "not_equal"}, X: "not_equal"}, - }, { - label: label + "StructA2", - x: ts.StructA2{StructA: &ts.StructA{X: "NotEqual"}, X: "equal"}, - y: ts.StructA2{StructA: &ts.StructA{X: "not_equal"}, X: "equal"}, - }, { - label: label + "StructA2", - x: ts.StructA2{StructA: &ts.StructA{X: "NotEqual"}, X: "NotEqual"}, - y: ts.StructA2{StructA: &ts.StructA{X: "not_equal"}, X: "not_equal"}, - wantDiff: ` - teststructs.StructA2{ - StructA: &teststructs.StructA{X: "NotEqual"}, -- X: "NotEqual", -+ X: "not_equal", - } -`, - }, { - label: label + "StructA2", - x: &ts.StructA2{StructA: &ts.StructA{X: "NotEqual"}, X: "equal"}, - y: &ts.StructA2{StructA: &ts.StructA{X: "not_equal"}, X: "equal"}, - }, { - label: label + "StructA2", - x: &ts.StructA2{StructA: &ts.StructA{X: "NotEqual"}, X: "NotEqual"}, - y: &ts.StructA2{StructA: &ts.StructA{X: "not_equal"}, X: "not_equal"}, - wantDiff: ` - &teststructs.StructA2{ - StructA: &teststructs.StructA{X: "NotEqual"}, -- X: "NotEqual", -+ X: "not_equal", - } -`, - }, { - label: label + "StructB2", - x: ts.StructB2{StructB: &ts.StructB{X: "NotEqual"}, X: "equal"}, - y: ts.StructB2{StructB: &ts.StructB{X: "not_equal"}, X: "equal"}, - }, { - label: label + "StructB2", - x: ts.StructB2{StructB: &ts.StructB{X: "NotEqual"}, X: "NotEqual"}, - y: ts.StructB2{StructB: &ts.StructB{X: "not_equal"}, X: "not_equal"}, - wantDiff: ` - teststructs.StructB2{ - StructB: &teststructs.StructB{X: "NotEqual"}, -- X: "NotEqual", -+ X: "not_equal", - } -`, - }, { - label: label + "StructB2", - x: &ts.StructB2{StructB: &ts.StructB{X: "NotEqual"}, X: "equal"}, - y: &ts.StructB2{StructB: &ts.StructB{X: "not_equal"}, X: "equal"}, - }, { - label: label + "StructB2", - x: &ts.StructB2{StructB: &ts.StructB{X: "NotEqual"}, X: "NotEqual"}, - y: &ts.StructB2{StructB: &ts.StructB{X: "not_equal"}, X: "not_equal"}, - wantDiff: ` - &teststructs.StructB2{ - StructB: &teststructs.StructB{X: "NotEqual"}, -- X: "NotEqual", -+ X: "not_equal", - } -`, - }, { - label: label + "StructC2", - x: ts.StructC2{StructC: &ts.StructC{X: "NotEqual"}, X: "NotEqual"}, - y: ts.StructC2{StructC: &ts.StructC{X: "not_equal"}, X: "not_equal"}, - }, { - label: label + "StructC2", - x: &ts.StructC2{StructC: &ts.StructC{X: "NotEqual"}, X: "NotEqual"}, - y: &ts.StructC2{StructC: &ts.StructC{X: "not_equal"}, X: "not_equal"}, - }, { - label: label + "StructD2", - x: ts.StructD2{StructD: &ts.StructD{X: "NotEqual"}, X: "NotEqual"}, - y: ts.StructD2{StructD: &ts.StructD{X: "not_equal"}, X: "not_equal"}, - }, { - label: label + "StructD2", - x: &ts.StructD2{StructD: &ts.StructD{X: "NotEqual"}, X: "NotEqual"}, - y: &ts.StructD2{StructD: &ts.StructD{X: "not_equal"}, X: "not_equal"}, - }, { - label: label + "StructE2", - x: ts.StructE2{StructE: &ts.StructE{X: "NotEqual"}, X: "NotEqual"}, - y: ts.StructE2{StructE: &ts.StructE{X: "not_equal"}, X: "not_equal"}, - }, { - label: label + "StructE2", - x: &ts.StructE2{StructE: &ts.StructE{X: "NotEqual"}, X: "NotEqual"}, - y: &ts.StructE2{StructE: &ts.StructE{X: "not_equal"}, X: "not_equal"}, - }, { - label: label + "StructF2", - x: ts.StructF2{StructF: &ts.StructF{X: "NotEqual"}, X: "NotEqual"}, - y: ts.StructF2{StructF: &ts.StructF{X: "not_equal"}, X: "not_equal"}, - }, { - label: label + "StructF2", - x: &ts.StructF2{StructF: &ts.StructF{X: "NotEqual"}, X: "NotEqual"}, - y: &ts.StructF2{StructF: &ts.StructF{X: "not_equal"}, X: "not_equal"}, - }, { - label: label + "StructNo", - x: ts.StructNo{X: "NotEqual"}, - y: ts.StructNo{X: "not_equal"}, - wantDiff: ` - teststructs.StructNo{ -- X: "NotEqual", -+ X: "not_equal", - } -`, - }, { - label: label + "AssignA", - x: ts.AssignA(func() int { return 0 }), - y: ts.AssignA(func() int { return 1 }), - }, { - label: label + "AssignB", - x: ts.AssignB(struct{ A int }{0}), - y: ts.AssignB(struct{ A int }{1}), - }, { - label: label + "AssignC", - x: ts.AssignC(make(chan bool)), - y: ts.AssignC(make(chan bool)), - }, { - label: label + "AssignD", - x: ts.AssignD(make(chan bool)), - y: ts.AssignD(make(chan bool)), - }} -} - -func project1Tests() []test { - const label = "Project1" - - ignoreUnexported := cmpopts.IgnoreUnexported( - ts.EagleImmutable{}, - ts.DreamerImmutable{}, - ts.SlapImmutable{}, - ts.GoatImmutable{}, - ts.DonkeyImmutable{}, - ts.LoveRadius{}, - ts.SummerLove{}, - ts.SummerLoveSummary{}, - ) - - createEagle := func() ts.Eagle { - return ts.Eagle{ - Name: "eagle", - Hounds: []string{"buford", "tannen"}, - Desc: "some description", - Dreamers: []ts.Dreamer{{}, { - Name: "dreamer2", - Animal: []interface{}{ - ts.Goat{ - Target: "corporation", - Immutable: &ts.GoatImmutable{ - ID: "southbay", - State: (*pb.Goat_States)(intPtr(5)), - Started: now, - }, - }, - ts.Donkey{}, - }, - Amoeba: 53, - }}, - Slaps: []ts.Slap{{ - Name: "slapID", - Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}}, - Immutable: &ts.SlapImmutable{ - ID: "immutableSlap", - MildSlap: true, - Started: now, - LoveRadius: &ts.LoveRadius{ - Summer: &ts.SummerLove{ - Summary: &ts.SummerLoveSummary{ - Devices: []string{"foo", "bar", "baz"}, - ChangeType: []pb.SummerType{1, 2, 3}, - }, - }, - }, - }, - }}, - Immutable: &ts.EagleImmutable{ - ID: "eagleID", - Birthday: now, - MissingCall: (*pb.Eagle_MissingCalls)(intPtr(55)), - }, - } - } - - return []test{{ - label: label, - x: ts.Eagle{Slaps: []ts.Slap{{ - Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}}, - }}}, - y: ts.Eagle{Slaps: []ts.Slap{{ - Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}}, - }}}, - wantPanic: "cannot handle unexported field", - }, { - label: label, - x: ts.Eagle{Slaps: []ts.Slap{{ - Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}}, - }}}, - y: ts.Eagle{Slaps: []ts.Slap{{ - Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}}, - }}}, - opts: []cmp.Option{cmp.Comparer(pb.Equal)}, - }, { - label: label, - x: ts.Eagle{Slaps: []ts.Slap{{}, {}, {}, {}, { - Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}}, - }}}, - y: ts.Eagle{Slaps: []ts.Slap{{}, {}, {}, {}, { - Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata2"}}, - }}}, - opts: []cmp.Option{cmp.Comparer(pb.Equal)}, - wantDiff: ` - teststructs.Eagle{ - ... // 4 identical fields - Dreamers: nil, - Prong: 0, - Slaps: []teststructs.Slap{ - ... // 2 identical elements - {}, - {}, - { - Name: "", - Desc: "", - DescLong: "", -- Args: s"metadata", -+ Args: s"metadata2", - Tense: 0, - Interval: 0, - ... // 3 identical fields - }, - }, - StateGoverner: "", - PrankRating: "", - ... // 2 identical fields - } -`, - }, { - label: label, - x: createEagle(), - y: createEagle(), - opts: []cmp.Option{ignoreUnexported, cmp.Comparer(pb.Equal)}, - }, { - label: label, - x: func() ts.Eagle { - eg := createEagle() - eg.Dreamers[1].Animal[0].(ts.Goat).Immutable.ID = "southbay2" - eg.Dreamers[1].Animal[0].(ts.Goat).Immutable.State = (*pb.Goat_States)(intPtr(6)) - eg.Slaps[0].Immutable.MildSlap = false - return eg - }(), - y: func() ts.Eagle { - eg := createEagle() - devs := eg.Slaps[0].Immutable.LoveRadius.Summer.Summary.Devices - eg.Slaps[0].Immutable.LoveRadius.Summer.Summary.Devices = devs[:1] - return eg - }(), - opts: []cmp.Option{ignoreUnexported, cmp.Comparer(pb.Equal)}, - wantDiff: ` - teststructs.Eagle{ - ... // 2 identical fields - Desc: "some description", - DescLong: "", - Dreamers: []teststructs.Dreamer{ - {}, - { - ... // 4 identical fields - ContSlaps: nil, - ContSlapsInterval: 0, - Animal: []interface{}{ - teststructs.Goat{ - Target: "corporation", - Slaps: nil, - FunnyPrank: "", - Immutable: &teststructs.GoatImmutable{ -- ID: "southbay2", -+ ID: "southbay", -- State: &6, -+ State: &5, - Started: s"2009-11-10 23:00:00 +0000 UTC", - Stopped: s"0001-01-01 00:00:00 +0000 UTC", - ... // 1 ignored and 1 identical fields - }, - }, - teststructs.Donkey{}, - }, - Ornamental: false, - Amoeba: 53, - ... // 5 identical fields - }, - }, - Prong: 0, - Slaps: []teststructs.Slap{ - { - ... // 6 identical fields - Homeland: 0x00, - FunnyPrank: "", - Immutable: &teststructs.SlapImmutable{ - ID: "immutableSlap", - Out: nil, -- MildSlap: false, -+ MildSlap: true, - PrettyPrint: "", - State: nil, - Started: s"2009-11-10 23:00:00 +0000 UTC", - Stopped: s"0001-01-01 00:00:00 +0000 UTC", - LastUpdate: s"0001-01-01 00:00:00 +0000 UTC", - LoveRadius: &teststructs.LoveRadius{ - Summer: &teststructs.SummerLove{ - Summary: &teststructs.SummerLoveSummary{ - Devices: []string{ - "foo", -- "bar", -- "baz", - }, - ChangeType: []testprotos.SummerType{1, 2, 3}, - ... // 1 ignored field - }, - ... // 1 ignored field - }, - ... // 1 ignored field - }, - ... // 1 ignored field - }, - }, - }, - StateGoverner: "", - PrankRating: "", - ... // 2 identical fields - } -`, - }} -} - -type germSorter []*pb.Germ - -func (gs germSorter) Len() int { return len(gs) } -func (gs germSorter) Less(i, j int) bool { return gs[i].String() < gs[j].String() } -func (gs germSorter) Swap(i, j int) { gs[i], gs[j] = gs[j], gs[i] } - -func project2Tests() []test { - const label = "Project2" - - sortGerms := cmp.Transformer("Sort", func(in []*pb.Germ) []*pb.Germ { - out := append([]*pb.Germ(nil), in...) // Make copy - sort.Sort(germSorter(out)) - return out - }) - - equalDish := cmp.Comparer(func(x, y *ts.Dish) bool { - if x == nil || y == nil { - return x == nil && y == nil - } - px, err1 := x.Proto() - py, err2 := y.Proto() - if err1 != nil || err2 != nil { - return err1 == err2 - } - return pb.Equal(px, py) - }) - - createBatch := func() ts.GermBatch { - return ts.GermBatch{ - DirtyGerms: map[int32][]*pb.Germ{ - 17: { - {Stringer: pb.Stringer{X: "germ1"}}, - }, - 18: { - {Stringer: pb.Stringer{X: "germ2"}}, - {Stringer: pb.Stringer{X: "germ3"}}, - {Stringer: pb.Stringer{X: "germ4"}}, - }, - }, - GermMap: map[int32]*pb.Germ{ - 13: {Stringer: pb.Stringer{X: "germ13"}}, - 21: {Stringer: pb.Stringer{X: "germ21"}}, - }, - DishMap: map[int32]*ts.Dish{ - 0: ts.CreateDish(nil, io.EOF), - 1: ts.CreateDish(nil, io.ErrUnexpectedEOF), - 2: ts.CreateDish(&pb.Dish{Stringer: pb.Stringer{X: "dish"}}, nil), - }, - HasPreviousResult: true, - DirtyID: 10, - GermStrain: 421, - InfectedAt: now, - } - } - - return []test{{ - label: label, - x: createBatch(), - y: createBatch(), - wantPanic: "cannot handle unexported field", - }, { - label: label, - x: createBatch(), - y: createBatch(), - opts: []cmp.Option{cmp.Comparer(pb.Equal), sortGerms, equalDish}, - }, { - label: label, - x: createBatch(), - y: func() ts.GermBatch { - gb := createBatch() - s := gb.DirtyGerms[18] - s[0], s[1], s[2] = s[1], s[2], s[0] - return gb - }(), - opts: []cmp.Option{cmp.Comparer(pb.Equal), equalDish}, - wantDiff: ` - teststructs.GermBatch{ - DirtyGerms: map[int32][]*testprotos.Germ{ - 17: {s"germ1"}, - 18: { -- s"germ2", - s"germ3", - s"germ4", -+ s"germ2", - }, - }, - CleanGerms: nil, - GermMap: map[int32]*testprotos.Germ{13: s"germ13", 21: s"germ21"}, - ... // 7 identical fields - } -`, - }, { - label: label, - x: createBatch(), - y: func() ts.GermBatch { - gb := createBatch() - s := gb.DirtyGerms[18] - s[0], s[1], s[2] = s[1], s[2], s[0] - return gb - }(), - opts: []cmp.Option{cmp.Comparer(pb.Equal), sortGerms, equalDish}, - }, { - label: label, - x: func() ts.GermBatch { - gb := createBatch() - delete(gb.DirtyGerms, 17) - gb.DishMap[1] = nil - return gb - }(), - y: func() ts.GermBatch { - gb := createBatch() - gb.DirtyGerms[18] = gb.DirtyGerms[18][:2] - gb.GermStrain = 22 - return gb - }(), - opts: []cmp.Option{cmp.Comparer(pb.Equal), sortGerms, equalDish}, - wantDiff: ` - teststructs.GermBatch{ - DirtyGerms: map[int32][]*testprotos.Germ{ -+ 17: {s"germ1"}, - 18: Inverse(Sort, []*testprotos.Germ{ - s"germ2", - s"germ3", -- s"germ4", - }), - }, - CleanGerms: nil, - GermMap: map[int32]*testprotos.Germ{13: s"germ13", 21: s"germ21"}, - DishMap: map[int32]*teststructs.Dish{ - 0: &{err: &errors.errorString{s: "EOF"}}, -- 1: nil, -+ 1: &{err: &errors.errorString{s: "unexpected EOF"}}, - 2: &{pb: &testprotos.Dish{Stringer: testprotos.Stringer{X: "dish"}}}, - }, - HasPreviousResult: true, - DirtyID: 10, - CleanID: 0, -- GermStrain: 421, -+ GermStrain: 22, - TotalDirtyGerms: 0, - InfectedAt: s"2009-11-10 23:00:00 +0000 UTC", - } -`, - }} -} - -func project3Tests() []test { - const label = "Project3" - - allowVisibility := cmp.AllowUnexported(ts.Dirt{}) - - ignoreLocker := cmpopts.IgnoreInterfaces(struct{ sync.Locker }{}) - - transformProtos := cmp.Transformer("λ", func(x pb.Dirt) *pb.Dirt { - return &x - }) - - equalTable := cmp.Comparer(func(x, y ts.Table) bool { - tx, ok1 := x.(*ts.MockTable) - ty, ok2 := y.(*ts.MockTable) - if !ok1 || !ok2 { - panic("table type must be MockTable") - } - return cmp.Equal(tx.State(), ty.State()) - }) - - createDirt := func() (d ts.Dirt) { - d.SetTable(ts.CreateMockTable([]string{"a", "b", "c"})) - d.SetTimestamp(12345) - d.Discord = 554 - d.Proto = pb.Dirt{Stringer: pb.Stringer{X: "proto"}} - d.SetWizard(map[string]*pb.Wizard{ - "harry": {Stringer: pb.Stringer{X: "potter"}}, - "albus": {Stringer: pb.Stringer{X: "dumbledore"}}, - }) - d.SetLastTime(54321) - return d - } - - return []test{{ - label: label, - x: createDirt(), - y: createDirt(), - wantPanic: "cannot handle unexported field", - }, { - label: label, - x: createDirt(), - y: createDirt(), - opts: []cmp.Option{allowVisibility, ignoreLocker, cmp.Comparer(pb.Equal), equalTable}, - wantPanic: "cannot handle unexported field", - }, { - label: label, - x: createDirt(), - y: createDirt(), - opts: []cmp.Option{allowVisibility, transformProtos, ignoreLocker, cmp.Comparer(pb.Equal), equalTable}, - }, { - label: label, - x: func() ts.Dirt { - d := createDirt() - d.SetTable(ts.CreateMockTable([]string{"a", "c"})) - d.Proto = pb.Dirt{Stringer: pb.Stringer{X: "blah"}} - return d - }(), - y: func() ts.Dirt { - d := createDirt() - d.Discord = 500 - d.SetWizard(map[string]*pb.Wizard{ - "harry": {Stringer: pb.Stringer{X: "otter"}}, - }) - return d - }(), - opts: []cmp.Option{allowVisibility, transformProtos, ignoreLocker, cmp.Comparer(pb.Equal), equalTable}, - wantDiff: ` - teststructs.Dirt{ -- table: &teststructs.MockTable{state: []string{"a", "c"}}, -+ table: &teststructs.MockTable{state: []string{"a", "b", "c"}}, - ts: 12345, -- Discord: 554, -+ Discord: 500, -- Proto: testprotos.Dirt(Inverse(λ, s"blah")), -+ Proto: testprotos.Dirt(Inverse(λ, s"proto")), - wizard: map[string]*testprotos.Wizard{ -- "albus": s"dumbledore", -- "harry": s"potter", -+ "harry": s"otter", - }, - sadistic: nil, - lastTime: 54321, - ... // 1 ignored field - } -`, - }} -} - -func project4Tests() []test { - const label = "Project4" - - allowVisibility := cmp.AllowUnexported( - ts.Cartel{}, - ts.Headquarter{}, - ts.Poison{}, - ) - - transformProtos := cmp.Transformer("λ", func(x pb.Restrictions) *pb.Restrictions { - return &x - }) - - createCartel := func() ts.Cartel { - var p ts.Poison - p.SetPoisonType(5) - p.SetExpiration(now) - p.SetManufacturer("acme") - - var hq ts.Headquarter - hq.SetID(5) - hq.SetLocation("moon") - hq.SetSubDivisions([]string{"alpha", "bravo", "charlie"}) - hq.SetMetaData(&pb.MetaData{Stringer: pb.Stringer{X: "metadata"}}) - hq.SetPublicMessage([]byte{1, 2, 3, 4, 5}) - hq.SetHorseBack("abcdef") - hq.SetStatus(44) - - var c ts.Cartel - c.Headquarter = hq - c.SetSource("mars") - c.SetCreationTime(now) - c.SetBoss("al capone") - c.SetPoisons([]*ts.Poison{&p}) - - return c - } - - return []test{{ - label: label, - x: createCartel(), - y: createCartel(), - wantPanic: "cannot handle unexported field", - }, { - label: label, - x: createCartel(), - y: createCartel(), - opts: []cmp.Option{allowVisibility, cmp.Comparer(pb.Equal)}, - wantPanic: "cannot handle unexported field", - }, { - label: label, - x: createCartel(), - y: createCartel(), - opts: []cmp.Option{allowVisibility, transformProtos, cmp.Comparer(pb.Equal)}, - }, { - label: label, - x: func() ts.Cartel { - d := createCartel() - var p1, p2 ts.Poison - p1.SetPoisonType(1) - p1.SetExpiration(now) - p1.SetManufacturer("acme") - p2.SetPoisonType(2) - p2.SetManufacturer("acme2") - d.SetPoisons([]*ts.Poison{&p1, &p2}) - return d - }(), - y: func() ts.Cartel { - d := createCartel() - d.SetSubDivisions([]string{"bravo", "charlie"}) - d.SetPublicMessage([]byte{1, 2, 4, 3, 5}) - return d - }(), - opts: []cmp.Option{allowVisibility, transformProtos, cmp.Comparer(pb.Equal)}, - wantDiff: ` - teststructs.Cartel{ - Headquarter: teststructs.Headquarter{ - id: 0x05, - location: "moon", - subDivisions: []string{ -- "alpha", - "bravo", - "charlie", - }, - incorporatedDate: s"0001-01-01 00:00:00 +0000 UTC", - metaData: s"metadata", - privateMessage: nil, - publicMessage: []uint8{ - 0x01, - 0x02, -- 0x03, -+ 0x04, -- 0x04, -+ 0x03, - 0x05, - }, - horseBack: "abcdef", - rattle: "", - ... // 5 identical fields - }, - source: "mars", - creationDate: s"0001-01-01 00:00:00 +0000 UTC", - boss: "al capone", - lastCrimeDate: s"0001-01-01 00:00:00 +0000 UTC", - poisons: []*teststructs.Poison{ - &{ -- poisonType: 1, -+ poisonType: 5, - expiration: s"2009-11-10 23:00:00 +0000 UTC", - manufacturer: "acme", - potency: 0, - }, -- &{poisonType: 2, manufacturer: "acme2"}, - }, - } -`, - }} -} - -// BenchmarkBytes benchmarks the performance of performing Equal or Diff on -// large slices of bytes. -func BenchmarkBytes(b *testing.B) { - // Create a list of PathFilters that never apply, but are evaluated. - const maxFilters = 5 - var filters cmp.Options - errorIface := reflect.TypeOf((*error)(nil)).Elem() - for i := 0; i <= maxFilters; i++ { - filters = append(filters, cmp.FilterPath(func(p cmp.Path) bool { - return p.Last().Type().AssignableTo(errorIface) // Never true - }, cmp.Ignore())) - } - - type benchSize struct { - label string - size int64 - } - for _, ts := range []benchSize{ - {"4KiB", 1 << 12}, - {"64KiB", 1 << 16}, - {"1MiB", 1 << 20}, - {"16MiB", 1 << 24}, - } { - bx := append(append(make([]byte, ts.size/2), 'x'), make([]byte, ts.size/2)...) - by := append(append(make([]byte, ts.size/2), 'y'), make([]byte, ts.size/2)...) - b.Run(ts.label, func(b *testing.B) { - // Iteratively add more filters that never apply, but are evaluated - // to measure the cost of simply evaluating each filter. - for i := 0; i <= maxFilters; i++ { - b.Run(fmt.Sprintf("EqualFilter%d", i), func(b *testing.B) { - b.ReportAllocs() - b.SetBytes(2 * ts.size) - for j := 0; j < b.N; j++ { - cmp.Equal(bx, by, filters[:i]...) - } - }) - } - for i := 0; i <= maxFilters; i++ { - b.Run(fmt.Sprintf("DiffFilter%d", i), func(b *testing.B) { - b.ReportAllocs() - b.SetBytes(2 * ts.size) - for j := 0; j < b.N; j++ { - cmp.Diff(bx, by, filters[:i]...) - } - }) - } - }) - } -} diff --git a/vendor/github.com/google/go-cmp/cmp/example_reporter_test.go b/vendor/github.com/google/go-cmp/cmp/example_reporter_test.go deleted file mode 100644 index bc1932e..0000000 --- a/vendor/github.com/google/go-cmp/cmp/example_reporter_test.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2019, 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.md file. - -package cmp_test - -import ( - "fmt" - "strings" - - "github.com/google/go-cmp/cmp" -) - -// DiffReporter is a simple custom reporter that only records differences -// detected during comparison. -type DiffReporter struct { - path cmp.Path - diffs []string -} - -func (r *DiffReporter) PushStep(ps cmp.PathStep) { - r.path = append(r.path, ps) -} - -func (r *DiffReporter) Report(rs cmp.Result) { - if !rs.Equal() { - vx, vy := r.path.Last().Values() - r.diffs = append(r.diffs, fmt.Sprintf("%#v:\n\t-: %+v\n\t+: %+v\n", r.path, vx, vy)) - } -} - -func (r *DiffReporter) PopStep() { - r.path = r.path[:len(r.path)-1] -} - -func (r *DiffReporter) String() string { - return strings.Join(r.diffs, "\n") -} - -func ExampleReporter() { - x, y := MakeGatewayInfo() - - var r DiffReporter - cmp.Equal(x, y, cmp.Reporter(&r)) - fmt.Print(r.String()) - - // Output: - // {cmp_test.Gateway}.IPAddress: - // -: 192.168.0.1 - // +: 192.168.0.2 - // - // {cmp_test.Gateway}.Clients[4].IPAddress: - // -: 192.168.0.219 - // +: 192.168.0.221 - // - // {cmp_test.Gateway}.Clients[5->?]: - // -: {Hostname:americano IPAddress:192.168.0.188 LastSeen:2009-11-10 23:03:05 +0000 UTC} - // +: -} diff --git a/vendor/github.com/google/go-cmp/cmp/example_test.go b/vendor/github.com/google/go-cmp/cmp/example_test.go deleted file mode 100644 index 5954780..0000000 --- a/vendor/github.com/google/go-cmp/cmp/example_test.go +++ /dev/null @@ -1,376 +0,0 @@ -// Copyright 2017, 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.md file. - -package cmp_test - -import ( - "fmt" - "math" - "net" - "reflect" - "sort" - "strings" - "time" - - "github.com/google/go-cmp/cmp" -) - -// TODO: Re-write these examples in terms of how you actually use the -// fundamental options and filters and not in terms of what cool things you can -// do with them since that overlaps with cmp/cmpopts. - -// Use Diff to print out a human-readable report of differences for tests -// comparing nested or structured data. -func ExampleDiff_testing() { - // Let got be the hypothetical value obtained from some logic under test - // and want be the expected golden data. - got, want := MakeGatewayInfo() - - if diff := cmp.Diff(want, got); diff != "" { - t.Errorf("MakeGatewayInfo() mismatch (-want +got):\n%s", diff) - } - - // Output: - // MakeGatewayInfo() mismatch (-want +got): - // cmp_test.Gateway{ - // SSID: "CoffeeShopWiFi", - // - IPAddress: s"192.168.0.2", - // + IPAddress: s"192.168.0.1", - // NetMask: net.IPMask{0xff, 0xff, 0x00, 0x00}, - // Clients: []cmp_test.Client{ - // ... // 2 identical elements - // {Hostname: "macchiato", IPAddress: s"192.168.0.153", LastSeen: s"2009-11-10 23:39:43 +0000 UTC"}, - // {Hostname: "espresso", IPAddress: s"192.168.0.121"}, - // { - // Hostname: "latte", - // - IPAddress: s"192.168.0.221", - // + IPAddress: s"192.168.0.219", - // LastSeen: s"2009-11-10 23:00:23 +0000 UTC", - // }, - // + { - // + Hostname: "americano", - // + IPAddress: s"192.168.0.188", - // + LastSeen: s"2009-11-10 23:03:05 +0000 UTC", - // + }, - // }, - // } -} - -// Approximate equality for floats can be handled by defining a custom -// comparer on floats that determines two values to be equal if they are within -// some range of each other. -// -// This example is for demonstrative purposes; use cmpopts.EquateApprox instead. -func ExampleOption_approximateFloats() { - // This Comparer only operates on float64. - // To handle float32s, either define a similar function for that type - // or use a Transformer to convert float32s into float64s. - opt := cmp.Comparer(func(x, y float64) bool { - delta := math.Abs(x - y) - mean := math.Abs(x+y) / 2.0 - return delta/mean < 0.00001 - }) - - x := []float64{1.0, 1.1, 1.2, math.Pi} - y := []float64{1.0, 1.1, 1.2, 3.14159265359} // Accurate enough to Pi - z := []float64{1.0, 1.1, 1.2, 3.1415} // Diverges too far from Pi - - fmt.Println(cmp.Equal(x, y, opt)) - fmt.Println(cmp.Equal(y, z, opt)) - fmt.Println(cmp.Equal(z, x, opt)) - - // Output: - // true - // false - // false -} - -// Normal floating-point arithmetic defines == to be false when comparing -// NaN with itself. In certain cases, this is not the desired property. -// -// This example is for demonstrative purposes; use cmpopts.EquateNaNs instead. -func ExampleOption_equalNaNs() { - // This Comparer only operates on float64. - // To handle float32s, either define a similar function for that type - // or use a Transformer to convert float32s into float64s. - opt := cmp.Comparer(func(x, y float64) bool { - return (math.IsNaN(x) && math.IsNaN(y)) || x == y - }) - - x := []float64{1.0, math.NaN(), math.E, -0.0, +0.0} - y := []float64{1.0, math.NaN(), math.E, -0.0, +0.0} - z := []float64{1.0, math.NaN(), math.Pi, -0.0, +0.0} // Pi constant instead of E - - fmt.Println(cmp.Equal(x, y, opt)) - fmt.Println(cmp.Equal(y, z, opt)) - fmt.Println(cmp.Equal(z, x, opt)) - - // Output: - // true - // false - // false -} - -// To have floating-point comparisons combine both properties of NaN being -// equal to itself and also approximate equality of values, filters are needed -// to restrict the scope of the comparison so that they are composable. -// -// This example is for demonstrative purposes; -// use cmpopts.EquateNaNs and cmpopts.EquateApprox instead. -func ExampleOption_equalNaNsAndApproximateFloats() { - alwaysEqual := cmp.Comparer(func(_, _ interface{}) bool { return true }) - - opts := cmp.Options{ - // This option declares that a float64 comparison is equal only if - // both inputs are NaN. - cmp.FilterValues(func(x, y float64) bool { - return math.IsNaN(x) && math.IsNaN(y) - }, alwaysEqual), - - // This option declares approximate equality on float64s only if - // both inputs are not NaN. - cmp.FilterValues(func(x, y float64) bool { - return !math.IsNaN(x) && !math.IsNaN(y) - }, cmp.Comparer(func(x, y float64) bool { - delta := math.Abs(x - y) - mean := math.Abs(x+y) / 2.0 - return delta/mean < 0.00001 - })), - } - - x := []float64{math.NaN(), 1.0, 1.1, 1.2, math.Pi} - y := []float64{math.NaN(), 1.0, 1.1, 1.2, 3.14159265359} // Accurate enough to Pi - z := []float64{math.NaN(), 1.0, 1.1, 1.2, 3.1415} // Diverges too far from Pi - - fmt.Println(cmp.Equal(x, y, opts)) - fmt.Println(cmp.Equal(y, z, opts)) - fmt.Println(cmp.Equal(z, x, opts)) - - // Output: - // true - // false - // false -} - -// Sometimes, an empty map or slice is considered equal to an allocated one -// of zero length. -// -// This example is for demonstrative purposes; use cmpopts.EquateEmpty instead. -func ExampleOption_equalEmpty() { - alwaysEqual := cmp.Comparer(func(_, _ interface{}) bool { return true }) - - // This option handles slices and maps of any type. - opt := cmp.FilterValues(func(x, y interface{}) bool { - vx, vy := reflect.ValueOf(x), reflect.ValueOf(y) - return (vx.IsValid() && vy.IsValid() && vx.Type() == vy.Type()) && - (vx.Kind() == reflect.Slice || vx.Kind() == reflect.Map) && - (vx.Len() == 0 && vy.Len() == 0) - }, alwaysEqual) - - type S struct { - A []int - B map[string]bool - } - x := S{nil, make(map[string]bool, 100)} - y := S{make([]int, 0, 200), nil} - z := S{[]int{0}, nil} // []int has a single element (i.e., not empty) - - fmt.Println(cmp.Equal(x, y, opt)) - fmt.Println(cmp.Equal(y, z, opt)) - fmt.Println(cmp.Equal(z, x, opt)) - - // Output: - // true - // false - // false -} - -// Two slices may be considered equal if they have the same elements, -// regardless of the order that they appear in. Transformations can be used -// to sort the slice. -// -// This example is for demonstrative purposes; use cmpopts.SortSlices instead. -func ExampleOption_sortedSlice() { - // This Transformer sorts a []int. - trans := cmp.Transformer("Sort", func(in []int) []int { - out := append([]int(nil), in...) // Copy input to avoid mutating it - sort.Ints(out) - return out - }) - - x := struct{ Ints []int }{[]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}} - y := struct{ Ints []int }{[]int{2, 8, 0, 9, 6, 1, 4, 7, 3, 5}} - z := struct{ Ints []int }{[]int{0, 0, 1, 2, 3, 4, 5, 6, 7, 8}} - - fmt.Println(cmp.Equal(x, y, trans)) - fmt.Println(cmp.Equal(y, z, trans)) - fmt.Println(cmp.Equal(z, x, trans)) - - // Output: - // true - // false - // false -} - -type otherString string - -func (x otherString) Equal(y otherString) bool { - return strings.ToLower(string(x)) == strings.ToLower(string(y)) -} - -// If the Equal method defined on a type is not suitable, the type can be be -// dynamically transformed to be stripped of the Equal method (or any method -// for that matter). -func ExampleOption_avoidEqualMethod() { - // Suppose otherString.Equal performs a case-insensitive equality, - // which is too loose for our needs. - // We can avoid the methods of otherString by declaring a new type. - type myString otherString - - // This transformer converts otherString to myString, allowing Equal to use - // other Options to determine equality. - trans := cmp.Transformer("", func(in otherString) myString { - return myString(in) - }) - - x := []otherString{"foo", "bar", "baz"} - y := []otherString{"fOO", "bAr", "Baz"} // Same as before, but with different case - - fmt.Println(cmp.Equal(x, y)) // Equal because of case-insensitivity - fmt.Println(cmp.Equal(x, y, trans)) // Not equal because of more exact equality - - // Output: - // true - // false -} - -func roundF64(z float64) float64 { - if z < 0 { - return math.Ceil(z - 0.5) - } - return math.Floor(z + 0.5) -} - -// The complex numbers complex64 and complex128 can really just be decomposed -// into a pair of float32 or float64 values. It would be convenient to be able -// define only a single comparator on float64 and have float32, complex64, and -// complex128 all be able to use that comparator. Transformations can be used -// to handle this. -func ExampleOption_transformComplex() { - opts := []cmp.Option{ - // This transformer decomposes complex128 into a pair of float64s. - cmp.Transformer("T1", func(in complex128) (out struct{ Real, Imag float64 }) { - out.Real, out.Imag = real(in), imag(in) - return out - }), - // This transformer converts complex64 to complex128 to allow the - // above transform to take effect. - cmp.Transformer("T2", func(in complex64) complex128 { - return complex128(in) - }), - // This transformer converts float32 to float64. - cmp.Transformer("T3", func(in float32) float64 { - return float64(in) - }), - // This equality function compares float64s as rounded integers. - cmp.Comparer(func(x, y float64) bool { - return roundF64(x) == roundF64(y) - }), - } - - x := []interface{}{ - complex128(3.0), complex64(5.1 + 2.9i), float32(-1.2), float64(12.3), - } - y := []interface{}{ - complex128(3.1), complex64(4.9 + 3.1i), float32(-1.3), float64(11.7), - } - z := []interface{}{ - complex128(3.8), complex64(4.9 + 3.1i), float32(-1.3), float64(11.7), - } - - fmt.Println(cmp.Equal(x, y, opts...)) - fmt.Println(cmp.Equal(y, z, opts...)) - fmt.Println(cmp.Equal(z, x, opts...)) - - // Output: - // true - // false - // false -} - -type ( - Gateway struct { - SSID string - IPAddress net.IP - NetMask net.IPMask - Clients []Client - } - Client struct { - Hostname string - IPAddress net.IP - LastSeen time.Time - } -) - -func MakeGatewayInfo() (x, y Gateway) { - x = Gateway{ - SSID: "CoffeeShopWiFi", - IPAddress: net.IPv4(192, 168, 0, 1), - NetMask: net.IPv4Mask(255, 255, 0, 0), - Clients: []Client{{ - Hostname: "ristretto", - IPAddress: net.IPv4(192, 168, 0, 116), - }, { - Hostname: "aribica", - IPAddress: net.IPv4(192, 168, 0, 104), - LastSeen: time.Date(2009, time.November, 10, 23, 6, 32, 0, time.UTC), - }, { - Hostname: "macchiato", - IPAddress: net.IPv4(192, 168, 0, 153), - LastSeen: time.Date(2009, time.November, 10, 23, 39, 43, 0, time.UTC), - }, { - Hostname: "espresso", - IPAddress: net.IPv4(192, 168, 0, 121), - }, { - Hostname: "latte", - IPAddress: net.IPv4(192, 168, 0, 219), - LastSeen: time.Date(2009, time.November, 10, 23, 0, 23, 0, time.UTC), - }, { - Hostname: "americano", - IPAddress: net.IPv4(192, 168, 0, 188), - LastSeen: time.Date(2009, time.November, 10, 23, 3, 5, 0, time.UTC), - }}, - } - y = Gateway{ - SSID: "CoffeeShopWiFi", - IPAddress: net.IPv4(192, 168, 0, 2), - NetMask: net.IPv4Mask(255, 255, 0, 0), - Clients: []Client{{ - Hostname: "ristretto", - IPAddress: net.IPv4(192, 168, 0, 116), - }, { - Hostname: "aribica", - IPAddress: net.IPv4(192, 168, 0, 104), - LastSeen: time.Date(2009, time.November, 10, 23, 6, 32, 0, time.UTC), - }, { - Hostname: "macchiato", - IPAddress: net.IPv4(192, 168, 0, 153), - LastSeen: time.Date(2009, time.November, 10, 23, 39, 43, 0, time.UTC), - }, { - Hostname: "espresso", - IPAddress: net.IPv4(192, 168, 0, 121), - }, { - Hostname: "latte", - IPAddress: net.IPv4(192, 168, 0, 221), - LastSeen: time.Date(2009, time.November, 10, 23, 0, 23, 0, time.UTC), - }}, - } - return x, y -} - -var t fakeT - -type fakeT struct{} - -func (t fakeT) Errorf(format string, args ...interface{}) { fmt.Printf(format+"\n", args...) } diff --git a/vendor/github.com/google/go-cmp/cmp/export.go b/vendor/github.com/google/go-cmp/cmp/export.go new file mode 100644 index 0000000..29f82fe --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/export.go @@ -0,0 +1,31 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmp + +import ( + "reflect" + "unsafe" +) + +// retrieveUnexportedField uses unsafe to forcibly retrieve any field from +// a struct such that the value has read-write permissions. +// +// The parent struct, v, must be addressable, while f must be a StructField +// describing the field to retrieve. If addr is false, +// then the returned value will be shallowed copied to be non-addressable. +func retrieveUnexportedField(v reflect.Value, f reflect.StructField, addr bool) reflect.Value { + ve := reflect.NewAt(f.Type, unsafe.Pointer(uintptr(unsafe.Pointer(v.UnsafeAddr()))+f.Offset)).Elem() + if !addr { + // A field is addressable if and only if the struct is addressable. + // If the original parent value was not addressable, shallow copy the + // value to make it non-addressable to avoid leaking an implementation + // detail of how forcibly exporting a field works. + if ve.Kind() == reflect.Interface && ve.IsNil() { + return reflect.Zero(f.Type) + } + return reflect.ValueOf(ve.Interface()).Convert(f.Type) + } + return ve +} diff --git a/vendor/github.com/google/go-cmp/cmp/export_panic.go b/vendor/github.com/google/go-cmp/cmp/export_panic.go deleted file mode 100644 index abc3a1c..0000000 --- a/vendor/github.com/google/go-cmp/cmp/export_panic.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2017, 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.md file. - -// +build purego - -package cmp - -import "reflect" - -const supportAllowUnexported = false - -func retrieveUnexportedField(reflect.Value, reflect.StructField) reflect.Value { - panic("retrieveUnexportedField is not implemented") -} diff --git a/vendor/github.com/google/go-cmp/cmp/export_unsafe.go b/vendor/github.com/google/go-cmp/cmp/export_unsafe.go deleted file mode 100644 index 59d4ee9..0000000 --- a/vendor/github.com/google/go-cmp/cmp/export_unsafe.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2017, 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.md file. - -// +build !purego - -package cmp - -import ( - "reflect" - "unsafe" -) - -const supportAllowUnexported = true - -// retrieveUnexportedField uses unsafe to forcibly retrieve any field from -// a struct such that the value has read-write permissions. -// -// The parent struct, v, must be addressable, while f must be a StructField -// describing the field to retrieve. -func retrieveUnexportedField(v reflect.Value, f reflect.StructField) reflect.Value { - return reflect.NewAt(f.Type, unsafe.Pointer(v.UnsafeAddr()+f.Offset)).Elem() -} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go b/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go index fe98dcc..36062a6 100644 --- a/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go +++ b/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go @@ -1,7 +1,8 @@ // Copyright 2017, 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.md file. +// license that can be found in the LICENSE file. +//go:build !cmp_debug // +build !cmp_debug package diff diff --git a/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go b/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go index 597b6ae..a3b97a1 100644 --- a/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go +++ b/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go @@ -1,7 +1,8 @@ // Copyright 2017, 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.md file. +// license that can be found in the LICENSE file. +//go:build cmp_debug // +build cmp_debug package diff diff --git a/vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go b/vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go index 3d2e426..a248e54 100644 --- a/vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go +++ b/vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go @@ -1,6 +1,6 @@ // Copyright 2017, 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.md file. +// license that can be found in the LICENSE file. // Package diff implements an algorithm for producing edit-scripts. // The edit-script is a sequence of operations needed to transform one list @@ -12,6 +12,13 @@ // is more important than obtaining a minimal Levenshtein distance. package diff +import ( + "math/rand" + "time" + + "github.com/google/go-cmp/cmp/internal/flags" +) + // EditType represents a single operation within an edit-script. type EditType uint8 @@ -112,15 +119,17 @@ func (r Result) Similar() bool { return r.NumSame+1 >= r.NumDiff } +var randBool = rand.New(rand.NewSource(time.Now().Unix())).Intn(2) == 0 + // Difference reports whether two lists of lengths nx and ny are equal // given the definition of equality provided as f. // // This function returns an edit-script, which is a sequence of operations // needed to convert one list into the other. The following invariants for // the edit-script are maintained: -// • eq == (es.Dist()==0) -// • nx == es.LenX() -// • ny == es.LenY() +// - eq == (es.Dist()==0) +// - nx == es.LenX() +// - ny == es.LenY() // // This algorithm is not guaranteed to be an optimal solution (i.e., one that // produces an edit-script with a minimal Levenshtein distance). This algorithm @@ -160,12 +169,13 @@ func Difference(nx, ny int, f EqualFunc) (es EditScript) { // A diagonal edge is equivalent to a matching symbol between both X and Y. // Invariants: - // • 0 ≤ fwdPath.X ≤ (fwdFrontier.X, revFrontier.X) ≤ revPath.X ≤ nx - // • 0 ≤ fwdPath.Y ≤ (fwdFrontier.Y, revFrontier.Y) ≤ revPath.Y ≤ ny + // - 0 ≤ fwdPath.X ≤ (fwdFrontier.X, revFrontier.X) ≤ revPath.X ≤ nx + // - 0 ≤ fwdPath.Y ≤ (fwdFrontier.Y, revFrontier.Y) ≤ revPath.Y ≤ ny // // In general: - // • fwdFrontier.X < revFrontier.X - // • fwdFrontier.Y < revFrontier.Y + // - fwdFrontier.X < revFrontier.X + // - fwdFrontier.Y < revFrontier.Y + // // Unless, it is time for the algorithm to terminate. fwdPath := path{+1, point{0, 0}, make(EditScript, 0, (nx+ny)/2)} revPath := path{-1, point{nx, ny}, make(EditScript, 0)} @@ -177,37 +187,50 @@ func Difference(nx, ny int, f EqualFunc) (es EditScript) { // approximately the square-root of the search budget. searchBudget := 4 * (nx + ny) // O(n) + // Running the tests with the "cmp_debug" build tag prints a visualization + // of the algorithm running in real-time. This is educational for + // understanding how the algorithm works. See debug_enable.go. + f = debug.Begin(nx, ny, f, &fwdPath.es, &revPath.es) + // The algorithm below is a greedy, meet-in-the-middle algorithm for // computing sub-optimal edit-scripts between two lists. // // The algorithm is approximately as follows: - // • Searching for differences switches back-and-forth between - // a search that starts at the beginning (the top-left corner), and - // a search that starts at the end (the bottom-right corner). The goal of - // the search is connect with the search from the opposite corner. - // • As we search, we build a path in a greedy manner, where the first - // match seen is added to the path (this is sub-optimal, but provides a - // decent result in practice). When matches are found, we try the next pair - // of symbols in the lists and follow all matches as far as possible. - // • When searching for matches, we search along a diagonal going through - // through the "frontier" point. If no matches are found, we advance the - // frontier towards the opposite corner. - // • This algorithm terminates when either the X coordinates or the - // Y coordinates of the forward and reverse frontier points ever intersect. - // + // - Searching for differences switches back-and-forth between + // a search that starts at the beginning (the top-left corner), and + // a search that starts at the end (the bottom-right corner). + // The goal of the search is connect with the search + // from the opposite corner. + // - As we search, we build a path in a greedy manner, + // where the first match seen is added to the path (this is sub-optimal, + // but provides a decent result in practice). When matches are found, + // we try the next pair of symbols in the lists and follow all matches + // as far as possible. + // - When searching for matches, we search along a diagonal going through + // through the "frontier" point. If no matches are found, + // we advance the frontier towards the opposite corner. + // - This algorithm terminates when either the X coordinates or the + // Y coordinates of the forward and reverse frontier points ever intersect. + // This algorithm is correct even if searching only in the forward direction // or in the reverse direction. We do both because it is commonly observed // that two lists commonly differ because elements were added to the front // or end of the other list. // - // Running the tests with the "cmp_debug" build tag prints a visualization - // of the algorithm running in real-time. This is educational for - // understanding how the algorithm works. See debug_enable.go. - f = debug.Begin(nx, ny, f, &fwdPath.es, &revPath.es) - for { + // Non-deterministically start with either the forward or reverse direction + // to introduce some deliberate instability so that we have the flexibility + // to change this algorithm in the future. + if flags.Deterministic || randBool { + goto forwardSearch + } else { + goto reverseSearch + } + +forwardSearch: + { // Forward search from the beginning. if fwdFrontier.X >= revFrontier.X || fwdFrontier.Y >= revFrontier.Y || searchBudget == 0 { - break + goto finishSearch } for stop1, stop2, i := false, false, 0; !(stop1 && stop2) && searchBudget > 0; i++ { // Search in a diagonal pattern for a match. @@ -242,10 +265,14 @@ func Difference(nx, ny int, f EqualFunc) (es EditScript) { } else { fwdFrontier.Y++ } + goto reverseSearch + } +reverseSearch: + { // Reverse search from the end. if fwdFrontier.X >= revFrontier.X || fwdFrontier.Y >= revFrontier.Y || searchBudget == 0 { - break + goto finishSearch } for stop1, stop2, i := false, false, 0; !(stop1 && stop2) && searchBudget > 0; i++ { // Search in a diagonal pattern for a match. @@ -280,8 +307,10 @@ func Difference(nx, ny int, f EqualFunc) (es EditScript) { } else { revFrontier.Y-- } + goto forwardSearch } +finishSearch: // Join the forward and reverse paths and then append the reverse path. fwdPath.connect(revPath.point, f) for i := len(revPath.es) - 1; i >= 0; i-- { @@ -363,6 +392,7 @@ type point struct{ X, Y int } func (p *point) add(dx, dy int) { p.X += dx; p.Y += dy } // zigzag maps a consecutive sequence of integers to a zig-zag sequence. +// // [0 1 2 3 4 5 ...] => [0 -1 +1 -2 +2 ...] func zigzag(x int) int { if x&1 != 0 { diff --git a/vendor/github.com/google/go-cmp/cmp/internal/diff/diff_test.go b/vendor/github.com/google/go-cmp/cmp/internal/diff/diff_test.go deleted file mode 100644 index ef39077..0000000 --- a/vendor/github.com/google/go-cmp/cmp/internal/diff/diff_test.go +++ /dev/null @@ -1,444 +0,0 @@ -// Copyright 2017, 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.md file. - -package diff - -import ( - "fmt" - "math/rand" - "strings" - "testing" - "unicode" -) - -func TestDifference(t *testing.T) { - tests := []struct { - // Before passing x and y to Difference, we strip all spaces so that - // they can be used by the test author to indicate a missing symbol - // in one of the lists. - x, y string - want string - }{{ - x: "", - y: "", - want: "", - }, { - x: "#", - y: "#", - want: ".", - }, { - x: "##", - y: "# ", - want: ".X", - }, { - x: "a#", - y: "A ", - want: "MX", - }, { - x: "#a", - y: " A", - want: "XM", - }, { - x: "# ", - y: "##", - want: ".Y", - }, { - x: " #", - y: "@#", - want: "Y.", - }, { - x: "@#", - y: " #", - want: "X.", - }, { - x: "##########0123456789", - y: " 0123456789", - want: "XXXXXXXXXX..........", - }, { - x: " 0123456789", - y: "##########0123456789", - want: "YYYYYYYYYY..........", - }, { - x: "#####0123456789#####", - y: " 0123456789 ", - want: "XXXXX..........XXXXX", - }, { - x: " 0123456789 ", - y: "#####0123456789#####", - want: "YYYYY..........YYYYY", - }, { - x: "01234##########56789", - y: "01234 56789", - want: ".....XXXXXXXXXX.....", - }, { - x: "01234 56789", - y: "01234##########56789", - want: ".....YYYYYYYYYY.....", - }, { - x: "0123456789##########", - y: "0123456789 ", - want: "..........XXXXXXXXXX", - }, { - x: "0123456789 ", - y: "0123456789##########", - want: "..........YYYYYYYYYY", - }, { - x: "abcdefghij0123456789", - y: "ABCDEFGHIJ0123456789", - want: "MMMMMMMMMM..........", - }, { - x: "ABCDEFGHIJ0123456789", - y: "abcdefghij0123456789", - want: "MMMMMMMMMM..........", - }, { - x: "01234abcdefghij56789", - y: "01234ABCDEFGHIJ56789", - want: ".....MMMMMMMMMM.....", - }, { - x: "01234ABCDEFGHIJ56789", - y: "01234abcdefghij56789", - want: ".....MMMMMMMMMM.....", - }, { - x: "0123456789abcdefghij", - y: "0123456789ABCDEFGHIJ", - want: "..........MMMMMMMMMM", - }, { - x: "0123456789ABCDEFGHIJ", - y: "0123456789abcdefghij", - want: "..........MMMMMMMMMM", - }, { - x: "ABCDEFGHIJ0123456789 ", - y: " 0123456789abcdefghij", - want: "XXXXXXXXXX..........YYYYYYYYYY", - }, { - x: " 0123456789abcdefghij", - y: "ABCDEFGHIJ0123456789 ", - want: "YYYYYYYYYY..........XXXXXXXXXX", - }, { - x: "ABCDE0123456789 FGHIJ", - y: " 0123456789abcdefghij", - want: "XXXXX..........YYYYYMMMMM", - }, { - x: " 0123456789abcdefghij", - y: "ABCDE0123456789 FGHIJ", - want: "YYYYY..........XXXXXMMMMM", - }, { - x: "ABCDE01234F G H I J 56789 ", - y: " 01234 a b c d e56789fghij", - want: "XXXXX.....XYXYXYXYXY.....YYYYY", - }, { - x: " 01234a b c d e 56789fghij", - y: "ABCDE01234 F G H I J56789 ", - want: "YYYYY.....XYXYXYXYXY.....XXXXX", - }, { - x: "FGHIJ01234ABCDE56789 ", - y: " 01234abcde56789fghij", - want: "XXXXX.....MMMMM.....YYYYY", - }, { - x: " 01234abcde56789fghij", - y: "FGHIJ01234ABCDE56789 ", - want: "YYYYY.....MMMMM.....XXXXX", - }, { - x: "ABCAB BA ", - y: " C BABAC", - want: "XX.X.Y..Y", - }, { - x: "# #### ###", - y: "#y####yy###", - want: ".Y....YY...", - }, { - x: "# #### # ##x#x", - y: "#y####y y## # ", - want: ".Y....YXY..X.X", - }, { - x: "###z#z###### x #", - y: "#y##Z#Z###### yy#", - want: ".Y..M.M......XYY.", - }, { - x: "0 12z3x 456789 x x 0", - y: "0y12Z3 y456789y y y0", - want: ".Y..M.XY......YXYXY.", - }, { - x: "0 2 4 6 8 ..................abXXcdEXF.ghXi", - y: " 1 3 5 7 9..................AB CDE F.GH I", - want: "XYXYXYXYXY..................MMXXMM.X..MMXM", - }, { - x: "I HG.F EDC BA..................9 7 5 3 1 ", - y: "iXhg.FXEdcXXba.................. 8 6 4 2 0", - want: "MYMM..Y.MMYYMM..................XYXYXYXYXY", - }, { - x: "x1234", - y: " 1234", - want: "X....", - }, { - x: "x123x4", - y: " 123 4", - want: "X...X.", - }, { - x: "x1234x56", - y: " 1234 ", - want: "X....XXX", - }, { - x: "x1234xxx56", - y: " 1234 56", - want: "X....XXX..", - }, { - x: ".1234...ab", - y: " 1234 AB", - want: "X....XXXMM", - }, { - x: "x1234xxab.", - y: " 1234 AB ", - want: "X....XXMMX", - }, { - x: " 0123456789", - y: "9012345678 ", - want: "Y.........X", - }, { - x: " 0123456789", - y: "8901234567 ", - want: "YY........XX", - }, { - x: " 0123456789", - y: "7890123456 ", - want: "YYY.......XXX", - }, { - x: " 0123456789", - y: "6789012345 ", - want: "YYYY......XXXX", - }, { - x: "0123456789 ", - y: " 5678901234", - want: "XXXXX.....YYYYY", - }, { - x: "0123456789 ", - y: " 4567890123", - want: "XXXX......YYYY", - }, { - x: "0123456789 ", - y: " 3456789012", - want: "XXX.......YYY", - }, { - x: "0123456789 ", - y: " 2345678901", - want: "XX........YY", - }, { - x: "0123456789 ", - y: " 1234567890", - want: "X.........Y", - }, { - x: "0 1 2 3 45 6 7 8 9 ", - y: " 9 8 7 6 54 3 2 1 0", - want: "XYXYXYXYX.YXYXYXYXY", - }, { - x: "0 1 2345678 9 ", - y: " 6 72 5 819034", - want: "XYXY.XX.XX.Y.YYY", - }, { - x: "F B Q M O I G T L N72X90 E 4S P 651HKRJU DA 83CVZW", - y: " 5 W H XO10R9IV K ZLCTAJ8P3N SEQM4 7 2G6 UBD F ", - want: "XYXYXYXY.YYYY.YXYXY.YYYYYYY.XXXXXY.YY.XYXYY.XXXXXX.Y.XYXXXXXX", - }} - - for _, tt := range tests { - t.Run("", func(t *testing.T) { - x := strings.Replace(tt.x, " ", "", -1) - y := strings.Replace(tt.y, " ", "", -1) - es := testStrings(t, x, y) - if got := es.String(); got != tt.want { - t.Errorf("Difference(%s, %s):\ngot %s\nwant %s", x, y, got, tt.want) - } - }) - } -} - -func TestDifferenceFuzz(t *testing.T) { - tests := []struct{ px, py, pm float32 }{ - {px: 0.0, py: 0.0, pm: 0.1}, - {px: 0.0, py: 0.1, pm: 0.0}, - {px: 0.1, py: 0.0, pm: 0.0}, - {px: 0.0, py: 0.1, pm: 0.1}, - {px: 0.1, py: 0.0, pm: 0.1}, - {px: 0.2, py: 0.2, pm: 0.2}, - {px: 0.3, py: 0.1, pm: 0.2}, - {px: 0.1, py: 0.3, pm: 0.2}, - {px: 0.2, py: 0.2, pm: 0.2}, - {px: 0.3, py: 0.3, pm: 0.3}, - {px: 0.1, py: 0.1, pm: 0.5}, - {px: 0.4, py: 0.1, pm: 0.5}, - {px: 0.3, py: 0.2, pm: 0.5}, - {px: 0.2, py: 0.3, pm: 0.5}, - {px: 0.1, py: 0.4, pm: 0.5}, - } - - for i, tt := range tests { - t.Run(fmt.Sprintf("P%d", i), func(t *testing.T) { - // Sweep from 1B to 1KiB. - for n := 1; n <= 1024; n <<= 1 { - t.Run(fmt.Sprintf("N%d", n), func(t *testing.T) { - for j := 0; j < 10; j++ { - x, y := generateStrings(n, tt.px, tt.py, tt.pm, int64(j)) - testStrings(t, x, y) - } - }) - } - }) - } -} - -func BenchmarkDifference(b *testing.B) { - for n := 1 << 10; n <= 1<<20; n <<= 2 { - b.Run(fmt.Sprintf("N%d", n), func(b *testing.B) { - x, y := generateStrings(n, 0.05, 0.05, 0.10, 0) - b.ReportAllocs() - b.SetBytes(int64(len(x) + len(y))) - for i := 0; i < b.N; i++ { - Difference(len(x), len(y), func(ix, iy int) Result { - return compareByte(x[ix], y[iy]) - }) - } - }) - } -} - -func generateStrings(n int, px, py, pm float32, seed int64) (string, string) { - if px+py+pm > 1.0 { - panic("invalid probabilities") - } - py += px - pm += py - - b := make([]byte, n) - r := rand.New(rand.NewSource(seed)) - r.Read(b) - - var x, y []byte - for len(b) > 0 { - switch p := r.Float32(); { - case p < px: // UniqueX - x = append(x, b[0]) - case p < py: // UniqueY - y = append(y, b[0]) - case p < pm: // Modified - x = append(x, 'A'+(b[0]%26)) - y = append(y, 'a'+(b[0]%26)) - default: // Identity - x = append(x, b[0]) - y = append(y, b[0]) - } - b = b[1:] - } - return string(x), string(y) -} - -func testStrings(t *testing.T, x, y string) EditScript { - es := Difference(len(x), len(y), func(ix, iy int) Result { - return compareByte(x[ix], y[iy]) - }) - if es.LenX() != len(x) { - t.Errorf("es.LenX = %d, want %d", es.LenX(), len(x)) - } - if es.LenY() != len(y) { - t.Errorf("es.LenY = %d, want %d", es.LenY(), len(y)) - } - if !validateScript(x, y, es) { - t.Errorf("invalid edit script: %v", es) - } - return es -} - -func validateScript(x, y string, es EditScript) bool { - var bx, by []byte - for _, e := range es { - switch e { - case Identity: - if !compareByte(x[len(bx)], y[len(by)]).Equal() { - return false - } - bx = append(bx, x[len(bx)]) - by = append(by, y[len(by)]) - case UniqueX: - bx = append(bx, x[len(bx)]) - case UniqueY: - by = append(by, y[len(by)]) - case Modified: - if !compareByte(x[len(bx)], y[len(by)]).Similar() { - return false - } - bx = append(bx, x[len(bx)]) - by = append(by, y[len(by)]) - } - } - return string(bx) == x && string(by) == y -} - -// compareByte returns a Result where the result is Equal if x == y, -// similar if x and y differ only in casing, and different otherwise. -func compareByte(x, y byte) (r Result) { - switch { - case x == y: - return equalResult // Identity - case unicode.ToUpper(rune(x)) == unicode.ToUpper(rune(y)): - return similarResult // Modified - default: - return differentResult // UniqueX or UniqueY - } -} - -var ( - equalResult = Result{NumDiff: 0} - similarResult = Result{NumDiff: 1} - differentResult = Result{NumDiff: 2} -) - -func TestResult(t *testing.T) { - tests := []struct { - result Result - wantEqual bool - wantSimilar bool - }{ - // equalResult is equal since NumDiff == 0, by definition of Equal method. - {equalResult, true, true}, - // similarResult is similar since it is a binary result where only one - // element was compared (i.e., Either NumSame==1 or NumDiff==1). - {similarResult, false, true}, - // differentResult is different since there are enough differences that - // it isn't even considered similar. - {differentResult, false, false}, - - // Zero value is always equal. - {Result{NumSame: 0, NumDiff: 0}, true, true}, - - // Binary comparisons (where NumSame+NumDiff == 1) are always similar. - {Result{NumSame: 1, NumDiff: 0}, true, true}, - {Result{NumSame: 0, NumDiff: 1}, false, true}, - - // More complex ratios. The exact ratio for similarity may change, - // and may require updates to these test cases. - {Result{NumSame: 1, NumDiff: 1}, false, true}, - {Result{NumSame: 1, NumDiff: 2}, false, true}, - {Result{NumSame: 1, NumDiff: 3}, false, false}, - {Result{NumSame: 2, NumDiff: 1}, false, true}, - {Result{NumSame: 2, NumDiff: 2}, false, true}, - {Result{NumSame: 2, NumDiff: 3}, false, true}, - {Result{NumSame: 3, NumDiff: 1}, false, true}, - {Result{NumSame: 3, NumDiff: 2}, false, true}, - {Result{NumSame: 3, NumDiff: 3}, false, true}, - {Result{NumSame: 1000, NumDiff: 0}, true, true}, - {Result{NumSame: 1000, NumDiff: 1}, false, true}, - {Result{NumSame: 1000, NumDiff: 2}, false, true}, - {Result{NumSame: 0, NumDiff: 1000}, false, false}, - {Result{NumSame: 1, NumDiff: 1000}, false, false}, - {Result{NumSame: 2, NumDiff: 1000}, false, false}, - } - - for _, tt := range tests { - if got := tt.result.Equal(); got != tt.wantEqual { - t.Errorf("%#v.Equal() = %v, want %v", tt.result, got, tt.wantEqual) - } - if got := tt.result.Similar(); got != tt.wantSimilar { - t.Errorf("%#v.Similar() = %v, want %v", tt.result, got, tt.wantSimilar) - } - } -} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/flags/flags.go b/vendor/github.com/google/go-cmp/cmp/internal/flags/flags.go index a9e7fc0..d8e459c 100644 --- a/vendor/github.com/google/go-cmp/cmp/internal/flags/flags.go +++ b/vendor/github.com/google/go-cmp/cmp/internal/flags/flags.go @@ -1,6 +1,6 @@ // Copyright 2019, 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.md file. +// license that can be found in the LICENSE file. package flags diff --git a/vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_legacy.go b/vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_legacy.go deleted file mode 100644 index 01aed0a..0000000 --- a/vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_legacy.go +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2019, 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.md file. - -// +build !go1.10 - -package flags - -// AtLeastGo110 reports whether the Go toolchain is at least Go 1.10. -const AtLeastGo110 = false diff --git a/vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_recent.go b/vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_recent.go deleted file mode 100644 index c0b667f..0000000 --- a/vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_recent.go +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2019, 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.md file. - -// +build go1.10 - -package flags - -// AtLeastGo110 reports whether the Go toolchain is at least Go 1.10. -const AtLeastGo110 = true diff --git a/vendor/github.com/google/go-cmp/cmp/internal/function/func.go b/vendor/github.com/google/go-cmp/cmp/internal/function/func.go index ace1dbe..d127d43 100644 --- a/vendor/github.com/google/go-cmp/cmp/internal/function/func.go +++ b/vendor/github.com/google/go-cmp/cmp/internal/function/func.go @@ -1,6 +1,6 @@ // Copyright 2017, 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.md file. +// license that can be found in the LICENSE file. // Package function provides functionality for identifying function types. package function diff --git a/vendor/github.com/google/go-cmp/cmp/internal/function/func_test.go b/vendor/github.com/google/go-cmp/cmp/internal/function/func_test.go deleted file mode 100644 index 61eeccd..0000000 --- a/vendor/github.com/google/go-cmp/cmp/internal/function/func_test.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2019, 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.md file. - -package function - -import ( - "bytes" - "reflect" - "testing" -) - -type myType struct{ bytes.Buffer } - -func (myType) valueMethod() {} -func (myType) ValueMethod() {} - -func (*myType) pointerMethod() {} -func (*myType) PointerMethod() {} - -func TestNameOf(t *testing.T) { - tests := []struct { - fnc interface{} - want string - }{ - {TestNameOf, "function.TestNameOf"}, - {func() {}, "function.TestNameOf.func1"}, - {(myType).valueMethod, "function.myType.valueMethod"}, - {(myType).ValueMethod, "function.myType.ValueMethod"}, - {(myType{}).valueMethod, "function.myType.valueMethod"}, - {(myType{}).ValueMethod, "function.myType.ValueMethod"}, - {(*myType).valueMethod, "function.myType.valueMethod"}, - {(*myType).ValueMethod, "function.myType.ValueMethod"}, - {(&myType{}).valueMethod, "function.myType.valueMethod"}, - {(&myType{}).ValueMethod, "function.myType.ValueMethod"}, - {(*myType).pointerMethod, "function.myType.pointerMethod"}, - {(*myType).PointerMethod, "function.myType.PointerMethod"}, - {(&myType{}).pointerMethod, "function.myType.pointerMethod"}, - {(&myType{}).PointerMethod, "function.myType.PointerMethod"}, - {(*myType).Write, "function.myType.Write"}, - {(&myType{}).Write, "bytes.Buffer.Write"}, - } - for _, tt := range tests { - t.Run("", func(t *testing.T) { - got := NameOf(reflect.ValueOf(tt.fnc)) - if got != tt.want { - t.Errorf("NameOf() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/testprotos/protos.go b/vendor/github.com/google/go-cmp/cmp/internal/testprotos/protos.go deleted file mode 100644 index 120c8b0..0000000 --- a/vendor/github.com/google/go-cmp/cmp/internal/testprotos/protos.go +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2017, 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.md file. - -package testprotos - -func Equal(x, y Message) bool { - if x == nil || y == nil { - return x == nil && y == nil - } - return x.String() == y.String() -} - -type Message interface { - Proto() - String() string -} - -type proto interface { - Proto() -} - -type notComparable struct { - unexportedField func() -} - -type Stringer struct{ X string } - -func (s *Stringer) String() string { return s.X } - -// Project1 protocol buffers -type ( - Eagle_States int - Eagle_MissingCalls int - Dreamer_States int - Dreamer_MissingCalls int - Slap_States int - Goat_States int - Donkey_States int - SummerType int - - Eagle struct { - proto - notComparable - Stringer - } - Dreamer struct { - proto - notComparable - Stringer - } - Slap struct { - proto - notComparable - Stringer - } - Goat struct { - proto - notComparable - Stringer - } - Donkey struct { - proto - notComparable - Stringer - } -) - -// Project2 protocol buffers -type ( - Germ struct { - proto - notComparable - Stringer - } - Dish struct { - proto - notComparable - Stringer - } -) - -// Project3 protocol buffers -type ( - Dirt struct { - proto - notComparable - Stringer - } - Wizard struct { - proto - notComparable - Stringer - } - Sadistic struct { - proto - notComparable - Stringer - } -) - -// Project4 protocol buffers -type ( - HoneyStatus int - PoisonType int - MetaData struct { - proto - notComparable - Stringer - } - Restrictions struct { - proto - notComparable - Stringer - } -) diff --git a/vendor/github.com/google/go-cmp/cmp/internal/teststructs/project1.go b/vendor/github.com/google/go-cmp/cmp/internal/teststructs/project1.go deleted file mode 100644 index 1999e38..0000000 --- a/vendor/github.com/google/go-cmp/cmp/internal/teststructs/project1.go +++ /dev/null @@ -1,267 +0,0 @@ -// Copyright 2017, 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.md file. - -package teststructs - -import ( - "time" - - pb "github.com/google/go-cmp/cmp/internal/testprotos" -) - -// This is an sanitized example of equality from a real use-case. -// The original equality function was as follows: -/* -func equalEagle(x, y Eagle) bool { - if x.Name != y.Name && - !reflect.DeepEqual(x.Hounds, y.Hounds) && - x.Desc != y.Desc && - x.DescLong != y.DescLong && - x.Prong != y.Prong && - x.StateGoverner != y.StateGoverner && - x.PrankRating != y.PrankRating && - x.FunnyPrank != y.FunnyPrank && - !pb.Equal(x.Immutable.Proto(), y.Immutable.Proto()) { - return false - } - - if len(x.Dreamers) != len(y.Dreamers) { - return false - } - for i := range x.Dreamers { - if !equalDreamer(x.Dreamers[i], y.Dreamers[i]) { - return false - } - } - if len(x.Slaps) != len(y.Slaps) { - return false - } - for i := range x.Slaps { - if !equalSlap(x.Slaps[i], y.Slaps[i]) { - return false - } - } - return true -} -func equalDreamer(x, y Dreamer) bool { - if x.Name != y.Name || - x.Desc != y.Desc || - x.DescLong != y.DescLong || - x.ContSlapsInterval != y.ContSlapsInterval || - x.Ornamental != y.Ornamental || - x.Amoeba != y.Amoeba || - x.Heroes != y.Heroes || - x.FloppyDisk != y.FloppyDisk || - x.MightiestDuck != y.MightiestDuck || - x.FunnyPrank != y.FunnyPrank || - !pb.Equal(x.Immutable.Proto(), y.Immutable.Proto()) { - - return false - } - if len(x.Animal) != len(y.Animal) { - return false - } - for i := range x.Animal { - vx := x.Animal[i] - vy := y.Animal[i] - if reflect.TypeOf(x.Animal) != reflect.TypeOf(y.Animal) { - return false - } - switch vx.(type) { - case Goat: - if !equalGoat(vx.(Goat), vy.(Goat)) { - return false - } - case Donkey: - if !equalDonkey(vx.(Donkey), vy.(Donkey)) { - return false - } - default: - panic(fmt.Sprintf("unknown type: %T", vx)) - } - } - if len(x.PreSlaps) != len(y.PreSlaps) { - return false - } - for i := range x.PreSlaps { - if !equalSlap(x.PreSlaps[i], y.PreSlaps[i]) { - return false - } - } - if len(x.ContSlaps) != len(y.ContSlaps) { - return false - } - for i := range x.ContSlaps { - if !equalSlap(x.ContSlaps[i], y.ContSlaps[i]) { - return false - } - } - return true -} -func equalSlap(x, y Slap) bool { - return x.Name == y.Name && - x.Desc == y.Desc && - x.DescLong == y.DescLong && - pb.Equal(x.Args, y.Args) && - x.Tense == y.Tense && - x.Interval == y.Interval && - x.Homeland == y.Homeland && - x.FunnyPrank == y.FunnyPrank && - pb.Equal(x.Immutable.Proto(), y.Immutable.Proto()) -} -func equalGoat(x, y Goat) bool { - if x.Target != y.Target || - x.FunnyPrank != y.FunnyPrank || - !pb.Equal(x.Immutable.Proto(), y.Immutable.Proto()) { - return false - } - if len(x.Slaps) != len(y.Slaps) { - return false - } - for i := range x.Slaps { - if !equalSlap(x.Slaps[i], y.Slaps[i]) { - return false - } - } - return true -} -func equalDonkey(x, y Donkey) bool { - return x.Pause == y.Pause && - x.Sleep == y.Sleep && - x.FunnyPrank == y.FunnyPrank && - pb.Equal(x.Immutable.Proto(), y.Immutable.Proto()) -} -*/ - -type Eagle struct { - Name string - Hounds []string - Desc string - DescLong string - Dreamers []Dreamer - Prong int64 - Slaps []Slap - StateGoverner string - PrankRating string - FunnyPrank string - Immutable *EagleImmutable -} - -type EagleImmutable struct { - ID string - State *pb.Eagle_States - MissingCall *pb.Eagle_MissingCalls - Birthday time.Time - Death time.Time - Started time.Time - LastUpdate time.Time - Creator string - empty bool -} - -type Dreamer struct { - Name string - Desc string - DescLong string - PreSlaps []Slap - ContSlaps []Slap - ContSlapsInterval int32 - Animal []interface{} // Could be either Goat or Donkey - Ornamental bool - Amoeba int64 - Heroes int32 - FloppyDisk int32 - MightiestDuck bool - FunnyPrank string - Immutable *DreamerImmutable -} - -type DreamerImmutable struct { - ID string - State *pb.Dreamer_States - MissingCall *pb.Dreamer_MissingCalls - Calls int32 - Started time.Time - Stopped time.Time - LastUpdate time.Time - empty bool -} - -type Slap struct { - Name string - Desc string - DescLong string - Args pb.Message - Tense int32 - Interval int32 - Homeland uint32 - FunnyPrank string - Immutable *SlapImmutable -} - -type SlapImmutable struct { - ID string - Out pb.Message - MildSlap bool - PrettyPrint string - State *pb.Slap_States - Started time.Time - Stopped time.Time - LastUpdate time.Time - LoveRadius *LoveRadius - empty bool -} - -type Goat struct { - Target string - Slaps []Slap - FunnyPrank string - Immutable *GoatImmutable -} - -type GoatImmutable struct { - ID string - State *pb.Goat_States - Started time.Time - Stopped time.Time - LastUpdate time.Time - empty bool -} -type Donkey struct { - Pause bool - Sleep int32 - FunnyPrank string - Immutable *DonkeyImmutable -} - -type DonkeyImmutable struct { - ID string - State *pb.Donkey_States - Started time.Time - Stopped time.Time - LastUpdate time.Time - empty bool -} - -type LoveRadius struct { - Summer *SummerLove - empty bool -} - -type SummerLove struct { - Summary *SummerLoveSummary - empty bool -} - -type SummerLoveSummary struct { - Devices []string - ChangeType []pb.SummerType - empty bool -} - -func (EagleImmutable) Proto() *pb.Eagle { return nil } -func (DreamerImmutable) Proto() *pb.Dreamer { return nil } -func (SlapImmutable) Proto() *pb.Slap { return nil } -func (GoatImmutable) Proto() *pb.Goat { return nil } -func (DonkeyImmutable) Proto() *pb.Donkey { return nil } diff --git a/vendor/github.com/google/go-cmp/cmp/internal/teststructs/project2.go b/vendor/github.com/google/go-cmp/cmp/internal/teststructs/project2.go deleted file mode 100644 index 536592b..0000000 --- a/vendor/github.com/google/go-cmp/cmp/internal/teststructs/project2.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2017, 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.md file. - -package teststructs - -import ( - "time" - - pb "github.com/google/go-cmp/cmp/internal/testprotos" -) - -// This is an sanitized example of equality from a real use-case. -// The original equality function was as follows: -/* -func equalBatch(b1, b2 *GermBatch) bool { - for _, b := range []*GermBatch{b1, b2} { - for _, l := range b.DirtyGerms { - sort.Slice(l, func(i, j int) bool { return l[i].String() < l[j].String() }) - } - for _, l := range b.CleanGerms { - sort.Slice(l, func(i, j int) bool { return l[i].String() < l[j].String() }) - } - } - if !pb.DeepEqual(b1.DirtyGerms, b2.DirtyGerms) || - !pb.DeepEqual(b1.CleanGerms, b2.CleanGerms) || - !pb.DeepEqual(b1.GermMap, b2.GermMap) { - return false - } - if len(b1.DishMap) != len(b2.DishMap) { - return false - } - for id := range b1.DishMap { - kpb1, err1 := b1.DishMap[id].Proto() - kpb2, err2 := b2.DishMap[id].Proto() - if !pb.Equal(kpb1, kpb2) || !reflect.DeepEqual(err1, err2) { - return false - } - } - return b1.HasPreviousResult == b2.HasPreviousResult && - b1.DirtyID == b2.DirtyID && - b1.CleanID == b2.CleanID && - b1.GermStrain == b2.GermStrain && - b1.TotalDirtyGerms == b2.TotalDirtyGerms && - b1.InfectedAt.Equal(b2.InfectedAt) -} -*/ - -type GermBatch struct { - DirtyGerms, CleanGerms map[int32][]*pb.Germ - GermMap map[int32]*pb.Germ - DishMap map[int32]*Dish - HasPreviousResult bool - DirtyID, CleanID int32 - GermStrain int32 - TotalDirtyGerms int - InfectedAt time.Time -} - -type Dish struct { - pb *pb.Dish - err error -} - -func CreateDish(m *pb.Dish, err error) *Dish { - return &Dish{pb: m, err: err} -} - -func (d *Dish) Proto() (*pb.Dish, error) { - if d.err != nil { - return nil, d.err - } - return d.pb, nil -} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/teststructs/project3.go b/vendor/github.com/google/go-cmp/cmp/internal/teststructs/project3.go deleted file mode 100644 index 957d093..0000000 --- a/vendor/github.com/google/go-cmp/cmp/internal/teststructs/project3.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2017, 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.md file. - -package teststructs - -import ( - "sync" - - pb "github.com/google/go-cmp/cmp/internal/testprotos" -) - -// This is an sanitized example of equality from a real use-case. -// The original equality function was as follows: -/* -func equalDirt(x, y *Dirt) bool { - if !reflect.DeepEqual(x.table, y.table) || - !reflect.DeepEqual(x.ts, y.ts) || - x.Discord != y.Discord || - !pb.Equal(&x.Proto, &y.Proto) || - len(x.wizard) != len(y.wizard) || - len(x.sadistic) != len(y.sadistic) || - x.lastTime != y.lastTime { - return false - } - for k, vx := range x.wizard { - vy, ok := y.wizard[k] - if !ok || !pb.Equal(vx, vy) { - return false - } - } - for k, vx := range x.sadistic { - vy, ok := y.sadistic[k] - if !ok || !pb.Equal(vx, vy) { - return false - } - } - return true -} -*/ - -type FakeMutex struct { - sync.Locker - x struct{} -} - -type Dirt struct { - table Table // Always concrete type of MockTable - ts Timestamp - Discord DiscordState - Proto pb.Dirt - wizard map[string]*pb.Wizard - sadistic map[string]*pb.Sadistic - lastTime int64 - mu FakeMutex -} - -type DiscordState int - -type Timestamp int64 - -func (d *Dirt) SetTable(t Table) { d.table = t } -func (d *Dirt) SetTimestamp(t Timestamp) { d.ts = t } -func (d *Dirt) SetWizard(m map[string]*pb.Wizard) { d.wizard = m } -func (d *Dirt) SetSadistic(m map[string]*pb.Sadistic) { d.sadistic = m } -func (d *Dirt) SetLastTime(t int64) { d.lastTime = t } - -type Table interface { - Operation1() error - Operation2() error - Operation3() error -} - -type MockTable struct { - state []string -} - -func CreateMockTable(s []string) *MockTable { return &MockTable{s} } -func (mt *MockTable) Operation1() error { return nil } -func (mt *MockTable) Operation2() error { return nil } -func (mt *MockTable) Operation3() error { return nil } -func (mt *MockTable) State() []string { return mt.state } diff --git a/vendor/github.com/google/go-cmp/cmp/internal/teststructs/project4.go b/vendor/github.com/google/go-cmp/cmp/internal/teststructs/project4.go deleted file mode 100644 index 49920f2..0000000 --- a/vendor/github.com/google/go-cmp/cmp/internal/teststructs/project4.go +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2017, 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.md file. - -package teststructs - -import ( - "time" - - pb "github.com/google/go-cmp/cmp/internal/testprotos" -) - -// This is an sanitized example of equality from a real use-case. -// The original equality function was as follows: -/* -func equalCartel(x, y Cartel) bool { - if !(equalHeadquarter(x.Headquarter, y.Headquarter) && - x.Source() == y.Source() && - x.CreationDate().Equal(y.CreationDate()) && - x.Boss() == y.Boss() && - x.LastCrimeDate().Equal(y.LastCrimeDate())) { - return false - } - if len(x.Poisons()) != len(y.Poisons()) { - return false - } - for i := range x.Poisons() { - if !equalPoison(*x.Poisons()[i], *y.Poisons()[i]) { - return false - } - } - return true -} -func equalHeadquarter(x, y Headquarter) bool { - xr, yr := x.Restrictions(), y.Restrictions() - return x.ID() == y.ID() && - x.Location() == y.Location() && - reflect.DeepEqual(x.SubDivisions(), y.SubDivisions()) && - x.IncorporatedDate().Equal(y.IncorporatedDate()) && - pb.Equal(x.MetaData(), y.MetaData()) && - bytes.Equal(x.PrivateMessage(), y.PrivateMessage()) && - bytes.Equal(x.PublicMessage(), y.PublicMessage()) && - x.HorseBack() == y.HorseBack() && - x.Rattle() == y.Rattle() && - x.Convulsion() == y.Convulsion() && - x.Expansion() == y.Expansion() && - x.Status() == y.Status() && - pb.Equal(&xr, &yr) && - x.CreationTime().Equal(y.CreationTime()) -} -func equalPoison(x, y Poison) bool { - return x.PoisonType() == y.PoisonType() && - x.Expiration().Equal(y.Expiration()) && - x.Manufacturer() == y.Manufacturer() && - x.Potency() == y.Potency() -} -*/ - -type Cartel struct { - Headquarter - source string - creationDate time.Time - boss string - lastCrimeDate time.Time - poisons []*Poison -} - -func (p Cartel) Source() string { return p.source } -func (p Cartel) CreationDate() time.Time { return p.creationDate } -func (p Cartel) Boss() string { return p.boss } -func (p Cartel) LastCrimeDate() time.Time { return p.lastCrimeDate } -func (p Cartel) Poisons() []*Poison { return p.poisons } - -func (p *Cartel) SetSource(x string) { p.source = x } -func (p *Cartel) SetCreationDate(x time.Time) { p.creationDate = x } -func (p *Cartel) SetBoss(x string) { p.boss = x } -func (p *Cartel) SetLastCrimeDate(x time.Time) { p.lastCrimeDate = x } -func (p *Cartel) SetPoisons(x []*Poison) { p.poisons = x } - -type Headquarter struct { - id uint64 - location string - subDivisions []string - incorporatedDate time.Time - metaData *pb.MetaData - privateMessage []byte - publicMessage []byte - horseBack string - rattle string - convulsion bool - expansion uint64 - status pb.HoneyStatus - restrictions pb.Restrictions - creationTime time.Time -} - -func (hq Headquarter) ID() uint64 { return hq.id } -func (hq Headquarter) Location() string { return hq.location } -func (hq Headquarter) SubDivisions() []string { return hq.subDivisions } -func (hq Headquarter) IncorporatedDate() time.Time { return hq.incorporatedDate } -func (hq Headquarter) MetaData() *pb.MetaData { return hq.metaData } -func (hq Headquarter) PrivateMessage() []byte { return hq.privateMessage } -func (hq Headquarter) PublicMessage() []byte { return hq.publicMessage } -func (hq Headquarter) HorseBack() string { return hq.horseBack } -func (hq Headquarter) Rattle() string { return hq.rattle } -func (hq Headquarter) Convulsion() bool { return hq.convulsion } -func (hq Headquarter) Expansion() uint64 { return hq.expansion } -func (hq Headquarter) Status() pb.HoneyStatus { return hq.status } -func (hq Headquarter) Restrictions() pb.Restrictions { return hq.restrictions } -func (hq Headquarter) CreationTime() time.Time { return hq.creationTime } - -func (hq *Headquarter) SetID(x uint64) { hq.id = x } -func (hq *Headquarter) SetLocation(x string) { hq.location = x } -func (hq *Headquarter) SetSubDivisions(x []string) { hq.subDivisions = x } -func (hq *Headquarter) SetIncorporatedDate(x time.Time) { hq.incorporatedDate = x } -func (hq *Headquarter) SetMetaData(x *pb.MetaData) { hq.metaData = x } -func (hq *Headquarter) SetPrivateMessage(x []byte) { hq.privateMessage = x } -func (hq *Headquarter) SetPublicMessage(x []byte) { hq.publicMessage = x } -func (hq *Headquarter) SetHorseBack(x string) { hq.horseBack = x } -func (hq *Headquarter) SetRattle(x string) { hq.rattle = x } -func (hq *Headquarter) SetConvulsion(x bool) { hq.convulsion = x } -func (hq *Headquarter) SetExpansion(x uint64) { hq.expansion = x } -func (hq *Headquarter) SetStatus(x pb.HoneyStatus) { hq.status = x } -func (hq *Headquarter) SetRestrictions(x pb.Restrictions) { hq.restrictions = x } -func (hq *Headquarter) SetCreationTime(x time.Time) { hq.creationTime = x } - -type Poison struct { - poisonType pb.PoisonType - expiration time.Time - manufacturer string - potency int -} - -func (p Poison) PoisonType() pb.PoisonType { return p.poisonType } -func (p Poison) Expiration() time.Time { return p.expiration } -func (p Poison) Manufacturer() string { return p.manufacturer } -func (p Poison) Potency() int { return p.potency } - -func (p *Poison) SetPoisonType(x pb.PoisonType) { p.poisonType = x } -func (p *Poison) SetExpiration(x time.Time) { p.expiration = x } -func (p *Poison) SetManufacturer(x string) { p.manufacturer = x } -func (p *Poison) SetPotency(x int) { p.potency = x } diff --git a/vendor/github.com/google/go-cmp/cmp/internal/teststructs/structs.go b/vendor/github.com/google/go-cmp/cmp/internal/teststructs/structs.go deleted file mode 100644 index 6b4d2a7..0000000 --- a/vendor/github.com/google/go-cmp/cmp/internal/teststructs/structs.go +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright 2017, 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.md file. - -package teststructs - -type InterfaceA interface { - InterfaceA() -} - -type ( - StructA struct{ X string } // Equal method on value receiver - StructB struct{ X string } // Equal method on pointer receiver - StructC struct{ X string } // Equal method (with interface argument) on value receiver - StructD struct{ X string } // Equal method (with interface argument) on pointer receiver - StructE struct{ X string } // Equal method (with interface argument on value receiver) on pointer receiver - StructF struct{ X string } // Equal method (with interface argument on pointer receiver) on value receiver - - // These embed the above types as a value. - StructA1 struct { - StructA - X string - } - StructB1 struct { - StructB - X string - } - StructC1 struct { - StructC - X string - } - StructD1 struct { - StructD - X string - } - StructE1 struct { - StructE - X string - } - StructF1 struct { - StructF - X string - } - - // These embed the above types as a pointer. - StructA2 struct { - *StructA - X string - } - StructB2 struct { - *StructB - X string - } - StructC2 struct { - *StructC - X string - } - StructD2 struct { - *StructD - X string - } - StructE2 struct { - *StructE - X string - } - StructF2 struct { - *StructF - X string - } - - StructNo struct{ X string } // Equal method (with interface argument) on non-satisfying receiver - - AssignA func() int - AssignB struct{ A int } - AssignC chan bool - AssignD <-chan bool -) - -func (x StructA) Equal(y StructA) bool { return true } -func (x *StructB) Equal(y *StructB) bool { return true } -func (x StructC) Equal(y InterfaceA) bool { return true } -func (x StructC) InterfaceA() {} -func (x *StructD) Equal(y InterfaceA) bool { return true } -func (x *StructD) InterfaceA() {} -func (x *StructE) Equal(y InterfaceA) bool { return true } -func (x StructE) InterfaceA() {} -func (x StructF) Equal(y InterfaceA) bool { return true } -func (x *StructF) InterfaceA() {} -func (x StructNo) Equal(y InterfaceA) bool { return true } - -func (x AssignA) Equal(y func() int) bool { return true } -func (x AssignB) Equal(y struct{ A int }) bool { return true } -func (x AssignC) Equal(y chan bool) bool { return true } -func (x AssignD) Equal(y <-chan bool) bool { return true } - -var _ = func( - a StructA, b StructB, c StructC, d StructD, e StructE, f StructF, - ap *StructA, bp *StructB, cp *StructC, dp *StructD, ep *StructE, fp *StructF, - a1 StructA1, b1 StructB1, c1 StructC1, d1 StructD1, e1 StructE1, f1 StructF1, - a2 StructA2, b2 StructB2, c2 StructC2, d2 StructD2, e2 StructE2, f2 StructF1, -) { - a.Equal(a) - b.Equal(&b) - c.Equal(c) - d.Equal(&d) - e.Equal(e) - f.Equal(&f) - - ap.Equal(*ap) - bp.Equal(bp) - cp.Equal(*cp) - dp.Equal(dp) - ep.Equal(*ep) - fp.Equal(fp) - - a1.Equal(a1.StructA) - b1.Equal(&b1.StructB) - c1.Equal(c1) - d1.Equal(&d1) - e1.Equal(e1) - f1.Equal(&f1) - - a2.Equal(*a2.StructA) - b2.Equal(b2.StructB) - c2.Equal(c2) - d2.Equal(&d2) - e2.Equal(e2) - f2.Equal(&f2) -} - -type ( - privateStruct struct{ Public, private int } - PublicStruct struct{ Public, private int } - ParentStructA struct{ privateStruct } - ParentStructB struct{ PublicStruct } - ParentStructC struct { - privateStruct - Public, private int - } - ParentStructD struct { - PublicStruct - Public, private int - } - ParentStructE struct { - privateStruct - PublicStruct - } - ParentStructF struct { - privateStruct - PublicStruct - Public, private int - } - ParentStructG struct { - *privateStruct - } - ParentStructH struct { - *PublicStruct - } - ParentStructI struct { - *privateStruct - *PublicStruct - } - ParentStructJ struct { - *privateStruct - *PublicStruct - Public PublicStruct - private privateStruct - } -) - -func NewParentStructG() *ParentStructG { - return &ParentStructG{new(privateStruct)} -} -func NewParentStructH() *ParentStructH { - return &ParentStructH{new(PublicStruct)} -} -func NewParentStructI() *ParentStructI { - return &ParentStructI{new(privateStruct), new(PublicStruct)} -} -func NewParentStructJ() *ParentStructJ { - return &ParentStructJ{ - privateStruct: new(privateStruct), PublicStruct: new(PublicStruct), - } -} -func (s *privateStruct) SetPrivate(i int) { s.private = i } -func (s *PublicStruct) SetPrivate(i int) { s.private = i } -func (s *ParentStructC) SetPrivate(i int) { s.private = i } -func (s *ParentStructD) SetPrivate(i int) { s.private = i } -func (s *ParentStructF) SetPrivate(i int) { s.private = i } -func (s *ParentStructA) PrivateStruct() *privateStruct { return &s.privateStruct } -func (s *ParentStructC) PrivateStruct() *privateStruct { return &s.privateStruct } -func (s *ParentStructE) PrivateStruct() *privateStruct { return &s.privateStruct } -func (s *ParentStructF) PrivateStruct() *privateStruct { return &s.privateStruct } -func (s *ParentStructG) PrivateStruct() *privateStruct { return s.privateStruct } -func (s *ParentStructI) PrivateStruct() *privateStruct { return s.privateStruct } -func (s *ParentStructJ) PrivateStruct() *privateStruct { return s.privateStruct } -func (s *ParentStructJ) Private() *privateStruct { return &s.private } diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/name.go b/vendor/github.com/google/go-cmp/cmp/internal/value/name.go new file mode 100644 index 0000000..7b498bb --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/internal/value/name.go @@ -0,0 +1,164 @@ +// Copyright 2020, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package value + +import ( + "reflect" + "strconv" +) + +var anyType = reflect.TypeOf((*interface{})(nil)).Elem() + +// TypeString is nearly identical to reflect.Type.String, +// but has an additional option to specify that full type names be used. +func TypeString(t reflect.Type, qualified bool) string { + return string(appendTypeName(nil, t, qualified, false)) +} + +func appendTypeName(b []byte, t reflect.Type, qualified, elideFunc bool) []byte { + // BUG: Go reflection provides no way to disambiguate two named types + // of the same name and within the same package, + // but declared within the namespace of different functions. + + // Use the "any" alias instead of "interface{}" for better readability. + if t == anyType { + return append(b, "any"...) + } + + // Named type. + if t.Name() != "" { + if qualified && t.PkgPath() != "" { + b = append(b, '"') + b = append(b, t.PkgPath()...) + b = append(b, '"') + b = append(b, '.') + b = append(b, t.Name()...) + } else { + b = append(b, t.String()...) + } + return b + } + + // Unnamed type. + switch k := t.Kind(); k { + case reflect.Bool, reflect.String, reflect.UnsafePointer, + reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, + reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: + b = append(b, k.String()...) + case reflect.Chan: + if t.ChanDir() == reflect.RecvDir { + b = append(b, "<-"...) + } + b = append(b, "chan"...) + if t.ChanDir() == reflect.SendDir { + b = append(b, "<-"...) + } + b = append(b, ' ') + b = appendTypeName(b, t.Elem(), qualified, false) + case reflect.Func: + if !elideFunc { + b = append(b, "func"...) + } + b = append(b, '(') + for i := 0; i < t.NumIn(); i++ { + if i > 0 { + b = append(b, ", "...) + } + if i == t.NumIn()-1 && t.IsVariadic() { + b = append(b, "..."...) + b = appendTypeName(b, t.In(i).Elem(), qualified, false) + } else { + b = appendTypeName(b, t.In(i), qualified, false) + } + } + b = append(b, ')') + switch t.NumOut() { + case 0: + // Do nothing + case 1: + b = append(b, ' ') + b = appendTypeName(b, t.Out(0), qualified, false) + default: + b = append(b, " ("...) + for i := 0; i < t.NumOut(); i++ { + if i > 0 { + b = append(b, ", "...) + } + b = appendTypeName(b, t.Out(i), qualified, false) + } + b = append(b, ')') + } + case reflect.Struct: + b = append(b, "struct{ "...) + for i := 0; i < t.NumField(); i++ { + if i > 0 { + b = append(b, "; "...) + } + sf := t.Field(i) + if !sf.Anonymous { + if qualified && sf.PkgPath != "" { + b = append(b, '"') + b = append(b, sf.PkgPath...) + b = append(b, '"') + b = append(b, '.') + } + b = append(b, sf.Name...) + b = append(b, ' ') + } + b = appendTypeName(b, sf.Type, qualified, false) + if sf.Tag != "" { + b = append(b, ' ') + b = strconv.AppendQuote(b, string(sf.Tag)) + } + } + if b[len(b)-1] == ' ' { + b = b[:len(b)-1] + } else { + b = append(b, ' ') + } + b = append(b, '}') + case reflect.Slice, reflect.Array: + b = append(b, '[') + if k == reflect.Array { + b = strconv.AppendUint(b, uint64(t.Len()), 10) + } + b = append(b, ']') + b = appendTypeName(b, t.Elem(), qualified, false) + case reflect.Map: + b = append(b, "map["...) + b = appendTypeName(b, t.Key(), qualified, false) + b = append(b, ']') + b = appendTypeName(b, t.Elem(), qualified, false) + case reflect.Ptr: + b = append(b, '*') + b = appendTypeName(b, t.Elem(), qualified, false) + case reflect.Interface: + b = append(b, "interface{ "...) + for i := 0; i < t.NumMethod(); i++ { + if i > 0 { + b = append(b, "; "...) + } + m := t.Method(i) + if qualified && m.PkgPath != "" { + b = append(b, '"') + b = append(b, m.PkgPath...) + b = append(b, '"') + b = append(b, '.') + } + b = append(b, m.Name...) + b = appendTypeName(b, m.Type, qualified, true) + } + if b[len(b)-1] == ' ' { + b = b[:len(b)-1] + } else { + b = append(b, ' ') + } + b = append(b, '}') + default: + panic("invalid kind: " + k.String()) + } + return b +} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_unsafe.go b/vendor/github.com/google/go-cmp/cmp/internal/value/pointer.go similarity index 70% rename from vendor/github.com/google/go-cmp/cmp/internal/value/pointer_unsafe.go rename to vendor/github.com/google/go-cmp/cmp/internal/value/pointer.go index da134ae..e5dfff6 100644 --- a/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_unsafe.go +++ b/vendor/github.com/google/go-cmp/cmp/internal/value/pointer.go @@ -1,8 +1,6 @@ // Copyright 2018, 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.md file. - -// +build !purego +// license that can be found in the LICENSE file. package value @@ -24,3 +22,13 @@ func PointerOf(v reflect.Value) Pointer { // which is necessary if the GC ever uses a moving collector. return Pointer{unsafe.Pointer(v.Pointer()), v.Type()} } + +// IsNil reports whether the pointer is nil. +func (p Pointer) IsNil() bool { + return p.p == nil +} + +// Uintptr returns the pointer as a uintptr. +func (p Pointer) Uintptr() uintptr { + return uintptr(p.p) +} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go b/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go deleted file mode 100644 index 0a01c47..0000000 --- a/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2018, 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.md file. - -// +build purego - -package value - -import "reflect" - -// Pointer is an opaque typed pointer and is guaranteed to be comparable. -type Pointer struct { - p uintptr - t reflect.Type -} - -// PointerOf returns a Pointer from v, which must be a -// reflect.Ptr, reflect.Slice, or reflect.Map. -func PointerOf(v reflect.Value) Pointer { - // NOTE: Storing a pointer as an uintptr is technically incorrect as it - // assumes that the GC implementation does not use a moving collector. - return Pointer{v.Pointer(), v.Type()} -} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go b/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go index 24fbae6..98533b0 100644 --- a/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go +++ b/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go @@ -1,6 +1,6 @@ // Copyright 2017, 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.md file. +// license that can be found in the LICENSE file. package value diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/sort_test.go b/vendor/github.com/google/go-cmp/cmp/internal/value/sort_test.go deleted file mode 100644 index fb86fce..0000000 --- a/vendor/github.com/google/go-cmp/cmp/internal/value/sort_test.go +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2017, 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.md file. - -package value_test - -import ( - "math" - "reflect" - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/internal/value" -) - -func TestSortKeys(t *testing.T) { - type ( - MyString string - MyArray [2]int - MyStruct struct { - A MyString - B MyArray - C chan float64 - } - EmptyStruct struct{} - ) - - opts := []cmp.Option{ - cmp.Comparer(func(x, y float64) bool { - if math.IsNaN(x) && math.IsNaN(y) { - return true - } - return x == y - }), - cmp.Comparer(func(x, y complex128) bool { - rx, ix, ry, iy := real(x), imag(x), real(y), imag(y) - if math.IsNaN(rx) && math.IsNaN(ry) { - rx, ry = 0, 0 - } - if math.IsNaN(ix) && math.IsNaN(iy) { - ix, iy = 0, 0 - } - return rx == ry && ix == iy - }), - cmp.Comparer(func(x, y chan bool) bool { return true }), - cmp.Comparer(func(x, y chan int) bool { return true }), - cmp.Comparer(func(x, y chan float64) bool { return true }), - cmp.Comparer(func(x, y chan interface{}) bool { return true }), - cmp.Comparer(func(x, y *int) bool { return true }), - } - - tests := []struct { - in map[interface{}]bool // Set of keys to sort - want []interface{} - }{{ - in: map[interface{}]bool{1: true, 2: true, 3: true}, - want: []interface{}{1, 2, 3}, - }, { - in: map[interface{}]bool{ - nil: true, - true: true, - false: true, - -5: true, - -55: true, - -555: true, - uint(1): true, - uint(11): true, - uint(111): true, - "abc": true, - "abcd": true, - "abcde": true, - "foo": true, - "bar": true, - MyString("abc"): true, - MyString("abcd"): true, - MyString("abcde"): true, - new(int): true, - new(int): true, - make(chan bool): true, - make(chan bool): true, - make(chan int): true, - make(chan interface{}): true, - math.Inf(+1): true, - math.Inf(-1): true, - 1.2345: true, - 12.345: true, - 123.45: true, - 1234.5: true, - 0 + 0i: true, - 1 + 0i: true, - 2 + 0i: true, - 0 + 1i: true, - 0 + 2i: true, - 0 + 3i: true, - [2]int{2, 3}: true, - [2]int{4, 0}: true, - [2]int{2, 4}: true, - MyArray([2]int{2, 4}): true, - EmptyStruct{}: true, - MyStruct{ - "bravo", [2]int{2, 3}, make(chan float64), - }: true, - MyStruct{ - "alpha", [2]int{3, 3}, make(chan float64), - }: true, - }, - want: []interface{}{ - nil, false, true, - -555, -55, -5, uint(1), uint(11), uint(111), - math.Inf(-1), 1.2345, 12.345, 123.45, 1234.5, math.Inf(+1), - (0 + 0i), (0 + 1i), (0 + 2i), (0 + 3i), (1 + 0i), (2 + 0i), - [2]int{2, 3}, [2]int{2, 4}, [2]int{4, 0}, MyArray([2]int{2, 4}), - make(chan bool), make(chan bool), make(chan int), make(chan interface{}), - new(int), new(int), - "abc", "abcd", "abcde", "bar", "foo", - MyString("abc"), MyString("abcd"), MyString("abcde"), - EmptyStruct{}, - MyStruct{"alpha", [2]int{3, 3}, make(chan float64)}, - MyStruct{"bravo", [2]int{2, 3}, make(chan float64)}, - }, - }, { - // NaN values cannot be properly deduplicated. - // This is okay since map entries with NaN in the keys cannot be - // retrieved anyways. - in: map[interface{}]bool{ - math.NaN(): true, - math.NaN(): true, - complex(0, math.NaN()): true, - complex(0, math.NaN()): true, - complex(math.NaN(), 0): true, - complex(math.NaN(), 0): true, - complex(math.NaN(), math.NaN()): true, - }, - want: []interface{}{ - math.NaN(), - complex(math.NaN(), math.NaN()), - complex(math.NaN(), 0), - complex(0, math.NaN()), - }, - }} - - for i, tt := range tests { - // Intentionally pass the map via an unexported field to detect panics. - // Unfortunately, we cannot actually test the keys without using unsafe. - v := reflect.ValueOf(struct{ x map[interface{}]bool }{tt.in}).Field(0) - value.SortKeys(append(v.MapKeys(), v.MapKeys()...)) - - // Try again, with keys that have read-write access in reflect. - v = reflect.ValueOf(tt.in) - keys := append(v.MapKeys(), v.MapKeys()...) - var got []interface{} - for _, k := range value.SortKeys(keys) { - got = append(got, k.Interface()) - } - if d := cmp.Diff(got, tt.want, opts...); d != "" { - t.Errorf("test %d, Sort() mismatch (-got +want):\n%s", i, d) - } - } -} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/zero.go b/vendor/github.com/google/go-cmp/cmp/internal/value/zero.go deleted file mode 100644 index 06a8ffd..0000000 --- a/vendor/github.com/google/go-cmp/cmp/internal/value/zero.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2017, 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.md file. - -package value - -import ( - "math" - "reflect" -) - -// IsZero reports whether v is the zero value. -// This does not rely on Interface and so can be used on unexported fields. -func IsZero(v reflect.Value) bool { - switch v.Kind() { - case reflect.Bool: - return v.Bool() == false - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return v.Int() == 0 - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return v.Uint() == 0 - case reflect.Float32, reflect.Float64: - return math.Float64bits(v.Float()) == 0 - case reflect.Complex64, reflect.Complex128: - return math.Float64bits(real(v.Complex())) == 0 && math.Float64bits(imag(v.Complex())) == 0 - case reflect.String: - return v.String() == "" - case reflect.UnsafePointer: - return v.Pointer() == 0 - case reflect.Chan, reflect.Func, reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice: - return v.IsNil() - case reflect.Array: - for i := 0; i < v.Len(); i++ { - if !IsZero(v.Index(i)) { - return false - } - } - return true - case reflect.Struct: - for i := 0; i < v.NumField(); i++ { - if !IsZero(v.Field(i)) { - return false - } - } - return true - } - return false -} diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/zero_test.go b/vendor/github.com/google/go-cmp/cmp/internal/value/zero_test.go deleted file mode 100644 index 1d6c434..0000000 --- a/vendor/github.com/google/go-cmp/cmp/internal/value/zero_test.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2019, 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.md file. - -package value - -import ( - "archive/tar" - "math" - "reflect" - "testing" -) - -func TestIsZero(t *testing.T) { - tests := []struct { - in interface{} - want bool - }{ - {0, true}, - {1, false}, - {"", true}, - {"foo", false}, - {[]byte(nil), true}, - {[]byte{}, false}, - {map[string]bool(nil), true}, - {map[string]bool{}, false}, - {tar.Header{}, true}, - {&tar.Header{}, false}, - {tar.Header{Name: "foo"}, false}, - {(chan bool)(nil), true}, - {make(chan bool), false}, - {(func(*testing.T))(nil), true}, - {TestIsZero, false}, - {[...]int{0, 0, 0}, true}, - {[...]int{0, 1, 0}, false}, - {math.Copysign(0, +1), true}, - {math.Copysign(0, -1), false}, - {complex(math.Copysign(0, +1), math.Copysign(0, +1)), true}, - {complex(math.Copysign(0, -1), math.Copysign(0, +1)), false}, - {complex(math.Copysign(0, +1), math.Copysign(0, -1)), false}, - {complex(math.Copysign(0, -1), math.Copysign(0, -1)), false}, - } - - for _, tt := range tests { - t.Run("", func(t *testing.T) { - got := IsZero(reflect.ValueOf(tt.in)) - if got != tt.want { - t.Errorf("IsZero(%v) = %v, want %v", tt.in, got, tt.want) - } - }) - } -} diff --git a/vendor/github.com/google/go-cmp/cmp/options.go b/vendor/github.com/google/go-cmp/cmp/options.go index 7934481..754496f 100644 --- a/vendor/github.com/google/go-cmp/cmp/options.go +++ b/vendor/github.com/google/go-cmp/cmp/options.go @@ -1,6 +1,6 @@ // Copyright 2017, 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.md file. +// license that can be found in the LICENSE file. package cmp @@ -13,15 +13,15 @@ import ( "github.com/google/go-cmp/cmp/internal/function" ) -// Option configures for specific behavior of Equal and Diff. In particular, -// the fundamental Option functions (Ignore, Transformer, and Comparer), +// Option configures for specific behavior of [Equal] and [Diff]. In particular, +// the fundamental Option functions ([Ignore], [Transformer], and [Comparer]), // configure how equality is determined. // -// The fundamental options may be composed with filters (FilterPath and -// FilterValues) to control the scope over which they are applied. +// The fundamental options may be composed with filters ([FilterPath] and +// [FilterValues]) to control the scope over which they are applied. // -// The cmp/cmpopts package provides helper functions for creating options that -// may be used with Equal and Diff. +// The [github.com/google/go-cmp/cmp/cmpopts] package provides helper functions +// for creating options that may be used with [Equal] and [Diff]. type Option interface { // filter applies all filters and returns the option that remains. // Each option may only read s.curPath and call s.callTTBFunc. @@ -33,6 +33,7 @@ type Option interface { } // applicableOption represents the following types: +// // Fundamental: ignore | validator | *comparer | *transformer // Grouping: Options type applicableOption interface { @@ -43,6 +44,7 @@ type applicableOption interface { } // coreOption represents the following types: +// // Fundamental: ignore | validator | *comparer | *transformer // Filters: *pathFilter | *valuesFilter type coreOption interface { @@ -54,9 +56,9 @@ type core struct{} func (core) isCore() {} -// Options is a list of Option values that also satisfies the Option interface. +// Options is a list of [Option] values that also satisfies the [Option] interface. // Helper comparison packages may return an Options value when packing multiple -// Option values into a single Option. When this package processes an Options, +// [Option] values into a single [Option]. When this package processes an Options, // it will be implicitly expanded into a flat list. // // Applying a filter on an Options is equivalent to applying that same filter @@ -103,16 +105,16 @@ func (opts Options) String() string { return fmt.Sprintf("Options{%s}", strings.Join(ss, ", ")) } -// FilterPath returns a new Option where opt is only evaluated if filter f -// returns true for the current Path in the value tree. +// FilterPath returns a new [Option] where opt is only evaluated if filter f +// returns true for the current [Path] in the value tree. // // This filter is called even if a slice element or map entry is missing and // provides an opportunity to ignore such cases. The filter function must be // symmetric such that the filter result is identical regardless of whether the // missing value is from x or y. // -// The option passed in may be an Ignore, Transformer, Comparer, Options, or -// a previously filtered Option. +// The option passed in may be an [Ignore], [Transformer], [Comparer], [Options], or +// a previously filtered [Option]. func FilterPath(f func(Path) bool, opt Option) Option { if f == nil { panic("invalid path filter function") @@ -140,7 +142,7 @@ func (f pathFilter) String() string { return fmt.Sprintf("FilterPath(%s, %v)", function.NameOf(reflect.ValueOf(f.fnc)), f.opt) } -// FilterValues returns a new Option where opt is only evaluated if filter f, +// FilterValues returns a new [Option] where opt is only evaluated if filter f, // which is a function of the form "func(T, T) bool", returns true for the // current pair of values being compared. If either value is invalid or // the type of the values is not assignable to T, then this filter implicitly @@ -152,8 +154,8 @@ func (f pathFilter) String() string { // If T is an interface, it is possible that f is called with two values with // different concrete types that both implement T. // -// The option passed in may be an Ignore, Transformer, Comparer, Options, or -// a previously filtered Option. +// The option passed in may be an [Ignore], [Transformer], [Comparer], [Options], or +// a previously filtered [Option]. func FilterValues(f interface{}, opt Option) Option { v := reflect.ValueOf(f) if !function.IsType(v.Type(), function.ValueFilter) || v.IsNil() { @@ -190,9 +192,9 @@ func (f valuesFilter) String() string { return fmt.Sprintf("FilterValues(%s, %v)", function.NameOf(f.fnc), f.opt) } -// Ignore is an Option that causes all comparisons to be ignored. -// This value is intended to be combined with FilterPath or FilterValues. -// It is an error to pass an unfiltered Ignore option to Equal. +// Ignore is an [Option] that causes all comparisons to be ignored. +// This value is intended to be combined with [FilterPath] or [FilterValues]. +// It is an error to pass an unfiltered Ignore option to [Equal]. func Ignore() Option { return ignore{} } type ignore struct{ core } @@ -225,8 +227,25 @@ func (validator) apply(s *state, vx, vy reflect.Value) { // Unable to Interface implies unexported field without visibility access. if !vx.CanInterface() || !vy.CanInterface() { - const help = "consider using a custom Comparer; if you control the implementation of type, you can also consider AllowUnexported or cmpopts.IgnoreUnexported" - panic(fmt.Sprintf("cannot handle unexported field: %#v\n%s", s.curPath, help)) + help := "consider using a custom Comparer; if you control the implementation of type, you can also consider using an Exporter, AllowUnexported, or cmpopts.IgnoreUnexported" + var name string + if t := s.curPath.Index(-2).Type(); t.Name() != "" { + // Named type with unexported fields. + name = fmt.Sprintf("%q.%v", t.PkgPath(), t.Name()) // e.g., "path/to/package".MyType + if _, ok := reflect.New(t).Interface().(error); ok { + help = "consider using cmpopts.EquateErrors to compare error values" + } else if t.Comparable() { + help = "consider using cmpopts.EquateComparable to compare comparable Go types" + } + } else { + // Unnamed type with unexported fields. Derive PkgPath from field. + var pkgPath string + for i := 0; i < t.NumField() && pkgPath == ""; i++ { + pkgPath = t.Field(i).PkgPath + } + name = fmt.Sprintf("%q.(%v)", pkgPath, t.String()) // e.g., "path/to/package".(struct { a int }) + } + panic(fmt.Sprintf("cannot handle unexported field at %#v:\n\t%v\n%s", s.curPath, name, help)) } panic("not reachable") @@ -237,7 +256,7 @@ const identRx = `[_\p{L}][_\p{L}\p{N}]*` var identsRx = regexp.MustCompile(`^` + identRx + `(\.` + identRx + `)*$`) -// Transformer returns an Option that applies a transformation function that +// Transformer returns an [Option] that applies a transformation function that // converts values of a certain type into that of another. // // The transformer f must be a function "func(T) R" that converts values of @@ -248,13 +267,14 @@ var identsRx = regexp.MustCompile(`^` + identRx + `(\.` + identRx + `)*$`) // same transform to the output of itself (e.g., in the case where the // input and output types are the same), an implicit filter is added such that // a transformer is applicable only if that exact transformer is not already -// in the tail of the Path since the last non-Transform step. +// in the tail of the [Path] since the last non-[Transform] step. // For situations where the implicit filter is still insufficient, -// consider using cmpopts.AcyclicTransformer, which adds a filter -// to prevent the transformer from being recursively applied upon itself. +// consider using [github.com/google/go-cmp/cmp/cmpopts.AcyclicTransformer], +// which adds a filter to prevent the transformer from +// being recursively applied upon itself. // -// The name is a user provided label that is used as the Transform.Name in the -// transformation PathStep (and eventually shown in the Diff output). +// The name is a user provided label that is used as the [Transform.Name] in the +// transformation [PathStep] (and eventually shown in the [Diff] output). // The name must be a valid identifier or qualified identifier in Go syntax. // If empty, an arbitrary name is used. func Transformer(name string, f interface{}) Option { @@ -312,7 +332,7 @@ func (tr transformer) String() string { return fmt.Sprintf("Transformer(%s, %s)", tr.name, function.NameOf(tr.fnc)) } -// Comparer returns an Option that determines whether two values are equal +// Comparer returns an [Option] that determines whether two values are equal // to each other. // // The comparer f must be a function "func(T, T) bool" and is implicitly @@ -321,9 +341,9 @@ func (tr transformer) String() string { // both implement T. // // The equality function must be: -// • Symmetric: equal(x, y) == equal(y, x) -// • Deterministic: equal(x, y) == equal(x, y) -// • Pure: equal(x, y) does not modify x or y +// - Symmetric: equal(x, y) == equal(y, x) +// - Deterministic: equal(x, y) == equal(x, y) +// - Pure: equal(x, y) does not modify x or y func Comparer(f interface{}) Option { v := reflect.ValueOf(f) if !function.IsType(v.Type(), function.Equal) || v.IsNil() { @@ -360,36 +380,46 @@ func (cm comparer) String() string { return fmt.Sprintf("Comparer(%s)", function.NameOf(cm.fnc)) } -// AllowUnexported returns an Option that forcibly allows operations on -// unexported fields in certain structs, which are specified by passing in a -// value of each struct type. +// Exporter returns an [Option] that specifies whether [Equal] is allowed to +// introspect into the unexported fields of certain struct types. // // Users of this option must understand that comparing on unexported fields // from external packages is not safe since changes in the internal -// implementation of some external package may cause the result of Equal +// implementation of some external package may cause the result of [Equal] // to unexpectedly change. However, it may be valid to use this option on types // defined in an internal package where the semantic meaning of an unexported // field is in the control of the user. // -// In many cases, a custom Comparer should be used instead that defines +// In many cases, a custom [Comparer] should be used instead that defines // equality as a function of the public API of a type rather than the underlying // unexported implementation. // -// For example, the reflect.Type documentation defines equality to be determined +// For example, the [reflect.Type] documentation defines equality to be determined // by the == operator on the interface (essentially performing a shallow pointer -// comparison) and most attempts to compare *regexp.Regexp types are interested +// comparison) and most attempts to compare *[regexp.Regexp] types are interested // in only checking that the regular expression strings are equal. -// Both of these are accomplished using Comparers: +// Both of these are accomplished using [Comparer] options: // // Comparer(func(x, y reflect.Type) bool { return x == y }) // Comparer(func(x, y *regexp.Regexp) bool { return x.String() == y.String() }) // -// In other cases, the cmpopts.IgnoreUnexported option can be used to ignore -// all unexported fields on specified struct types. +// In other cases, the [github.com/google/go-cmp/cmp/cmpopts.IgnoreUnexported] +// option can be used to ignore all unexported fields on specified struct types. +func Exporter(f func(reflect.Type) bool) Option { + return exporter(f) +} + +type exporter func(reflect.Type) bool + +func (exporter) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableOption { + panic("not implemented") +} + +// AllowUnexported returns an [Option] that allows [Equal] to forcibly introspect +// unexported fields of the specified struct types. +// +// See [Exporter] for the proper use of this option. func AllowUnexported(types ...interface{}) Option { - if !supportAllowUnexported { - panic("AllowUnexported is not supported on purego builds, Google App Engine Standard, or GopherJS") - } m := make(map[reflect.Type]bool) for _, typ := range types { t := reflect.TypeOf(typ) @@ -398,17 +428,11 @@ func AllowUnexported(types ...interface{}) Option { } m[t] = true } - return visibleStructs(m) -} - -type visibleStructs map[reflect.Type]bool - -func (visibleStructs) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableOption { - panic("not implemented") + return exporter(func(t reflect.Type) bool { return m[t] }) } // Result represents the comparison result for a single node and -// is provided by cmp when calling Result (see Reporter). +// is provided by cmp when calling Report (see [Reporter]). type Result struct { _ [0]func() // Make Result incomparable flags resultFlags @@ -421,7 +445,7 @@ func (r Result) Equal() bool { } // ByIgnore reports whether the node is equal because it was ignored. -// This never reports true if Equal reports false. +// This never reports true if [Result.Equal] reports false. func (r Result) ByIgnore() bool { return r.flags&reportByIgnore != 0 } @@ -431,11 +455,16 @@ func (r Result) ByMethod() bool { return r.flags&reportByMethod != 0 } -// ByFunc reports whether a Comparer function determined equality. +// ByFunc reports whether a [Comparer] function determined equality. func (r Result) ByFunc() bool { return r.flags&reportByFunc != 0 } +// ByCycle reports whether a reference cycle was detected. +func (r Result) ByCycle() bool { + return r.flags&reportByCycle != 0 +} + type resultFlags uint const ( @@ -446,9 +475,10 @@ const ( reportByIgnore reportByMethod reportByFunc + reportByCycle ) -// Reporter is an Option that can be passed to Equal. When Equal traverses +// Reporter is an [Option] that can be passed to [Equal]. When [Equal] traverses // the value trees, it calls PushStep as it descends into each node in the // tree and PopStep as it ascend out of the node. The leaves of the tree are // either compared (determined to be equal or not equal) or ignored and reported diff --git a/vendor/github.com/google/go-cmp/cmp/options_test.go b/vendor/github.com/google/go-cmp/cmp/options_test.go deleted file mode 100644 index f8066c7..0000000 --- a/vendor/github.com/google/go-cmp/cmp/options_test.go +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright 2017, 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.md file. - -package cmp - -import ( - "io" - "reflect" - "strings" - "testing" - - ts "github.com/google/go-cmp/cmp/internal/teststructs" -) - -// Test that the creation of Option values with non-sensible inputs produces -// a run-time panic with a decent error message -func TestOptionPanic(t *testing.T) { - type myBool bool - tests := []struct { - label string // Test description - fnc interface{} // Option function to call - args []interface{} // Arguments to pass in - wantPanic string // Expected panic message - }{{ - label: "AllowUnexported", - fnc: AllowUnexported, - args: []interface{}{}, - }, { - label: "AllowUnexported", - fnc: AllowUnexported, - args: []interface{}{1}, - wantPanic: "invalid struct type", - }, { - label: "AllowUnexported", - fnc: AllowUnexported, - args: []interface{}{ts.StructA{}}, - }, { - label: "AllowUnexported", - fnc: AllowUnexported, - args: []interface{}{ts.StructA{}, ts.StructB{}, ts.StructA{}}, - }, { - label: "AllowUnexported", - fnc: AllowUnexported, - args: []interface{}{ts.StructA{}, &ts.StructB{}, ts.StructA{}}, - wantPanic: "invalid struct type", - }, { - label: "Comparer", - fnc: Comparer, - args: []interface{}{5}, - wantPanic: "invalid comparer function", - }, { - label: "Comparer", - fnc: Comparer, - args: []interface{}{func(x, y interface{}) bool { return true }}, - }, { - label: "Comparer", - fnc: Comparer, - args: []interface{}{func(x, y io.Reader) bool { return true }}, - }, { - label: "Comparer", - fnc: Comparer, - args: []interface{}{func(x, y io.Reader) myBool { return true }}, - wantPanic: "invalid comparer function", - }, { - label: "Comparer", - fnc: Comparer, - args: []interface{}{func(x string, y interface{}) bool { return true }}, - wantPanic: "invalid comparer function", - }, { - label: "Comparer", - fnc: Comparer, - args: []interface{}{(func(int, int) bool)(nil)}, - wantPanic: "invalid comparer function", - }, { - label: "Transformer", - fnc: Transformer, - args: []interface{}{"", 0}, - wantPanic: "invalid transformer function", - }, { - label: "Transformer", - fnc: Transformer, - args: []interface{}{"", func(int) int { return 0 }}, - }, { - label: "Transformer", - fnc: Transformer, - args: []interface{}{"", func(bool) bool { return true }}, - }, { - label: "Transformer", - fnc: Transformer, - args: []interface{}{"", func(int) bool { return true }}, - }, { - label: "Transformer", - fnc: Transformer, - args: []interface{}{"", func(int, int) bool { return true }}, - wantPanic: "invalid transformer function", - }, { - label: "Transformer", - fnc: Transformer, - args: []interface{}{"", (func(int) uint)(nil)}, - wantPanic: "invalid transformer function", - }, { - label: "Transformer", - fnc: Transformer, - args: []interface{}{"Func", func(Path) Path { return nil }}, - }, { - label: "Transformer", - fnc: Transformer, - args: []interface{}{"世界", func(int) bool { return true }}, - }, { - label: "Transformer", - fnc: Transformer, - args: []interface{}{"/*", func(int) bool { return true }}, - wantPanic: "invalid name", - }, { - label: "Transformer", - fnc: Transformer, - args: []interface{}{"_", func(int) bool { return true }}, - }, { - label: "FilterPath", - fnc: FilterPath, - args: []interface{}{(func(Path) bool)(nil), Ignore()}, - wantPanic: "invalid path filter function", - }, { - label: "FilterPath", - fnc: FilterPath, - args: []interface{}{func(Path) bool { return true }, Ignore()}, - }, { - label: "FilterPath", - fnc: FilterPath, - args: []interface{}{func(Path) bool { return true }, Reporter(&defaultReporter{})}, - wantPanic: "invalid option type", - }, { - label: "FilterPath", - fnc: FilterPath, - args: []interface{}{func(Path) bool { return true }, Options{Ignore(), Ignore()}}, - }, { - label: "FilterPath", - fnc: FilterPath, - args: []interface{}{func(Path) bool { return true }, Options{Ignore(), Reporter(&defaultReporter{})}}, - wantPanic: "invalid option type", - }, { - label: "FilterValues", - fnc: FilterValues, - args: []interface{}{0, Ignore()}, - wantPanic: "invalid values filter function", - }, { - label: "FilterValues", - fnc: FilterValues, - args: []interface{}{func(x, y int) bool { return true }, Ignore()}, - }, { - label: "FilterValues", - fnc: FilterValues, - args: []interface{}{func(x, y interface{}) bool { return true }, Ignore()}, - }, { - label: "FilterValues", - fnc: FilterValues, - args: []interface{}{func(x, y interface{}) myBool { return true }, Ignore()}, - wantPanic: "invalid values filter function", - }, { - label: "FilterValues", - fnc: FilterValues, - args: []interface{}{func(x io.Reader, y interface{}) bool { return true }, Ignore()}, - wantPanic: "invalid values filter function", - }, { - label: "FilterValues", - fnc: FilterValues, - args: []interface{}{(func(int, int) bool)(nil), Ignore()}, - wantPanic: "invalid values filter function", - }, { - label: "FilterValues", - fnc: FilterValues, - args: []interface{}{func(int, int) bool { return true }, Reporter(&defaultReporter{})}, - wantPanic: "invalid option type", - }, { - label: "FilterValues", - fnc: FilterValues, - args: []interface{}{func(int, int) bool { return true }, Options{Ignore(), Ignore()}}, - }, { - label: "FilterValues", - fnc: FilterValues, - args: []interface{}{func(int, int) bool { return true }, Options{Ignore(), Reporter(&defaultReporter{})}}, - wantPanic: "invalid option type", - }} - - for _, tt := range tests { - t.Run(tt.label, func(t *testing.T) { - var gotPanic string - func() { - defer func() { - if ex := recover(); ex != nil { - if s, ok := ex.(string); ok { - gotPanic = s - } else { - panic(ex) - } - } - }() - var vargs []reflect.Value - for _, arg := range tt.args { - vargs = append(vargs, reflect.ValueOf(arg)) - } - reflect.ValueOf(tt.fnc).Call(vargs) - }() - if tt.wantPanic == "" { - if gotPanic != "" { - t.Fatalf("unexpected panic message: %s", gotPanic) - } - } else { - if !strings.Contains(gotPanic, tt.wantPanic) { - t.Fatalf("panic message:\ngot: %s\nwant: %s", gotPanic, tt.wantPanic) - } - } - }) - } -} diff --git a/vendor/github.com/google/go-cmp/cmp/path.go b/vendor/github.com/google/go-cmp/cmp/path.go index 96fffd2..c3c1456 100644 --- a/vendor/github.com/google/go-cmp/cmp/path.go +++ b/vendor/github.com/google/go-cmp/cmp/path.go @@ -1,6 +1,6 @@ // Copyright 2017, 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.md file. +// license that can be found in the LICENSE file. package cmp @@ -10,11 +10,13 @@ import ( "strings" "unicode" "unicode/utf8" + + "github.com/google/go-cmp/cmp/internal/value" ) -// Path is a list of PathSteps describing the sequence of operations to get +// Path is a list of [PathStep] describing the sequence of operations to get // from some root type to the current position in the value tree. -// The first Path element is always an operation-less PathStep that exists +// The first Path element is always an operation-less [PathStep] that exists // simply to identify the initial type. // // When traversing structs with embedded structs, the embedded struct will @@ -27,8 +29,13 @@ type Path []PathStep // a value's tree structure. Users of this package never need to implement // these types as values of this type will be returned by this package. // -// Implementations of this interface are -// StructField, SliceIndex, MapIndex, Indirect, TypeAssertion, and Transform. +// Implementations of this interface: +// - [StructField] +// - [SliceIndex] +// - [MapIndex] +// - [Indirect] +// - [TypeAssertion] +// - [Transform] type PathStep interface { String() string @@ -39,13 +46,13 @@ type PathStep interface { // The type of each valid value is guaranteed to be identical to Type. // // In some cases, one or both may be invalid or have restrictions: - // • For StructField, both are not interface-able if the current field - // is unexported and the struct type is not explicitly permitted by - // AllowUnexported to traverse unexported fields. - // • For SliceIndex, one may be invalid if an element is missing from - // either the x or y slice. - // • For MapIndex, one may be invalid if an entry is missing from - // either the x or y map. + // - For StructField, both are not interface-able if the current field + // is unexported and the struct type is not explicitly permitted by + // an Exporter to traverse unexported fields. + // - For SliceIndex, one may be invalid if an element is missing from + // either the x or y slice. + // - For MapIndex, one may be invalid if an entry is missing from + // either the x or y map. // // The provided values must not be mutated. Values() (vx, vy reflect.Value) @@ -68,8 +75,9 @@ func (pa *Path) pop() { *pa = (*pa)[:len(*pa)-1] } -// Last returns the last PathStep in the Path. -// If the path is empty, this returns a non-nil PathStep that reports a nil Type. +// Last returns the last [PathStep] in the Path. +// If the path is empty, this returns a non-nil [PathStep] +// that reports a nil [PathStep.Type]. func (pa Path) Last() PathStep { return pa.Index(-1) } @@ -77,7 +85,8 @@ func (pa Path) Last() PathStep { // Index returns the ith step in the Path and supports negative indexing. // A negative index starts counting from the tail of the Path such that -1 // refers to the last step, -2 refers to the second-to-last step, and so on. -// If index is invalid, this returns a non-nil PathStep that reports a nil Type. +// If index is invalid, this returns a non-nil [PathStep] +// that reports a nil [PathStep.Type]. func (pa Path) Index(i int) PathStep { if i < 0 { i = len(pa) + i @@ -92,6 +101,7 @@ func (pa Path) Index(i int) PathStep { // The simplified path only contains struct field accesses. // // For example: +// // MyMap.MySlices.MyField func (pa Path) String() string { var ss []string @@ -106,6 +116,7 @@ func (pa Path) String() string { // GoString returns the path to a specific node using Go syntax. // // For example: +// // (*root.MyMap["key"].(*mypkg.MyStruct).MySlices)[2][3].MyField func (pa Path) GoString() string { var ssPre, ssPost []string @@ -157,14 +168,15 @@ func (ps pathStep) String() string { if ps.typ == nil { return "" } - s := ps.typ.String() + s := value.TypeString(ps.typ, false) if s == "" || strings.ContainsAny(s, "{}\n") { return "root" // Type too simple or complex to print } return fmt.Sprintf("{%s}", s) } -// StructField represents a struct field access on a field called Name. +// StructField is a [PathStep] that represents a struct field access +// on a field called [StructField.Name]. type StructField struct{ *structField } type structField struct { pathStep @@ -175,7 +187,8 @@ type structField struct { // pvx, pvy, and field are only valid if unexported is true. unexported bool mayForce bool // Forcibly allow visibility - pvx, pvy reflect.Value // Parent values + paddr bool // Was parent addressable? + pvx, pvy reflect.Value // Parent values (always addressable) field reflect.StructField // Field information } @@ -187,8 +200,8 @@ func (sf StructField) Values() (vx, vy reflect.Value) { // Forcibly obtain read-write access to an unexported struct field. if sf.mayForce { - vx = retrieveUnexportedField(sf.pvx, sf.field) - vy = retrieveUnexportedField(sf.pvy, sf.field) + vx = retrieveUnexportedField(sf.pvx, sf.field, sf.paddr) + vy = retrieveUnexportedField(sf.pvy, sf.field, sf.paddr) return vx, vy // CanInterface reports true } return sf.vx, sf.vy // CanInterface reports false @@ -199,14 +212,16 @@ func (sf StructField) String() string { return fmt.Sprintf(".%s", sf.name) } func (sf StructField) Name() string { return sf.name } // Index is the index of the field in the parent struct type. -// See reflect.Type.Field. +// See [reflect.Type.Field]. func (sf StructField) Index() int { return sf.idx } -// SliceIndex is an index operation on a slice or array at some index Key. +// SliceIndex is a [PathStep] that represents an index operation on +// a slice or array at some index [SliceIndex.Key]. type SliceIndex struct{ *sliceIndex } type sliceIndex struct { pathStep xkey, ykey int + isSlice bool // False for reflect.Array } func (si SliceIndex) Type() reflect.Type { return si.typ } @@ -241,12 +256,12 @@ func (si SliceIndex) Key() int { // all of the indexes to be shifted. If an index is -1, then that // indicates that the element does not exist in the associated slice. // -// Key is guaranteed to return -1 if and only if the indexes returned -// by SplitKeys are not the same. SplitKeys will never return -1 for +// [SliceIndex.Key] is guaranteed to return -1 if and only if the indexes +// returned by SplitKeys are not the same. SplitKeys will never return -1 for // both indexes. func (si SliceIndex) SplitKeys() (ix, iy int) { return si.xkey, si.ykey } -// MapIndex is an index operation on a map at some index Key. +// MapIndex is a [PathStep] that represents an index operation on a map at some index Key. type MapIndex struct{ *mapIndex } type mapIndex struct { pathStep @@ -260,7 +275,7 @@ func (mi MapIndex) String() string { return fmt.Sprintf("[%#v]", // Key is the value of the map key. func (mi MapIndex) Key() reflect.Value { return mi.key } -// Indirect represents pointer indirection on the parent type. +// Indirect is a [PathStep] that represents pointer indirection on the parent type. type Indirect struct{ *indirect } type indirect struct { pathStep @@ -270,7 +285,7 @@ func (in Indirect) Type() reflect.Type { return in.typ } func (in Indirect) Values() (vx, vy reflect.Value) { return in.vx, in.vy } func (in Indirect) String() string { return "*" } -// TypeAssertion represents a type assertion on an interface. +// TypeAssertion is a [PathStep] that represents a type assertion on an interface. type TypeAssertion struct{ *typeAssertion } type typeAssertion struct { pathStep @@ -278,9 +293,10 @@ type typeAssertion struct { func (ta TypeAssertion) Type() reflect.Type { return ta.typ } func (ta TypeAssertion) Values() (vx, vy reflect.Value) { return ta.vx, ta.vy } -func (ta TypeAssertion) String() string { return fmt.Sprintf(".(%v)", ta.typ) } +func (ta TypeAssertion) String() string { return fmt.Sprintf(".(%v)", value.TypeString(ta.typ, false)) } -// Transform is a transformation from the parent type to the current type. +// Transform is a [PathStep] that represents a transformation +// from the parent type to the current type. type Transform struct{ *transform } type transform struct { pathStep @@ -291,16 +307,82 @@ func (tf Transform) Type() reflect.Type { return tf.typ } func (tf Transform) Values() (vx, vy reflect.Value) { return tf.vx, tf.vy } func (tf Transform) String() string { return fmt.Sprintf("%s()", tf.trans.name) } -// Name is the name of the Transformer. +// Name is the name of the [Transformer]. func (tf Transform) Name() string { return tf.trans.name } // Func is the function pointer to the transformer function. func (tf Transform) Func() reflect.Value { return tf.trans.fnc } -// Option returns the originally constructed Transformer option. +// Option returns the originally constructed [Transformer] option. // The == operator can be used to detect the exact option used. func (tf Transform) Option() Option { return tf.trans } +// pointerPath represents a dual-stack of pointers encountered when +// recursively traversing the x and y values. This data structure supports +// detection of cycles and determining whether the cycles are equal. +// In Go, cycles can occur via pointers, slices, and maps. +// +// The pointerPath uses a map to represent a stack; where descension into a +// pointer pushes the address onto the stack, and ascension from a pointer +// pops the address from the stack. Thus, when traversing into a pointer from +// reflect.Ptr, reflect.Slice element, or reflect.Map, we can detect cycles +// by checking whether the pointer has already been visited. The cycle detection +// uses a separate stack for the x and y values. +// +// If a cycle is detected we need to determine whether the two pointers +// should be considered equal. The definition of equality chosen by Equal +// requires two graphs to have the same structure. To determine this, both the +// x and y values must have a cycle where the previous pointers were also +// encountered together as a pair. +// +// Semantically, this is equivalent to augmenting Indirect, SliceIndex, and +// MapIndex with pointer information for the x and y values. +// Suppose px and py are two pointers to compare, we then search the +// Path for whether px was ever encountered in the Path history of x, and +// similarly so with py. If either side has a cycle, the comparison is only +// equal if both px and py have a cycle resulting from the same PathStep. +// +// Using a map as a stack is more performant as we can perform cycle detection +// in O(1) instead of O(N) where N is len(Path). +type pointerPath struct { + // mx is keyed by x pointers, where the value is the associated y pointer. + mx map[value.Pointer]value.Pointer + // my is keyed by y pointers, where the value is the associated x pointer. + my map[value.Pointer]value.Pointer +} + +func (p *pointerPath) Init() { + p.mx = make(map[value.Pointer]value.Pointer) + p.my = make(map[value.Pointer]value.Pointer) +} + +// Push indicates intent to descend into pointers vx and vy where +// visited reports whether either has been seen before. If visited before, +// equal reports whether both pointers were encountered together. +// Pop must be called if and only if the pointers were never visited. +// +// The pointers vx and vy must be a reflect.Ptr, reflect.Slice, or reflect.Map +// and be non-nil. +func (p pointerPath) Push(vx, vy reflect.Value) (equal, visited bool) { + px := value.PointerOf(vx) + py := value.PointerOf(vy) + _, ok1 := p.mx[px] + _, ok2 := p.my[py] + if ok1 || ok2 { + equal = p.mx[px] == py && p.my[py] == px // Pointers paired together + return equal, true + } + p.mx[px] = py + p.my[py] = px + return false, false +} + +// Pop ascends from pointers vx and vy. +func (p pointerPath) Pop(vx, vy reflect.Value) { + delete(p.mx, value.PointerOf(vx)) + delete(p.my, value.PointerOf(vy)) +} + // isExported reports whether the identifier is exported. func isExported(id string) bool { r, _ := utf8.DecodeRuneInString(id) diff --git a/vendor/github.com/google/go-cmp/cmp/report.go b/vendor/github.com/google/go-cmp/cmp/report.go index 6ddf299..f43cd12 100644 --- a/vendor/github.com/google/go-cmp/cmp/report.go +++ b/vendor/github.com/google/go-cmp/cmp/report.go @@ -1,6 +1,6 @@ // Copyright 2017, 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.md file. +// license that can be found in the LICENSE file. package cmp @@ -41,7 +41,10 @@ func (r *defaultReporter) String() string { if r.root.NumDiff == 0 { return "" } - return formatOptions{}.FormatDiff(r.root).String() + ptrs := new(pointerReferences) + text := formatOptions{}.FormatDiff(r.root, ptrs) + resolveReferences(text) + return text.String() } func assert(ok bool) { diff --git a/vendor/github.com/google/go-cmp/cmp/report_compare.go b/vendor/github.com/google/go-cmp/cmp/report_compare.go index 17a05ee..2050bf6 100644 --- a/vendor/github.com/google/go-cmp/cmp/report_compare.go +++ b/vendor/github.com/google/go-cmp/cmp/report_compare.go @@ -1,24 +1,14 @@ // Copyright 2019, 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.md file. +// license that can be found in the LICENSE file. package cmp import ( "fmt" "reflect" - - "github.com/google/go-cmp/cmp/internal/value" ) -// TODO: Enforce limits? -// * Enforce maximum number of records to print per node? -// * Enforce maximum size in bytes allowed? -// * As a heuristic, use less verbosity for equal nodes than unequal nodes. -// TODO: Enforce unique outputs? -// * Avoid Stringer methods if it results in same output? -// * Print pointer address if outputs still equal? - // numContextRecords is the number of surrounding equal records to print. const numContextRecords = 2 @@ -71,24 +61,69 @@ func (opts formatOptions) WithTypeMode(t typeMode) formatOptions { opts.TypeMode = t return opts } +func (opts formatOptions) WithVerbosity(level int) formatOptions { + opts.VerbosityLevel = level + opts.LimitVerbosity = true + return opts +} +func (opts formatOptions) verbosity() uint { + switch { + case opts.VerbosityLevel < 0: + return 0 + case opts.VerbosityLevel > 16: + return 16 // some reasonable maximum to avoid shift overflow + default: + return uint(opts.VerbosityLevel) + } +} + +const maxVerbosityPreset = 6 + +// verbosityPreset modifies the verbosity settings given an index +// between 0 and maxVerbosityPreset, inclusive. +func verbosityPreset(opts formatOptions, i int) formatOptions { + opts.VerbosityLevel = int(opts.verbosity()) + 2*i + if i > 0 { + opts.AvoidStringer = true + } + if i >= maxVerbosityPreset { + opts.PrintAddresses = true + opts.QualifiedNames = true + } + return opts +} // FormatDiff converts a valueNode tree into a textNode tree, where the later // is a textual representation of the differences detected in the former. -func (opts formatOptions) FormatDiff(v *valueNode) textNode { +func (opts formatOptions) FormatDiff(v *valueNode, ptrs *pointerReferences) (out textNode) { + if opts.DiffMode == diffIdentical { + opts = opts.WithVerbosity(1) + } else if opts.verbosity() < 3 { + opts = opts.WithVerbosity(3) + } + // Check whether we have specialized formatting for this node. // This is not necessary, but helpful for producing more readable outputs. if opts.CanFormatDiffSlice(v) { return opts.FormatDiffSlice(v) } + var parentKind reflect.Kind + if v.parent != nil && v.parent.TransformerName == "" { + parentKind = v.parent.Type.Kind() + } + // For leaf nodes, format the value based on the reflect.Values alone. - if v.MaxDepth == 0 { + // As a special case, treat equal []byte as a leaf nodes. + isBytes := v.Type.Kind() == reflect.Slice && v.Type.Elem() == byteType + isEqualBytes := isBytes && v.NumDiff+v.NumIgnored+v.NumTransformed == 0 + if v.MaxDepth == 0 || isEqualBytes { switch opts.DiffMode { case diffUnknown, diffIdentical: // Format Equal. if v.NumDiff == 0 { - outx := opts.FormatValue(v.ValueX, visitedPointers{}) - outy := opts.FormatValue(v.ValueY, visitedPointers{}) + outx := opts.FormatValue(v.ValueX, parentKind, ptrs) + outy := opts.FormatValue(v.ValueY, parentKind, ptrs) if v.NumIgnored > 0 && v.NumSame == 0 { return textEllipsis } else if outx.Len() < outy.Len() { @@ -101,8 +136,13 @@ func (opts formatOptions) FormatDiff(v *valueNode) textNode { // Format unequal. assert(opts.DiffMode == diffUnknown) var list textList - outx := opts.WithTypeMode(elideType).FormatValue(v.ValueX, visitedPointers{}) - outy := opts.WithTypeMode(elideType).FormatValue(v.ValueY, visitedPointers{}) + outx := opts.WithTypeMode(elideType).FormatValue(v.ValueX, parentKind, ptrs) + outy := opts.WithTypeMode(elideType).FormatValue(v.ValueY, parentKind, ptrs) + for i := 0; i <= maxVerbosityPreset && outx != nil && outy != nil && outx.Equal(outy); i++ { + opts2 := verbosityPreset(opts, i).WithTypeMode(elideType) + outx = opts2.FormatValue(v.ValueX, parentKind, ptrs) + outy = opts2.FormatValue(v.ValueY, parentKind, ptrs) + } if outx != nil { list = append(list, textRecord{Diff: '-', Value: outx}) } @@ -111,34 +151,57 @@ func (opts formatOptions) FormatDiff(v *valueNode) textNode { } return opts.WithTypeMode(emitType).FormatType(v.Type, list) case diffRemoved: - return opts.FormatValue(v.ValueX, visitedPointers{}) + return opts.FormatValue(v.ValueX, parentKind, ptrs) case diffInserted: - return opts.FormatValue(v.ValueY, visitedPointers{}) + return opts.FormatValue(v.ValueY, parentKind, ptrs) default: panic("invalid diff mode") } } + // Register slice element to support cycle detection. + if parentKind == reflect.Slice { + ptrRefs := ptrs.PushPair(v.ValueX, v.ValueY, opts.DiffMode, true) + defer ptrs.Pop() + defer func() { out = wrapTrunkReferences(ptrRefs, out) }() + } + // Descend into the child value node. if v.TransformerName != "" { - out := opts.WithTypeMode(emitType).FormatDiff(v.Value) - out = textWrap{"Inverse(" + v.TransformerName + ", ", out, ")"} + out := opts.WithTypeMode(emitType).FormatDiff(v.Value, ptrs) + out = &textWrap{Prefix: "Inverse(" + v.TransformerName + ", ", Value: out, Suffix: ")"} return opts.FormatType(v.Type, out) } else { switch k := v.Type.Kind(); k { - case reflect.Struct, reflect.Array, reflect.Slice, reflect.Map: - return opts.FormatType(v.Type, opts.formatDiffList(v.Records, k)) + case reflect.Struct, reflect.Array, reflect.Slice: + out = opts.formatDiffList(v.Records, k, ptrs) + out = opts.FormatType(v.Type, out) + case reflect.Map: + // Register map to support cycle detection. + ptrRefs := ptrs.PushPair(v.ValueX, v.ValueY, opts.DiffMode, false) + defer ptrs.Pop() + + out = opts.formatDiffList(v.Records, k, ptrs) + out = wrapTrunkReferences(ptrRefs, out) + out = opts.FormatType(v.Type, out) case reflect.Ptr: - return textWrap{"&", opts.FormatDiff(v.Value), ""} + // Register pointer to support cycle detection. + ptrRefs := ptrs.PushPair(v.ValueX, v.ValueY, opts.DiffMode, false) + defer ptrs.Pop() + + out = opts.FormatDiff(v.Value, ptrs) + out = wrapTrunkReferences(ptrRefs, out) + out = &textWrap{Prefix: "&", Value: out} case reflect.Interface: - return opts.WithTypeMode(emitType).FormatDiff(v.Value) + out = opts.WithTypeMode(emitType).FormatDiff(v.Value, ptrs) default: panic(fmt.Sprintf("%v cannot have children", k)) } + return out } } -func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind) textNode { +func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind, ptrs *pointerReferences) textNode { // Derive record name based on the data structure kind. var name string var formatKey func(reflect.Value) string @@ -154,7 +217,17 @@ func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind) te case reflect.Map: name = "entry" opts = opts.WithTypeMode(elideType) - formatKey = formatMapKey + formatKey = func(v reflect.Value) string { return formatMapKey(v, false, ptrs) } + } + + maxLen := -1 + if opts.LimitVerbosity { + if opts.DiffMode == diffIdentical { + maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc... + } else { + maxLen = (1 << opts.verbosity()) << 1 // 2, 4, 8, 16, 32, 64, etc... + } + opts.VerbosityLevel-- } // Handle unification. @@ -163,16 +236,21 @@ func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind) te var list textList var deferredEllipsis bool // Add final "..." to indicate records were dropped for _, r := range recs { + if len(list) == maxLen { + deferredEllipsis = true + break + } + // Elide struct fields that are zero value. if k == reflect.Struct { var isZero bool switch opts.DiffMode { case diffIdentical: - isZero = value.IsZero(r.Value.ValueX) || value.IsZero(r.Value.ValueY) + isZero = r.Value.ValueX.IsZero() || r.Value.ValueY.IsZero() case diffRemoved: - isZero = value.IsZero(r.Value.ValueX) + isZero = r.Value.ValueX.IsZero() case diffInserted: - isZero = value.IsZero(r.Value.ValueY) + isZero = r.Value.ValueY.IsZero() } if isZero { continue @@ -186,23 +264,31 @@ func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind) te } continue } - if out := opts.FormatDiff(r.Value); out != nil { + if out := opts.FormatDiff(r.Value, ptrs); out != nil { list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) } } if deferredEllipsis { list.AppendEllipsis(diffStats{}) } - return textWrap{"{", list, "}"} + return &textWrap{Prefix: "{", Value: list, Suffix: "}"} case diffUnknown: default: panic("invalid diff mode") } // Handle differencing. + var numDiffs int var list textList + var keys []reflect.Value // invariant: len(list) == len(keys) groups := coalesceAdjacentRecords(name, recs) + maxGroup := diffStats{Name: name} for i, ds := range groups { + if maxLen >= 0 && numDiffs >= maxLen { + maxGroup = maxGroup.Append(ds) + continue + } + // Handle equal records. if ds.NumDiff() == 0 { // Compute the number of leading and trailing records to print. @@ -226,16 +312,21 @@ func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind) te // Format the equal values. for _, r := range recs[:numLo] { - out := opts.WithDiffMode(diffIdentical).FormatDiff(r.Value) + out := opts.WithDiffMode(diffIdentical).FormatDiff(r.Value, ptrs) list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) + keys = append(keys, r.Key) } if numEqual > numLo+numHi { ds.NumIdentical -= numLo + numHi list.AppendEllipsis(ds) + for len(keys) < len(list) { + keys = append(keys, reflect.Value{}) + } } for _, r := range recs[numEqual-numHi : numEqual] { - out := opts.WithDiffMode(diffIdentical).FormatDiff(r.Value) + out := opts.WithDiffMode(diffIdentical).FormatDiff(r.Value, ptrs) list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) + keys = append(keys, r.Key) } recs = recs[numEqual:] continue @@ -247,24 +338,70 @@ func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind) te case opts.CanFormatDiffSlice(r.Value): out := opts.FormatDiffSlice(r.Value) list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) + keys = append(keys, r.Key) case r.Value.NumChildren == r.Value.MaxDepth: - outx := opts.WithDiffMode(diffRemoved).FormatDiff(r.Value) - outy := opts.WithDiffMode(diffInserted).FormatDiff(r.Value) + outx := opts.WithDiffMode(diffRemoved).FormatDiff(r.Value, ptrs) + outy := opts.WithDiffMode(diffInserted).FormatDiff(r.Value, ptrs) + for i := 0; i <= maxVerbosityPreset && outx != nil && outy != nil && outx.Equal(outy); i++ { + opts2 := verbosityPreset(opts, i) + outx = opts2.WithDiffMode(diffRemoved).FormatDiff(r.Value, ptrs) + outy = opts2.WithDiffMode(diffInserted).FormatDiff(r.Value, ptrs) + } if outx != nil { list = append(list, textRecord{Diff: diffRemoved, Key: formatKey(r.Key), Value: outx}) + keys = append(keys, r.Key) } if outy != nil { list = append(list, textRecord{Diff: diffInserted, Key: formatKey(r.Key), Value: outy}) + keys = append(keys, r.Key) } default: - out := opts.FormatDiff(r.Value) + out := opts.FormatDiff(r.Value, ptrs) list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) + keys = append(keys, r.Key) } } recs = recs[ds.NumDiff():] + numDiffs += ds.NumDiff() + } + if maxGroup.IsZero() { + assert(len(recs) == 0) + } else { + list.AppendEllipsis(maxGroup) + for len(keys) < len(list) { + keys = append(keys, reflect.Value{}) + } } - assert(len(recs) == 0) - return textWrap{"{", list, "}"} + assert(len(list) == len(keys)) + + // For maps, the default formatting logic uses fmt.Stringer which may + // produce ambiguous output. Avoid calling String to disambiguate. + if k == reflect.Map { + var ambiguous bool + seenKeys := map[string]reflect.Value{} + for i, currKey := range keys { + if currKey.IsValid() { + strKey := list[i].Key + prevKey, seen := seenKeys[strKey] + if seen && prevKey.CanInterface() && currKey.CanInterface() { + ambiguous = prevKey.Interface() != currKey.Interface() + if ambiguous { + break + } + } + seenKeys[strKey] = currKey + } + } + if ambiguous { + for i, k := range keys { + if k.IsValid() { + list[i].Key = formatMapKey(k, true, ptrs) + } + } + } + } + + return &textWrap{Prefix: "{", Value: list, Suffix: "}"} } // coalesceAdjacentRecords coalesces the list of records into groups of diff --git a/vendor/github.com/google/go-cmp/cmp/report_references.go b/vendor/github.com/google/go-cmp/cmp/report_references.go new file mode 100644 index 0000000..be31b33 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/report_references.go @@ -0,0 +1,264 @@ +// Copyright 2020, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cmp + +import ( + "fmt" + "reflect" + "strings" + + "github.com/google/go-cmp/cmp/internal/flags" + "github.com/google/go-cmp/cmp/internal/value" +) + +const ( + pointerDelimPrefix = "⟪" + pointerDelimSuffix = "⟫" +) + +// formatPointer prints the address of the pointer. +func formatPointer(p value.Pointer, withDelims bool) string { + v := p.Uintptr() + if flags.Deterministic { + v = 0xdeadf00f // Only used for stable testing purposes + } + if withDelims { + return pointerDelimPrefix + formatHex(uint64(v)) + pointerDelimSuffix + } + return formatHex(uint64(v)) +} + +// pointerReferences is a stack of pointers visited so far. +type pointerReferences [][2]value.Pointer + +func (ps *pointerReferences) PushPair(vx, vy reflect.Value, d diffMode, deref bool) (pp [2]value.Pointer) { + if deref && vx.IsValid() { + vx = vx.Addr() + } + if deref && vy.IsValid() { + vy = vy.Addr() + } + switch d { + case diffUnknown, diffIdentical: + pp = [2]value.Pointer{value.PointerOf(vx), value.PointerOf(vy)} + case diffRemoved: + pp = [2]value.Pointer{value.PointerOf(vx), value.Pointer{}} + case diffInserted: + pp = [2]value.Pointer{value.Pointer{}, value.PointerOf(vy)} + } + *ps = append(*ps, pp) + return pp +} + +func (ps *pointerReferences) Push(v reflect.Value) (p value.Pointer, seen bool) { + p = value.PointerOf(v) + for _, pp := range *ps { + if p == pp[0] || p == pp[1] { + return p, true + } + } + *ps = append(*ps, [2]value.Pointer{p, p}) + return p, false +} + +func (ps *pointerReferences) Pop() { + *ps = (*ps)[:len(*ps)-1] +} + +// trunkReferences is metadata for a textNode indicating that the sub-tree +// represents the value for either pointer in a pair of references. +type trunkReferences struct{ pp [2]value.Pointer } + +// trunkReference is metadata for a textNode indicating that the sub-tree +// represents the value for the given pointer reference. +type trunkReference struct{ p value.Pointer } + +// leafReference is metadata for a textNode indicating that the value is +// truncated as it refers to another part of the tree (i.e., a trunk). +type leafReference struct{ p value.Pointer } + +func wrapTrunkReferences(pp [2]value.Pointer, s textNode) textNode { + switch { + case pp[0].IsNil(): + return &textWrap{Value: s, Metadata: trunkReference{pp[1]}} + case pp[1].IsNil(): + return &textWrap{Value: s, Metadata: trunkReference{pp[0]}} + case pp[0] == pp[1]: + return &textWrap{Value: s, Metadata: trunkReference{pp[0]}} + default: + return &textWrap{Value: s, Metadata: trunkReferences{pp}} + } +} +func wrapTrunkReference(p value.Pointer, printAddress bool, s textNode) textNode { + var prefix string + if printAddress { + prefix = formatPointer(p, true) + } + return &textWrap{Prefix: prefix, Value: s, Metadata: trunkReference{p}} +} +func makeLeafReference(p value.Pointer, printAddress bool) textNode { + out := &textWrap{Prefix: "(", Value: textEllipsis, Suffix: ")"} + var prefix string + if printAddress { + prefix = formatPointer(p, true) + } + return &textWrap{Prefix: prefix, Value: out, Metadata: leafReference{p}} +} + +// resolveReferences walks the textNode tree searching for any leaf reference +// metadata and resolves each against the corresponding trunk references. +// Since pointer addresses in memory are not particularly readable to the user, +// it replaces each pointer value with an arbitrary and unique reference ID. +func resolveReferences(s textNode) { + var walkNodes func(textNode, func(textNode)) + walkNodes = func(s textNode, f func(textNode)) { + f(s) + switch s := s.(type) { + case *textWrap: + walkNodes(s.Value, f) + case textList: + for _, r := range s { + walkNodes(r.Value, f) + } + } + } + + // Collect all trunks and leaves with reference metadata. + var trunks, leaves []*textWrap + walkNodes(s, func(s textNode) { + if s, ok := s.(*textWrap); ok { + switch s.Metadata.(type) { + case leafReference: + leaves = append(leaves, s) + case trunkReference, trunkReferences: + trunks = append(trunks, s) + } + } + }) + + // No leaf references to resolve. + if len(leaves) == 0 { + return + } + + // Collect the set of all leaf references to resolve. + leafPtrs := make(map[value.Pointer]bool) + for _, leaf := range leaves { + leafPtrs[leaf.Metadata.(leafReference).p] = true + } + + // Collect the set of trunk pointers that are always paired together. + // This allows us to assign a single ID to both pointers for brevity. + // If a pointer in a pair ever occurs by itself or as a different pair, + // then the pair is broken. + pairedTrunkPtrs := make(map[value.Pointer]value.Pointer) + unpair := func(p value.Pointer) { + if !pairedTrunkPtrs[p].IsNil() { + pairedTrunkPtrs[pairedTrunkPtrs[p]] = value.Pointer{} // invalidate other half + } + pairedTrunkPtrs[p] = value.Pointer{} // invalidate this half + } + for _, trunk := range trunks { + switch p := trunk.Metadata.(type) { + case trunkReference: + unpair(p.p) // standalone pointer cannot be part of a pair + case trunkReferences: + p0, ok0 := pairedTrunkPtrs[p.pp[0]] + p1, ok1 := pairedTrunkPtrs[p.pp[1]] + switch { + case !ok0 && !ok1: + // Register the newly seen pair. + pairedTrunkPtrs[p.pp[0]] = p.pp[1] + pairedTrunkPtrs[p.pp[1]] = p.pp[0] + case ok0 && ok1 && p0 == p.pp[1] && p1 == p.pp[0]: + // Exact pair already seen; do nothing. + default: + // Pair conflicts with some other pair; break all pairs. + unpair(p.pp[0]) + unpair(p.pp[1]) + } + } + } + + // Correlate each pointer referenced by leaves to a unique identifier, + // and print the IDs for each trunk that matches those pointers. + var nextID uint + ptrIDs := make(map[value.Pointer]uint) + newID := func() uint { + id := nextID + nextID++ + return id + } + for _, trunk := range trunks { + switch p := trunk.Metadata.(type) { + case trunkReference: + if print := leafPtrs[p.p]; print { + id, ok := ptrIDs[p.p] + if !ok { + id = newID() + ptrIDs[p.p] = id + } + trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id)) + } + case trunkReferences: + print0 := leafPtrs[p.pp[0]] + print1 := leafPtrs[p.pp[1]] + if print0 || print1 { + id0, ok0 := ptrIDs[p.pp[0]] + id1, ok1 := ptrIDs[p.pp[1]] + isPair := pairedTrunkPtrs[p.pp[0]] == p.pp[1] && pairedTrunkPtrs[p.pp[1]] == p.pp[0] + if isPair { + var id uint + assert(ok0 == ok1) // must be seen together or not at all + if ok0 { + assert(id0 == id1) // must have the same ID + id = id0 + } else { + id = newID() + ptrIDs[p.pp[0]] = id + ptrIDs[p.pp[1]] = id + } + trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id)) + } else { + if print0 && !ok0 { + id0 = newID() + ptrIDs[p.pp[0]] = id0 + } + if print1 && !ok1 { + id1 = newID() + ptrIDs[p.pp[1]] = id1 + } + switch { + case print0 && print1: + trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id0)+","+formatReference(id1)) + case print0: + trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id0)) + case print1: + trunk.Prefix = updateReferencePrefix(trunk.Prefix, formatReference(id1)) + } + } + } + } + } + + // Update all leaf references with the unique identifier. + for _, leaf := range leaves { + if id, ok := ptrIDs[leaf.Metadata.(leafReference).p]; ok { + leaf.Prefix = updateReferencePrefix(leaf.Prefix, formatReference(id)) + } + } +} + +func formatReference(id uint) string { + return fmt.Sprintf("ref#%d", id) +} + +func updateReferencePrefix(prefix, ref string) string { + if prefix == "" { + return pointerDelimPrefix + ref + pointerDelimSuffix + } + suffix := strings.TrimPrefix(prefix, pointerDelimPrefix) + return pointerDelimPrefix + ref + ": " + suffix +} diff --git a/vendor/github.com/google/go-cmp/cmp/report_reflect.go b/vendor/github.com/google/go-cmp/cmp/report_reflect.go index 2761b62..e39f422 100644 --- a/vendor/github.com/google/go-cmp/cmp/report_reflect.go +++ b/vendor/github.com/google/go-cmp/cmp/report_reflect.go @@ -1,33 +1,49 @@ // Copyright 2019, 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.md file. +// license that can be found in the LICENSE file. package cmp import ( + "bytes" "fmt" "reflect" "strconv" "strings" "unicode" + "unicode/utf8" - "github.com/google/go-cmp/cmp/internal/flags" "github.com/google/go-cmp/cmp/internal/value" ) +var ( + anyType = reflect.TypeOf((*interface{})(nil)).Elem() + stringType = reflect.TypeOf((*string)(nil)).Elem() + bytesType = reflect.TypeOf((*[]byte)(nil)).Elem() + byteType = reflect.TypeOf((*byte)(nil)).Elem() +) + type formatValueOptions struct { // AvoidStringer controls whether to avoid calling custom stringer // methods like error.Error or fmt.Stringer.String. AvoidStringer bool - // ShallowPointers controls whether to avoid descending into pointers. - // Useful when printing map keys, where pointer comparison is performed - // on the pointer address rather than the pointed-at value. - ShallowPointers bool - // PrintAddresses controls whether to print the address of all pointers, // slice elements, and maps. PrintAddresses bool + + // QualifiedNames controls whether FormatType uses the fully qualified name + // (including the full package path as opposed to just the package name). + QualifiedNames bool + + // VerbosityLevel controls the amount of output to produce. + // A higher value produces more output. A value of zero or lower produces + // no output (represented using an ellipsis). + // If LimitVerbosity is false, then the level is treated as infinite. + VerbosityLevel int + + // LimitVerbosity specifies that formatting should respect VerbosityLevel. + LimitVerbosity bool } // FormatType prints the type as if it were wrapping s. @@ -44,12 +60,15 @@ func (opts formatOptions) FormatType(t reflect.Type, s textNode) textNode { default: return s } + if opts.DiffMode == diffIdentical { + return s // elide type for identical nodes + } case elideType: return s } // Determine the type label, applying special handling for unnamed types. - typeName := t.String() + typeName := value.TypeString(t, opts.QualifiedNames) if t.Name() == "" { // According to Go grammar, certain type literals contain symbols that // do not strongly bind to the next lexicographical token (e.g., *T). @@ -57,39 +76,77 @@ func (opts formatOptions) FormatType(t reflect.Type, s textNode) textNode { case reflect.Chan, reflect.Func, reflect.Ptr: typeName = "(" + typeName + ")" } - typeName = strings.Replace(typeName, "struct {", "struct{", -1) - typeName = strings.Replace(typeName, "interface {", "interface{", -1) } + return &textWrap{Prefix: typeName, Value: wrapParens(s)} +} + +// wrapParens wraps s with a set of parenthesis, but avoids it if the +// wrapped node itself is already surrounded by a pair of parenthesis or braces. +// It handles unwrapping one level of pointer-reference nodes. +func wrapParens(s textNode) textNode { + var refNode *textWrap + if s2, ok := s.(*textWrap); ok { + // Unwrap a single pointer reference node. + switch s2.Metadata.(type) { + case leafReference, trunkReference, trunkReferences: + refNode = s2 + if s3, ok := refNode.Value.(*textWrap); ok { + s2 = s3 + } + } - // Avoid wrap the value in parenthesis if unnecessary. - if s, ok := s.(textWrap); ok { - hasParens := strings.HasPrefix(s.Prefix, "(") && strings.HasSuffix(s.Suffix, ")") - hasBraces := strings.HasPrefix(s.Prefix, "{") && strings.HasSuffix(s.Suffix, "}") + // Already has delimiters that make parenthesis unnecessary. + hasParens := strings.HasPrefix(s2.Prefix, "(") && strings.HasSuffix(s2.Suffix, ")") + hasBraces := strings.HasPrefix(s2.Prefix, "{") && strings.HasSuffix(s2.Suffix, "}") if hasParens || hasBraces { - return textWrap{typeName, s, ""} + return s } } - return textWrap{typeName + "(", s, ")"} + if refNode != nil { + refNode.Value = &textWrap{Prefix: "(", Value: refNode.Value, Suffix: ")"} + return s + } + return &textWrap{Prefix: "(", Value: s, Suffix: ")"} } // FormatValue prints the reflect.Value, taking extra care to avoid descending -// into pointers already in m. As pointers are visited, m is also updated. -func (opts formatOptions) FormatValue(v reflect.Value, m visitedPointers) (out textNode) { +// into pointers already in ptrs. As pointers are visited, ptrs is also updated. +func (opts formatOptions) FormatValue(v reflect.Value, parentKind reflect.Kind, ptrs *pointerReferences) (out textNode) { if !v.IsValid() { return nil } t := v.Type() + // Check slice element for cycles. + if parentKind == reflect.Slice { + ptrRef, visited := ptrs.Push(v.Addr()) + if visited { + return makeLeafReference(ptrRef, false) + } + defer ptrs.Pop() + defer func() { out = wrapTrunkReference(ptrRef, false, out) }() + } + // Check whether there is an Error or String method to call. if !opts.AvoidStringer && v.CanInterface() { // Avoid calling Error or String methods on nil receivers since many // implementations crash when doing so. if (t.Kind() != reflect.Ptr && t.Kind() != reflect.Interface) || !v.IsNil() { - switch v := v.Interface().(type) { - case error: - return textLine("e" + formatString(v.Error())) - case fmt.Stringer: - return textLine("s" + formatString(v.String())) + var prefix, strVal string + func() { + // Swallow and ignore any panics from String or Error. + defer func() { recover() }() + switch v := v.Interface().(type) { + case error: + strVal = v.Error() + prefix = "e" + case fmt.Stringer: + strVal = v.String() + prefix = "s" + } + }() + if prefix != "" { + return opts.formatString(prefix, strVal) } } } @@ -102,114 +159,213 @@ func (opts formatOptions) FormatValue(v reflect.Value, m visitedPointers) (out t } }() - var ptr string switch t.Kind() { case reflect.Bool: return textLine(fmt.Sprint(v.Bool())) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return textLine(fmt.Sprint(v.Int())) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - // Unnamed uints are usually bytes or words, so use hexadecimal. - if t.PkgPath() == "" || t.Kind() == reflect.Uintptr { + case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return textLine(fmt.Sprint(v.Uint())) + case reflect.Uint8: + if parentKind == reflect.Slice || parentKind == reflect.Array { return textLine(formatHex(v.Uint())) } return textLine(fmt.Sprint(v.Uint())) + case reflect.Uintptr: + return textLine(formatHex(v.Uint())) case reflect.Float32, reflect.Float64: return textLine(fmt.Sprint(v.Float())) case reflect.Complex64, reflect.Complex128: return textLine(fmt.Sprint(v.Complex())) case reflect.String: - return textLine(formatString(v.String())) + return opts.formatString("", v.String()) case reflect.UnsafePointer, reflect.Chan, reflect.Func: - return textLine(formatPointer(v)) + return textLine(formatPointer(value.PointerOf(v), true)) case reflect.Struct: var list textList + v := makeAddressable(v) // needed for retrieveUnexportedField + maxLen := v.NumField() + if opts.LimitVerbosity { + maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc... + opts.VerbosityLevel-- + } for i := 0; i < v.NumField(); i++ { vv := v.Field(i) - if value.IsZero(vv) { + if vv.IsZero() { continue // Elide fields with zero values } - s := opts.WithTypeMode(autoType).FormatValue(vv, m) - list = append(list, textRecord{Key: t.Field(i).Name, Value: s}) + if len(list) == maxLen { + list.AppendEllipsis(diffStats{}) + break + } + sf := t.Field(i) + if !isExported(sf.Name) { + vv = retrieveUnexportedField(v, sf, true) + } + s := opts.WithTypeMode(autoType).FormatValue(vv, t.Kind(), ptrs) + list = append(list, textRecord{Key: sf.Name, Value: s}) } - return textWrap{"{", list, "}"} + return &textWrap{Prefix: "{", Value: list, Suffix: "}"} case reflect.Slice: if v.IsNil() { return textNil } - if opts.PrintAddresses { - ptr = formatPointer(v) + + // Check whether this is a []byte of text data. + if t.Elem() == byteType { + b := v.Bytes() + isPrintSpace := func(r rune) bool { return unicode.IsPrint(r) || unicode.IsSpace(r) } + if len(b) > 0 && utf8.Valid(b) && len(bytes.TrimFunc(b, isPrintSpace)) == 0 { + out = opts.formatString("", string(b)) + skipType = true + return opts.FormatType(t, out) + } } + fallthrough case reflect.Array: + maxLen := v.Len() + if opts.LimitVerbosity { + maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc... + opts.VerbosityLevel-- + } var list textList for i := 0; i < v.Len(); i++ { - vi := v.Index(i) - if vi.CanAddr() { // Check for cyclic elements - p := vi.Addr() - if m.Visit(p) { - var out textNode - out = textLine(formatPointer(p)) - out = opts.WithTypeMode(emitType).FormatType(p.Type(), out) - out = textWrap{"*", out, ""} - list = append(list, textRecord{Value: out}) - continue - } + if len(list) == maxLen { + list.AppendEllipsis(diffStats{}) + break } - s := opts.WithTypeMode(elideType).FormatValue(vi, m) + s := opts.WithTypeMode(elideType).FormatValue(v.Index(i), t.Kind(), ptrs) list = append(list, textRecord{Value: s}) } - return textWrap{ptr + "{", list, "}"} + + out = &textWrap{Prefix: "{", Value: list, Suffix: "}"} + if t.Kind() == reflect.Slice && opts.PrintAddresses { + header := fmt.Sprintf("ptr:%v, len:%d, cap:%d", formatPointer(value.PointerOf(v), false), v.Len(), v.Cap()) + out = &textWrap{Prefix: pointerDelimPrefix + header + pointerDelimSuffix, Value: out} + } + return out case reflect.Map: if v.IsNil() { return textNil } - if m.Visit(v) { - return textLine(formatPointer(v)) + + // Check pointer for cycles. + ptrRef, visited := ptrs.Push(v) + if visited { + return makeLeafReference(ptrRef, opts.PrintAddresses) } + defer ptrs.Pop() + maxLen := v.Len() + if opts.LimitVerbosity { + maxLen = ((1 << opts.verbosity()) >> 1) << 2 // 0, 4, 8, 16, 32, etc... + opts.VerbosityLevel-- + } var list textList for _, k := range value.SortKeys(v.MapKeys()) { - sk := formatMapKey(k) - sv := opts.WithTypeMode(elideType).FormatValue(v.MapIndex(k), m) + if len(list) == maxLen { + list.AppendEllipsis(diffStats{}) + break + } + sk := formatMapKey(k, false, ptrs) + sv := opts.WithTypeMode(elideType).FormatValue(v.MapIndex(k), t.Kind(), ptrs) list = append(list, textRecord{Key: sk, Value: sv}) } - if opts.PrintAddresses { - ptr = formatPointer(v) - } - return textWrap{ptr + "{", list, "}"} + + out = &textWrap{Prefix: "{", Value: list, Suffix: "}"} + out = wrapTrunkReference(ptrRef, opts.PrintAddresses, out) + return out case reflect.Ptr: if v.IsNil() { return textNil } - if m.Visit(v) || opts.ShallowPointers { - return textLine(formatPointer(v)) + + // Check pointer for cycles. + ptrRef, visited := ptrs.Push(v) + if visited { + out = makeLeafReference(ptrRef, opts.PrintAddresses) + return &textWrap{Prefix: "&", Value: out} } - if opts.PrintAddresses { - ptr = formatPointer(v) + defer ptrs.Pop() + + // Skip the name only if this is an unnamed pointer type. + // Otherwise taking the address of a value does not reproduce + // the named pointer type. + if v.Type().Name() == "" { + skipType = true // Let the underlying value print the type instead } - skipType = true // Let the underlying value print the type instead - return textWrap{"&" + ptr, opts.FormatValue(v.Elem(), m), ""} + out = opts.FormatValue(v.Elem(), t.Kind(), ptrs) + out = wrapTrunkReference(ptrRef, opts.PrintAddresses, out) + out = &textWrap{Prefix: "&", Value: out} + return out case reflect.Interface: if v.IsNil() { return textNil } // Interfaces accept different concrete types, // so configure the underlying value to explicitly print the type. - skipType = true // Print the concrete type instead - return opts.WithTypeMode(emitType).FormatValue(v.Elem(), m) + return opts.WithTypeMode(emitType).FormatValue(v.Elem(), t.Kind(), ptrs) default: panic(fmt.Sprintf("%v kind not handled", v.Kind())) } } +func (opts formatOptions) formatString(prefix, s string) textNode { + maxLen := len(s) + maxLines := strings.Count(s, "\n") + 1 + if opts.LimitVerbosity { + maxLen = (1 << opts.verbosity()) << 5 // 32, 64, 128, 256, etc... + maxLines = (1 << opts.verbosity()) << 2 // 4, 8, 16, 32, 64, etc... + } + + // For multiline strings, use the triple-quote syntax, + // but only use it when printing removed or inserted nodes since + // we only want the extra verbosity for those cases. + lines := strings.Split(strings.TrimSuffix(s, "\n"), "\n") + isTripleQuoted := len(lines) >= 4 && (opts.DiffMode == '-' || opts.DiffMode == '+') + for i := 0; i < len(lines) && isTripleQuoted; i++ { + lines[i] = strings.TrimPrefix(strings.TrimSuffix(lines[i], "\r"), "\r") // trim leading/trailing carriage returns for legacy Windows endline support + isPrintable := func(r rune) bool { + return unicode.IsPrint(r) || r == '\t' // specially treat tab as printable + } + line := lines[i] + isTripleQuoted = !strings.HasPrefix(strings.TrimPrefix(line, prefix), `"""`) && !strings.HasPrefix(line, "...") && strings.TrimFunc(line, isPrintable) == "" && len(line) <= maxLen + } + if isTripleQuoted { + var list textList + list = append(list, textRecord{Diff: opts.DiffMode, Value: textLine(prefix + `"""`), ElideComma: true}) + for i, line := range lines { + if numElided := len(lines) - i; i == maxLines-1 && numElided > 1 { + comment := commentString(fmt.Sprintf("%d elided lines", numElided)) + list = append(list, textRecord{Diff: opts.DiffMode, Value: textEllipsis, ElideComma: true, Comment: comment}) + break + } + list = append(list, textRecord{Diff: opts.DiffMode, Value: textLine(line), ElideComma: true}) + } + list = append(list, textRecord{Diff: opts.DiffMode, Value: textLine(prefix + `"""`), ElideComma: true}) + return &textWrap{Prefix: "(", Value: list, Suffix: ")"} + } + + // Format the string as a single-line quoted string. + if len(s) > maxLen+len(textEllipsis) { + return textLine(prefix + formatString(s[:maxLen]) + string(textEllipsis)) + } + return textLine(prefix + formatString(s)) +} + // formatMapKey formats v as if it were a map key. // The result is guaranteed to be a single line. -func formatMapKey(v reflect.Value) string { +func formatMapKey(v reflect.Value, disambiguate bool, ptrs *pointerReferences) string { var opts formatOptions + opts.DiffMode = diffIdentical opts.TypeMode = elideType - opts.ShallowPointers = true - s := opts.FormatValue(v, visitedPointers{}).String() + opts.PrintAddresses = disambiguate + opts.AvoidStringer = disambiguate + opts.QualifiedNames = disambiguate + opts.VerbosityLevel = maxVerbosityPreset + opts.LimitVerbosity = true + s := opts.FormatValue(v, reflect.Map, ptrs).String() return strings.TrimSpace(s) } @@ -227,7 +383,7 @@ func formatString(s string) string { rawInvalid := func(r rune) bool { return r == '`' || r == '\n' || !(unicode.IsPrint(r) || r == '\t') } - if strings.IndexFunc(s, rawInvalid) < 0 { + if utf8.ValidString(s) && strings.IndexFunc(s, rawInvalid) < 0 { return "`" + s + "`" } return qs @@ -256,23 +412,3 @@ func formatHex(u uint64) string { } return fmt.Sprintf(f, u) } - -// formatPointer prints the address of the pointer. -func formatPointer(v reflect.Value) string { - p := v.Pointer() - if flags.Deterministic { - p = 0xdeadf00f // Only used for stable testing purposes - } - return fmt.Sprintf("⟪0x%x⟫", p) -} - -type visitedPointers map[value.Pointer]struct{} - -// Visit inserts pointer v into the visited map and reports whether it had -// already been visited before. -func (m visitedPointers) Visit(v reflect.Value) bool { - p := value.PointerOf(v) - _, visited := m[p] - m[p] = struct{}{} - return visited -} diff --git a/vendor/github.com/google/go-cmp/cmp/report_slices.go b/vendor/github.com/google/go-cmp/cmp/report_slices.go index eafcf2e..23e444f 100644 --- a/vendor/github.com/google/go-cmp/cmp/report_slices.go +++ b/vendor/github.com/google/go-cmp/cmp/report_slices.go @@ -1,13 +1,15 @@ // Copyright 2019, 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.md file. +// license that can be found in the LICENSE file. package cmp import ( "bytes" "fmt" + "math" "reflect" + "strconv" "strings" "unicode" "unicode/utf8" @@ -23,14 +25,35 @@ func (opts formatOptions) CanFormatDiffSlice(v *valueNode) bool { return false // Must be formatting in diff mode case v.NumDiff == 0: return false // No differences detected - case v.NumIgnored+v.NumCompared+v.NumTransformed > 0: - // TODO: Handle the case where someone uses bytes.Equal on a large slice. - return false // Some custom option was used to determined equality case !v.ValueX.IsValid() || !v.ValueY.IsValid(): return false // Both values must be valid + case v.NumIgnored > 0: + return false // Some ignore option was used + case v.NumTransformed > 0: + return false // Some transform option was used + case v.NumCompared > 1: + return false // More than one comparison was used + case v.NumCompared == 1 && v.Type.Name() != "": + // The need for cmp to check applicability of options on every element + // in a slice is a significant performance detriment for large []byte. + // The workaround is to specify Comparer(bytes.Equal), + // which enables cmp to compare []byte more efficiently. + // If they differ, we still want to provide batched diffing. + // The logic disallows named types since they tend to have their own + // String method, with nicer formatting than what this provides. + return false + } + + // Check whether this is an interface with the same concrete types. + t := v.Type + vx, vy := v.ValueX, v.ValueY + if t.Kind() == reflect.Interface && !vx.IsNil() && !vy.IsNil() && vx.Elem().Type() == vy.Elem().Type() { + vx, vy = vx.Elem(), vy.Elem() + t = vx.Type() } - switch t := v.Type; t.Kind() { + // Check whether we provide specialized diffing for this type. + switch t.Kind() { case reflect.String: case reflect.Array, reflect.Slice: // Only slices of primitive types have specialized handling. @@ -42,6 +65,11 @@ func (opts formatOptions) CanFormatDiffSlice(v *valueNode) bool { return false } + // Both slice values have to be non-empty. + if t.Kind() == reflect.Slice && (vx.Len() == 0 || vy.Len() == 0) { + return false + } + // If a sufficient number of elements already differ, // use specialized formatting even if length requirement is not met. if v.NumDiff > v.NumSame { @@ -52,8 +80,8 @@ func (opts formatOptions) CanFormatDiffSlice(v *valueNode) bool { } // Use specialized string diffing for longer slices or strings. - const minLength = 64 - return v.ValueX.Len() >= minLength && v.ValueY.Len() >= minLength + const minLength = 32 + return vx.Len() >= minLength && vy.Len() >= minLength } // FormatDiffSlice prints a diff for the slices (or strings) represented by v. @@ -62,17 +90,23 @@ func (opts formatOptions) CanFormatDiffSlice(v *valueNode) bool { func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode { assert(opts.DiffMode == diffUnknown) t, vx, vy := v.Type, v.ValueX, v.ValueY + if t.Kind() == reflect.Interface { + vx, vy = vx.Elem(), vy.Elem() + t = vx.Type() + opts = opts.WithTypeMode(emitType) + } // Auto-detect the type of the data. - var isLinedText, isText, isBinary bool var sx, sy string + var ssx, ssy []string + var isString, isMostlyText, isPureLinedText, isBinary bool switch { case t.Kind() == reflect.String: sx, sy = vx.String(), vy.String() - isText = true // Initial estimate, verify later - case t.Kind() == reflect.Slice && t.Elem() == reflect.TypeOf(byte(0)): + isString = true + case t.Kind() == reflect.Slice && t.Elem() == byteType: sx, sy = string(vx.Bytes()), string(vy.Bytes()) - isBinary = true // Initial estimate, verify later + isString = true case t.Kind() == reflect.Array: // Arrays need to be addressable for slice operations to work. vx2, vy2 := reflect.New(t).Elem(), reflect.New(t).Elem() @@ -80,13 +114,12 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode { vy2.Set(vy) vx, vy = vx2, vy2 } - if isText || isBinary { - var numLines, lastLineIdx, maxLineLen int - isBinary = false + if isString { + var numTotalRunes, numValidRunes, numLines, lastLineIdx, maxLineLen int for i, r := range sx + sy { - if !(unicode.IsPrint(r) || unicode.IsSpace(r)) || r == utf8.RuneError { - isBinary = true - break + numTotalRunes++ + if (unicode.IsPrint(r) || unicode.IsSpace(r)) && r != utf8.RuneError { + numValidRunes++ } if r == '\n' { if maxLineLen < i-lastLineIdx { @@ -96,8 +129,29 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode { numLines++ } } - isText = !isBinary - isLinedText = isText && numLines >= 4 && maxLineLen <= 256 + isPureText := numValidRunes == numTotalRunes + isMostlyText = float64(numValidRunes) > math.Floor(0.90*float64(numTotalRunes)) + isPureLinedText = isPureText && numLines >= 4 && maxLineLen <= 1024 + isBinary = !isMostlyText + + // Avoid diffing by lines if it produces a significantly more complex + // edit script than diffing by bytes. + if isPureLinedText { + ssx = strings.Split(sx, "\n") + ssy = strings.Split(sy, "\n") + esLines := diff.Difference(len(ssx), len(ssy), func(ix, iy int) diff.Result { + return diff.BoolResult(ssx[ix] == ssy[iy]) + }) + esBytes := diff.Difference(len(sx), len(sy), func(ix, iy int) diff.Result { + return diff.BoolResult(sx[ix] == sy[iy]) + }) + efficiencyLines := float64(esLines.Dist()) / float64(len(esLines)) + efficiencyBytes := float64(esBytes.Dist()) / float64(len(esBytes)) + quotedLength := len(strconv.Quote(sx + sy)) + unquotedLength := len(sx) + len(sy) + escapeExpansionRatio := float64(quotedLength) / float64(unquotedLength) + isPureLinedText = efficiencyLines < 4*efficiencyBytes || escapeExpansionRatio > 1.1 + } } // Format the string into printable records. @@ -106,9 +160,7 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode { switch { // If the text appears to be multi-lined text, // then perform differencing across individual lines. - case isLinedText: - ssx := strings.Split(sx, "\n") - ssy := strings.Split(sy, "\n") + case isPureLinedText: list = opts.formatDiffSlice( reflect.ValueOf(ssx), reflect.ValueOf(ssy), 1, "line", func(v reflect.Value, d diffMode) textRecord { @@ -117,10 +169,88 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode { }, ) delim = "\n" + + // If possible, use a custom triple-quote (""") syntax for printing + // differences in a string literal. This format is more readable, + // but has edge-cases where differences are visually indistinguishable. + // This format is avoided under the following conditions: + // - A line starts with `"""` + // - A line starts with "..." + // - A line contains non-printable characters + // - Adjacent different lines differ only by whitespace + // + // For example: + // + // """ + // ... // 3 identical lines + // foo + // bar + // - baz + // + BAZ + // """ + isTripleQuoted := true + prevRemoveLines := map[string]bool{} + prevInsertLines := map[string]bool{} + var list2 textList + list2 = append(list2, textRecord{Value: textLine(`"""`), ElideComma: true}) + for _, r := range list { + if !r.Value.Equal(textEllipsis) { + line, _ := strconv.Unquote(string(r.Value.(textLine))) + line = strings.TrimPrefix(strings.TrimSuffix(line, "\r"), "\r") // trim leading/trailing carriage returns for legacy Windows endline support + normLine := strings.Map(func(r rune) rune { + if unicode.IsSpace(r) { + return -1 // drop whitespace to avoid visually indistinguishable output + } + return r + }, line) + isPrintable := func(r rune) bool { + return unicode.IsPrint(r) || r == '\t' // specially treat tab as printable + } + isTripleQuoted = !strings.HasPrefix(line, `"""`) && !strings.HasPrefix(line, "...") && strings.TrimFunc(line, isPrintable) == "" + switch r.Diff { + case diffRemoved: + isTripleQuoted = isTripleQuoted && !prevInsertLines[normLine] + prevRemoveLines[normLine] = true + case diffInserted: + isTripleQuoted = isTripleQuoted && !prevRemoveLines[normLine] + prevInsertLines[normLine] = true + } + if !isTripleQuoted { + break + } + r.Value = textLine(line) + r.ElideComma = true + } + if !(r.Diff == diffRemoved || r.Diff == diffInserted) { // start a new non-adjacent difference group + prevRemoveLines = map[string]bool{} + prevInsertLines = map[string]bool{} + } + list2 = append(list2, r) + } + if r := list2[len(list2)-1]; r.Diff == diffIdentical && len(r.Value.(textLine)) == 0 { + list2 = list2[:len(list2)-1] // elide single empty line at the end + } + list2 = append(list2, textRecord{Value: textLine(`"""`), ElideComma: true}) + if isTripleQuoted { + var out textNode = &textWrap{Prefix: "(", Value: list2, Suffix: ")"} + switch t.Kind() { + case reflect.String: + if t != stringType { + out = opts.FormatType(t, out) + } + case reflect.Slice: + // Always emit type for slices since the triple-quote syntax + // looks like a string (not a slice). + opts = opts.WithTypeMode(emitType) + out = opts.FormatType(t, out) + } + return out + } + // If the text appears to be single-lined text, // then perform differencing in approximately fixed-sized chunks. // The output is printed as quoted strings. - case isText: + case isMostlyText: list = opts.formatDiffSlice( reflect.ValueOf(sx), reflect.ValueOf(sy), 64, "byte", func(v reflect.Value, d diffMode) textRecord { @@ -128,7 +258,7 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode { return textRecord{Diff: d, Value: textLine(s)} }, ) - delim = "" + // If the text appears to be binary data, // then perform differencing in approximately fixed-sized chunks. // The output is inspired by hexdump. @@ -145,6 +275,7 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode { return textRecord{Diff: d, Value: textLine(s), Comment: comment} }, ) + // For all other slices of primitive types, // then perform differencing in approximately fixed-sized chunks. // The size of each chunk depends on the width of the element kind. @@ -172,7 +303,9 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode { switch t.Elem().Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: ss = append(ss, fmt.Sprint(v.Index(i).Int())) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64: + ss = append(ss, fmt.Sprint(v.Index(i).Uint())) + case reflect.Uint8, reflect.Uintptr: ss = append(ss, formatHex(v.Index(i).Uint())) case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: ss = append(ss, fmt.Sprint(v.Index(i).Interface())) @@ -185,8 +318,8 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode { } // Wrap the output with appropriate type information. - var out textNode = textWrap{"{", list, "}"} - if !isText { + var out textNode = &textWrap{Prefix: "{", Value: list, Suffix: "}"} + if !isMostlyText { // The "{...}" byte-sequence literal is not valid Go syntax for strings. // Emit the type for extra clarity (e.g. "string{...}"). if t.Kind() == reflect.String { @@ -196,13 +329,13 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode { } switch t.Kind() { case reflect.String: - out = textWrap{"strings.Join(", out, fmt.Sprintf(", %q)", delim)} - if t != reflect.TypeOf(string("")) { + out = &textWrap{Prefix: "strings.Join(", Value: out, Suffix: fmt.Sprintf(", %q)", delim)} + if t != stringType { out = opts.FormatType(t, out) } case reflect.Slice: - out = textWrap{"bytes.Join(", out, fmt.Sprintf(", %q)", delim)} - if t != reflect.TypeOf([]byte(nil)) { + out = &textWrap{Prefix: "bytes.Join(", Value: out, Suffix: fmt.Sprintf(", %q)", delim)} + if t != bytesType { out = opts.FormatType(t, out) } } @@ -225,8 +358,11 @@ func (opts formatOptions) formatDiffSlice( vx, vy reflect.Value, chunkSize int, name string, makeRec func(reflect.Value, diffMode) textRecord, ) (list textList) { - es := diff.Difference(vx.Len(), vy.Len(), func(ix int, iy int) diff.Result { - return diff.BoolResult(vx.Index(ix).Interface() == vy.Index(iy).Interface()) + eq := func(ix, iy int) bool { + return vx.Index(ix).Interface() == vy.Index(iy).Interface() + } + es := diff.Difference(vx.Len(), vy.Len(), func(ix, iy int) diff.Result { + return diff.BoolResult(eq(ix, iy)) }) appendChunks := func(v reflect.Value, d diffMode) int { @@ -242,9 +378,23 @@ func (opts formatOptions) formatDiffSlice( return n0 - v.Len() } + var numDiffs int + maxLen := -1 + if opts.LimitVerbosity { + maxLen = (1 << opts.verbosity()) << 2 // 4, 8, 16, 32, 64, etc... + opts.VerbosityLevel-- + } + groups := coalesceAdjacentEdits(name, es) groups = coalesceInterveningIdentical(groups, chunkSize/4) + groups = cleanupSurroundingIdentical(groups, eq) + maxGroup := diffStats{Name: name} for i, ds := range groups { + if maxLen >= 0 && numDiffs >= maxLen { + maxGroup = maxGroup.Append(ds) + continue + } + // Print equal. if ds.NumDiff() == 0 { // Compute the number of leading and trailing equal bytes to print. @@ -273,36 +423,52 @@ func (opts formatOptions) formatDiffSlice( } // Print unequal. + len0 := len(list) nx := appendChunks(vx.Slice(0, ds.NumIdentical+ds.NumRemoved+ds.NumModified), diffRemoved) vx = vx.Slice(nx, vx.Len()) ny := appendChunks(vy.Slice(0, ds.NumIdentical+ds.NumInserted+ds.NumModified), diffInserted) vy = vy.Slice(ny, vy.Len()) + numDiffs += len(list) - len0 + } + if maxGroup.IsZero() { + assert(vx.Len() == 0 && vy.Len() == 0) + } else { + list.AppendEllipsis(maxGroup) } - assert(vx.Len() == 0 && vy.Len() == 0) return list } // coalesceAdjacentEdits coalesces the list of edits into groups of adjacent // equal or unequal counts. +// +// Example: +// +// Input: "..XXY...Y" +// Output: [ +// {NumIdentical: 2}, +// {NumRemoved: 2, NumInserted 1}, +// {NumIdentical: 3}, +// {NumInserted: 1}, +// ] func coalesceAdjacentEdits(name string, es diff.EditScript) (groups []diffStats) { - var prevCase int // Arbitrary index into which case last occurred - lastStats := func(i int) *diffStats { - if prevCase != i { + var prevMode byte + lastStats := func(mode byte) *diffStats { + if prevMode != mode { groups = append(groups, diffStats{Name: name}) - prevCase = i + prevMode = mode } return &groups[len(groups)-1] } for _, e := range es { switch e { case diff.Identity: - lastStats(1).NumIdentical++ + lastStats('=').NumIdentical++ case diff.UniqueX: - lastStats(2).NumRemoved++ + lastStats('!').NumRemoved++ case diff.UniqueY: - lastStats(2).NumInserted++ + lastStats('!').NumInserted++ case diff.Modified: - lastStats(2).NumModified++ + lastStats('!').NumModified++ } } return groups @@ -312,6 +478,34 @@ func coalesceAdjacentEdits(name string, es diff.EditScript) (groups []diffStats) // equal groups into adjacent unequal groups that currently result in a // dual inserted/removed printout. This acts as a high-pass filter to smooth // out high-frequency changes within the windowSize. +// +// Example: +// +// WindowSize: 16, +// Input: [ +// {NumIdentical: 61}, // group 0 +// {NumRemoved: 3, NumInserted: 1}, // group 1 +// {NumIdentical: 6}, // ├── coalesce +// {NumInserted: 2}, // ├── coalesce +// {NumIdentical: 1}, // ├── coalesce +// {NumRemoved: 9}, // └── coalesce +// {NumIdentical: 64}, // group 2 +// {NumRemoved: 3, NumInserted: 1}, // group 3 +// {NumIdentical: 6}, // ├── coalesce +// {NumInserted: 2}, // ├── coalesce +// {NumIdentical: 1}, // ├── coalesce +// {NumRemoved: 7}, // ├── coalesce +// {NumIdentical: 1}, // ├── coalesce +// {NumRemoved: 2}, // └── coalesce +// {NumIdentical: 63}, // group 4 +// ] +// Output: [ +// {NumIdentical: 61}, +// {NumIdentical: 7, NumRemoved: 12, NumInserted: 3}, +// {NumIdentical: 64}, +// {NumIdentical: 8, NumRemoved: 12, NumInserted: 3}, +// {NumIdentical: 63}, +// ] func coalesceInterveningIdentical(groups []diffStats, windowSize int) []diffStats { groups, groupsOrig := groups[:0], groups for i, ds := range groupsOrig { @@ -331,3 +525,90 @@ func coalesceInterveningIdentical(groups []diffStats, windowSize int) []diffStat } return groups } + +// cleanupSurroundingIdentical scans through all unequal groups, and +// moves any leading sequence of equal elements to the preceding equal group and +// moves and trailing sequence of equal elements to the succeeding equal group. +// +// This is necessary since coalesceInterveningIdentical may coalesce edit groups +// together such that leading/trailing spans of equal elements becomes possible. +// Note that this can occur even with an optimal diffing algorithm. +// +// Example: +// +// Input: [ +// {NumIdentical: 61}, +// {NumIdentical: 1 , NumRemoved: 11, NumInserted: 2}, // assume 3 leading identical elements +// {NumIdentical: 67}, +// {NumIdentical: 7, NumRemoved: 12, NumInserted: 3}, // assume 10 trailing identical elements +// {NumIdentical: 54}, +// ] +// Output: [ +// {NumIdentical: 64}, // incremented by 3 +// {NumRemoved: 9}, +// {NumIdentical: 67}, +// {NumRemoved: 9}, +// {NumIdentical: 64}, // incremented by 10 +// ] +func cleanupSurroundingIdentical(groups []diffStats, eq func(i, j int) bool) []diffStats { + var ix, iy int // indexes into sequence x and y + for i, ds := range groups { + // Handle equal group. + if ds.NumDiff() == 0 { + ix += ds.NumIdentical + iy += ds.NumIdentical + continue + } + + // Handle unequal group. + nx := ds.NumIdentical + ds.NumRemoved + ds.NumModified + ny := ds.NumIdentical + ds.NumInserted + ds.NumModified + var numLeadingIdentical, numTrailingIdentical int + for j := 0; j < nx && j < ny && eq(ix+j, iy+j); j++ { + numLeadingIdentical++ + } + for j := 0; j < nx && j < ny && eq(ix+nx-1-j, iy+ny-1-j); j++ { + numTrailingIdentical++ + } + if numIdentical := numLeadingIdentical + numTrailingIdentical; numIdentical > 0 { + if numLeadingIdentical > 0 { + // Remove leading identical span from this group and + // insert it into the preceding group. + if i-1 >= 0 { + groups[i-1].NumIdentical += numLeadingIdentical + } else { + // No preceding group exists, so prepend a new group, + // but do so after we finish iterating over all groups. + defer func() { + groups = append([]diffStats{{Name: groups[0].Name, NumIdentical: numLeadingIdentical}}, groups...) + }() + } + // Increment indexes since the preceding group would have handled this. + ix += numLeadingIdentical + iy += numLeadingIdentical + } + if numTrailingIdentical > 0 { + // Remove trailing identical span from this group and + // insert it into the succeeding group. + if i+1 < len(groups) { + groups[i+1].NumIdentical += numTrailingIdentical + } else { + // No succeeding group exists, so append a new group, + // but do so after we finish iterating over all groups. + defer func() { + groups = append(groups, diffStats{Name: groups[len(groups)-1].Name, NumIdentical: numTrailingIdentical}) + }() + } + // Do not increment indexes since the succeeding group will handle this. + } + + // Update this group since some identical elements were removed. + nx -= numIdentical + ny -= numIdentical + groups[i] = diffStats{Name: ds.Name, NumRemoved: nx, NumInserted: ny} + } + ix += nx + iy += ny + } + return groups +} diff --git a/vendor/github.com/google/go-cmp/cmp/report_text.go b/vendor/github.com/google/go-cmp/cmp/report_text.go index 8b8fcab..388fcf5 100644 --- a/vendor/github.com/google/go-cmp/cmp/report_text.go +++ b/vendor/github.com/google/go-cmp/cmp/report_text.go @@ -1,6 +1,6 @@ // Copyright 2019, 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.md file. +// license that can be found in the LICENSE file. package cmp @@ -10,12 +10,15 @@ import ( "math/rand" "strings" "time" + "unicode/utf8" "github.com/google/go-cmp/cmp/internal/flags" ) var randBool = rand.New(rand.NewSource(time.Now().Unix())).Intn(2) == 0 +const maxColumnLength = 80 + type indentMode int func (n indentMode) appendIndent(b []byte, d diffMode) []byte { @@ -91,21 +94,22 @@ type textNode interface { // textWrap is a wrapper that concatenates a prefix and/or a suffix // to the underlying node. type textWrap struct { - Prefix string // e.g., "bytes.Buffer{" - Value textNode // textWrap | textList | textLine - Suffix string // e.g., "}" + Prefix string // e.g., "bytes.Buffer{" + Value textNode // textWrap | textList | textLine + Suffix string // e.g., "}" + Metadata interface{} // arbitrary metadata; has no effect on formatting } -func (s textWrap) Len() int { +func (s *textWrap) Len() int { return len(s.Prefix) + s.Value.Len() + len(s.Suffix) } -func (s1 textWrap) Equal(s2 textNode) bool { - if s2, ok := s2.(textWrap); ok { +func (s1 *textWrap) Equal(s2 textNode) bool { + if s2, ok := s2.(*textWrap); ok { return s1.Prefix == s2.Prefix && s1.Value.Equal(s2.Value) && s1.Suffix == s2.Suffix } return false } -func (s textWrap) String() string { +func (s *textWrap) String() string { var d diffMode var n indentMode _, s2 := s.formatCompactTo(nil, d) @@ -114,7 +118,7 @@ func (s textWrap) String() string { b = append(b, '\n') // Trailing newline return string(b) } -func (s textWrap) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) { +func (s *textWrap) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) { n0 := len(b) // Original buffer length b = append(b, s.Prefix...) b, s.Value = s.Value.formatCompactTo(b, d) @@ -124,7 +128,7 @@ func (s textWrap) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) { } return b, s } -func (s textWrap) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte { +func (s *textWrap) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte { b = append(b, s.Prefix...) b = s.Value.formatExpandedTo(b, d, n) b = append(b, s.Suffix...) @@ -136,22 +140,23 @@ func (s textWrap) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte { // of the textList.formatCompactTo method. type textList []textRecord type textRecord struct { - Diff diffMode // e.g., 0 or '-' or '+' - Key string // e.g., "MyField" - Value textNode // textWrap | textLine - Comment fmt.Stringer // e.g., "6 identical fields" + Diff diffMode // e.g., 0 or '-' or '+' + Key string // e.g., "MyField" + Value textNode // textWrap | textLine + ElideComma bool // avoid trailing comma + Comment fmt.Stringer // e.g., "6 identical fields" } // AppendEllipsis appends a new ellipsis node to the list if none already // exists at the end. If cs is non-zero it coalesces the statistics with the // previous diffStats. func (s *textList) AppendEllipsis(ds diffStats) { - hasStats := ds != diffStats{} + hasStats := !ds.IsZero() if len(*s) == 0 || !(*s)[len(*s)-1].Value.Equal(textEllipsis) { if hasStats { - *s = append(*s, textRecord{Value: textEllipsis, Comment: ds}) + *s = append(*s, textRecord{Value: textEllipsis, ElideComma: true, Comment: ds}) } else { - *s = append(*s, textRecord{Value: textEllipsis}) + *s = append(*s, textRecord{Value: textEllipsis, ElideComma: true}) } return } @@ -191,7 +196,7 @@ func (s1 textList) Equal(s2 textNode) bool { } func (s textList) String() string { - return textWrap{"{", s, "}"}.String() + return (&textWrap{Prefix: "{", Value: s, Suffix: "}"}).String() } func (s textList) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) { @@ -221,7 +226,7 @@ func (s textList) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) { } // Force multi-lined output when printing a removed/inserted node that // is sufficiently long. - if (d == diffInserted || d == diffRemoved) && len(b[n0:]) > 80 { + if (d == diffInserted || d == diffRemoved) && len(b[n0:]) > maxColumnLength { multiLine = true } if !multiLine { @@ -236,16 +241,50 @@ func (s textList) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte { _, isLine := r.Value.(textLine) return r.Key == "" || !isLine }, - func(r textRecord) int { return len(r.Key) }, + func(r textRecord) int { return utf8.RuneCountInString(r.Key) }, ) alignValueLens := s.alignLens( func(r textRecord) bool { _, isLine := r.Value.(textLine) return !isLine || r.Value.Equal(textEllipsis) || r.Comment == nil }, - func(r textRecord) int { return len(r.Value.(textLine)) }, + func(r textRecord) int { return utf8.RuneCount(r.Value.(textLine)) }, ) + // Format lists of simple lists in a batched form. + // If the list is sequence of only textLine values, + // then batch multiple values on a single line. + var isSimple bool + for _, r := range s { + _, isLine := r.Value.(textLine) + isSimple = r.Diff == 0 && r.Key == "" && isLine && r.Comment == nil + if !isSimple { + break + } + } + if isSimple { + n++ + var batch []byte + emitBatch := func() { + if len(batch) > 0 { + b = n.appendIndent(append(b, '\n'), d) + b = append(b, bytes.TrimRight(batch, " ")...) + batch = batch[:0] + } + } + for _, r := range s { + line := r.Value.(textLine) + if len(batch)+len(line)+len(", ") > maxColumnLength { + emitBatch() + } + batch = append(batch, line...) + batch = append(batch, ", "...) + } + emitBatch() + n-- + return n.appendIndent(append(b, '\n'), d) + } + // Format the list as a multi-lined output. n++ for i, r := range s { @@ -256,7 +295,7 @@ func (s textList) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte { b = alignKeyLens[i].appendChar(b, ' ') b = r.Value.formatExpandedTo(b, d|r.Diff, n) - if !r.Value.Equal(textEllipsis) { + if !r.ElideComma { b = append(b, ',') } b = alignValueLens[i].appendChar(b, ' ') @@ -332,6 +371,11 @@ type diffStats struct { NumModified int } +func (s diffStats) IsZero() bool { + s.Name = "" + return s == diffStats{} +} + func (s diffStats) NumDiff() int { return s.NumRemoved + s.NumInserted + s.NumModified } @@ -349,6 +393,7 @@ func (s diffStats) Append(ds diffStats) diffStats { // String prints a humanly-readable summary of coalesced records. // // Example: +// // diffStats{Name: "Field", NumIgnored: 5}.String() => "5 ignored fields" func (s diffStats) String() string { var ss []string diff --git a/vendor/github.com/google/go-cmp/cmp/report_value.go b/vendor/github.com/google/go-cmp/cmp/report_value.go index 83031a7..668d470 100644 --- a/vendor/github.com/google/go-cmp/cmp/report_value.go +++ b/vendor/github.com/google/go-cmp/cmp/report_value.go @@ -1,6 +1,6 @@ // Copyright 2019, 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.md file. +// license that can be found in the LICENSE file. package cmp diff --git a/vendor/github.com/google/go-cmp/go.mod b/vendor/github.com/google/go-cmp/go.mod deleted file mode 100644 index 6c0e40e..0000000 --- a/vendor/github.com/google/go-cmp/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/google/go-cmp - -go 1.8 diff --git a/vendor/github.com/grid-x/serial/go.mod b/vendor/github.com/grid-x/serial/go.mod deleted file mode 100644 index 30d4080..0000000 --- a/vendor/github.com/grid-x/serial/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/grid-x/serial - -go 1.13 diff --git a/vendor/github.com/grid-x/serial/serial_bsd.go b/vendor/github.com/grid-x/serial/serial_bsd.go index 398870c..5e23da6 100644 --- a/vendor/github.com/grid-x/serial/serial_bsd.go +++ b/vendor/github.com/grid-x/serial/serial_bsd.go @@ -1,4 +1,4 @@ -// +build freebsd openbsd netbsd +// +build freebsd netbsd package serial diff --git a/vendor/github.com/grid-x/serial/serial_openbsd.go b/vendor/github.com/grid-x/serial/serial_openbsd.go new file mode 100644 index 0000000..01913aa --- /dev/null +++ b/vendor/github.com/grid-x/serial/serial_openbsd.go @@ -0,0 +1,90 @@ +package serial + +import ( + "fmt" + "syscall" + "unsafe" +) + +var baudRates = map[int]uint32{ + 50: syscall.B50, + 75: syscall.B75, + 110: syscall.B110, + 134: syscall.B134, + 150: syscall.B150, + 200: syscall.B200, + 300: syscall.B300, + 600: syscall.B600, + 1200: syscall.B1200, + 1800: syscall.B1800, + 2400: syscall.B2400, + 4800: syscall.B4800, + 9600: syscall.B9600, + 19200: syscall.B19200, + 38400: syscall.B38400, + 57600: syscall.B57600, + 115200: syscall.B115200, + 230400: syscall.B230400, +} + +var charSizes = map[int]uint32{ + 5: syscall.CS5, + 6: syscall.CS6, + 7: syscall.CS7, + 8: syscall.CS8, +} + +// syscallSelect is a wapper for syscall.Select that only returns error. +func syscallSelect(n int, r *syscall.FdSet, w *syscall.FdSet, e *syscall.FdSet, tv *syscall.Timeval) error { + return syscall.Select(n, r, w, e, tv) +} + +// tcsetattr sets terminal file descriptor parameters. +// See man tcsetattr(3). +func tcsetattr(fd int, termios *syscall.Termios) (err error) { + r, _, errno := syscall.Syscall(uintptr(syscall.SYS_IOCTL), + uintptr(fd), uintptr(syscall.TIOCSETA), uintptr(unsafe.Pointer(termios))) + if errno != 0 { + err = errno + return + } + if r != 0 { + err = fmt.Errorf("tcsetattr failed %v", r) + } + return +} + +// tcgetattr gets terminal file descriptor parameters. +// See man tcgetattr(3). +func tcgetattr(fd int, termios *syscall.Termios) (err error) { + r, _, errno := syscall.Syscall(uintptr(syscall.SYS_IOCTL), + uintptr(fd), uintptr(syscall.TIOCGETA), uintptr(unsafe.Pointer(termios))) + if errno != 0 { + err = errno + return + } + if r != 0 { + err = fmt.Errorf("tcgetattr failed %v", r) + return + } + return +} + +// fdget returns index and offset of fd in fds. +func fdget(fd int, fds *syscall.FdSet) (index, offset int) { + index = fd / (syscall.FD_SETSIZE / len(fds.Bits)) % len(fds.Bits) + offset = fd % (syscall.FD_SETSIZE / len(fds.Bits)) + return +} + +// fdset implements FD_SET macro. +func fdset(fd int, fds *syscall.FdSet) { + idx, pos := fdget(fd, fds) + fds.Bits[idx] = 1 << uint(pos) +} + +// fdisset implements FD_ISSET macro. +func fdisset(fd int, fds *syscall.FdSet) bool { + idx, pos := fdget(fd, fds) + return fds.Bits[idx]&(1<= 0, generated slices have minimum length of minLen. +// If maxLen >= 0, generated slices have maximum length of maxLen. SliceOfN panics if maxLen >= 0 +// and minLen > maxLen. +func SliceOfN[E any](elem *Generator[E], minLen int, maxLen int) *Generator[[]E] { assertValidRange(minLen, maxLen) - return newGenerator(&sliceGen{ - typ: reflect.SliceOf(elem.type_()), + return newGenerator[[]E](&sliceGen[E, struct{}]{ minLen: minLen, maxLen: maxLen, elem: elem, }) } -func SliceOfDistinct(elem *Generator, keyFn interface{}) *Generator { +// SliceOfDistinct is a shorthand for [SliceOfNDistinct](elem, -1, -1, keyFn). +func SliceOfDistinct[E any, K comparable](elem *Generator[E], keyFn func(E) K) *Generator[[]E] { return SliceOfNDistinct(elem, -1, -1, keyFn) } -func SliceOfNDistinct(elem *Generator, minLen int, maxLen int, keyFn interface{}) *Generator { +// SliceOfNDistinct creates a []E generator. Elements of each generated slice are distinct according to keyFn. +// If minLen >= 0, generated slices have minimum length of minLen. If maxLen >= 0, generated slices +// have maximum length of maxLen. SliceOfNDistinct panics if maxLen >= 0 and minLen > maxLen. +// [ID] helper can be used as keyFn to generate slices of distinct comparable elements. +func SliceOfNDistinct[E any, K comparable](elem *Generator[E], minLen int, maxLen int, keyFn func(E) K) *Generator[[]E] { assertValidRange(minLen, maxLen) - keyTyp := elem.type_() - if keyFn != nil { - t := reflect.TypeOf(keyFn) - assertCallable(t, elem.type_(), "keyFn") - keyTyp = t.Out(0) - } - assertf(keyTyp.Comparable(), "key type should be comparable (got %v)", keyTyp) - - return newGenerator(&sliceGen{ - typ: reflect.SliceOf(elem.type_()), + return newGenerator[[]E](&sliceGen[E, K]{ minLen: minLen, maxLen: maxLen, elem: elem, - keyTyp: keyTyp, - keyFn: reflect.ValueOf(keyFn), + keyFn: keyFn, }) } -type sliceGen struct { - typ reflect.Type +type sliceGen[E any, K comparable] struct { minLen int maxLen int - elem *Generator - keyTyp reflect.Type - keyFn reflect.Value + elem *Generator[E] + keyFn func(E) K } -func (g *sliceGen) String() string { - if g.keyTyp == nil { +func (g *sliceGen[E, K]) String() string { + if g.keyFn == nil { if g.minLen < 0 && g.maxLen < 0 { return fmt.Sprintf("SliceOf(%v)", g.elem) } else { return fmt.Sprintf("SliceOfN(%v, minLen=%v, maxLen=%v)", g.elem, g.minLen, g.maxLen) } } else { - key := "" - if g.keyFn.IsValid() { - key = fmt.Sprintf(", key=func(%v) %v", g.elem.type_(), g.keyTyp) - } - if g.minLen < 0 && g.maxLen < 0 { - return fmt.Sprintf("SliceOfDistinct(%v%v)", g.elem, key) + return fmt.Sprintf("SliceOfDistinct(%v, key=%T)", g.elem, g.keyFn) } else { - return fmt.Sprintf("SliceOfNDistinct(%v, minLen=%v, maxLen=%v%v)", g.elem, g.minLen, g.maxLen, key) + return fmt.Sprintf("SliceOfNDistinct(%v, minLen=%v, maxLen=%v, key=%T)", g.elem, g.minLen, g.maxLen, g.keyFn) } } } -func (g *sliceGen) type_() reflect.Type { - return g.typ -} - -func (g *sliceGen) value(t *T) value { - repeat := newRepeat(g.minLen, g.maxLen, -1) +func (g *sliceGen[E, K]) value(t *T) []E { + repeat := newRepeat(g.minLen, g.maxLen, -1, g.elem.String()) - var seen reflect.Value - if g.keyTyp != nil { - seen = reflect.MakeMapWithSize(reflect.MapOf(g.keyTyp, emptyStructType), repeat.avg()) + var seen map[K]struct{} + if g.keyFn != nil { + seen = make(map[K]struct{}, repeat.avg()) } - sl := reflect.MakeSlice(g.typ, 0, repeat.avg()) - for repeat.more(t.s, g.elem.String()) { - e := reflect.ValueOf(g.elem.value(t)) - if g.keyTyp == nil { - sl = reflect.Append(sl, e) + sl := make([]E, 0, repeat.avg()) + for repeat.more(t.s) { + e := g.elem.value(t) + if g.keyFn == nil { + sl = append(sl, e) } else { - k := e - if g.keyFn.IsValid() { - k = g.keyFn.Call([]reflect.Value{k})[0] - } - - if seen.MapIndex(k).IsValid() { + k := g.keyFn(e) + if _, ok := seen[k]; ok { repeat.reject() } else { - seen.SetMapIndex(k, emptyStructValue) - sl = reflect.Append(sl, e) + seen[k] = struct{}{} + sl = append(sl, e) } } } - return sl.Interface() + return sl } -func MapOf(key *Generator, val *Generator) *Generator { +// MapOf is a shorthand for [MapOfN](key, val, -1, -1). +func MapOf[K comparable, V any](key *Generator[K], val *Generator[V]) *Generator[map[K]V] { return MapOfN(key, val, -1, -1) } -func MapOfN(key *Generator, val *Generator, minLen int, maxLen int) *Generator { +// MapOfN creates a map[K]V generator. If minLen >= 0, generated maps have minimum length of minLen. +// If maxLen >= 0, generated maps have maximum length of maxLen. MapOfN panics if maxLen >= 0 +// and minLen > maxLen. +func MapOfN[K comparable, V any](key *Generator[K], val *Generator[V], minLen int, maxLen int) *Generator[map[K]V] { assertValidRange(minLen, maxLen) - assertf(key.type_().Comparable(), "key type should be comparable (got %v)", key.type_()) - return newGenerator(&mapGen{ - typ: reflect.MapOf(key.type_(), val.type_()), + return newGenerator[map[K]V](&mapGen[K, V]{ minLen: minLen, maxLen: maxLen, key: key, @@ -133,133 +120,75 @@ func MapOfN(key *Generator, val *Generator, minLen int, maxLen int) *Generator { }) } -func MapOfValues(val *Generator, keyFn interface{}) *Generator { +// MapOfValues is a shorthand for [MapOfNValues](val, -1, -1, keyFn). +func MapOfValues[K comparable, V any](val *Generator[V], keyFn func(V) K) *Generator[map[K]V] { return MapOfNValues(val, -1, -1, keyFn) } -func MapOfNValues(val *Generator, minLen int, maxLen int, keyFn interface{}) *Generator { +// MapOfNValues creates a map[K]V generator, where keys are generated by applying keyFn to values. +// If minLen >= 0, generated maps have minimum length of minLen. If maxLen >= 0, generated maps +// have maximum length of maxLen. MapOfNValues panics if maxLen >= 0 and minLen > maxLen. +func MapOfNValues[K comparable, V any](val *Generator[V], minLen int, maxLen int, keyFn func(V) K) *Generator[map[K]V] { assertValidRange(minLen, maxLen) - keyTyp := val.type_() - if keyFn != nil { - t := reflect.TypeOf(keyFn) - assertCallable(t, val.type_(), "keyFn") - keyTyp = t.Out(0) - } - assertf(keyTyp.Comparable(), "key type should be comparable (got %v)", keyTyp) - - return newGenerator(&mapGen{ - typ: reflect.MapOf(keyTyp, val.type_()), + return newGenerator[map[K]V](&mapGen[K, V]{ minLen: minLen, maxLen: maxLen, val: val, - keyTyp: keyTyp, - keyFn: reflect.ValueOf(keyFn), + keyFn: keyFn, }) } -type mapGen struct { - typ reflect.Type +type mapGen[K comparable, V any] struct { minLen int maxLen int - key *Generator - val *Generator - keyTyp reflect.Type - keyFn reflect.Value + key *Generator[K] + val *Generator[V] + keyFn func(V) K } -func (g *mapGen) String() string { - if g.keyTyp == nil { +func (g *mapGen[K, V]) String() string { + if g.key != nil { if g.minLen < 0 && g.maxLen < 0 { return fmt.Sprintf("MapOf(%v, %v)", g.key, g.val) } else { return fmt.Sprintf("MapOfN(%v, %v, minLen=%v, maxLen=%v)", g.key, g.val, g.minLen, g.maxLen) } } else { - key := "" - if g.keyFn.IsValid() { - key = fmt.Sprintf(", key=func(%v) %v", g.val.type_(), g.keyTyp) - } - if g.minLen < 0 && g.maxLen < 0 { - return fmt.Sprintf("MapOfValues(%v%v)", g.val, key) + return fmt.Sprintf("MapOfValues(%v, key=%T)", g.val, g.keyFn) } else { - return fmt.Sprintf("MapOfNValues(%v, minLen=%v, maxLen=%v%v)", g.val, g.minLen, g.maxLen, key) + return fmt.Sprintf("MapOfNValues(%v, minLen=%v, maxLen=%v, key=%T)", g.val, g.minLen, g.maxLen, g.keyFn) } } } -func (g *mapGen) type_() reflect.Type { - return g.typ -} - -func (g *mapGen) value(t *T) value { +func (g *mapGen[K, V]) value(t *T) map[K]V { label := g.val.String() if g.key != nil { label = g.key.String() + "," + label } - repeat := newRepeat(g.minLen, g.maxLen, -1) + repeat := newRepeat(g.minLen, g.maxLen, -1, label) - m := reflect.MakeMapWithSize(g.typ, repeat.avg()) - for repeat.more(t.s, label) { - var k, v reflect.Value - if g.keyTyp == nil { - k = reflect.ValueOf(g.key.value(t)) - v = reflect.ValueOf(g.val.value(t)) + m := make(map[K]V, repeat.avg()) + for repeat.more(t.s) { + var k K + var v V + if g.key != nil { + k = g.key.value(t) + v = g.val.value(t) } else { - v = reflect.ValueOf(g.val.value(t)) - k = v - if g.keyFn.IsValid() { - k = g.keyFn.Call([]reflect.Value{v})[0] - } + v = g.val.value(t) + k = g.keyFn(v) } - if m.MapIndex(k).IsValid() { + if _, ok := m[k]; ok { repeat.reject() } else { - m.SetMapIndex(k, v) - } - } - - return m.Interface() -} - -func ArrayOf(count int, elem *Generator) *Generator { - assertf(count >= 0 && count < 1024, "array element count should be in [0, 1024] (got %v)", count) - - return newGenerator(&arrayGen{ - typ: reflect.ArrayOf(count, elem.type_()), - count: count, - elem: elem, - }) -} - -type arrayGen struct { - typ reflect.Type - count int - elem *Generator -} - -func (g *arrayGen) String() string { - return fmt.Sprintf("ArrayOf(%v, %v)", g.count, g.elem) -} - -func (g *arrayGen) type_() reflect.Type { - return g.typ -} - -func (g *arrayGen) value(t *T) value { - a := reflect.Indirect(reflect.New(g.typ)) - - if g.count == 0 { - t.s.drawBits(0) - } else { - for i := 0; i < g.count; i++ { - e := reflect.ValueOf(g.elem.value(t)) - a.Index(i).Set(e) + m[k] = v } } - return a.Interface() + return m } diff --git a/vendor/pgregory.net/rapid/collections_example_test.go b/vendor/pgregory.net/rapid/collections_example_test.go deleted file mode 100644 index 2f0bd07..0000000 --- a/vendor/pgregory.net/rapid/collections_example_test.go +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2020 Gregory Petrosyan -// -// 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 https://mozilla.org/MPL/2.0/. - -package rapid_test - -import ( - "fmt" - - "pgregory.net/rapid" -) - -func ExampleSliceOf() { - gen := rapid.SliceOf(rapid.Int()) - - for i := 0; i < 5; i++ { - fmt.Println(gen.Example(i)) - } - // Output: - // [1 -1902 7 -236 14 -433 -1572631 -1 4219826 -50 1414 -3890044391133 -9223372036854775808 5755498240 -10 680558 10 -80458281 0 -27] - // [-3 -2 -1 -3 -2172865589 -5 -2 -2503553836720] - // [4 308 -2 21 -5843 3 1 78 6129321692 -59] - // [590 -131 -15 -769 16 -1 14668 14 -1 -58784] - // [] -} - -func ExampleSliceOfN() { - gen := rapid.SliceOfN(rapid.Int(), 5, 5) - - for i := 0; i < 5; i++ { - fmt.Println(gen.Example(i)) - } - // Output: - // [1 -1902 7 -236 14] - // [-3 -2 -1 -3 -2172865589] - // [4 308 -2 21 -5843] - // [590 -131 -15 -769 16] - // [4629136912 270 141395 -129322425838843911 -7] -} - -func ExampleSliceOfDistinct() { - gen := rapid.SliceOfDistinct(rapid.IntMin(0), func(i int) int { return i % 2 }) - - for i := 0; i < 5; i++ { - fmt.Println(gen.Example(i)) - } - // Output: - // [1] - // [2 1] - // [4 1] - // [590] - // [] -} - -func ExampleSliceOfNDistinct() { - gen := rapid.SliceOfNDistinct(rapid.IntMin(0), 2, 2, func(i int) int { return i % 2 }) - - for i := 0; i < 5; i++ { - fmt.Println(gen.Example(i)) - } - // Output: - // [4219826 49] - // [2 1] - // [4 1] - // [0 58783] - // [4629136912 141395] -} - -func ExampleMapOf() { - gen := rapid.MapOf(rapid.Int(), rapid.StringMatching(`[a-z]+`)) - - for i := 0; i < 5; i++ { - fmt.Println(gen.Example(i)) - } - // Output: - // map[1:nhlgqwasbggbaociac 561860:r] - // map[-3752:pizpv -3:bacuabp 0:bi] - // map[-33086515648293:gewf -264276:b -1313:a -258:v -4:b -2:fdhbzcz 4:ubfsdbowrja 1775:tcozav 8334:lvcprss 376914:braigey] - // map[-350:h 590:coaaamcasnapgaad] - // map[] -} - -func ExampleMapOfN() { - gen := rapid.MapOfN(rapid.Int(), rapid.StringMatching(`[a-z]+`), 5, 5) - - for i := 0; i < 5; i++ { - fmt.Println(gen.Example(i)) - } - // Output: - // map[-130450326583:bd -2983:bbdbcs 1:nhlgqwasbggbaociac 31:kmdnpmcbuagzr 561860:r] - // map[-82024404:d -3752:pizpv -3:bacuabp 0:bi 179745:rzkneb] - // map[-33086515648293:gewf -258:v 4:ubfsdbowrja 1775:tcozav 8334:lvcprss] - // map[-4280678227:j -25651:aafmd -3308:o -350:h 590:coaaamcasnapgaad] - // map[-9614404661322:gsb -378:y 2:paai 4629136912:otg 1476419818092:qign] -} - -func ExampleMapOfValues() { - gen := rapid.MapOfValues(rapid.StringMatching(`[a-z]+`), func(s string) int { return len(s) }) - - for i := 0; i < 5; i++ { - fmt.Println(gen.Example(i)) - } - // Output: - // map[2:dr 7:xguehfc 11:sbggbaociac] - // map[2:bp 5:jarxz 6:ebzkwa] - // map[1:j 2:aj 3:gjl 4:vayt 5:eeeqa 6:riacaa 7:stcozav 8:mfdhbzcz 9:fxmcadagf 10:bgsbraigey 15:gxongygnxqlovib] - // map[2:ub 8:waraafmd 10:bfiqcaxazu 16:rjgqimcasnapgaad 17:gckfbljafcedhcvfc] - // map[] -} - -func ExampleMapOfNValues() { - gen := rapid.MapOfNValues(rapid.StringMatching(`[a-z]+`), 5, 5, func(s string) int { return len(s) }) - - for i := 0; i < 5; i++ { - fmt.Println(gen.Example(i)) - } - // Output: - // map[1:s 2:dr 3:anc 7:xguehfc 11:sbggbaociac] - // map[1:b 2:bp 4:ydag 5:jarxz 6:ebzkwa] - // map[1:j 3:gjl 5:eeeqa 7:stcozav 9:fxmcadagf] - // map[2:ub 8:waraafmd 10:bfiqcaxazu 16:rjgqimcasnapgaad 17:gckfbljafcedhcvfc] - // map[1:k 2:ay 3:wzb 4:dign 7:faabhcb] -} - -func ExampleArrayOf() { - gen := rapid.ArrayOf(5, rapid.Int()) - - for i := 0; i < 5; i++ { - fmt.Println(gen.Example(i)) - } - // Output: - // [-3 1303 184 7 236258] - // [-186981 -59881619 0 -1 168442] - // [4 441488606 -4008258 -2 297] - // [-2 -5863986 22973756520 -15 766316951] - // [43 -3513 16 141395 -9223372036854775808] -} diff --git a/vendor/pgregory.net/rapid/collections_external_test.go b/vendor/pgregory.net/rapid/collections_external_test.go deleted file mode 100644 index 0ef838e..0000000 --- a/vendor/pgregory.net/rapid/collections_external_test.go +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2019 Gregory Petrosyan -// -// 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 https://mozilla.org/MPL/2.0/. - -package rapid_test - -import ( - "reflect" - "strconv" - "testing" - - . "pgregory.net/rapid" -) - -func TestSliceOf(t *testing.T) { - t.Parallel() - - gens := []*Generator{ - SliceOf(Bool()), - SliceOf(Byte()), - SliceOf(Int()), - SliceOf(Uint()), - } - - for _, g := range gens { - t.Run(g.String(), MakeCheck(func(t *T) { - v := g.Draw(t, "v") - if rv(v).Kind() != reflect.Slice { - t.Fatalf("got not a slice") - } - if rv(v).Len() == 0 { - t.Skip("empty") - } - })) - } -} - -func TestSliceOfDistinct(t *testing.T) { - t.Parallel() - - g := SliceOfDistinct(Int(), nil) - - Check(t, func(t *T) { - s := g.Draw(t, "s").([]int) - m := map[int]struct{}{} - for _, i := range s { - m[i] = struct{}{} - } - if len(m) != len(s) { - t.Fatalf("%v unique out of %v", len(m), len(s)) - } - }) -} - -func TestSliceOfDistinctBy(t *testing.T) { - t.Parallel() - - g := SliceOfDistinct(Int(), func(i int) string { return strconv.Itoa(i % 5) }) - - Check(t, func(t *T) { - s := g.Draw(t, "s").([]int) - m := map[int]struct{}{} - for _, i := range s { - m[i%5] = struct{}{} - } - if len(m) != len(s) { - t.Fatalf("%v unique out of %v", len(m), len(s)) - } - }) -} - -func TestMapOf(t *testing.T) { - t.Parallel() - - gens := []*Generator{ - MapOf(Bool(), Int()), - MapOf(Int(), Uint()), - MapOf(Uint(), SliceOf(Bool())), - } - - for _, g := range gens { - t.Run(g.String(), MakeCheck(func(t *T) { - v := g.Draw(t, "v") - if rv(v).Kind() != reflect.Map { - t.Fatalf("got not a map") - } - if rv(v).Len() == 0 { - t.Skip("empty") - } - })) - } -} - -func TestMapOfValues(t *testing.T) { - t.Parallel() - - g := MapOfValues(Custom(genStruct), func(s testStruct) int { return s.x }) - - Check(t, func(t *T) { - m := g.Draw(t, "m").(map[int]testStruct) - for k, v := range m { - if k != v.x { - t.Fatalf("got key %v with value %v", k, v) - } - } - }) -} - -func TestArrayOf(t *testing.T) { - t.Parallel() - - elems := []*Generator{Bool(), Int(), Uint()} - counts := []int{0, 1, 3, 17} - - for _, e := range elems { - for _, c := range counts { - g := ArrayOf(c, e) - t.Run(g.String(), MakeCheck(func(t *T) { - v := g.Draw(t, "v") - if rv(v).Len() != c { - t.Fatalf("len is %v instead of %v", rv(v).Len(), c) - } - })) - } - } -} - -func TestCollectionLenLimits(t *testing.T) { - t.Parallel() - - genFuncs := []func(i, j int) *Generator{ - func(i, j int) *Generator { return StringOfN(Byte(), i, j, -1) }, - func(i, j int) *Generator { return SliceOfN(Byte(), i, j) }, - func(i, j int) *Generator { return SliceOfNDistinct(Byte(), i, j, nil) }, - func(i, j int) *Generator { return SliceOfNDistinct(Int(), i, j, func(n int) int { return n % j }) }, - func(i, j int) *Generator { return MapOfN(Int(), Int(), i, j) }, - func(i, j int) *Generator { return MapOfNValues(Int(), i, j, nil) }, - func(i, j int) *Generator { return MapOfNValues(Int(), i, j, func(n int) int { return n % j }) }, - } - - for i, gf := range genFuncs { - t.Run(strconv.Itoa(i), MakeCheck(func(t *T) { - minLen := IntRange(0, 256).Draw(t, "minLen").(int) - maxLen := IntMin(minLen).Draw(t, "maxLen").(int) - - s := rv(gf(minLen, maxLen).Draw(t, "s")) - if s.Len() < minLen { - t.Fatalf("got collection of length %v with minLen %v", s.Len(), minLen) - } - if s.Len() > maxLen { - t.Fatalf("got collection of length %v with maxLen %v", s.Len(), maxLen) - } - })) - } -} diff --git a/vendor/pgregory.net/rapid/collections_test.go b/vendor/pgregory.net/rapid/collections_test.go deleted file mode 100644 index b0fd029..0000000 --- a/vendor/pgregory.net/rapid/collections_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2019 Gregory Petrosyan -// -// 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 https://mozilla.org/MPL/2.0/. - -package rapid - -import "testing" - -func TestCollectionsWithImpossibleMinSize(t *testing.T) { - t.Parallel() - - s := createRandomBitStream(t) - gens := []*Generator{ - MapOfN(Bool(), Int(), 10, -1), - SliceOfNDistinct(Int(), 10, -1, func(i int) int { return i % 5 }), - } - - for _, g := range gens { - t.Run(g.String(), func(t *testing.T) { - _, err := recoverValue(g, newT(nil, s, false, nil)) - if err == nil || !err.isInvalidData() { - t.Fatalf("got error %v instead of invalid data", err) - } - }) - } -} diff --git a/vendor/pgregory.net/rapid/combinators.go b/vendor/pgregory.net/rapid/combinators.go index 991063d..b3b1808 100644 --- a/vendor/pgregory.net/rapid/combinators.go +++ b/vendor/pgregory.net/rapid/combinators.go @@ -8,48 +8,35 @@ package rapid import ( "fmt" - "reflect" + "math" "strings" ) const tryLabel = "try" -var ( - boolType = reflect.TypeOf(false) - tPtrType = reflect.TypeOf((*T)(nil)) - emptyInterfaceType = reflect.TypeOf([]interface{}{}).Elem() -) - -func Custom(fn interface{}) *Generator { - f := reflect.ValueOf(fn) - t := f.Type() - - assertCallable(t, tPtrType, "fn") - - return newGenerator(&customGen{ - typ: t.Out(0), - fn: f, +// Custom creates a generator which produces results of calling fn. In fn, values should be generated +// by calling other generators; it is invalid to return a value from fn without using any other generator. +// Custom is a primary way of creating user-defined generators. +func Custom[V any](fn func(*T) V) *Generator[V] { + return newGenerator[V](&customGen[V]{ + fn: fn, }) } -type customGen struct { - typ reflect.Type - fn reflect.Value +type customGen[V any] struct { + fn func(*T) V } -func (g *customGen) String() string { - return fmt.Sprintf("Custom(%v)", g.typ) +func (g *customGen[V]) String() string { + var v V + return fmt.Sprintf("Custom(%T)", v) } -func (g *customGen) type_() reflect.Type { - return g.typ -} - -func (g *customGen) value(t *T) value { +func (g *customGen[V]) value(t *T) V { return find(g.maybeValue, t, small) } -func (g *customGen) maybeValue(t *T) value { +func (g *customGen[V]) maybeValue(t *T) (V, bool) { t = newT(t.tb, t.s, flags.debug, nil) defer func() { @@ -60,57 +47,69 @@ func (g *customGen) maybeValue(t *T) value { } }() - return call(g.fn, reflect.ValueOf(t)) + return g.fn(t), true } -func filter(g *Generator, fn interface{}) *Generator { - f := reflect.ValueOf(fn) - t := f.Type() +// Deferred creates a generator which defers calling fn until attempting to produce a value. This allows +// to define recursive generators. +func Deferred[V any](fn func() *Generator[V]) *Generator[V] { + return newGenerator[V](&deferredGen[V]{ + fn: fn, + }) +} - assertCallable(t, g.type_(), "fn") - assertf(t.Out(0) == boolType, "fn should return bool, not %v", t.Out(0)) +type deferredGen[V any] struct { + g *Generator[V] + fn func() *Generator[V] +} - return newGenerator(&filteredGen{ - g: g, - fn: func(v value) bool { - return call(f, reflect.ValueOf(v)).(bool) - }, - }) +func (g *deferredGen[V]) String() string { + var v V + return fmt.Sprintf("Deferred(%T)", v) } -type filteredGen struct { - g *Generator - fn func(value) bool +func (g *deferredGen[V]) value(t *T) V { + if g.g == nil { + g.g = g.fn() + } + return g.g.value(t) } -func (g *filteredGen) String() string { - return fmt.Sprintf("%v.Filter(...)", g.g) +func filter[V any](g *Generator[V], fn func(V) bool) *Generator[V] { + return newGenerator[V](&filteredGen[V]{ + g: g, + fn: fn, + }) } -func (g *filteredGen) type_() reflect.Type { - return g.g.type_() +type filteredGen[V any] struct { + g *Generator[V] + fn func(V) bool } -func (g *filteredGen) value(t *T) value { +func (g *filteredGen[V]) String() string { + return fmt.Sprintf("%v.Filter(...)", g.g) +} + +func (g *filteredGen[V]) value(t *T) V { return find(g.maybeValue, t, small) } -func (g *filteredGen) maybeValue(t *T) value { +func (g *filteredGen[V]) maybeValue(t *T) (V, bool) { v := g.g.value(t) if g.fn(v) { - return v + return v, true } else { - return nil + var zero V + return zero, false } } -func find(gen func(*T) value, t *T, tries int) value { +func find[V any](gen func(*T) (V, bool), t *T, tries int) V { for n := 0; n < tries; n++ { i := t.s.beginGroup(tryLabel, false) - v := gen(t) - ok := v != nil + v, ok := gen(t) t.s.endGroup(i, !ok) - if ok { return v } @@ -119,103 +118,110 @@ func find(gen func(*T) value, t *T, tries int) value { panic(invalidData(fmt.Sprintf("failed to find suitable value in %d tries", tries))) } -func map_(g *Generator, fn interface{}) *Generator { - f := reflect.ValueOf(fn) - t := f.Type() - - assertCallable(t, g.type_(), "fn") - - return newGenerator(&mappedGen{ - typ: t.Out(0), - g: g, - fn: f, +// Map creates a generator producing fn(u) for each u produced by g. +func Map[U any, V any](g *Generator[U], fn func(U) V) *Generator[V] { + return newGenerator[V](&mappedGen[U, V]{ + g: g, + fn: fn, }) } -type mappedGen struct { - typ reflect.Type - g *Generator - fn reflect.Value -} - -func (g *mappedGen) String() string { - return fmt.Sprintf("%v.Map(func(...) %v)", g.g, g.typ) +type mappedGen[U any, V any] struct { + g *Generator[U] + fn func(U) V } -func (g *mappedGen) type_() reflect.Type { - return g.typ +func (g *mappedGen[U, V]) String() string { + return fmt.Sprintf("Map(%v, %T)", g.g, g.fn) } -func (g *mappedGen) value(t *T) value { - v := reflect.ValueOf(g.g.value(t)) - return call(g.fn, v) +func (g *mappedGen[U, V]) value(t *T) V { + return g.fn(g.g.value(t)) } -func Just(val interface{}) *Generator { - return SampledFrom([]interface{}{val}) +// Just creates a generator which always produces the given value. +// Just(val) is a shorthand for [SampledFrom]([]V{val}). +func Just[V any](val V) *Generator[V] { + return SampledFrom([]V{val}) } -func SampledFrom(slice interface{}) *Generator { - v := reflect.ValueOf(slice) - t := v.Type() +// SampledFrom creates a generator which produces values from the given slice. +// SampledFrom panics if slice is empty. +func SampledFrom[S ~[]E, E any](slice S) *Generator[E] { + assertf(len(slice) > 0, "slice should not be empty") - assertf(t.Kind() == reflect.Slice, "argument should be a slice, not %v", t.Kind()) - assertf(v.Len() > 0, "slice should not be empty") - - return newGenerator(&sampledGen{ - typ: t.Elem(), - slice: v, - n: v.Len(), + return newGenerator[E](&sampledGen[E]{ + slice: slice, }) } -type sampledGen struct { - typ reflect.Type - slice reflect.Value - n int +type sampledGen[E any] struct { + slice []E } -func (g *sampledGen) String() string { - if g.n == 1 { - return fmt.Sprintf("Just(%v)", g.slice.Index(0).Interface()) +func (g *sampledGen[E]) String() string { + if len(g.slice) == 1 { + return fmt.Sprintf("Just(%v)", g.slice[0]) } else { - return fmt.Sprintf("SampledFrom(%v %v)", g.n, g.typ) + return fmt.Sprintf("SampledFrom(%v %T)", len(g.slice), g.slice[0]) } } -func (g *sampledGen) type_() reflect.Type { - return g.typ +func (g *sampledGen[E]) value(t *T) E { + i := genIndex(t.s, len(g.slice), true) + + return g.slice[i] } -func (g *sampledGen) value(t *T) value { - i := genIndex(t.s, g.n, true) +// Permutation creates a generator which produces permutations of the given slice. +func Permutation[S ~[]E, E any](slice S) *Generator[S] { + return newGenerator[S](&permGen[S, E]{ + slice: slice, + }) +} - return g.slice.Index(i).Interface() +type permGen[S ~[]E, E any] struct { + slice S } -func OneOf(gens ...*Generator) *Generator { - assertf(len(gens) > 0, "at least one generator should be specified") +func (g *permGen[S, E]) String() string { + var zero E + return fmt.Sprintf("Permutation(%v %T)", len(g.slice), zero) +} - typ := gens[0].type_() - for _, g := range gens { - if g.type_() != gens[0].type_() { - typ = emptyInterfaceType - break - } +func (g *permGen[S, E]) value(t *T) S { + s := append(S(nil), g.slice...) + n := len(s) + m := n - 1 + if m < 0 { + m = 0 + } + + // shrink-friendly variant of Fisher–Yates shuffle: shrinks to lower number of smaller distance swaps + repeat := newRepeat(0, m, math.MaxInt, "permute") + for i := 0; repeat.more(t.s); i++ { + j, _, _ := genUintRange(t.s, uint64(i), uint64(n-1), false) + s[i], s[j] = s[j], s[i] } - return newGenerator(&oneOfGen{ - typ: typ, + return s +} + +// OneOf creates a generator which produces each value by selecting one of gens and producing a value from it. +// OneOf panics if gens is empty. +func OneOf[V any](gens ...*Generator[V]) *Generator[V] { + assertf(len(gens) > 0, "at least one generator should be specified") + + return newGenerator[V](&oneOfGen[V]{ gens: gens, }) } -type oneOfGen struct { - typ reflect.Type - gens []*Generator +type oneOfGen[V any] struct { + gens []*Generator[V] } -func (g *oneOfGen) String() string { +func (g *oneOfGen[V]) String() string { strs := make([]string, len(g.gens)) for i, g := range g.gens { strs[i] = g.String() @@ -224,49 +230,57 @@ func (g *oneOfGen) String() string { return fmt.Sprintf("OneOf(%v)", strings.Join(strs, ", ")) } -func (g *oneOfGen) type_() reflect.Type { - return g.typ -} - -func (g *oneOfGen) value(t *T) value { +func (g *oneOfGen[V]) value(t *T) V { i := genIndex(t.s, len(g.gens), true) return g.gens[i].value(t) } -func Ptr(elem *Generator, allowNil bool) *Generator { - return newGenerator(&ptrGen{ - typ: reflect.PtrTo(elem.type_()), +// Ptr creates a *E generator. If allowNil is true, Ptr can return nil pointers. +func Ptr[E any](elem *Generator[E], allowNil bool) *Generator[*E] { + return newGenerator[*E](&ptrGen[E]{ elem: elem, allowNil: allowNil, }) } -type ptrGen struct { - typ reflect.Type - elem *Generator +type ptrGen[E any] struct { + elem *Generator[E] allowNil bool } -func (g *ptrGen) String() string { +func (g *ptrGen[E]) String() string { return fmt.Sprintf("Ptr(%v, allowNil=%v)", g.elem, g.allowNil) } -func (g *ptrGen) type_() reflect.Type { - return g.typ -} - -func (g *ptrGen) value(t *T) value { +func (g *ptrGen[E]) value(t *T) *E { pNonNil := float64(1) if g.allowNil { pNonNil = 0.5 } if flipBiasedCoin(t.s, pNonNil) { - p := reflect.New(g.elem.type_()) - p.Elem().Set(reflect.ValueOf(g.elem.value(t))) - return p.Interface() + e := g.elem.value(t) + return &e } else { - return reflect.Zero(g.typ).Interface() + return nil } } + +func asAny[V any](g *Generator[V]) *Generator[any] { + return newGenerator[any](&asAnyGen[V]{ + gen: g, + }) +} + +type asAnyGen[V any] struct { + gen *Generator[V] +} + +func (g *asAnyGen[V]) String() string { + return fmt.Sprintf("%v.AsAny()", g.gen) +} + +func (g *asAnyGen[V]) value(t *T) any { + return g.gen.value(t) +} diff --git a/vendor/pgregory.net/rapid/combinators_example_test.go b/vendor/pgregory.net/rapid/combinators_example_test.go deleted file mode 100644 index ffed5ff..0000000 --- a/vendor/pgregory.net/rapid/combinators_example_test.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2020 Gregory Petrosyan -// -// 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 https://mozilla.org/MPL/2.0/. - -package rapid_test - -import ( - "fmt" - - "pgregory.net/rapid" -) - -func ExampleCustom() { - type point struct { - x int - y int - } - - gen := rapid.Custom(func(t *rapid.T) point { - return point{ - x: rapid.Int().Draw(t, "x").(int), - y: rapid.Int().Draw(t, "y").(int), - } - }) - - for i := 0; i < 5; i++ { - fmt.Println(gen.Example(i)) - } - // Output: - // {-3 1303} - // {-186981 -59881619} - // {4 441488606} - // {-2 -5863986} - // {43 -3513} -} - -func ExampleJust() { - gen := rapid.Just(42) - - for i := 0; i < 5; i++ { - fmt.Println(gen.Example(i)) - } - // Output: - // 42 - // 42 - // 42 - // 42 - // 42 -} - -func ExampleSampledFrom() { - gen := rapid.SampledFrom([]int{1, 2, 3}) - - for i := 0; i < 5; i++ { - fmt.Println(gen.Example(i)) - } - // Output: - // 2 - // 3 - // 2 - // 3 - // 1 -} - -func ExampleOneOf() { - gen := rapid.OneOf(rapid.Int32Range(1, 10), rapid.Float32Range(100, 1000)) - - for i := 0; i < 5; i++ { - fmt.Println(gen.Example(i)) - } - // Output: - // 997.0737 - // 10 - // 475.3125 - // 2 - // 9 -} - -func ExamplePtr() { - gen := rapid.Ptr(rapid.Int(), true) - - for i := 0; i < 5; i++ { - v := gen.Example(i).(*int) - if v == nil { - fmt.Println("") - } else { - fmt.Println("(*int)", *v) - } - } - // Output: - // (*int) 1 - // (*int) -3 - // - // (*int) 590 - // -} diff --git a/vendor/pgregory.net/rapid/combinators_external_test.go b/vendor/pgregory.net/rapid/combinators_external_test.go deleted file mode 100644 index 6acd8e9..0000000 --- a/vendor/pgregory.net/rapid/combinators_external_test.go +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright 2019 Gregory Petrosyan -// -// 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 https://mozilla.org/MPL/2.0/. - -package rapid_test - -import ( - "fmt" - "strconv" - "testing" - - . "pgregory.net/rapid" -) - -type testStruct struct { - x int - y int -} - -func genBool(t *T) bool { - return Bool().Draw(t, "").(bool) -} - -func genInterface(t *T) interface{} { - if Bool().Draw(t, "coinflip").(bool) { - return Int8().Draw(t, "") - } else { - return Float64().Draw(t, "") - } -} - -func genSlice(t *T) []uint64 { - return []uint64{ - Uint64().Draw(t, "").(uint64), - Uint64().Draw(t, "").(uint64), - } -} - -func genStruct(t *T) testStruct { - return testStruct{ - x: Int().Draw(t, "x").(int), - y: Int().Draw(t, "y").(int), - } -} - -func TestCustom(t *testing.T) { - t.Parallel() - - gens := []*Generator{ - Custom(genBool), - Custom(genInterface), - Custom(genSlice), - Custom(genStruct), - } - - for _, g := range gens { - t.Run(g.String(), MakeCheck(func(t *T) { g.Draw(t, "") })) - } -} - -func TestFilter(t *testing.T) { - t.Parallel() - - g := Int().Filter(func(i int) bool { return i >= 0 }) - - Check(t, func(t *T) { - v := g.Draw(t, "v").(int) - if v < 0 { - t.Fatalf("got negative %v", v) - } - }) -} - -func TestMap(t *testing.T) { - t.Parallel() - - g := Int().Map(strconv.Itoa) - - Check(t, func(t *T) { - s := g.Draw(t, "s").(string) - _, err := strconv.Atoi(s) - if err != nil { - t.Fatalf("Atoi() error %v", err) - } - }) -} - -func TestSampledFrom(t *testing.T) { - t.Parallel() - - gens := []*Generator{ - Just(3), - SampledFrom([]int{3, 5, 7}), - } - - for _, g := range gens { - t.Run(g.String(), MakeCheck(func(t *T) { - n := g.Draw(t, "n").(int) - if n != 3 && n != 5 && n != 7 { - t.Fatalf("got impossible %v", n) - } - })) - } -} - -func TestOneOf_SameType(t *testing.T) { - t.Parallel() - - pos := Int().Filter(func(v int) bool { return v >= 10 }) - neg := Int().Filter(func(v int) bool { return v <= -10 }) - g := OneOf(pos, neg) - - Check(t, func(t *T) { - n := g.Draw(t, "n").(int) - if n > -10 && n < 10 { - t.Fatalf("got impossible %v", n) - } - }) -} - -func TestOneOf_DifferentTypes(t *testing.T) { - t.Parallel() - - g := OneOf(Int(), Int8(), Int16(), Int32(), Int64()) - - Check(t, func(t *T) { - n := g.Draw(t, "n") - rv(n).Int() - }) -} - -func TestPtr(t *testing.T) { - t.Parallel() - - for _, allowNil := range []bool{false, true} { - t.Run(fmt.Sprintf("allowNil=%v", allowNil), MakeCheck(func(t *T) { - i := Ptr(Int(), allowNil).Draw(t, "i").(*int) - if i == nil && !allowNil { - t.Fatalf("got nil pointer") - } - })) - } -} diff --git a/vendor/pgregory.net/rapid/combinators_test.go b/vendor/pgregory.net/rapid/combinators_test.go deleted file mode 100644 index a89747d..0000000 --- a/vendor/pgregory.net/rapid/combinators_test.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2019 Gregory Petrosyan -// -// 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 https://mozilla.org/MPL/2.0/. - -package rapid - -import "testing" - -type intPair struct { - x int - y int -} - -func BenchmarkHeavyChain3(b *testing.B) { - t := newT(nil, newRandomBitStream(baseSeed(), false), false, nil) - g := Custom(func(t *T) int { return Int().Draw(t, "").(int) }). - Map(func(i int) intPair { return intPair{i, i << 13} }). - Map(func(p intPair) int { return p.x + p.y }) - b.ResetTimer() - - for i := 0; i < b.N; i++ { - g.value(t) - } -} diff --git a/vendor/pgregory.net/rapid/data.go b/vendor/pgregory.net/rapid/data.go index 3791d39..d68b6de 100644 --- a/vendor/pgregory.net/rapid/data.go +++ b/vendor/pgregory.net/rapid/data.go @@ -7,14 +7,11 @@ package rapid import ( + "hash/maphash" "math" "math/bits" - "sync/atomic" - "time" ) -var seedCounter uint32 - type bitStream interface { drawBits(n int) uint64 beginGroup(label string, standalone bool) int @@ -26,7 +23,7 @@ func baseSeed() uint64 { return flags.seed } - return uint64(time.Now().UnixNano())<<32 + uint64(atomic.AddUint32(&seedCounter, 1)) + return new(maphash.Hash).Sum64() } type randomBitStream struct { @@ -125,7 +122,8 @@ func (rec *recordedBits) beginGroup(label string, standalone bool) int { } func (rec *recordedBits) endGroup(i int, discard bool) { - assertf((!rec.persist && rec.dataLen != i) || (rec.persist && len(rec.data) != rec.groups[i].begin), "group did not use any data from bitstream") + assertf(discard || (!rec.persist && rec.dataLen > i) || (rec.persist && len(rec.data) > rec.groups[i].begin), + "group did not use any data from bitstream; this is likely a result of Custom generator not calling any of the built-in generators") if !rec.persist { return diff --git a/vendor/pgregory.net/rapid/data_test.go b/vendor/pgregory.net/rapid/data_test.go deleted file mode 100644 index 8ffce96..0000000 --- a/vendor/pgregory.net/rapid/data_test.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2019 Gregory Petrosyan -// -// 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 https://mozilla.org/MPL/2.0/. - -package rapid - -import ( - "fmt" - "math/bits" - "math/rand" - "testing" -) - -func TestJsfRand(t *testing.T) { - t.Parallel() - - // using https://gist.github.com/imneme/85cff47d4bad8de6bdeb671f9c76c814 - golden := [10]uint64{ - 0xe7ac7348cb3c6182, - 0xe20e62c321f18c3f, - 0x592927f9846891ae, - 0xda5c2b6e56ace47a, - 0x3c5987be726a7740, - 0x1463137b89c7292a, - 0xd118e05a46bc8156, - 0xeb72c3391969bc15, - 0xe94f306afee04198, - 0x0f57e93805e22a54, - } - - ctx := &jsf64ctx{} - ctx.init(0xcafe5eed00000001) - - for _, g := range golden { - u := ctx.rand() - if u != g { - t.Errorf("0x%x instead of golden 0x%x", u, g) - } - } -} - -func BenchmarkJsfRand(b *testing.B) { - ctx := &jsf64ctx{} - ctx.init(1) - b.ResetTimer() - - for i := 0; i < b.N; i++ { - ctx.rand() - } -} - -func BenchmarkMathRand(b *testing.B) { - s := rand.NewSource(1).(rand.Source64) - b.ResetTimer() - - for i := 0; i < b.N; i++ { - s.Uint64() - } -} - -func TestRandomBitSteam_DrawBits(t *testing.T) { - t.Parallel() - - s := createRandomBitStream(t) - - for n := 1; n <= 64; n++ { - for i := 0; i < 100; i++ { - t.Run(fmt.Sprintf("%v bits #%v", n, i), func(t *testing.T) { - b := s.drawBits(n) - if bits.Len64(b) > n { - t.Errorf("%v: bitlen too big for %v bits", b, n) - } - if bits.OnesCount64(b) > n { - t.Errorf("%v: too much ones for %v bits", b, n) - } - }) - } - } -} - -func BenchmarkBaseSeed(b *testing.B) { - for i := 0; i < b.N; i++ { - baseSeed() - } -} - -func BenchmarkRandomBitStream_DrawBits1(b *testing.B) { - s := newRandomBitStream(baseSeed(), false) - b.ResetTimer() - - for i := 0; i < b.N; i++ { - s.drawBits(1) - } -} - -func BenchmarkRandomBitStream_DrawBits64(b *testing.B) { - s := newRandomBitStream(baseSeed(), false) - b.ResetTimer() - - for i := 0; i < b.N; i++ { - s.drawBits(64) - } -} diff --git a/vendor/pgregory.net/rapid/doc.go b/vendor/pgregory.net/rapid/doc.go index 6b32ec1..7d5d751 100644 --- a/vendor/pgregory.net/rapid/doc.go +++ b/vendor/pgregory.net/rapid/doc.go @@ -7,35 +7,51 @@ /* Package rapid implements utilities for property-based testing. -Rapid checks that properties you define hold for a large number +[Check] verifies that properties you define hold for a large number of automatically generated test cases. If a failure is found, rapid fails the current test and presents an automatically minimized version of the failing test case. -Here is what a trivial test using rapid looks like: - - package rapid_test - - import ( - "net" - "testing" - - "pgregory.net/rapid" - ) - - func TestParseValidIPv4(t *testing.T) { - const ipv4re = `(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])` + - `\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])` + - `\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])` + - `\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])` - - rapid.Check(t, func(t *rapid.T) { - addr := rapid.StringMatching(ipv4re).Draw(t, "addr").(string) - ip := net.ParseIP(addr) - if ip == nil || ip.String() != addr { - t.Fatalf("parsed %q into %v", addr, ip) - } - }) - } +[T.Repeat] is used to construct state machine (sometimes called "stateful" +or "model-based") tests. + +# Generators + +Primitives: + - [Bool] + - [Rune], [RuneFrom] + - [Byte], [ByteMin], [ByteMax], [ByteRange] + - [Int], [IntMin], [IntMax], [IntRange] + - [Int8], [Int8Min], [Int8Max], [Int8Range] + - [Int16], [Int16Min], [Int16Max], [Int16Range] + - [Int32], [Int32Min], [Int32Max], [Int32Range] + - [Int64], [Int64Min], [Int64Max], [Int64Range] + - [Uint], [UintMin], [UintMax], [UintRange] + - [Uint8], [Uint8Min], [Uint8Max], [Uint8Range] + - [Uint16], [Uint16Min], [Uint16Max], [Uint16Range] + - [Uint32], [Uint32Min], [Uint32Max], [Uint32Range] + - [Uint64], [Uint64Min], [Uint64Max], [Uint64Range] + - [Uintptr], [UintptrMin], [UintptrMax], [UintptrRange] + - [Float32], [Float32Min], [Float32Max], [Float32Range] + - [Float64], [Float64Min], [Float64Max], [Float64Range] + +Collections: + - [String], [StringMatching], [StringOf], [StringOfN], [StringN] + - [SliceOfBytesMatching] + - [SliceOf], [SliceOfN], [SliceOfDistinct], [SliceOfNDistinct] + - [Permutation] + - [MapOf], [MapOfN], [MapOfValues], [MapOfNValues] + +User-defined types: + - [Custom] + - [Make] + +Other: + - [Map], + - [Generator.Filter] + - [SampledFrom], [Just] + - [OneOf] + - [Deferred] + - [Ptr] */ package rapid diff --git a/vendor/pgregory.net/rapid/engine.go b/vendor/pgregory.net/rapid/engine.go index 431a96a..a2ea5c3 100644 --- a/vendor/pgregory.net/rapid/engine.go +++ b/vendor/pgregory.net/rapid/engine.go @@ -8,11 +8,12 @@ package rapid import ( "bytes" + "encoding/binary" "flag" "fmt" "log" "os" - "reflect" + "path/filepath" "regexp" "runtime" "strings" @@ -26,6 +27,9 @@ const ( invalidChecksMult = 10 exampleMaxTries = 1000 + maxTestTimeout = 24 * time.Hour + shrinkStepBound = 10 * time.Second // can be improved by taking average checkOnce runtime into account + tracebackLen = 32 tracebackStop = "pgregory.net/rapid.checkOnce" runtimePrefix = "runtime." @@ -34,12 +38,9 @@ const ( var ( flags cmdline - emptyStructType = reflect.TypeOf(struct{}{}) - emptyStructValue = reflect.ValueOf(struct{}{}) - tracebackBlacklist = map[string]bool{ - "pgregory.net/rapid.(*customGen).maybeValue.func1": true, - "pgregory.net/rapid.runAction.func1": true, + "pgregory.net/rapid.(*customGen[...]).maybeValue.func1": true, + "pgregory.net/rapid.runAction.func1": true, } ) @@ -47,6 +48,7 @@ type cmdline struct { checks int steps int failfile string + nofailfile bool seed uint64 log bool verbose bool @@ -57,8 +59,9 @@ type cmdline struct { func init() { flag.IntVar(&flags.checks, "rapid.checks", 100, "rapid: number of checks to perform") - flag.IntVar(&flags.steps, "rapid.steps", 100, "rapid: number of state machine steps to perform") + flag.IntVar(&flags.steps, "rapid.steps", 30, "rapid: average number of Repeat actions to execute") flag.StringVar(&flags.failfile, "rapid.failfile", "", "rapid: fail file to use to reproduce test failure") + flag.BoolVar(&flags.nofailfile, "rapid.nofailfile", false, "rapid: do not write fail files on test failures") flag.Uint64Var(&flags.seed, "rapid.seed", 0, "rapid: PRNG seed to start with (0 to use a random one)") flag.BoolVar(&flags.log, "rapid.log", false, "rapid: eager verbose output to stdout (to aid with unrecoverable test failures)") flag.BoolVar(&flags.verbose, "rapid.v", false, "rapid: verbose output") @@ -73,75 +76,148 @@ func assert(ok bool) { } } -func assertf(ok bool, format string, args ...interface{}) { +func assertf(ok bool, format string, args ...any) { if !ok { panic(fmt.Sprintf(format, args...)) } } func assertValidRange(min int, max int) { - assertf(max < 0 || min <= max, fmt.Sprintf("invalid range [%d, %d]", min, max)) + if max >= 0 && min > max { + panic(fmt.Sprintf("invalid range [%d, %d]", min, max)) + } +} + +func checkDeadline(tb tb) time.Time { + t, ok := tb.(*testing.T) + if !ok { + return time.Now().Add(maxTestTimeout) + } + d, ok := t.Deadline() + if !ok { + return time.Now().Add(maxTestTimeout) + } + return d +} + +func shrinkDeadline(deadline time.Time) time.Time { + d := time.Now().Add(flags.shrinkTime) + max := deadline.Add(-shrinkStepBound) // account for the fact that shrink deadline is checked before the step + if d.After(max) { + d = max + } + return d } // Check fails the current test if rapid can find a test case which falsifies prop. // // Property is falsified in case of a panic or a call to -// (*T).Fatalf, (*T).Fatal, (*T).Errorf, (*T).Error, (*T).FailNow or (*T).Fail. -func Check(t *testing.T, prop func(*T)) { +// [*T.Fatalf], [*T.Fatal], [*T.Errorf], [*T.Error], [*T.FailNow] or [*T.Fail]. +func Check(t TB, prop func(*T)) { t.Helper() - checkTB(t, prop) + checkTB(t, checkDeadline(t), prop) } // MakeCheck is a convenience function for defining subtests suitable for -// (*testing.T).Run. It allows you to write this: +// [*testing.T.Run]. It allows you to write this: // -// t.Run("subtest name", rapid.MakeCheck(func(t *rapid.T) { -// // test code -// })) +// t.Run("subtest name", rapid.MakeCheck(func(t *rapid.T) { +// // test code +// })) // // instead of this: // -// t.Run("subtest name", func(t *testing.T) { -// rapid.Check(t, func(t *rapid.T) { -// // test code -// }) -// }) -// +// t.Run("subtest name", func(t *testing.T) { +// rapid.Check(t, func(t *rapid.T) { +// // test code +// }) +// }) func MakeCheck(prop func(*T)) func(*testing.T) { return func(t *testing.T) { t.Helper() - checkTB(t, prop) + checkTB(t, checkDeadline(t), prop) } } -func checkTB(tb tb, prop func(*T)) { +// MakeFuzz creates a fuzz target for [*testing.F.Fuzz]: +// +// func FuzzFoo(f *testing.F) { +// f.Fuzz(rapid.MakeFuzz(func(t *rapid.T) { +// // test code +// })) +// } +func MakeFuzz(prop func(*T)) func(*testing.T, []byte) { + return func(t *testing.T, input []byte) { + t.Helper() + checkFuzz(t, prop, input) + } +} + +func checkFuzz(tb tb, prop func(*T), input []byte) { + tb.Helper() + + var buf []uint64 + for len(input) > 0 { + var tmp [8]byte + n := copy(tmp[:], input) + buf = append(buf, binary.LittleEndian.Uint64(tmp[:])) + input = input[n:] + } + + t := newT(tb, newBufBitStream(buf, false), true, nil) + err := checkOnce(t, prop) + + switch { + case err == nil: + // do nothing + case err.isInvalidData(): + tb.SkipNow() + case err.isStopTest(): + tb.Fatalf("[rapid] failed: %v", err) + default: + tb.Fatalf("[rapid] panic: %v\nTraceback:\n%v", err, traceback(err)) + } +} + +func checkTB(tb tb, deadline time.Time, prop func(*T)) { tb.Helper() + checks := flags.checks + if testing.Short() { + checks /= 5 + } + start := time.Now() - valid, invalid, seed, buf, err1, err2 := doCheck(tb, flags.failfile, flags.checks, baseSeed(), prop) + valid, invalid, earlyExit, seed, failfile, buf, err1, err2 := doCheck(tb, deadline, checks, baseSeed(), flags.failfile, true, prop) dt := time.Since(start) if err1 == nil && err2 == nil { - if valid == flags.checks { + if valid == checks || (earlyExit && valid > 0) { tb.Logf("[rapid] OK, passed %v tests (%v)", valid, dt) } else { tb.Errorf("[rapid] only generated %v valid tests from %v total (%v)", valid, valid+invalid, dt) } } else { - repr := fmt.Sprintf("-rapid.seed=%d", seed) - if flags.failfile != "" && seed == 0 { - repr = fmt.Sprintf("-rapid.failfile=%q", flags.failfile) - } else { - failfile := failFileName(tb.Name()) + if failfile == "" && !flags.nofailfile { + _, failfile = failFileName(tb.Name()) out := captureTestOutput(tb, prop, buf) - err := saveFailFile(failfile, out, buf) - if err == nil { - repr = fmt.Sprintf("-rapid.failfile=%q", failfile) - } else { + err := saveFailFile(failfile, rapidVersion, out, seed, buf) + if err != nil { tb.Logf("[rapid] %v", err) + failfile = "" } } + var repr string + switch { + case failfile != "" && seed != 0: + repr = fmt.Sprintf("-rapid.failfile=%q (or -rapid.seed=%d)", failfile, seed) + case failfile != "": + repr = fmt.Sprintf("-rapid.failfile=%q", failfile) + case seed != 0: + repr = fmt.Sprintf("-rapid.seed=%d", seed) + } + name := regexp.QuoteMeta(tb.Name()) if traceback(err1) == traceback(err2) { if err2.isStopTest() { @@ -161,21 +237,29 @@ func checkTB(tb tb, prop func(*T)) { } } -func doCheck(tb tb, failfile string, checks int, seed uint64, prop func(*T)) (int, int, uint64, []uint64, *testError, *testError) { +func doCheck(tb tb, deadline time.Time, checks int, seed uint64, failfile string, globFailFiles bool, prop func(*T)) (int, int, bool, uint64, string, []uint64, *testError, *testError) { tb.Helper() assertf(!tb.Failed(), "check function called with *testing.T which has already failed") + var failfiles []string if failfile != "" { + failfiles = []string{failfile} + } + if globFailFiles { + matches, _ := filepath.Glob(failFilePattern(tb.Name())) + failfiles = append(failfiles, matches...) + } + for _, failfile := range failfiles { buf, err1, err2 := checkFailFile(tb, failfile, prop) if err1 != nil || err2 != nil { - return 0, 0, 0, buf, err1, err2 + return 0, 0, false, 0, failfile, buf, err1, err2 } } - seed, valid, invalid, err1 := findBug(tb, checks, seed, prop) + valid, invalid, earlyExit, seed, err1 := findBug(tb, deadline, checks, seed, prop) if err1 == nil { - return valid, invalid, 0, nil, nil, nil + return valid, invalid, earlyExit, 0, "", nil, nil, nil } s := newRandomBitStream(seed, true) @@ -183,23 +267,27 @@ func doCheck(tb tb, failfile string, checks int, seed uint64, prop func(*T)) (in t.Logf("[rapid] trying to reproduce the failure") err2 := checkOnce(t, prop) if !sameError(err1, err2) { - return valid, invalid, seed, s.data, err1, err2 + return valid, invalid, false, seed, "", s.data, err1, err2 } t.Logf("[rapid] trying to minimize the failing test case") - buf, err3 := shrink(tb, s.recordedBits, err2, prop) + buf, err3 := shrink(tb, shrinkDeadline(deadline), s.recordedBits, err2, prop) - return valid, invalid, seed, buf, err2, err3 + return valid, invalid, false, seed, "", buf, err2, err3 } func checkFailFile(tb tb, failfile string, prop func(*T)) ([]uint64, *testError, *testError) { tb.Helper() - buf, err := loadFailFile(failfile) + version, _, buf, err := loadFailFile(failfile) if err != nil { tb.Logf("[rapid] ignoring fail file: %v", err) return nil, nil, nil } + if version != rapidVersion { + tb.Logf("[rapid] ignoring fail file: version %q differs from rapid version %q", version, rapidVersion) + return nil, nil, nil + } s1 := newBufBitStream(buf, false) t1 := newT(tb, s1, flags.verbose, nil) @@ -220,7 +308,7 @@ func checkFailFile(tb tb, failfile string, prop func(*T)) ([]uint64, *testError, return buf, err1, err2 } -func findBug(tb tb, checks int, seed uint64, prop func(*T)) (uint64, int, int, *testError) { +func findBug(tb tb, deadline time.Time, checks int, seed uint64, prop func(*T)) (int, int, bool, uint64, *testError) { tb.Helper() var ( @@ -230,39 +318,49 @@ func findBug(tb tb, checks int, seed uint64, prop func(*T)) (uint64, int, int, * invalid = 0 ) + var total time.Duration for valid < checks && invalid < checks*invalidChecksMult { - seed += uint64(valid) + uint64(invalid) + iter := valid + invalid + if iter > 0 && time.Until(deadline) < total/time.Duration(iter)*5 { + if t.shouldLog() { + t.Logf("[rapid] early exit after test #%v (%v)", iter, total) + } + return valid, invalid, true, 0, nil + } + + seed += uint64(iter) r.init(seed) - var start time.Time + start := time.Now() if t.shouldLog() { - t.Logf("[rapid] test #%v start (seed %v)", valid+invalid+1, seed) - start = time.Now() + t.Logf("[rapid] test #%v start (seed %v)", iter+1, seed) } err := checkOnce(t, prop) + dt := time.Since(start) + total += dt if err == nil { if t.shouldLog() { - t.Logf("[rapid] test #%v OK (%v)", valid+invalid+1, time.Since(start)) + t.Logf("[rapid] test #%v OK (%v)", iter+1, dt) } valid++ } else if err.isInvalidData() { if t.shouldLog() { - t.Logf("[rapid] test #%v invalid (%v)", valid+invalid+1, time.Since(start)) + t.Logf("[rapid] test #%v invalid (%v)", iter+1, dt) } invalid++ } else { if t.shouldLog() { - t.Logf("[rapid] test #%v failed: %v", valid+invalid+1, err) + t.Logf("[rapid] test #%v failed: %v", iter+1, err) } - return seed, valid, invalid, err + return valid, invalid, false, seed, err } } - return 0, valid, invalid, nil + return valid, invalid, false, 0, nil } func checkOnce(t *T, prop func(*T)) (err *testError) { - if t.tbLog && t.tb != nil { + if t.tbLog { t.tb.Helper() } defer func() { err = panicToError(recover(), 3) }() @@ -275,7 +373,7 @@ func checkOnce(t *T, prop func(*T)) (err *testError) { func captureTestOutput(tb tb, prop func(*T), buf []uint64) []byte { var b bytes.Buffer - l := log.New(&b, fmt.Sprintf("%s ", tb.Name()), log.Ldate|log.Ltime) // TODO: enable log.Lmsgprefix once all supported versions of Go have it + l := log.New(&b, fmt.Sprintf("[%v] ", tb.Name()), log.Lmsgprefix|log.Ldate|log.Ltime|log.Lmicroseconds) _ = checkOnce(newT(tb, newBufBitStream(buf, false), false, l), prop) return b.Bytes() } @@ -284,11 +382,11 @@ type invalidData string type stopTest string type testError struct { - data interface{} + data any traceback string } -func panicToError(p interface{}, skip int) *testError { +func panicToError(p any, skip int) *testError { if p == nil { return nil } @@ -359,32 +457,64 @@ func traceback(err *testError) string { return err.traceback } -type tb interface { +// TB is a common interface between [*testing.T], [*testing.B] and [*T]. +type TB interface { Helper() Name() string - Logf(format string, args ...interface{}) - Log(args ...interface{}) - Errorf(format string, args ...interface{}) - Error(args ...interface{}) - Fatalf(format string, args ...interface{}) - Fatal(args ...interface{}) + Logf(format string, args ...any) + Log(args ...any) + Skipf(format string, args ...any) + Skip(args ...any) + SkipNow() + Errorf(format string, args ...any) + Error(args ...any) + Fatalf(format string, args ...any) + Fatal(args ...any) FailNow() Fail() Failed() bool } +type tb TB // tb is a private copy of TB, made to avoid T having public fields + +type nilTB struct{} + +func (nilTB) Helper() {} +func (nilTB) Name() string { return "" } +func (nilTB) Logf(string, ...any) {} +func (nilTB) Log(...any) {} +func (nilTB) Skipf(string, ...any) { panic("call to TB.Skipf() outside a test") } +func (nilTB) Skip(...any) { panic("call to TB.Skip() outside a test") } +func (nilTB) SkipNow() { panic("call to TB.SkipNow() outside a test") } +func (nilTB) Errorf(string, ...any) { panic("call to TB.Errorf() outside a test") } +func (nilTB) Error(...any) { panic("call to TB.Error() outside a test") } +func (nilTB) Fatalf(string, ...any) { panic("call to TB.Fatalf() outside a test") } +func (nilTB) Fatal(...any) { panic("call to TB.Fatal() outside a test") } +func (nilTB) FailNow() { panic("call to TB.FailNow() outside a test") } +func (nilTB) Fail() { panic("call to TB.Fail() outside a test") } +func (nilTB) Failed() bool { panic("call to TB.Failed() outside a test") } + +// T is similar to [testing.T], but with extra bookkeeping for property-based tests. +// +// For tests to be reproducible, they should generally run in a single goroutine. +// If concurrency is unavoidable, methods on *T, such as [*testing.T.Helper] and [*T.Errorf], +// are safe for concurrent calls, but *Generator.Draw from a given *T is not. type T struct { tb // unnamed to force re-export of (*T).Helper() tbLog bool rawLog *log.Logger s bitStream draws int - refDraws []value + refDraws []any mu sync.RWMutex failed stopTest } -func newT(tb tb, s bitStream, tbLog bool, rawLog *log.Logger, refDraws ...value) *T { +func newT(tb tb, s bitStream, tbLog bool, rawLog *log.Logger, refDraws ...any) *T { + if tb == nil { + tb = nilTB{} + } + t := &T{ tb: tb, tbLog: tbLog, @@ -399,119 +529,93 @@ func newT(tb tb, s bitStream, tbLog bool, rawLog *log.Logger, refDraws ...value) testName = tb.Name() } - t.rawLog = log.New(os.Stdout, fmt.Sprintf("[%v] ", testName), 0) + t.rawLog = log.New(os.Stdout, fmt.Sprintf("[%v] ", testName), log.Lmsgprefix|log.Ldate|log.Ltime|log.Lmicroseconds) } return t } -func (t *T) draw(g *Generator, label string) value { - v := g.value(t) - - if len(t.refDraws) > 0 { - ref := t.refDraws[t.draws] - if !reflect.DeepEqual(v, ref) { - t.tb.Fatalf("draw %v differs: %#v vs expected %#v", t.draws, v, ref) - } - } - - if t.tbLog || t.rawLog != nil { - if label == "" { - label = fmt.Sprintf("#%v", t.draws) - } - - if t.tbLog && t.tb != nil { - t.tb.Helper() - } - t.Logf("[rapid] draw %v: %#v", label, v) - } - - t.draws++ - - return v -} - func (t *T) shouldLog() bool { - return t.rawLog != nil || (t.tbLog && t.tb != nil) + return t.rawLog != nil || t.tbLog } -func (t *T) Logf(format string, args ...interface{}) { +func (t *T) Logf(format string, args ...any) { if t.rawLog != nil { t.rawLog.Printf(format, args...) - } else if t.tbLog && t.tb != nil { + } else if t.tbLog { t.tb.Helper() t.tb.Logf(format, args...) } } -func (t *T) Log(args ...interface{}) { +func (t *T) Log(args ...any) { if t.rawLog != nil { t.rawLog.Print(args...) - } else if t.tbLog && t.tb != nil { + } else if t.tbLog { t.tb.Helper() t.tb.Log(args...) } } -// Skipf is equivalent to Logf followed by SkipNow. -func (t *T) Skipf(format string, args ...interface{}) { - if t.tbLog && t.tb != nil { +// Skipf is equivalent to [T.Logf] followed by [T.SkipNow]. +func (t *T) Skipf(format string, args ...any) { + if t.tbLog { t.tb.Helper() } t.Logf(format, args...) t.skip(fmt.Sprintf(format, args...)) } -// Skip is equivalent to Log followed by SkipNow. -func (t *T) Skip(args ...interface{}) { - if t.tbLog && t.tb != nil { +// Skip is equivalent to [T.Log] followed by [T.SkipNow]. +func (t *T) Skip(args ...any) { + if t.tbLog { t.tb.Helper() } t.Log(args...) t.skip(fmt.Sprint(args...)) } -// SkipNow marks the current test case as invalid (except state machine -// tests, where it marks current action as non-applicable instead). +// SkipNow marks the current test case as invalid (except in [T.Repeat] +// actions, where it marks current action as non-applicable instead). // If too many test cases are skipped, rapid will mark the test as failing // due to inability to generate enough valid test cases. // -// Prefer Filter to SkipNow, and prefer generators that always produce +// Prefer *Generator.Filter to SkipNow, and prefer generators that always produce // valid test cases to Filter. func (t *T) SkipNow() { t.skip("(*T).SkipNow() called") } -// Errorf is equivalent to Logf followed by Fail. -func (t *T) Errorf(format string, args ...interface{}) { - if t.tbLog && t.tb != nil { +// Errorf is equivalent to [T.Logf] followed by [T.Fail]. +func (t *T) Errorf(format string, args ...any) { + if t.tbLog { t.tb.Helper() } t.Logf(format, args...) t.fail(false, fmt.Sprintf(format, args...)) } -// Error is equivalent to Log followed by Fail. -func (t *T) Error(args ...interface{}) { - if t.tbLog && t.tb != nil { +// Error is equivalent to [T.Log] followed by [T.Fail]. +func (t *T) Error(args ...any) { + if t.tbLog { t.tb.Helper() } t.Log(args...) t.fail(false, fmt.Sprint(args...)) } -// Fatalf is equivalent to Logf followed by FailNow. -func (t *T) Fatalf(format string, args ...interface{}) { - if t.tbLog && t.tb != nil { +// Fatalf is equivalent to [T.Logf] followed by [T.FailNow]. +func (t *T) Fatalf(format string, args ...any) { + if t.tbLog { t.tb.Helper() } t.Logf(format, args...) t.fail(true, fmt.Sprintf(format, args...)) } -// Fatal is equivalent to Log followed by FailNow. -func (t *T) Fatal(args ...interface{}) { - if t.tbLog && t.tb != nil { +// Fatal is equivalent to [T.Log] followed by [T.FailNow]. +func (t *T) Fatal(args ...any) { + if t.tbLog { t.tb.Helper() } t.Log(args...) @@ -555,21 +659,3 @@ func (t *T) failOnError() { panic(t.failed) } } - -func assertCallable(fn reflect.Type, t reflect.Type, name string) { - assertf(fn.Kind() == reflect.Func, "%v should be a function, not %v", name, fn.Kind()) - assertf(fn.NumIn() == 1, "%v should have 1 parameter, not %v", name, fn.NumIn()) - assertf(fn.NumOut() == 1, "%v should have 1 output parameter, not %v", name, fn.NumOut()) - assertf(t.AssignableTo(fn.In(0)), "parameter #0 (%v) of %v should be assignable from %v", fn.In(0), name, t) -} - -func call(fn reflect.Value, arg reflect.Value) value { - r := fn.Call([]reflect.Value{arg}) - - if len(r) == 0 { - return nil - } else { - assert(len(r) == 1) - return r[0].Interface() - } -} diff --git a/vendor/pgregory.net/rapid/engine_test.go b/vendor/pgregory.net/rapid/engine_test.go deleted file mode 100644 index 443308f..0000000 --- a/vendor/pgregory.net/rapid/engine_test.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2019 Gregory Petrosyan -// -// 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 https://mozilla.org/MPL/2.0/. - -package rapid - -import ( - "strings" - "testing" -) - -func brokenGen(*T) int { panic("this generator is not working") } - -type brokenMachine struct{} - -func (m *brokenMachine) DoNothing(_ *T) { panic("this state machine is not working") } -func (m *brokenMachine) Check(*T) {} - -func TestPanicTraceback(t *testing.T) { - t.Parallel() - - testData := []struct { - name string - suffix string - fail func(*T) *testError - }{ - { - "impossible filter", - "pgregory.net/rapid.find", - func(t *T) *testError { - g := Bool().Filter(func(bool) bool { return false }) - _, err := recoverValue(g, t) - return err - }, - }, - { - "broken custom generator", - "pgregory.net/rapid.brokenGen", - func(t *T) *testError { - g := Custom(brokenGen) - _, err := recoverValue(g, t) - return err - }, - }, - { - "broken state machine", - "pgregory.net/rapid.(*brokenMachine).DoNothing", - func(t *T) *testError { - return checkOnce(t, Run(&brokenMachine{})) - }, - }, - } - - for _, td := range testData { - t.Run(td.name, func(t *testing.T) { - s := createRandomBitStream(t) - nt := newT(t, s, false, nil) - - err := td.fail(nt) - if err == nil { - t.Fatalf("test case did not fail") - } - - lines := strings.Split(err.traceback, "\n") - if !strings.HasSuffix(lines[0], td.suffix) { - t.Errorf("bad traceback:\n%v", err.traceback) - } - }) - } -} - -func BenchmarkCheckOverhead(b *testing.B) { - g := Uint() - f := func(t *T) { - g.Draw(t, "") - } - b.ResetTimer() - - for i := 0; i < b.N; i++ { - checkTB(b, f) - } -} diff --git a/vendor/pgregory.net/rapid/example_function_test.go b/vendor/pgregory.net/rapid/example_function_test.go deleted file mode 100644 index b47701f..0000000 --- a/vendor/pgregory.net/rapid/example_function_test.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2019 Gregory Petrosyan -// -// 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 https://mozilla.org/MPL/2.0/. - -package rapid_test - -import ( - "fmt" - "strconv" - "testing" - - "pgregory.net/rapid" -) - -// ParseDate parses dates in the YYYY-MM-DD format. -func ParseDate(s string) (int, int, int, error) { - if len(s) != 10 { - return 0, 0, 0, fmt.Errorf("%q has wrong length: %v instead of 10", s, len(s)) - } - - if s[4] != '-' || s[7] != '-' { - return 0, 0, 0, fmt.Errorf("'-' separators expected in %q", s) - } - - y, err := strconv.Atoi(s[0:4]) - if err != nil { - return 0, 0, 0, fmt.Errorf("failed to parse year: %v", err) - } - - m, err := strconv.Atoi(s[6:7]) - if err != nil { - return 0, 0, 0, fmt.Errorf("failed to parse month: %v", err) - } - - d, err := strconv.Atoi(s[8:10]) - if err != nil { - return 0, 0, 0, fmt.Errorf("failed to parse day: %v", err) - } - - return y, m, d, nil -} - -func testParseDate(t *rapid.T) { - y := rapid.IntRange(0, 9999).Draw(t, "y").(int) - m := rapid.IntRange(1, 12).Draw(t, "m").(int) - d := rapid.IntRange(1, 31).Draw(t, "d").(int) - - s := fmt.Sprintf("%04d-%02d-%02d", y, m, d) - - y_, m_, d_, err := ParseDate(s) - if err != nil { - t.Fatalf("failed to parse date %q: %v", s, err) - } - - if y_ != y || m_ != m || d_ != d { - t.Fatalf("got back wrong date: (%d, %d, %d)", y_, m_, d_) - } -} - -// Rename to TestParseDate(t *testing.T) to make an actual (failing) test. -func ExampleCheck_parseDate() { - var t *testing.T - rapid.Check(t, testParseDate) -} diff --git a/vendor/pgregory.net/rapid/example_statemachine_test.go b/vendor/pgregory.net/rapid/example_statemachine_test.go deleted file mode 100644 index 0f2c35d..0000000 --- a/vendor/pgregory.net/rapid/example_statemachine_test.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2019 Gregory Petrosyan -// -// 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 https://mozilla.org/MPL/2.0/. - -package rapid_test - -import ( - "testing" - - "pgregory.net/rapid" -) - -// Queue implements integer queue with a fixed maximum size. -type Queue struct { - buf []int - in int - out int -} - -func NewQueue(n int) *Queue { - return &Queue{ - buf: make([]int, n+1), - } -} - -// Precondition: Size() > 0. -func (q *Queue) Get() int { - i := q.buf[q.out] - q.out = (q.out + 1) % len(q.buf) - return i -} - -// Precondition: Size() < n. -func (q *Queue) Put(i int) { - q.buf[q.in] = i - q.in = (q.in + 1) % len(q.buf) -} - -func (q *Queue) Size() int { - return (q.in - q.out) % len(q.buf) -} - -// queueMachine is a description of a rapid state machine for testing Queue -type queueMachine struct { - q *Queue // queue being tested - n int // maximum queue size - state []int // model of the queue -} - -// Init is an action for initializing a queueMachine instance. -func (m *queueMachine) Init(t *rapid.T) { - n := rapid.IntRange(1, 1000).Draw(t, "n").(int) - m.q = NewQueue(n) - m.n = n -} - -// Get is a conditional action which removes an item from the queue. -func (m *queueMachine) Get(t *rapid.T) { - if m.q.Size() == 0 { - t.Skip("queue empty") - } - - i := m.q.Get() - if i != m.state[0] { - t.Fatalf("got invalid value: %v vs expected %v", i, m.state[0]) - } - m.state = m.state[1:] -} - -// Put is a conditional action which adds an items to the queue. -func (m *queueMachine) Put(t *rapid.T) { - if m.q.Size() == m.n { - t.Skip("queue full") - } - - i := rapid.Int().Draw(t, "i").(int) - m.q.Put(i) - m.state = append(m.state, i) -} - -// Check runs after every action and verifies that all required invariants hold. -func (m *queueMachine) Check(t *rapid.T) { - if m.q.Size() != len(m.state) { - t.Fatalf("queue size mismatch: %v vs expected %v", m.q.Size(), len(m.state)) - } -} - -// Rename to TestQueue(t *testing.T) to make an actual (failing) test. -func ExampleRun_queue() { - var t *testing.T - rapid.Check(t, rapid.Run(&queueMachine{})) -} diff --git a/vendor/pgregory.net/rapid/failure_external_test.go b/vendor/pgregory.net/rapid/failure_external_test.go deleted file mode 100644 index e25901e..0000000 --- a/vendor/pgregory.net/rapid/failure_external_test.go +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright 2019 Gregory Petrosyan -// -// 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 https://mozilla.org/MPL/2.0/. - -package rapid_test - -import ( - "math" - "testing" - - . "pgregory.net/rapid" -) - -// wrapper to test (*T).Helper() -func fatalf(t *T, format string, args ...interface{}) { - t.Helper() - t.Fatalf(format, args...) -} - -func TestFailure_ImpossibleData(t *testing.T) { - t.Skip("expected failure") - - Check(t, func(t *T) { - _ = Int().Filter(func(i int) bool { return false }).Draw(t, "i") - }) -} - -func TestFailure_Trivial(t *testing.T) { - t.Skip("expected failure") - - Check(t, func(t *T) { - i := Int().Draw(t, "i").(int) - if i > 1000000000 { - fatalf(t, "got a huge integer: %v", i) - } - }) -} - -func TestFailure_SimpleCollection(t *testing.T) { - t.Skip("expected failure") - - Check(t, func(t *T) { - s := SliceOf(Int().Filter(func(i int) bool { return i%2 == -1 })).Draw(t, "s").([]int) - if len(s) > 3 { - fatalf(t, "got a long sequence: %v", s) - } - }) -} - -func TestFailure_CollectionElements(t *testing.T) { - t.Skip("expected failure") - - Check(t, func(t *T) { - s := SliceOfN(Int(), 2, -1).Draw(t, "s").([]int) - - n := 0 - for _, i := range s { - if i > 1000000 { - n++ - } - } - - if n > 1 { - fatalf(t, "got %v huge elements", n) - } - }) -} - -func TestFailure_TrivialString(t *testing.T) { - t.Skip("expected failure") - - Check(t, func(t *T) { - s := String().Draw(t, "s").(string) - if len(s) > 7 { - fatalf(t, "got bad string %v", s) - } - }) -} - -func TestFailure_Make(t *testing.T) { - t.Skip("expected failure") - - Check(t, func(t *T) { - n := IntMin(0).Draw(t, "n").(int) - _ = make([]int, n) - }) -} - -func TestFailure_Mean(t *testing.T) { - t.Skip("expected failure") - - Check(t, func(t *T) { - s := SliceOf(Float64()).Draw(t, "s").([]float64) - - mean := 0.0 - for _, f := range s { - mean += f - } - mean /= float64(len(s)) - - min, max := math.Inf(0), math.Inf(-1) - for _, f := range s { - if f < min { - min = f - } - if f > max { - max = f - } - } - - if mean < min || mean > max { - t.Fatalf("got mean %v for range [%v, %v]", mean, min, max) - } - }) -} - -func TestFailure_ExampleParseDate(t *testing.T) { - t.Skip("expected failure") - - Check(t, testParseDate) -} - -func TestFailure_ExampleQueue(t *testing.T) { - t.Skip("expected failure") - - Check(t, Run(&queueMachine{})) -} - -// LastIndex returns the index of the last instance of x in list, or -// -1 if x is not present. The loop condition has a fault that -// causes some tests to fail. Change it to i >= 0 to see them pass. -func LastIndex(list []int, x int) int { - for i := len(list) - 1; i > 0; i-- { - if list[i] == x { - return i - } - } - return -1 -} - -// This can be a good example of property-based test; however, it is unclear -// what is the "best" way to generate input. Either it is concise (like in -// the test below), but requires high quality data generation (and even then -// is flaky), or it can be verbose, explicitly covering important input classes -- -// however, how do we know them when writing a test? -func TestFailure_LastIndex(t *testing.T) { - t.Skip("expected failure (flaky)") - - Check(t, func(t *T) { - s := SliceOf(Int()).Draw(t, "s").([]int) - x := Int().Draw(t, "x").(int) - ix := LastIndex(s, x) - - // index is either -1 or in bounds - if ix != -1 && (ix < 0 || ix >= len(s)) { - t.Fatalf("%v is not a valid last index", ix) - } - - // index is either -1 or a valid index of x - if ix != -1 && s[ix] != x { - t.Fatalf("%v is not a valid index of %v", ix, x) - } - - // no valid index of x is bigger than ix - for i := ix + 1; i < len(s); i++ { - if s[i] == x { - t.Fatalf("%v is not the last index of %v (%v is bigger)", ix, x, i) - } - } - }) -} diff --git a/vendor/pgregory.net/rapid/floats.go b/vendor/pgregory.net/rapid/floats.go index cf90fe8..aeaa974 100644 --- a/vendor/pgregory.net/rapid/floats.go +++ b/vendor/pgregory.net/rapid/floats.go @@ -10,7 +10,6 @@ import ( "fmt" "math" "math/bits" - "reflect" ) const ( @@ -24,77 +23,80 @@ const ( floatSignifLabel = "floatsignif" ) -var ( - float32Type = reflect.TypeOf(float32(0)) - float64Type = reflect.TypeOf(float64(0)) -) - -func Float32() *Generator { +// Float32 is a shorthand for [Float32Range](-[math.MaxFloat32], [math.MaxFloat32]). +func Float32() *Generator[float32] { return Float32Range(-math.MaxFloat32, math.MaxFloat32) } -func Float32Min(min float32) *Generator { +// Float32Min is a shorthand for [Float32Range](min, [math.MaxFloat32]). +func Float32Min(min float32) *Generator[float32] { return Float32Range(min, math.MaxFloat32) } -func Float32Max(max float32) *Generator { +// Float32Max is a shorthand for [Float32Range](-[math.MaxFloat32], max). +func Float32Max(max float32) *Generator[float32] { return Float32Range(-math.MaxFloat32, max) } -func Float32Range(min float32, max float32) *Generator { +// Float32Range creates a generator of 32-bit floating-point numbers in range [min, max]. +// Both min and max can be infinite. +func Float32Range(min float32, max float32) *Generator[float32] { assertf(min == min, "min should not be a NaN") assertf(max == max, "max should not be a NaN") assertf(min <= max, "invalid range [%v, %v]", min, max) - return newGenerator(&floatGen{ - typ: float32Type, - min: float64(min), - max: float64(max), - minVal: -math.MaxFloat32, - maxVal: math.MaxFloat32, + return newGenerator[float32](&float32Gen{ + floatGen{ + min: float64(min), + max: float64(max), + minVal: -math.MaxFloat32, + maxVal: math.MaxFloat32, + }, }) } -func Float64() *Generator { +// Float64 is a shorthand for [Float64Range](-[math.MaxFloat64], [math.MaxFloat64]). +func Float64() *Generator[float64] { return Float64Range(-math.MaxFloat64, math.MaxFloat64) } -func Float64Min(min float64) *Generator { +// Float64Min is a shorthand for [Float64Range](min, [math.MaxFloat64]). +func Float64Min(min float64) *Generator[float64] { return Float64Range(min, math.MaxFloat64) } -func Float64Max(max float64) *Generator { +// Float64Max is a shorthand for [Float64Range](-[math.MaxFloat64], max). +func Float64Max(max float64) *Generator[float64] { return Float64Range(-math.MaxFloat64, max) } -func Float64Range(min float64, max float64) *Generator { +// Float64Range creates a generator of 64-bit floating-point numbers in range [min, max]. +// Both min and max can be infinite. +func Float64Range(min float64, max float64) *Generator[float64] { assertf(min == min, "min should not be a NaN") assertf(max == max, "max should not be a NaN") assertf(min <= max, "invalid range [%v, %v]", min, max) - return newGenerator(&floatGen{ - typ: float64Type, - min: min, - max: max, - minVal: -math.MaxFloat64, - maxVal: math.MaxFloat64, + return newGenerator[float64](&float64Gen{ + floatGen{ + min: min, + max: max, + minVal: -math.MaxFloat64, + maxVal: math.MaxFloat64, + }, }) } type floatGen struct { - typ reflect.Type min float64 max float64 minVal float64 maxVal float64 } +type float32Gen struct{ floatGen } +type float64Gen struct{ floatGen } -func (g *floatGen) String() string { - kind := "Float64" - if g.typ == float32Type { - kind = "Float32" - } - +func (g *floatGen) stringImpl(kind string) string { if g.min != g.minVal && g.max != g.maxVal { return fmt.Sprintf("%sRange(%g, %g)", kind, g.min, g.max) } else if g.min != g.minVal { @@ -105,17 +107,18 @@ func (g *floatGen) String() string { return fmt.Sprintf("%s()", kind) } - -func (g *floatGen) type_() reflect.Type { - return g.typ +func (g *float32Gen) String() string { + return g.stringImpl("Float32") +} +func (g *float64Gen) String() string { + return g.stringImpl("Float64") } -func (g *floatGen) value(t *T) value { - if g.typ == float32Type { - return float32FromParts(genFloatRange(t.s, g.min, g.max, float32SignifBits)) - } else { - return float64FromParts(genFloatRange(t.s, g.min, g.max, float64SignifBits)) - } +func (g *float32Gen) value(t *T) float32 { + return float32FromParts(genFloatRange(t.s, g.min, g.max, float32SignifBits)) +} +func (g *float64Gen) value(t *T) float64 { + return float64FromParts(genFloatRange(t.s, g.min, g.max, float64SignifBits)) } func ufloatFracBits(e int32, signifBits uint) uint { diff --git a/vendor/pgregory.net/rapid/floats_external_test.go b/vendor/pgregory.net/rapid/floats_external_test.go deleted file mode 100644 index 82bd82d..0000000 --- a/vendor/pgregory.net/rapid/floats_external_test.go +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright 2019 Gregory Petrosyan -// -// 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 https://mozilla.org/MPL/2.0/. - -package rapid_test - -import ( - "math" - "sort" - "testing" - - . "pgregory.net/rapid" -) - -func TestFloatNoInf(t *testing.T) { - t.Parallel() - - gens := []*Generator{ - Float32(), - Float32Min(0), - Float32Max(0), - Float64(), - Float64Min(0), - Float64Max(0), - } - - for _, g := range gens { - t.Run(g.String(), MakeCheck(func(t *T) { - f := g.Draw(t, "f") - if math.IsInf(rv(f).Float(), 0) { - t.Fatalf("got infinity: %v", f) - } - })) - } -} - -func TestFloatExamples(t *testing.T) { - gens := []*Generator{ - Float32(), - Float32Min(-0.1), - Float32Min(1), - Float32Max(0.1), - Float32Max(2.5), - Float32Range(0.3, 0.30001), - Float32Range(0.3, 0.301), - Float32Range(0.3, 0.7), - Float32Range(math.E, math.Pi), - Float32Range(0, 1), - Float32Range(1, 2.5), - Float32Range(0, 100), - Float32Range(0, 10000), - Float64(), - Float64Min(-0.1), - Float64Min(1), - Float64Max(0.1), - Float64Max(2.5), - Float64Range(0.3, 0.30000001), - Float64Range(0.3, 0.301), - Float64Range(0.3, 0.7), - Float64Range(math.E, math.Pi), - Float64Range(0, 1), - Float64Range(1, 2.5), - Float64Range(0, 100), - Float64Range(0, 10000), - } - - for _, g := range gens { - t.Run(g.String(), func(t *testing.T) { - var vals []float64 - var vals32 bool - for i := 0; i < 100; i++ { - f := g.Example() - _, vals32 = f.(float32) - vals = append(vals, rv(f).Float()) - } - sort.Float64s(vals) - - for _, f := range vals { - if vals32 { - t.Logf("%30g %10.3g % 5d % 20d % 16x", f, f, int(math.Log10(math.Abs(f))), int64(f), math.Float32bits(float32(f))) - } else { - t.Logf("%30g %10.3g % 5d % 20d % 16x", f, f, int(math.Log10(math.Abs(f))), int64(f), math.Float64bits(f)) - } - } - }) - } -} - -func TestFloat32BoundCoverage(t *testing.T) { - t.Parallel() - - Check(t, func(t *T) { - min := Float32().Draw(t, "min").(float32) - max := Float32().Draw(t, "max").(float32) - if min > max { - min, max = max, min - } - - g := Float32Range(min, max) - var gotMin, gotMax, gotZero bool - for i := 0; i < 400; i++ { - f := g.Example(i).(float32) - - gotMin = gotMin || f == min - gotMax = gotMax || f == max - gotZero = gotZero || f == 0 - - if gotMin && gotMax && (min > 0 || max < 0 || gotZero) { - return - } - } - - t.Fatalf("[%v, %v]: got min %v, got max %v, got zero %v", min, max, gotMin, gotMax, gotZero) - }) -} - -func TestFloat64BoundCoverage(t *testing.T) { - t.Parallel() - - Check(t, func(t *T) { - min := Float64().Draw(t, "min").(float64) - max := Float64().Draw(t, "max").(float64) - if min > max { - min, max = max, min - } - - g := Float64Range(min, max) - var gotMin, gotMax, gotZero bool - for i := 0; i < 400; i++ { - f := g.Example(i).(float64) - - gotMin = gotMin || f == min - gotMax = gotMax || f == max - gotZero = gotZero || f == 0 - - if gotMin && gotMax && (min > 0 || max < 0 || gotZero) { - return - } - } - - t.Fatalf("[%v, %v]: got min %v, got max %v, got zero %v", min, max, gotMin, gotMax, gotZero) - }) -} diff --git a/vendor/pgregory.net/rapid/floats_test.go b/vendor/pgregory.net/rapid/floats_test.go deleted file mode 100644 index 8ddcbfe..0000000 --- a/vendor/pgregory.net/rapid/floats_test.go +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright 2019 Gregory Petrosyan -// -// 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 https://mozilla.org/MPL/2.0/. - -package rapid - -import ( - "math" - "testing" -) - -func TestFloatConversionRoundtrip(t *testing.T) { - t.Parallel() - - Check(t, func(t *T) { - u := uint32(t.s.drawBits(32)) - f := math.Float32frombits(u) - if math.IsNaN(float64(f)) { - t.Skip("NaN") // we can get NaNs with different bit patterns back - } - g := float32(float64(f)) - if g != f { - t.Fatalf("got %v (0x%x) back from %v (0x%x)", g, math.Float32bits(g), f, math.Float32bits(f)) - } - }) -} - -func TestUfloat32FromParts(t *testing.T) { - t.Parallel() - - Check(t, func(t *T) { - f := Float32Min(0).Draw(t, "f").(float32) - g := ufloat32FromParts(ufloat32Parts(f)) - if g != f { - t.Fatalf("got %v (0x%x) back from %v (0x%x)", g, math.Float32bits(g), f, math.Float32bits(f)) - } - }) -} - -func TestUfloat64FromParts(t *testing.T) { - t.Parallel() - - Check(t, func(t *T) { - f := Float64Min(0).Draw(t, "f").(float64) - g := ufloat64FromParts(ufloat64Parts(f)) - if g != f { - t.Fatalf("got %v (0x%x) back from %v (0x%x)", g, math.Float64bits(g), f, math.Float64bits(f)) - } - }) -} - -func TestGenUfloat32Range(t *testing.T) { - t.Parallel() - - Check(t, func(t *T) { - min := Float32Min(0).Draw(t, "min").(float32) - max := Float32Min(0).Draw(t, "max").(float32) - if min > max { - min, max = max, min - } - - f := ufloat32FromParts(genUfloatRange(t.s, float64(min), float64(max), float32SignifBits)) - if f < min || f > max { - t.Fatalf("%v (0x%x) outside of [%v, %v] ([0x%x, 0x%x])", f, math.Float32bits(f), min, max, math.Float32bits(min), math.Float32bits(max)) - } - }) -} - -func TestGenUfloat64Range(t *testing.T) { - t.Parallel() - - Check(t, func(t *T) { - min := Float64Min(0).Draw(t, "min").(float64) - max := Float64Min(0).Draw(t, "max").(float64) - if min > max { - min, max = max, min - } - - f := ufloat64FromParts(genUfloatRange(t.s, min, max, float64SignifBits)) - if f < min || f > max { - t.Fatalf("%v (0x%x) outside of [%v, %v] ([0x%x, 0x%x])", f, math.Float64bits(f), min, max, math.Float64bits(min), math.Float64bits(max)) - } - }) -} - -func TestGenFloat32Range(t *testing.T) { - t.Parallel() - - Check(t, func(t *T) { - min := Float32().Draw(t, "min").(float32) - max := Float32().Draw(t, "max").(float32) - if min > max { - min, max = max, min - } - - f := float32FromParts(genFloatRange(t.s, float64(min), float64(max), float32SignifBits)) - if f < min || f > max { - t.Fatalf("%v (0x%x) outside of [%v, %v] ([0x%x, 0x%x])", f, math.Float32bits(f), min, max, math.Float32bits(min), math.Float32bits(max)) - } - }) -} - -func TestGenFloat64Range(t *testing.T) { - t.Parallel() - - Check(t, func(t *T) { - min := Float64().Draw(t, "min").(float64) - max := Float64().Draw(t, "max").(float64) - if min > max { - min, max = max, min - } - - f := float64FromParts(genFloatRange(t.s, min, max, float64SignifBits)) - if f < min || f > max { - t.Fatalf("%v (0x%x) outside of [%v, %v] ([0x%x, 0x%x])", f, math.Float64bits(f), min, max, math.Float64bits(min), math.Float64bits(max)) - } - }) -} diff --git a/vendor/pgregory.net/rapid/generator.go b/vendor/pgregory.net/rapid/generator.go index eb3c334..4dc424d 100644 --- a/vendor/pgregory.net/rapid/generator.go +++ b/vendor/pgregory.net/rapid/generator.go @@ -6,62 +6,79 @@ package rapid -import "reflect" +import ( + "fmt" + "reflect" + "sync" +) -type value interface{} - -type generatorImpl interface { +type generatorImpl[V any] interface { String() string - type_() reflect.Type - value(t *T) value + value(t *T) V } -type Generator struct { - impl generatorImpl - typ reflect.Type - str string +// Generator describes a generator of values of type V. +type Generator[V any] struct { + impl generatorImpl[V] + strOnce sync.Once + str string } -func newGenerator(impl generatorImpl) *Generator { - return &Generator{ +func newGenerator[V any](impl generatorImpl[V]) *Generator[V] { + return &Generator[V]{ impl: impl, - typ: impl.type_(), } } -func (g *Generator) String() string { - if g.str == "" { +func (g *Generator[V]) String() string { + g.strOnce.Do(func() { g.str = g.impl.String() - } + }) return g.str } -func (g *Generator) type_() reflect.Type { - return g.typ -} - -func (g *Generator) Draw(t *T, label string) interface{} { - if t.tbLog && t.tb != nil { +// Draw produces a value from the generator. +func (g *Generator[V]) Draw(t *T, label string) V { + if t.tbLog { t.tb.Helper() } - return t.draw(g, label) + + v := g.value(t) + + if len(t.refDraws) > 0 { + ref := t.refDraws[t.draws] + if !reflect.DeepEqual(v, ref) { + t.tb.Fatalf("draw %v differs: %#v vs expected %#v", t.draws, v, ref) + } + } + + if t.tbLog || t.rawLog != nil { + if label == "" { + label = fmt.Sprintf("#%v", t.draws) + } + + if t.tbLog { + t.tb.Helper() + } + t.Logf("[rapid] draw %v: %#v", label, v) + } + + t.draws++ + + return v } -func (g *Generator) value(t *T) value { +func (g *Generator[V]) value(t *T) V { i := t.s.beginGroup(g.str, true) - v := g.impl.value(t) - u := reflect.TypeOf(v) - assertf(v != nil, "%v has generated a nil value", g) - assertf(u.AssignableTo(g.typ), "%v has generated a value of type %v which is not assignable to %v", g, u, g.typ) - t.s.endGroup(i, false) - return v } -func (g *Generator) Example(seed ...int) interface{} { +// Example produces an example value from the generator. If seed is provided, value is produced deterministically +// based on seed. Example should only be used for examples; always use *Generator.Draw in property-based tests. +func (g *Generator[V]) Example(seed ...int) V { s := baseSeed() if len(seed) > 0 { s = uint64(seed[0]) @@ -73,26 +90,29 @@ func (g *Generator) Example(seed ...int) interface{} { return v } -func (g *Generator) Filter(fn interface{}) *Generator { +// Filter creates a generator producing only values from g for which fn returns true. +func (g *Generator[V]) Filter(fn func(V) bool) *Generator[V] { return filter(g, fn) } -func (g *Generator) Map(fn interface{}) *Generator { - return map_(g, fn) +// AsAny creates a generator producing values from g converted to any. +func (g *Generator[V]) AsAny() *Generator[any] { + return asAny(g) } -func example(g *Generator, t *T) (value, int, error) { +func example[V any](g *Generator[V], t *T) (V, int, error) { for i := 1; ; i++ { r, err := recoverValue(g, t) if err == nil { return r, i, nil } else if i == exampleMaxTries { - return nil, i, err + var zero V + return zero, i, err } } } -func recoverValue(g *Generator, t *T) (v value, err *testError) { +func recoverValue[V any](g *Generator[V], t *T) (v V, err *testError) { defer func() { err = panicToError(recover(), 3) }() return g.value(t), nil diff --git a/vendor/pgregory.net/rapid/generator_test.go b/vendor/pgregory.net/rapid/generator_test.go deleted file mode 100644 index 27fc50e..0000000 --- a/vendor/pgregory.net/rapid/generator_test.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2019 Gregory Petrosyan -// -// 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 https://mozilla.org/MPL/2.0/. - -package rapid - -import ( - "reflect" - "testing" -) - -type trivialGenImpl struct{} - -func (trivialGenImpl) String() string { return "" } -func (trivialGenImpl) type_() reflect.Type { return uint64Type } -func (trivialGenImpl) value(t *T) value { return t.s.drawBits(64) } - -func BenchmarkTrivialGenImplValue(b *testing.B) { - t := newT(nil, newRandomBitStream(baseSeed(), false), false, nil) - g := trivialGenImpl{} - b.ResetTimer() - - for i := 0; i < b.N; i++ { - g.value(t) - } -} - -func BenchmarkGenerator_Value(b *testing.B) { - t := newT(nil, newRandomBitStream(baseSeed(), false), false, nil) - g := newGenerator(trivialGenImpl{}) - b.ResetTimer() - - for i := 0; i < b.N; i++ { - g.value(t) - } -} diff --git a/vendor/pgregory.net/rapid/go.mod b/vendor/pgregory.net/rapid/go.mod deleted file mode 100644 index 8393317..0000000 --- a/vendor/pgregory.net/rapid/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module pgregory.net/rapid - -go 1.12 diff --git a/vendor/pgregory.net/rapid/integers.go b/vendor/pgregory.net/rapid/integers.go index 7a3b3f7..62d4e7f 100644 --- a/vendor/pgregory.net/rapid/integers.go +++ b/vendor/pgregory.net/rapid/integers.go @@ -9,7 +9,6 @@ package rapid import ( "fmt" "math" - "reflect" ) const ( @@ -30,44 +29,32 @@ const ( uintSize = 32 << (^uint(0) >> 32 & 1) intSize = uintSize - minInt = -1 << (intSize - 1) - maxInt = 1<<(intSize-1) - 1 - maxUint = 1< -// -// 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 https://mozilla.org/MPL/2.0/. - -package rapid_test - -import ( - "flag" - "math/bits" - "reflect" - "sort" - "strconv" - "testing" - - . "pgregory.net/rapid" -) - -var ( - flaky = flag.Bool("flaky.ext", false, "run flaky external tests") - rv = reflect.ValueOf -) - -func TestIntExamples(t *testing.T) { - gens := []*Generator{ - Int(), - IntMin(-3), - IntMax(3), - IntRange(-3, 7), - IntRange(-1000, 1000000), - IntRange(0, 9), - IntRange(0, 15), - IntRange(10, 100), - IntRange(100, 10000), - IntRange(100, 1000000), - IntRange(100, 1<<60-1), - } - - for _, g := range gens { - t.Run(g.String(), func(t *testing.T) { - var vals []int - for i := 0; i < 100; i++ { - vals = append(vals, g.Example().(int)) - } - sort.Ints(vals) - - for _, i := range vals { - t.Log(i) - } - }) - } -} - -func createGen(ctor interface{}, args ...interface{}) *Generator { - refArgs := make([]reflect.Value, len(args)) - for i, arg := range args { - refArgs[i] = rv(arg) - } - - return rv(ctor).Call(refArgs)[0].Interface().(*Generator) -} - -func TestIntMinMaxRange(t *testing.T) { - t.Parallel() - - data := []struct { - g *Generator - min interface{} - max interface{} - range_ interface{} - }{ - {Int(), IntMin, IntMax, IntRange}, - {Int8(), Int8Min, Int8Max, Int8Range}, - {Int16(), Int16Min, Int16Max, Int16Range}, - {Int32(), Int32Min, Int32Max, Int32Range}, - {Int64(), Int64Min, Int64Max, Int64Range}, - } - - for _, d := range data { - t.Run(d.g.String(), MakeCheck(func(t *T) { - min := d.g.Draw(t, "min") - max := d.g.Draw(t, "max") - if rv(min).Int() > rv(max).Int() { - t.Skip("min > max") - } - - i := createGen(d.min, min).Draw(t, "i") - if rv(i).Int() < rv(min).Int() { - t.Fatalf("got %v which is less than min %v", i, min) - } - - j := createGen(d.max, max).Draw(t, "j") - if rv(j).Int() > rv(max).Int() { - t.Fatalf("got %v which is more than max %v", j, max) - } - - k := createGen(d.range_, min, max).Draw(t, "k") - if rv(k).Int() < rv(min).Int() || rv(k).Int() > rv(max).Int() { - t.Fatalf("got %v which is out of bounds [%v, %v]", k, min, max) - } - })) - } -} - -func TestUintMinMaxRange(t *testing.T) { - t.Parallel() - - data := []struct { - g *Generator - min interface{} - max interface{} - range_ interface{} - }{ - {Byte(), ByteMin, ByteMax, ByteRange}, - {Uint(), UintMin, UintMax, UintRange}, - {Uint8(), Uint8Min, Uint8Max, Uint8Range}, - {Uint16(), Uint16Min, Uint16Max, Uint16Range}, - {Uint32(), Uint32Min, Uint32Max, Uint32Range}, - {Uint64(), Uint64Min, Uint64Max, Uint64Range}, - {Uintptr(), UintptrMin, UintptrMax, UintptrRange}, - } - - for _, d := range data { - t.Run(d.g.String(), MakeCheck(func(t *T) { - min := d.g.Draw(t, "min") - max := d.g.Draw(t, "max") - if rv(min).Uint() > rv(max).Uint() { - t.Skip("min > max") - } - - i := createGen(d.min, min).Draw(t, "i") - if rv(i).Uint() < rv(min).Uint() { - t.Fatalf("got %v which is less than min %v", i, min) - } - - j := createGen(d.max, max).Draw(t, "j") - if rv(j).Uint() > rv(max).Uint() { - t.Fatalf("got %v which is more than max %v", j, max) - } - - k := createGen(d.range_, min, max).Draw(t, "k") - if rv(k).Uint() < rv(min).Uint() || rv(k).Uint() > rv(max).Uint() { - t.Fatalf("got %v which is out of bounds [%v, %v]", k, min, max) - } - })) - } -} - -func TestIntBoundCoverage(t *testing.T) { - t.Parallel() - - Check(t, func(t *T) { - min := Int().Draw(t, "min").(int) - max := Int().Draw(t, "max").(int) - if min > max { - min, max = max, min - } - - g := IntRange(min, max) - var gotMin, gotMax, gotZero bool - for i := 0; i < 250; i++ { - n := g.Example(i).(int) - - gotMin = gotMin || n == min - gotMax = gotMax || n == max - gotZero = gotZero || n == 0 - - if gotMin && gotMax && (min > 0 || max < 0 || gotZero) { - return - } - } - - t.Fatalf("[%v, %v]: got min %v, got max %v, got zero %v", min, max, gotMin, gotMax, gotZero) - }) -} - -func TestByteCoverage(t *testing.T) { - t.Parallel() - if !*flaky { - t.Skip("flaky") - } - - for b := 0; b < 256; b++ { - t.Run(strconv.Itoa(b), func(t *testing.T) { - _ = Byte().Filter(func(v byte) bool { return v == byte(b) }).Example() - }) - } -} - -func TestIntCoverage(t *testing.T) { - t.Parallel() - if !*flaky { - t.Skip("flaky") - } - - filters := []func(int) bool{ - func(i int) bool { return i == 0 }, - func(i int) bool { return i == 1 }, - func(i int) bool { return i == -1 }, - func(i int) bool { return i%2 == 0 }, - func(i int) bool { return i%17 == 0 }, - func(i int) bool { return i > 0 && i < 100 }, - func(i int) bool { return i < 0 && i > -100 }, - func(i int) bool { return i > 1<<30 }, - func(i int) bool { return i < -(1 << 30) }, - } - - if bits.UintSize == 64 { - filters = append(filters, func(i int) bool { return i > 1<<62 }) - filters = append(filters, func(i int) bool { return i < -(1 << 62) }) - } - - for i, fn := range filters { - t.Run(strconv.Itoa(i), func(t *testing.T) { - _ = Int().Filter(fn).Example() - }) - } -} - -func TestUintCoverage(t *testing.T) { - t.Parallel() - if !*flaky { - t.Skip("flaky") - } - - filters := []func(uint) bool{ - func(i uint) bool { return i == 0 }, - func(i uint) bool { return i == 1 }, - func(i uint) bool { return i%2 == 0 }, - func(i uint) bool { return i%17 == 0 }, - func(i uint) bool { return i > 0 && i < 100 }, - func(i uint) bool { return i > 1<<31 }, - } - - if bits.UintSize == 64 { - filters = append(filters, func(i uint) bool { return i > 1<<63 }) - } - - for i, fn := range filters { - t.Run(strconv.Itoa(i), func(t *testing.T) { - _ = Uint().Filter(fn).Example() - }) - } -} diff --git a/vendor/pgregory.net/rapid/make.go b/vendor/pgregory.net/rapid/make.go new file mode 100644 index 0000000..81e1de9 --- /dev/null +++ b/vendor/pgregory.net/rapid/make.go @@ -0,0 +1,194 @@ +// Copyright 2022 Gregory Petrosyan +// +// 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 https://mozilla.org/MPL/2.0/. + +package rapid + +import ( + "fmt" + "reflect" +) + +// Make creates a generator of values of type V, using reflection to infer the required structure. +func Make[V any]() *Generator[V] { + var zero V + gen := newMakeGen(reflect.TypeOf(zero)) + return newGenerator[V](&makeGen[V]{ + gen: gen, + }) +} + +type makeGen[V any] struct { + gen *Generator[any] +} + +func (g *makeGen[V]) String() string { + var zero V + return fmt.Sprintf("Make[%T]()", zero) +} + +func (g *makeGen[V]) value(t *T) V { + return g.gen.value(t).(V) +} + +func newMakeGen(typ reflect.Type) *Generator[any] { + gen, mayNeedCast := newMakeKindGen(typ) + if !mayNeedCast || typ.String() == typ.Kind().String() { + return gen // fast path with less reflect + } + return newGenerator[any](&castGen{gen, typ}) +} + +type castGen struct { + gen *Generator[any] + typ reflect.Type +} + +func (g *castGen) String() string { + return fmt.Sprintf("cast(%v, %v)", g.gen, g.typ.Name()) +} + +func (g *castGen) value(t *T) any { + v := g.gen.value(t) + return reflect.ValueOf(v).Convert(g.typ).Interface() +} + +func newMakeKindGen(typ reflect.Type) (gen *Generator[any], mayNeedCast bool) { + switch typ.Kind() { + case reflect.Bool: + return Bool().AsAny(), true + case reflect.Int: + return Int().AsAny(), true + case reflect.Int8: + return Int8().AsAny(), true + case reflect.Int16: + return Int16().AsAny(), true + case reflect.Int32: + return Int32().AsAny(), true + case reflect.Int64: + return Int64().AsAny(), true + case reflect.Uint: + return Uint().AsAny(), true + case reflect.Uint8: + return Uint8().AsAny(), true + case reflect.Uint16: + return Uint16().AsAny(), true + case reflect.Uint32: + return Uint32().AsAny(), true + case reflect.Uint64: + return Uint64().AsAny(), true + case reflect.Uintptr: + return Uintptr().AsAny(), true + case reflect.Float32: + return Float32().AsAny(), true + case reflect.Float64: + return Float64().AsAny(), true + case reflect.Array: + return genAnyArray(typ), false + case reflect.Map: + return genAnyMap(typ), false + case reflect.Pointer: + return Deferred(func() *Generator[any] { return genAnyPointer(typ) }), false + case reflect.Slice: + return genAnySlice(typ), false + case reflect.String: + return String().AsAny(), true + case reflect.Struct: + return genAnyStruct(typ), false + default: + panic(fmt.Sprintf("unsupported type kind for Make: %v", typ.Kind())) + } +} + +func genAnyPointer(typ reflect.Type) *Generator[any] { + elem := typ.Elem() + elemGen := newMakeGen(elem) + const pNonNil = 0.5 + + return Custom[any](func(t *T) any { + if flipBiasedCoin(t.s, pNonNil) { + val := elemGen.value(t) + ptr := reflect.New(elem) + ptr.Elem().Set(reflect.ValueOf(val)) + return ptr.Interface() + } else { + return reflect.Zero(typ).Interface() + } + }) +} + +func genAnyArray(typ reflect.Type) *Generator[any] { + count := typ.Len() + elemGen := newMakeGen(typ.Elem()) + + return Custom[any](func(t *T) any { + a := reflect.Indirect(reflect.New(typ)) + if count == 0 { + t.s.drawBits(0) + } else { + for i := 0; i < count; i++ { + e := reflect.ValueOf(elemGen.value(t)) + a.Index(i).Set(e) + } + } + return a.Interface() + }) +} + +func genAnySlice(typ reflect.Type) *Generator[any] { + elemGen := newMakeGen(typ.Elem()) + + return Custom[any](func(t *T) any { + repeat := newRepeat(-1, -1, -1, elemGen.String()) + sl := reflect.MakeSlice(typ, 0, repeat.avg()) + for repeat.more(t.s) { + e := reflect.ValueOf(elemGen.value(t)) + sl = reflect.Append(sl, e) + } + return sl.Interface() + }) +} + +func genAnyMap(typ reflect.Type) *Generator[any] { + keyGen := newMakeGen(typ.Key()) + valGen := newMakeGen(typ.Elem()) + + return Custom[any](func(t *T) any { + label := keyGen.String() + "," + valGen.String() + repeat := newRepeat(-1, -1, -1, label) + m := reflect.MakeMapWithSize(typ, repeat.avg()) + for repeat.more(t.s) { + k := reflect.ValueOf(keyGen.value(t)) + v := reflect.ValueOf(valGen.value(t)) + if m.MapIndex(k).IsValid() { + repeat.reject() + } else { + m.SetMapIndex(k, v) + } + } + return m.Interface() + }) +} + +func genAnyStruct(typ reflect.Type) *Generator[any] { + numFields := typ.NumField() + fieldGens := make([]*Generator[any], numFields) + for i := 0; i < numFields; i++ { + fieldGens[i] = newMakeGen(typ.Field(i).Type) + } + + return Custom[any](func(t *T) any { + s := reflect.Indirect(reflect.New(typ)) + if numFields == 0 { + t.s.drawBits(0) + } else { + for i := 0; i < numFields; i++ { + f := reflect.ValueOf(fieldGens[i].value(t)) + s.Field(i).Set(f) + } + } + return s.Interface() + }) +} diff --git a/vendor/pgregory.net/rapid/persist.go b/vendor/pgregory.net/rapid/persist.go index bdf5ff1..155ebfe 100644 --- a/vendor/pgregory.net/rapid/persist.go +++ b/vendor/pgregory.net/rapid/persist.go @@ -9,7 +9,6 @@ package rapid import ( "bufio" "fmt" - "io/ioutil" "os" "path/filepath" "strconv" @@ -19,6 +18,8 @@ import ( ) const ( + rapidVersion = "v0.4.8" + persistDirMode = 0775 failfileTmpPattern = ".rapid-failfile-tmp-*" ) @@ -35,19 +36,27 @@ func kindaSafeFilename(f string) string { return s.String() } -func failFileName(testName string) string { +func failFileName(testName string) (string, string) { ts := time.Now().Format("20060102150405") - return fmt.Sprintf("%s-%s-%d.fail", kindaSafeFilename(testName), ts, os.Getpid()) + fileName := fmt.Sprintf("%s-%s-%d.fail", kindaSafeFilename(testName), ts, os.Getpid()) + dirName := filepath.Join("testdata", "rapid", kindaSafeFilename(testName)) + return dirName, filepath.Join(dirName, fileName) +} + +func failFilePattern(testName string) string { + fileName := fmt.Sprintf("%s-*.fail", kindaSafeFilename(testName)) + dirName := filepath.Join("testdata", "rapid", kindaSafeFilename(testName)) + return filepath.Join(dirName, fileName) } -func saveFailFile(filename string, output []byte, buf []uint64) error { +func saveFailFile(filename string, version string, output []byte, seed uint64, buf []uint64) error { dir := filepath.Dir(filename) err := os.MkdirAll(dir, persistDirMode) if err != nil { return fmt.Errorf("failed to create directory for fail file %q: %w", filename, err) } - f, err := ioutil.TempFile(dir, failfileTmpPattern) + f, err := os.CreateTemp(dir, failfileTmpPattern) if err != nil { return fmt.Errorf("failed to create temporary file for fail file %q: %w", filename, err) } @@ -62,12 +71,12 @@ func saveFailFile(filename string, output []byte, buf []uint64) error { } } - var bs []string + bs := []string{fmt.Sprintf("%v#%v", version, seed)} for _, u := range buf { bs = append(bs, fmt.Sprintf("0x%x", u)) } - _, err = f.WriteString(strings.Join(bs, " ")) + _, err = f.WriteString(strings.Join(bs, "\n")) if err != nil { return fmt.Errorf("failed to write data to fail file %q: %w", filename, err) } @@ -81,35 +90,47 @@ func saveFailFile(filename string, output []byte, buf []uint64) error { return nil } -func loadFailFile(filename string) ([]uint64, error) { +func loadFailFile(filename string) (string, uint64, []uint64, error) { f, err := os.Open(filename) if err != nil { - return nil, fmt.Errorf("failed to open fail file: %w", err) + return "", 0, nil, fmt.Errorf("failed to open fail file: %w", err) } defer func() { _ = f.Close() }() - var data string + var data []string scanner := bufio.NewScanner(f) for scanner.Scan() { s := strings.TrimSpace(scanner.Text()) if strings.HasPrefix(s, "#") || s == "" { continue } - data = s + data = append(data, s) } if err := scanner.Err(); err != nil { - return nil, fmt.Errorf("failed to load fail file %q: %w", filename, err) + return "", 0, nil, fmt.Errorf("failed to load fail file %q: %w", filename, err) + } + + if len(data) == 0 { + return "", 0, nil, fmt.Errorf("no data in fail file %q", filename) + } + + split := strings.Split(data[0], "#") + if len(split) != 2 { + return "", 0, nil, fmt.Errorf("invalid version/seed field %q in %q", data[0], filename) + } + seed, err := strconv.ParseUint(split[1], 10, 64) + if err != nil { + return "", 0, nil, fmt.Errorf("invalid seed %q in %q", split[1], filename) } var buf []uint64 - fields := strings.Fields(data) - for _, b := range fields { + for _, b := range data[1:] { u, err := strconv.ParseUint(b, 0, 64) if err != nil { - return nil, fmt.Errorf("failed to load fail file %q: %w", filename, err) + return "", 0, nil, fmt.Errorf("failed to load fail file %q: %w", filename, err) } buf = append(buf, u) } - return buf, nil + return split[0], seed, buf, nil } diff --git a/vendor/pgregory.net/rapid/persist_test.go b/vendor/pgregory.net/rapid/persist_test.go deleted file mode 100644 index 5d89ab3..0000000 --- a/vendor/pgregory.net/rapid/persist_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2020 Gregory Petrosyan -// -// 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 https://mozilla.org/MPL/2.0/. - -package rapid - -import ( - "os" - "testing" -) - -func TestFailFileRoundtrip(t *testing.T) { - t.Parallel() - - Check(t, func(t *T) { - var ( - testName = String().Draw(t, "testName").(string) - output = SliceOf(Byte()).Draw(t, "output").([]byte) - buf = SliceOf(Uint64()).Draw(t, "buf").([]uint64) - ) - - fileName := failFileName(testName) - err := saveFailFile(fileName, output, buf) - if err != nil { - t.Fatal(err) - } - defer func() { _ = os.Remove(fileName) }() - - buf2, err := loadFailFile(fileName) - if err != nil { - t.Fatal(err) - } - - if len(buf2) != len(buf) { - t.Fatalf("got buf of length %v instead of %v", len(buf2), len(buf)) - } - for i, u := range buf { - if buf2[i] != u { - t.Fatalf("got %v instead of %v at %v", buf2[i], u, i) - } - } - }) -} diff --git a/vendor/pgregory.net/rapid/regexp_external_test.go b/vendor/pgregory.net/rapid/regexp_external_test.go deleted file mode 100644 index 557da5d..0000000 --- a/vendor/pgregory.net/rapid/regexp_external_test.go +++ /dev/null @@ -1,1096 +0,0 @@ -// Copyright 2019 Gregory Petrosyan -// -// 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 https://mozilla.org/MPL/2.0/. - -package rapid_test - -import ( - "regexp" - "testing" - - . "pgregory.net/rapid" -) - -// Based on https://github.com/rust-lang/regex/blob/master/tests/crates_regex.rs -var crateRegexps = []string{ - `\s*(\d+)(\w)\s*`, // autoshutdown-0.1.0: r"\s*(\d+)(\w)\s*" - `/`, // epub-1.1.1: r"/" - "^Revision\t+: ([0-9a-fA-F]+)", // rpi-info-0.2.0: "^Revision\t+: ([0-9a-fA-F]+)" - "Serial\t+: ([0-9a-fA-F]+)", // rpi-info-0.2.0: "Serial\t+: ([0-9a-fA-F]+)" - `^u([0-9]+)(be|le|he)?$`, // pnet_macros-0.21.0: r"^u([0-9]+)(be|le|he)?$" - `^[A-Z]{2}\d{2}[A-Z\d]{1,30}$`, // iban_validate-1.0.3: r"^[A-Z]{2}\d{2}[A-Z\d]{1,30}$" - `.*\[(?P.+)%.*\].*`, // markifier-0.1.0: r".*\[(?P.+)%.*\].*" - `(#include) (\S*)(.*)`, // mallumo-0.3.0: r"(#include) (\S*)(.*)" - `(ERROR: \d+:)(\d+)(: )(.+)`, // mallumo-0.3.0: r"(ERROR: \d+:)(\d+)(: )(.+)" - `(\d+\()(\d+)(?:\) : )(.+)`, // mallumo-0.3.0: r"(\d+\()(\d+)(?:\) : )(.+)" - `(.+?)(\[.*?\])?`, // magnet_more-0.0.1: r"(.+?)(\[.*?\])?" - `:(?P[a-zA-Z_]+)`, // magnet_app-0.0.1: r":(?P[a-zA-Z_]+)" - `^\d{6}(?:\s*,\s*\d{6})*$`, // yubibomb-0.2.0: r"^\d{6}(?:\s*,\s*\d{6})*$" - `[\\/]([^\\/?]+)(\?.*)?$`, // multirust-rs-0.0.4: r"[\\/]([^\\/?]+)(\?.*)?$" - "\"[a-z]*\":null", // hueclient-0.3.2: "\"[a-z]*\":null" - ",+", // hueclient-0.3.2: ",+" - ",\\}", // hueclient-0.3.2: ",\\}" - "\\{,", // hueclient-0.3.2: "\\{," - `[a-zA-Z_\$][a-zA-Z_0-9]*`, // aerial-0.1.0: r"[a-zA-Z_\$][a-zA-Z_0-9]*" - `thi[sng]+`, // aerial-0.1.0: r"thi[sng]+" - `(.+)\s+\((.+?)\)`, // rvue-0.1.0: r"(.+)\s+\((.+?)\)" - `([\d\.]+)\s*out\s*of\s*([\d\.]+)`, // rvue-0.1.0: r"([\d\.]+)\s*out\s*of\s*([\d\.]+)" - `^([\d\.]+)\s*(?:\(\))?$`, // rvue-0.1.0: r"^([\d\.]+)\s*(?:\(\))?$" - `([\d\.]+)\s*Points\s*Possible`, // rvue-0.1.0: r"([\d\.]+)\s*Points\s*Possible" - `([\d\.]+)\s*/\s*([\d\.]+)`, // rvue-0.1.0: r"([\d\.]+)\s*/\s*([\d\.]+)" - `_?([_a-z0-9]+)\s*:\s*([_a-z0-9]+)\s*[,)]`, // rvsim-0.1.0: r"_?([_a-z0-9]+)\s*:\s*([_a-z0-9]+)\s*[,)]" - "(.*[^\\\\])\\{\\}(.*)", // nereon-0.1.4: "(.*[^\\\\])\\{\\}(.*)" - `((?i)^(.+).s(\d+)e(\d+).*)$`, // next_episode-0.3.0: r"((?i)^(.+).s(\d+)e(\d+).*)$" - `[^a-z0-9-]+`, // migrant_lib-0.19.2: r"[^a-z0-9-]+" - `[0-9]{14}_[a-z0-9-]+`, // migrant_lib-0.19.2: r"[0-9]{14}_[a-z0-9-]+" - `([0-9]{14}_)?[a-z0-9-]+`, // migrant_lib-0.19.2: r"([0-9]{14}_)?[a-z0-9-]+" - // minipre-0.2.0: "$_" - `>\s+<`, // minifier-0.0.13: r">\s+<" - `\s{2,}|[\r\n]`, // minifier-0.0.13: r"\s{2,}|[\r\n]" - `<(style|script)[\w|\s].*?>`, // minifier-0.0.13: r"<(style|script)[\w|\s].*?>" - "", // minifier-0.0.13: "" - `<\w.*?>`, // minifier-0.0.13: r"<\w.*?>" - ` \s+|\s +`, // minifier-0.0.13: r" \s+|\s +" - `\w\s+\w`, // minifier-0.0.13: r"\w\s+\w" - `'\s+>`, // minifier-0.0.13: r"'\s+>" - `\d\s+>`, // minifier-0.0.13: r"\d\s+>" - `(?P\([^)]+\))|(?P[a-zA-Z0-9_]+)`, // ggp-rs-0.1.2: r"(?P\([^)]+\))|(?P[a-zA-Z0-9_]+)" - `\((.*)\).`, // ggp-rs-0.1.2: r"\((.*)\)." - "[A-Za-z0-9_]", // poe-superfilter-0.2.0: "[A-Za-z0-9_]" - `(\d+)x(\d+)`, // poke-a-mango-0.5.0: r"(\d+)x(\d+)" - `(?P\d+) (?P\d+)`, // pop3-rs-0.1.0: r"(?P\d+) (?P\d+)" - `(?P\d+) (?P[\x21-\x7E]{1,70})`, // pop3-rs-0.1.0: r"(?P\d+) (?P[\x21-\x7E]{1,70})" - `(<.*>)\r\n$`, // pop3-rs-0.1.0: r"(<.*>)\r\n$" - `^(?P\+OK|-ERR) (?P.*)`, // pop3-rs-0.1.0: r"^(?P\+OK|-ERR) (?P.*)" - `^\.\r\n$`, // pop3-1.0.6: r"^\.\r\n$" - `\+OK(.*)`, // pop3-1.0.6: r"\+OK(.*)" - `-ERR(.*)`, // pop3-1.0.6: r"-ERR(.*)" - `\+OK (\d+) (\d+)\r\n`, // pop3-1.0.6: r"\+OK (\d+) (\d+)\r\n" - `(\d+) ([\x21-\x7e]+)\r\n`, // pop3-1.0.6: r"(\d+) ([\x21-\x7e]+)\r\n" - `\+OK (\d+) ([\x21-\x7e]+)\r\n`, // pop3-1.0.6: r"\+OK (\d+) ([\x21-\x7e]+)\r\n" - `(\d+) (\d+)\r\n`, // pop3-1.0.6: r"(\d+) (\d+)\r\n" - `\+OK (\d+) (\d+)\r\n`, // pop3-1.0.6: r"\+OK (\d+) (\d+)\r\n" - "github:(\\w+)/?(\\w+)?", // polk-1.1.3: "github:(\\w+)/?(\\w+)?" - "^[0-9]{5}", // geochunk-0.1.5: "^[0-9]{5}" - `((?:(?:0|1[\d]{0,2}|2(?:[0-4]\d?|5[0-5]?|[6-9])?|[3-9]\d?)\.){3}(?:0|1[\d]{0,2}|2(?:[0-4]\d?|5[0-5]?|[6-9])?|[3-9]\d?))`, // generic-dns-update-1.1.4: r"((?:(?:0|1[\d]{0,2}|2(?:[0-4]\d?|5[0-5]?|[6-9])?|[3-9]\d?)\.){3}(?:0|1[\d]{0,2}|2(?:[0-4]\d?|5[0-5]?|[6-9])?|[3-9]\d?))" - `((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d)\.){3}(\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d))|(([0-9A-Fa-f]{1,4}:){0,5}:((\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d)\.){3}(\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d))|(::([0-9A-Fa-f]{1,4}:){0,5}((\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d)\.){3}(\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))`, // generic-dns-update-1.1.4: r"((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d)\.){3}(\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d))|(([0-9A-Fa-f]{1,4}:){0,5}:((\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d)\.){3}(\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d))|(::([0-9A-Fa-f]{1,4}:){0,5}((\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d)\.){3}(\d((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\d))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))" - `([0-9.]*)`, // generic-dns-update-1.1.4: r"([0-9.]*)" - `([0-9]+)`, // generic-dns-update-1.1.4: r"([0-9]+)" - `([0-9]+)`, // generic-dns-update-1.1.4: r"([0-9]+)" - `([0-1]*)`, // generic-dns-update-1.1.4: r"([0-1]*)" - `(\d*)\.(\d*)\.(\d*)(-(\S*))?`, // generate-nix-pkg-0.3.0: r"(\d*)\.(\d*)\.(\d*)(-(\S*))?" - `^(\S*) (\d*)\.(\d*)\.(\d*)(-(\S*))?`, // generate-nix-pkg-0.3.0: r"^(\S*) (\d*)\.(\d*)\.(\d*)(-(\S*))?" - `arch/([a-z0-9_])+/`, // genact-0.6.0: r"arch/([a-z0-9_])+/" - `arch/([a-z0-9_])+/`, // genact-0.6.0: r"arch/([a-z0-9_])+/" - `^\s*((\*(/\d+)?)|[0-9-,/]+)(\s+((\*(/\d+)?)|[0-9-,/]+)){4,5}\s*$`, // cron_rs-0.1.6: r"^\s*((\*(/\d+)?)|[0-9-,/]+)(\s+((\*(/\d+)?)|[0-9-,/]+)){4,5}\s*$" - `^([a-zA-Z]+)::(.+)$`, // systemfd-0.3.0: r"^([a-zA-Z]+)::(.+)$" - "__?hidden#\\d+_", // symbolic-debuginfo-5.0.2: "__?hidden#\\d+_" - `^Linux ([^ ]+) (.*) \w+(?: GNU/Linux)?$`, // symbolic-minidump-5.0.2: r"^Linux ([^ ]+) (.*) \w+(?: GNU/Linux)?$" - // graphql-idl-parser-0.1.1: "^(?u:\\#)(?u:[\t-\r - \u{85}-\u{85}\u{a0}-\u{a0}\u{1680}-\u{1680}\u{2000}-\u{200a}\u{2028}-\u{2029}\u{202f}-\u{202f}\u{205f}-\u{205f}\u{3000}-\u{3000}])*(?u:.)+" - // graphql-idl-parser-0.1.1: "^(?u:=)(?u:[\t-\r - \u{85}-\u{85}\u{a0}-\u{a0}\u{1680}-\u{1680}\u{2000}-\u{200a}\u{2028}-\u{2029}\u{202f}-\u{202f}\u{205f}-\u{205f}\u{3000}-\u{3000}])*(?u:.)+" - "^([A-Z_-_a-z])([0-9A-Z_-_a-z])*", // graphql-idl-parser-0.1.1: "^(?u:[A-Z_-_a-z])(?u:[0-9A-Z_-_a-z])*" - "^(!)", // graphql-idl-parser-0.1.1: "^(?u:!)" - "^(\\()", // graphql-idl-parser-0.1.1: "^(?u:\\()" - "^(\\))", // graphql-idl-parser-0.1.1: "^(?u:\\))" - "^(,)", // graphql-idl-parser-0.1.1: "^(?u:,)" - "^(:)", // graphql-idl-parser-0.1.1: "^(?u::)" - "^(@)", // graphql-idl-parser-0.1.1: "^(?u:@)" - "^(\\[)", // graphql-idl-parser-0.1.1: "^(?u:\\[)" - "^(\\])", // graphql-idl-parser-0.1.1: "^(?u:\\])" - "^(enum)", // graphql-idl-parser-0.1.1: "^(?u:enum)" - "^(implements)", // graphql-idl-parser-0.1.1: "^(?u:implements)" - "^(input)", // graphql-idl-parser-0.1.1: "^(?u:input)" - "^(interface)", // graphql-idl-parser-0.1.1: "^(?u:interface)" - "^(scalar)", // graphql-idl-parser-0.1.1: "^(?u:scalar)" - "^(type)", // graphql-idl-parser-0.1.1: "^(?u:type)" - "^(union)", // graphql-idl-parser-0.1.1: "^(?u:union)" - "^(\\{)", // graphql-idl-parser-0.1.1: "^(?u:\\{)" - "^(\\})", // graphql-idl-parser-0.1.1: "^(?u:\\})" - `(?s)/\*(?P.*?)\*/`, // grimoire-0.1.0: r"(?s)/\*(?P.*?)\*/" - `[\d]+(?:[~\x{2053}\x{223C}\x{FF5E}][\d]+)?`, // phonenumber-0.2.0+8.9.0: r"[\d]+(?:[~\x{2053}\x{223C}\x{FF5E}][\d]+)?" - `[, \[\]]`, // phonenumber-0.2.0+8.9.0: r"[, \[\]]" - `[\\/] *x`, // phonenumber-0.2.0+8.9.0: r"[\\/] *x" - `[[\P{N}&&\P{L}]&&[^#]]+$`, // phonenumber-0.2.0+8.9.0: r"[[\P{N}&&\P{L}]&&[^#]]+$" - `(?:.*?[A-Za-z]){3}.*`, // phonenumber-0.2.0+8.9.0: r"(?:.*?[A-Za-z]){3}.*" - `(\D+)`, // phonenumber-0.2.0+8.9.0: r"(\D+)" - `(\$\d)`, // phonenumber-0.2.0+8.9.0: r"(\$\d)" - `\(?\$1\)?`, // phonenumber-0.2.0+8.9.0: r"\(?\$1\)?" - `\D`, // phone_number-0.1.0: r"\D" - `^0+`, // phone_number-0.1.0: r"^0+" - `^89`, // phone_number-0.1.0: r"^89" - `^8+`, // phone_number-0.1.0: r"^8+" - `^ *(\^_*\^) *$`, // phile-0.1.4: r"^ *(\^_*\^) *$" - // phile-0.1.4: r"^[_\p{XID_Start}]$" - // phile-0.1.4: r"^\p{XID_Continue}$" - "%25(?P[0-9a-fA-F][0-9a-fA-F])", // uritemplate-0.1.2: "%25(?P[0-9a-fA-F][0-9a-fA-F])" - "^package://(\\w+)/", // urdf-rs-0.4.2: "^package://(\\w+)/" - `(?P[?&.])`, // url-match-0.1.7: r"(?P[?&.])" - `:(?P[a-zA-Z0-9_-]+)`, // url-match-0.1.7: r":(?P[a-zA-Z0-9_-]+)" - `hello world`, // tsm-sys-0.1.0: r"hello world" - "^(?:(?:(?:\\d+:).+)|(?:[^:]+))$", // deb-version-0.1.0: "^(?:(?:(?:\\d+:).+)|(?:[^:]+))$" - `^(?i)(a|an|the)\s+`, // debcargo-2.1.0: r"^(?i)(a|an|the)\s+" - `^(?i)(rust\s+)?(implementation|library|tool|crate)\s+(of|to|for)\s+`, // debcargo-2.1.0: r"^(?i)(rust\s+)?(implementation|library|tool|crate)\s+(of|to|for)\s+" - `^.*\.h$`, // feaders-0.2.0: r"^.*\.h$" - `^.*\.c$`, // feaders-0.2.0: r"^.*\.c$" - `^.*\.hpp$`, // feaders-0.2.0: r"^.*\.hpp$" - `^.*\.cc$`, // feaders-0.2.0: r"^.*\.cc$" - `^.*\.cpp$`, // feaders-0.2.0: r"^.*\.cpp$" - `CPtr\(\w+\)`, // hyperscan-0.1.6: r"CPtr\(\w+\)" - `^Version:\s(\d\.\d\.\d)\sFeatures:\s+(\w+)?\sMode:\s(\w+)$`, // hyperscan-0.1.6: r"^Version:\s(\d\.\d\.\d)\sFeatures:\s+(\w+)?\sMode:\s(\w+)$" - `RawDatabase\{db: \w+\}`, // hyperscan-0.1.6: r"RawDatabase\{db: \w+\}" - `RawSerializedDatabase\{p: \w+, len: \d+\}`, // hyperscan-0.1.6: r"RawSerializedDatabase\{p: \w+, len: \d+\}" - `[0-9A-F]+`, // ucd-parse-0.1.1: r"[0-9A-F]+" - `.*`, // afsort-0.2.0: r".*" - `.*`, // afsort-0.2.0: r".*" - `.*`, // afsort-0.2.0: r".*" - `.*`, // afsort-0.2.0: r".*" - `.*`, // afsort-0.2.0: r".*" - `.*`, // afsort-0.2.0: r".*" - `^[a-z]+$`, // afsort-0.2.0: r"^[a-z]+$" - `^[a-z]+$`, // afsort-0.2.0: r"^[a-z]+$" - `(\.git|\.pijul|_darcs|\.hg)$`, // tin-summer-1.21.4: r"(\.git|\.pijul|_darcs|\.hg)$" - `.*?\.(a|la|lo|o|ll|keter|bc|dyn_o|d|rlib|crate|min\.js|hi|dyn_hi|S|jsexe|webapp|js\.externs|ibc|toc|aux|fdb_latexmk|fls|egg-info|whl|js_a|js_hi|jld|ji|js_o|so.*|dump-.*|vmb|crx|orig|elmo|elmi|pyc|mod|p_hi|p_o|prof|tix)$`, // tin-drummer-1.0.1: r".*?\.(a|la|lo|o|ll|keter|bc|dyn_o|d|rlib|crate|min\.js|hi|dyn_hi|S|jsexe|webapp|js\.externs|ibc|toc|aux|fdb_latexmk|fls|egg-info|whl|js_a|js_hi|jld|ji|js_o|so.*|dump-.*|vmb|crx|orig|elmo|elmi|pyc|mod|p_hi|p_o|prof|tix)$" - `.*?\.(stats|conf|h|out|cache.*|dat|pc|info|\.js)$`, // tin-drummer-1.0.1: r".*?\.(stats|conf|h|out|cache.*|dat|pc|info|\.js)$" - `.*?\.(exe|a|la|o|ll|keter|bc|dyn_o|d|rlib|crate|min\.js|hi|dyn_hi|jsexe|webapp|js\.externs|ibc|toc|aux|fdb_latexmk|fls|egg-info|whl|js_a|js_hi|jld|ji|js_o|so.*|dump-.*|vmb|crx|orig|elmo|elmi|pyc|mod|p_hi|p_o|prof|tix)$`, // tin-drummer-1.0.1: r".*?\.(exe|a|la|o|ll|keter|bc|dyn_o|d|rlib|crate|min\.js|hi|dyn_hi|jsexe|webapp|js\.externs|ibc|toc|aux|fdb_latexmk|fls|egg-info|whl|js_a|js_hi|jld|ji|js_o|so.*|dump-.*|vmb|crx|orig|elmo|elmi|pyc|mod|p_hi|p_o|prof|tix)$" - `.*?\.(stats|conf|h|out|cache.*|\.js)$`, // tin-drummer-1.0.1: r".*?\.(stats|conf|h|out|cache.*|\.js)$" - `(\.git|\.pijul|_darcs|\.hg)$`, // tin-drummer-1.0.1: r"(\.git|\.pijul|_darcs|\.hg)$" - `.*?\.(dyn_o|out|d|hi|dyn_hi|dump-.*|p_hi|p_o|prof|tix)$`, // tin-drummer-1.0.1: r".*?\.(dyn_o|out|d|hi|dyn_hi|dump-.*|p_hi|p_o|prof|tix)$" - `.*?\.(ibc)$`, // tin-drummer-1.0.1: r".*?\.(ibc)$" - `\.stack-work|dist-newstyle`, // tin-drummer-1.0.1: r"\.stack-work|dist-newstyle" - `_NET_WM_PID\(CARDINAL\) = (\d+)`, // timmy-0.3.0: r"_NET_WM_PID\(CARDINAL\) = (\d+)" - `today|yesterday|now`, // timmy-0.3.0: r"today|yesterday|now" - `(?P\d{1,2})/(?P\d{1,2})(/(?P\d{4}|\d{2}))?`, // timmy-0.3.0: r"(?P\d{1,2})/(?P\d{1,2})(/(?P\d{4}|\d{2}))?" - `(?P\d+) (days?|ds?)(?P( ago)?)`, // timmy-0.3.0: r"(?P\d+) (days?|ds?)(?P( ago)?)" - `(?P
\d{2}):(?P\d{2})`, // timmy-0.3.0: r"(?P
\d{2}):(?P\d{2})" - `^(\d+): \d+ windows \(.*\) \[\d+x\d+\]( \(attached\))?`, // tinfo-0.5.0: r"^(\d+): \d+ windows \(.*\) \[\d+x\d+\]( \(attached\))?" - `^(\d+):(\d+): (.*) \((\d+) panes\) \[(\d+)x(\d+)\]`, // tinfo-0.5.0: r"^(\d+):(\d+): (.*) \((\d+) panes\) \[(\d+)x(\d+)\]" - `(?:\\\{start\\\}|\\\{end\\\})`, // timespan-0.0.4: r"(?:\\\{start\\\}|\\\{end\\\})" - `(.*)\s+-\s+(.*)`, // timespan-0.0.4: r"(.*)\s+-\s+(.*)" - `(.*)\s+(\w+)$`, // timespan-0.0.4: r"(.*)\s+(\w+)$" - `(.*)\s+(\w+)$`, // timespan-0.0.4: r"(.*)\s+(\w+)$" - `(.*)\s+-\s+(.*)`, // timespan-0.0.4: r"(.*)\s+-\s+(.*)" - `[[:lower:]]`, // titlecase-0.10.0: r"[[:lower:]]" - `^\d+ (day|week|month|year)s?$`, // tight-0.1.3: r"^\d+ (day|week|month|year)s?$" - `^\d+ (day|week|month|year)s?$`, // tight-0.1.3: r"^\d+ (day|week|month|year)s?$" - `^[-+]?(0|[1-9][0-9_]*)$`, // yaml-0.2.1: r"^[-+]?(0|[1-9][0-9_]*)$" - `^([-+]?)0o?([0-7_]+)$`, // yaml-0.2.1: r"^([-+]?)0o?([0-7_]+)$" - `^([-+]?)0x([0-9a-fA-F_]+)$`, // yaml-0.2.1: r"^([-+]?)0x([0-9a-fA-F_]+)$" - `^([-+]?)0b([0-1_]+)$`, // yaml-0.2.1: r"^([-+]?)0b([0-1_]+)$" - `^([-+]?)(\.[0-9]+|[0-9]+(\.[0-9]*)?([eE][-+]?[0-9]+)?)$`, // yaml-0.2.1: r"^([-+]?)(\.[0-9]+|[0-9]+(\.[0-9]*)?([eE][-+]?[0-9]+)?)$" - `^[+]?(\.inf|\.Inf|\.INF)$`, // yaml-0.2.1: r"^[+]?(\.inf|\.Inf|\.INF)$" - `^-(\.inf|\.Inf|\.INF)$`, // yaml-0.2.1: r"^-(\.inf|\.Inf|\.INF)$" - `^(\.nan|\.NaN|\.NAN)$`, // yaml-0.2.1: r"^(\.nan|\.NaN|\.NAN)$" - `^(null|Null|NULL|~)$`, // yaml-0.2.1: r"^(null|Null|NULL|~)$" - `^(true|True|TRUE|yes|Yes|YES)$`, // yaml-0.2.1: r"^(true|True|TRUE|yes|Yes|YES)$" - `^(false|False|FALSE|no|No|NO)$`, // yaml-0.2.1: r"^(false|False|FALSE|no|No|NO)$" - `(?m)^(\S+)/(\S+) (\S+)(?: \((.*)\))?$`, // kefia-0.1.0: r"(?m)^(\S+)/(\S+) (\S+)(?: \((.*)\))?$" - "^(\\s+|;.*?(\n|$))+", // risp-0.7.0: "^(\\s+|;.*?(\n|$))+" - "^\".*?\"", // risp-0.7.0: "^\".*?\"" - `^[^\s\{\}()\[\]]+`, // risp-0.7.0: r"^[^\s\{\}()\[\]]+" - `^-?\d+`, // risp-0.7.0: r"^-?\d+" - "^([0-9]+)([KMG])?$", // ripgrep-0.8.1: "^([0-9]+)([KMG])?$" - `^\w+`, // riquid-0.0.1: r"^\w+" - `^\d+`, // riquid-0.0.1: r"^\d+" - `\A(0x)?([a-fA-F0-9]+)\z`, // recursive_disassembler-2.1.2: r"\A(0x)?([a-fA-F0-9]+)\z" - `^[a-zA-Z_][a-zA-Z0-9_]*`, // remake-0.1.0: r"^[a-zA-Z_][a-zA-Z0-9_]*" - `'(?P[^']+)'\s+\((?P<year>\d{4})\)`, // regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)" - `'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)`, // regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)" - `'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)`, // regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)" - `'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)`, // regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)" - `'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)`, // regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)" - `'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)`, // regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)" - `'(?P<title>[^']+)'\s+\((?P<year>\d{2})\)`, // regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{2})\)" - `'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)`, // regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)" - `'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)`, // regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})\)" - `'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)`, // regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)" - `'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)`, // regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)" - `'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)`, // regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)" - `'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)`, // regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)" - `'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)`, // regex-decode-0.1.0: r"'(?P<title>[^']+)'\s+\((?P<year>\d{4})?\)" - "[0-9]{3}-[0-9]{3}-[0-9]{4}", // regex-cache-0.2.0: "[0-9]{3}-[0-9]{3}-[0-9]{4}" - `^\d+$`, // regex-cache-0.2.0: r"^\d+$" - `^[a-z]+$`, // regex-cache-0.2.0: r"^[a-z]+$" - `^\d+$`, // regex-cache-0.2.0: r"^\d+$" - `^\d+$`, // regex-cache-0.2.0: r"^\d+$" - `\d{4}-\d{2}-\d{2}`, // regex_dfa-0.5.0: r"\d{4}-\d{2}-\d{2}" - `^[0-9\p{L} _\\.]{3,16}$`, // reaper-2.0.0: r"^[0-9\p{L} _\\.]{3,16}$" - `^attachment; filename=(.+)$`, // retdec-0.1.0: r"^attachment; filename=(.+)$" - `(\\)(?P<head>\$[0-9A-Za-z_{])`, // renvsubst-0.1.2: r"(\\)(?P<head>\$[0-9A-Za-z_{])" - `\$([[:word:]]+)`, // renvsubst-0.1.2: r"\$([[:word:]]+)" - `\$\{([[:word:]]+)\}`, // renvsubst-0.1.2: r"\$\{([[:word:]]+)\}" - `'[a-z]+'`, // rexpect-0.3.0: r"'[a-z]+'" - `^\d{4}-\d{2}-\d{2}$`, // rexpect-0.3.0: r"^\d{4}-\d{2}-\d{2}$" - `-\d{2}-`, // rexpect-0.3.0: r"-\d{2}-" - "^a(b|c)c*$", // luther-0.1.0: "^a(b|c)c*$" - `(\x9B|\x1B\[)[0-?]*[ -/]*[@-~]`, // little_boxes-1.6.0: r"(\x9B|\x1B\[)[0-?]*[ -/]*[@-~]" - "^[a-zA-Z]([a-zA-Z0-9_-]*)$", // libimagentrytag-0.8.0: "^[a-zA-Z]([a-zA-Z0-9_-]*)$" - `^[Yy](\n?)$`, // libimaginteraction-0.8.0: r"^[Yy](\n?)$" - `^[Nn](\n?)$`, // libimaginteraction-0.8.0: r"^[Nn](\n?)$" - "^(?P<KEY>([^=]*))=(.*)$", // libimagutil-0.8.0: "^(?P<KEY>([^=]*))=(.*)$" - "(.*)=(\"(?P<QVALUE>([^\"]*))\"|(?P<VALUE>(.*)))$", // libimagutil-0.8.0: "(.*)=(\"(?P<QVALUE>([^\"]*))\"|(?P<VALUE>(.*)))$" - `\s+`, // linux_ip-0.1.0: r"\s+" - `\s*[\n\r]+\s*`, // linux_ip-0.1.0: r"\s*[\n\r]+\s*" - `^([0-9a-fA-F\.:/]+)\s+dev\s+([a-z0-9\.]+)\s*(.*)$`, // linux_ip-0.1.0: r"^([0-9a-fA-F\.:/]+)\s+dev\s+([a-z0-9\.]+)\s*(.*)$" - `^([0-9a-fA-F\.:/]+|default)\s+via\s+([a-z0-9\.:]+)\s+dev\s+([a-z0-9\.]+)\s*(.*)$`, // linux_ip-0.1.0: r"^([0-9a-fA-F\.:/]+|default)\s+via\s+([a-z0-9\.:]+)\s+dev\s+([a-z0-9\.]+)\s*(.*)$" - `^(blackhole)\s+([0-9a-fA-F\.:/]+)$`, // linux_ip-0.1.0: r"^(blackhole)\s+([0-9a-fA-F\.:/]+)$" - `^(unreachable)\s+([0-9a-fA-F\.:/]+)\s+dev\s+([a-z0-9\.]+)\s+(.*)$`, // linux_ip-0.1.0: r"^(unreachable)\s+([0-9a-fA-F\.:/]+)\s+dev\s+([a-z0-9\.]+)\s+(.*)$" - `\s*[\n\r]+\s*`, // linux_ip-0.1.0: r"\s*[\n\r]+\s*" - `^\d+:\s+([a-zA-Z0-9\.-]+)(@\S+)*:\s+(.*)$`, // linux_ip-0.1.0: r"^\d+:\s+([a-zA-Z0-9\.-]+)(@\S+)*:\s+(.*)$" - `\s*link/ether\s+([a-f0-9:]+)\s+.*`, // linux_ip-0.1.0: r"\s*link/ether\s+([a-f0-9:]+)\s+.*" - `\s*inet[6]*\s+([0-9a-f:\./]+)\s+.*`, // linux_ip-0.1.0: r"\s*inet[6]*\s+([0-9a-f:\./]+)\s+.*" - `[^\w -]`, // linky-0.1.4: r"[^\w -]" - `^(.*):(\d+): [^ ]* ([^ ]*)$`, // linky-0.1.4: r"^(.*):(\d+): [^ ]* ([^ ]*)$" - `^(\d{4}-\d{2}-\d{2})-(\d{3})-(.+)$`, // limonite-0.2.1: r"^(\d{4}-\d{2}-\d{2})-(\d{3})-(.+)$" - `^[a-zA-Z]+$`, // process-queue-0.1.1: r"^[a-zA-Z]+$" - `^\{([a-zA-Z_]+)\}$`, // pronghorn-0.1.2: r"^\{([a-zA-Z_]+)\}$" - "(?m:^(\\d{3}) (.+)\r$)", // protocol-ftp-client-0.1.1: "(?m:^(\\d{3}) (.+)\r$)" - "\"(.+)\"", // protocol-ftp-client-0.1.1: "\"(.+)\"" - "(\\w+) [Tt]ype: (\\w+)", // protocol-ftp-client-0.1.1: "(\\w+) [Tt]ype: (\\w+)" - "(?m:^(\\d{3})-.+\r$)", // protocol-ftp-client-0.1.1: "(?m:^(\\d{3})-.+\r$)" - "Entering Passive Mode \\((\\d+),(\\d+),(\\d+),(\\d+),(\\d+),(\\d+)\\)", // protocol-ftp-client-0.1.1: "Entering Passive Mode \\((\\d+),(\\d+),(\\d+),(\\d+),(\\d+),(\\d+)\\)" - "(?m:^(.+)\r$)", // protocol-ftp-client-0.1.1: "(?m:^(.+)\r$)" - "^([d-])(?:[rwx-]{3}){3} +\\d+ +\\w+ +\\w+ +(\\d+) +(.+) +(.+)$", // protocol-ftp-client-0.1.1: "^([d-])(?:[rwx-]{3}){3} +\\d+ +\\w+ +\\w+ +(\\d+) +(.+) +(.+)$" - `([\./\-_]{0,1}(19|20)\d{2})[\./\-_]{0,1}(([0-3]{0,1}[0-9][\./\-_])|(\w{3,5}[\./\-_]))([0-3]{0,1}[0-9][\./\-]{0,1})`, // article-date-extractor-0.1.1: r"([\./\-_]{0,1}(19|20)\d{2})[\./\-_]{0,1}(([0-3]{0,1}[0-9][\./\-_])|(\w{3,5}[\./\-_]))([0-3]{0,1}[0-9][\./\-]{0,1})" - `(?i)publishdate|pubdate|timestamp|article_date|articledate|date`, // article-date-extractor-0.1.1: r"(?i)publishdate|pubdate|timestamp|article_date|articledate|date" - `type\((.*)\)`, // arthas_plugin-0.1.1: r"type\((.*)\)" - `Vec<(.*)>`, // arthas_plugin-0.1.1: r"Vec<(.*)>" - `Option<(.*)>`, // arthas_plugin-0.1.1: r"Option<(.*)>" - `HashMap<[a-z0-9A-Z]+, *(.*)>`, // arthas_plugin-0.1.1: r"HashMap<[a-z0-9A-Z]+, *(.*)>" - "Vec *< *(.*) *>", // arthas_derive-0.1.0: "Vec *< *(.*) *>" - `Option *< *(.*) *>`, // arthas_derive-0.1.0: r"Option *< *(.*) *>" - `HashMap *< *[a-z0-9A-Z]+ *, *(.*) *>`, // arthas_derive-0.1.0: r"HashMap *< *[a-z0-9A-Z]+ *, *(.*) *>" - `^([\w\-\(\)\.']+)\s+([^\s].*)\s*$`, // arpabet-0.2.0: r"^([\w\-\(\)\.']+)\s+([^\s].*)\s*$" - `^;;;\s+`, // arpabet-0.2.0: r"^;;;\s+" - `/\*.*?\*/|//.*`, // glossy_codegen-0.2.0: r"/\*.*?\*/|//.*" - "^\\s*#\\s*include\\s+<([:print:]+)>\\s*$", // glossy_codegen-0.2.0: "^\\s*#\\s*include\\s+<([:print:]+)>\\s*$" - "^\\s*#\\s*include\\s+\"([:print:]+)\"\\s*$", // glossy_codegen-0.2.0: "^\\s*#\\s*include\\s+\"([:print:]+)\"\\s*$" - `^\s*#\s*version\s+(\d+)`, // glossy_codegen-0.2.0: r"^\s*#\s*version\s+(\d+)" - `^\s*$`, // glossy_codegen-0.2.0: r"^\s*$" - `(?P<addr>via \S+)`, // gluster-1.0.1: r"(?P<addr>via \S+)" - `(?P<src>src \S+)`, // gluster-1.0.1: r"(?P<src>src \S+)" - `(.*)\[\d+\]`, // gl_helpers-0.1.7: r"(.*)\[\d+\]" - `(\d+).(\d+)`, // gl_helpers-0.1.7: r"(\d+).(\d+)" - `(?P<c>[\\\.\+\*\?\(\)\|\[\]\{\}\^\$])`, // glr-parser-0.0.1: r"(?P<c>[\\\.\+\*\?\(\)\|\[\]\{\}\^\$])" - `^\w+$`, // glr-parser-0.0.1: r"^\w+$" - "'[^']+'", // glr-parser-0.0.1: "'[^']+'" - `(?m)//.*`, // hoodlum-0.5.0: r"(?m)//.*" - `^1\d{10}$`, // form-checker-0.2.2: r"^1\d{10}$" - `(?i)^[\w.%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}$`, // form-checker-0.2.2: r"(?i)^[\w.%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}$" - `(?P<user_agent>[a-zA-Z0-9-_]+/[0-9\.]+)`, // wikibase-0.2.0: r"(?P<user_agent>[a-zA-Z0-9-_]+/[0-9\.]+)" - `Cell [0-9]{2,} - Address:`, // wifiscanner-0.3.6: r"Cell [0-9]{2,} - Address:" - `([0-9a-zA-Z]{1}[0-9a-zA-Z]{1}[:]{1}){5}[0-9a-zA-Z]{1}[0-9a-zA-Z]{1}`, // wifiscanner-0.3.6: r"([0-9a-zA-Z]{1}[0-9a-zA-Z]{1}[:]{1}){5}[0-9a-zA-Z]{1}[0-9a-zA-Z]{1}" - `Signal level=(\d+)/100`, // wifiscanner-0.3.6: r"Signal level=(\d+)/100" - `(?s)\[b\](.*?)\[/b\]`, // bbcode-1.0.2: r"(?s)\[b\](.*?)\[/b\]" - `(?s)\[i\](.*?)\[/i\]`, // bbcode-1.0.2: r"(?s)\[i\](.*?)\[/i\]" - `(?s)\[u\](.*?)\[/u\]`, // bbcode-1.0.2: r"(?s)\[u\](.*?)\[/u\]" - `(?s)\[s\](.*?)\[/s\]`, // bbcode-1.0.2: r"(?s)\[s\](.*?)\[/s\]" - `(?s)\[size=(\d+)](.*?)\[/size\]`, // bbcode-1.0.2: r"(?s)\[size=(\d+)](.*?)\[/size\]" - `(?s)\[color=(.+)](.*?)\[/color\]`, // bbcode-1.0.2: r"(?s)\[color=(.+)](.*?)\[/color\]" - `(?s)\[center\](.*?)\[/center\]`, // bbcode-1.0.2: r"(?s)\[center\](.*?)\[/center\]" - `(?s)\[left\](.*?)\[/left\]`, // bbcode-1.0.2: r"(?s)\[left\](.*?)\[/left\]" - `(?s)\[right\](.*?)\[/right\]`, // bbcode-1.0.2: r"(?s)\[right\](.*?)\[/right\]" - `(?s)\[table\](.*?)\[/table\]`, // bbcode-1.0.2: r"(?s)\[table\](.*?)\[/table\]" - `(?s)\[td\](.*?)\[/td\]`, // bbcode-1.0.2: r"(?s)\[td\](.*?)\[/td\]" - `(?s)\[tr\](.*?)\[/tr\]`, // bbcode-1.0.2: r"(?s)\[tr\](.*?)\[/tr\]" - `(?s)\[th\](.*?)\[/th\]`, // bbcode-1.0.2: r"(?s)\[th\](.*?)\[/th\]" - `(?s)\[url\](.*?)\[/url\]`, // bbcode-1.0.2: r"(?s)\[url\](.*?)\[/url\]" - `(?s)\[url=(.+)\](.*?)\[/url\]`, // bbcode-1.0.2: r"(?s)\[url=(.+)\](.*?)\[/url\]" - `(?s)\[quote\](.*?)\[/quote\]`, // bbcode-1.0.2: r"(?s)\[quote\](.*?)\[/quote\]" - `(?s)\[quote=(.+)\](.*?)\[/quote\]`, // bbcode-1.0.2: r"(?s)\[quote=(.+)\](.*?)\[/quote\]" - `(?s)\[img=(\d+)x(\d+)(\b.*)?\](.*?)\[/img\]`, // bbcode-1.0.2: r"(?s)\[img=(\d+)x(\d+)(\b.*)?\](.*?)\[/img\]" - `(?s)\[img=(.+)(\b.*)?\](.*?)\[/img\]`, // bbcode-1.0.2: r"(?s)\[img=(.+)(\b.*)?\](.*?)\[/img\]" - `(?s)\[img(\b.*)?\](.*?)\[/img\]`, // bbcode-1.0.2: r"(?s)\[img(\b.*)?\](.*?)\[/img\]" - `(?s)\[ol\](.*?)\[/ol\]`, // bbcode-1.0.2: r"(?s)\[ol\](.*?)\[/ol\]" - `(?s)\[ul\](.*?)\[/ul\]`, // bbcode-1.0.2: r"(?s)\[ul\](.*?)\[/ul\]" - `(?s)\[list\](.*?)\[/list\]`, // bbcode-1.0.2: r"(?s)\[list\](.*?)\[/list\]" - `(?s)\[youtube\](.*?)\[/youtube\]`, // bbcode-1.0.2: r"(?s)\[youtube\](.*?)\[/youtube\]" - `(?s)\[youtube=(\d+)x(\d+)\](.*?)\[/youtube\]`, // bbcode-1.0.2: r"(?s)\[youtube=(\d+)x(\d+)\](.*?)\[/youtube\]" - `(?s)\[li\](.*?)\[/li\]`, // bbcode-1.0.2: r"(?s)\[li\](.*?)\[/li\]" - `loop\d+`, // block-utils-0.5.0: r"loop\d+" - `ram\d+`, // block-utils-0.5.0: r"ram\d+" - `md\d+`, // block-utils-0.5.0: r"md\d+" - `^([1-9]) min$`, // kvvliveapi-0.1.0: r"^([1-9]) min$" - `(\d{2}):(\d{2}):(\d{2})`, // rfc822_sanitizer-0.3.3: r"(\d{2}):(\d{2}):(\d{2})" - `(\d{1,2}):(\d{1,2}):(\d{1,2})`, // rfc822_sanitizer-0.3.3: r"(\d{1,2}):(\d{1,2}):(\d{1,2})" - `[2-9]`, // faker-0.0.4: r"[2-9]" - `[1-9]`, // faker-0.0.4: r"[1-9]" - `[0-9]`, // faker-0.0.4: r"[0-9]" - `\d{10}`, // faker-0.0.4: r"\d{10}" - `\d{1}`, // faker-0.0.4: r"\d{1}" - `^\w+`, // faker-0.0.4: r"^\w+" - `^\w+`, // faker-0.0.4: r"^\w+" - `^(\w+\.? ?){2,3}$`, // faker-0.0.4: r"^(\w+\.? ?){2,3}$" - `^[A-Z][a-z]+\.?$`, // faker-0.0.4: r"^[A-Z][a-z]+\.?$" - `^[A-Z][A-Za-z]*\.?$`, // faker-0.0.4: r"^[A-Z][A-Za-z]*\.?$" - `http://lorempixel.com/100/100/\w+`, // faker-0.0.4: r"http://lorempixel.com/100/100/\w+" - `http://lorempixel.com/100/100/cats`, // faker-0.0.4: r"http://lorempixel.com/100/100/cats" - "(?i:ß)", // fancy-regex-0.1.0: "(?i:ß)" - "(?i:\\x{0587})", // fancy-regex-0.1.0: "(?i:\\x{0587})" - "^\\\\([!-/:-@\\[-`\\{-~aftnrv]|[0-7]{1,3}|x[0-9a-fA-F]{2}|x\\{[0-9a-fA-F]{1,6}\\})", // fancy-regex-0.1.0: "^\\\\([!-/:-@\\[-`\\{-~aftnrv]|[0-7]{1,3}|x[0-9a-fA-F]{2}|x\\{[0-9a-fA-F]{1,6}\\})" - `/([^/])[^/]+/`, // fancy-prompt-0.1.5: r"/([^/])[^/]+/" - `^([^:]+):.*?(?::([^:]+))?$`, // fancy-prompt-0.1.5: r"^([^:]+):.*?(?::([^:]+))?$" - `^(/?__\w+__)/(.*)`, // fanta-0.2.0: r"^(/?__\w+__)/(.*)" - `(.)([A-Z])`, // fanta-cli-0.1.1: r"(.)([A-Z])" - "\\{:[^\\s]+\\}", // fanta-cli-0.1.1: "\\{:[^\\s]+\\}" - "(?P<last>[^\r])\n", // amethyst_tools-0.7.1: "(?P<last>[^\r])\n" - `^-?\d+(\.\d)?`, // amigo-0.3.1: r"^-?\d+(\.\d)?" - `^[a-zA-Z_]+[\w-]*[!?_]?`, // amigo-0.3.1: r"^[a-zA-Z_]+[\w-]*[!?_]?" - `^\(`, // amigo-0.3.1: r"^\(" - `^\)`, // amigo-0.3.1: r"^\)" - `^\s+`, // amigo-0.3.1: r"^\s+" - "\x1b\\[[^m]+m", // ethcore-logger-1.12.0: "\x1b\\[[^m]+m" - `__.*?__`, // dash2html-1.0.1: r"__.*?__" - `(?i)@(?:time|clipboard|cursor|date)`, // dash2html-1.0.1: r"(?i)@(?:time|clipboard|cursor|date)" - `^Microsoft Windows \[Version\s(\d+\.\d+\.\d+)\]$`, // os_type-2.0.0: r"^Microsoft Windows \[Version\s(\d+\.\d+\.\d+)\]$" - `ProductName:\s([\w\s]+)\n`, // os_type-2.0.0: r"ProductName:\s([\w\s]+)\n" - `ProductVersion:\s(\w+\.\w+\.\w+)`, // os_type-2.0.0: r"ProductVersion:\s(\w+\.\w+\.\w+)" - `BuildVersion:\s(\w+)`, // os_type-2.0.0: r"BuildVersion:\s(\w+)" - `(\w+) Linux release`, // os_type-2.0.0: r"(\w+) Linux release" - `release\s([\w\.]+)`, // os_type-2.0.0: r"release\s([\w\.]+)" - `Distributor ID:\s(\w+)`, // os_type-2.0.0: r"Distributor ID:\s(\w+)" - `Release:\s([\w\.]+)`, // os_type-2.0.0: r"Release:\s([\w\.]+)" - `typename type\-parameter\-\d+\-\d+::.+`, // bindgen-0.37.0: r"typename type\-parameter\-\d+\-\d+::.+" - "^+(.*)\r\n", // imap-0.8.1: "^+(.*)\r\n" - `^ffd8ffe0`, // image-base64-0.1.0: r"^ffd8ffe0" - `^89504e47`, // image-base64-0.1.0: r"^89504e47" - `^47494638`, // image-base64-0.1.0: r"^47494638" - "^(/([^/~]|~[01])*)*$", // json-pointer-0.3.2: "^(/([^/~]|~[01])*)*$" - "^#(/([^/~%]|~[01]|%[0-9a-fA-F]{2})*)*$", // json-pointer-0.3.2: "^#(/([^/~%]|~[01]|%[0-9a-fA-F]{2})*)*$" - `^5.5.5-(\d{1,2})\.(\d{1,2})\.(\d{1,3})-MariaDB`, // mysql_common-0.7.0: r"^5.5.5-(\d{1,2})\.(\d{1,2})\.(\d{1,3})-MariaDB" - `^(\d{1,2})\.(\d{1,2})\.(\d{1,3})(.*)`, // mysql_common-0.7.0: r"^(\d{1,2})\.(\d{1,2})\.(\d{1,3})(.*)" - `^[0-9]{4}[0-9A-Z]{2}[0-9]{3}$`, // government_id-0.1.0: r"^[0-9]{4}[0-9A-Z]{2}[0-9]{3}$" - `UniqueIndexViolation: (\w+)`, // ohmers-0.1.1: r"UniqueIndexViolation: (\w+)" - `(.*) you are (.*)`, // eliza-1.0.0: r"(.*) you are (.*)" - `(.*) you are (.*)`, // eliza-1.0.0: r"(.*) you are (.*)" - `(.*) you are (.*)`, // eliza-1.0.0: r"(.*) you are (.*)" - "^\\s*\\*", // chema-0.0.5: "^\\s*\\*" - "^\\s*@(\\w+)\\s+(.*)", // chema-0.0.5: "^\\s*@(\\w+)\\s+(.*)" - `^\s*#`, // chord3-0.3.0: r"^\s*#" - `\{(?P<cmd>\w+)(?::?\s*(?P<arg>.*))?\}`, // chord3-0.3.0: r"\{(?P<cmd>\w+)(?::?\s*(?P<arg>.*))?\}" - `\{(eot|end_of_tab):?\s*`, // chord3-0.3.0: r"\{(eot|end_of_tab):?\s*" - `([^\[]*)(?:\[([^\]]*)\])?`, // chord3-0.3.0: r"([^\[]*)(?:\[([^\]]*)\])?" - "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$", // checkmail-0.1.1: "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$" - `\b\w\w+\b`, // cntk-0.2.1: r"\b\w\w+\b" - `\b\w\w+\b`, // cntk-0.2.1: r"\b\w\w+\b" - `\(id: (\d+)\)`, // cniguru-0.1.0: r"\(id: (\d+)\)" - `^(\d+)\.(\d+)\.(\d+)(?:-([\dA-Za-z-]+(?:\.[\dA-Za-z-]+)*))?(?:\+([\dA-Za-z-]+(?:\.[\dA-Za-z-]+)*))?$`, // upm_lib-0.3.0: r"^(\d+)\.(\d+)\.(\d+)(?:-([\dA-Za-z-]+(?:\.[\dA-Za-z-]+)*))?(?:\+([\dA-Za-z-]+(?:\.[\dA-Za-z-]+)*))?$" - `^\s*(\*+(\s+))?`, // avro-0.2.1: r"^\s*(\*+(\s+))?" - `^\s*(\*+)?`, // avro-0.2.1: r"^\s*(\*+)?" - "[0-9]+", // nomi-0.0.2: "[0-9]+" - "([0-9]+)@(?:nodes|n)?:([^@]+)?", // nodes-0.1.0: "([0-9]+)@(?:nodes|n)?:([^@]+)?" - `(?i)in (\d+) (second|minute|hour|day|week)s?`, // not-stakkr-1.0.0: r"(?i)in (\d+) (second|minute|hour|day|week)s?" - "^([A-Za-z0-9 -_:]+)\n-+\n", // notetxt-0.0.1: "^([A-Za-z0-9 -_:]+)\n-+\n" - `^-?[0-9]+(\.[0-9]+)?([eE]-?[0-9]+)?$`, // nail-0.1.0-pre.0: r"^-?[0-9]+(\.[0-9]+)?([eE]-?[0-9]+)?$" - `^-?[0-9]+$`, // nail-0.1.0-pre.0: r"^-?[0-9]+$" - `[^\w\s\pP]+`, // askalono-0.2.0: r"[^\w\s\pP]+" - `[ \t\p{Zs} \\ / \| \x2044 ]+`, // askalono-0.2.0: r"(?x)[ \t\p{Zs} \\ / \| \x2044 ]+" - `\p{Pd}+`, // askalono-0.2.0: r"\p{Pd}+" - `\p{Ps}+`, // askalono-0.2.0: r"\p{Ps}+" - `\p{Pe}+`, // askalono-0.2.0: r"\p{Pe}+" - `\p{Pc}+`, // askalono-0.2.0: r"\p{Pc}+" - `[©Ⓒⓒ]`, // askalono-0.2.0: r"[©Ⓒⓒ]" - `[\r\n\v\f]`, // askalono-0.2.0: r"[\r\n\v\f]" - `\n{3,}`, // askalono-0.2.0: r"\n{3,}" - `[^\w\s]+`, // askalono-0.2.0: r"[^\w\s]+" - `\s+`, // askalono-0.2.0: r"\s+" - `[^0-9a-zA-Z_]`, // assembunny_plus-0.0.3: r"[^0-9a-zA-Z_]" - `[0-9]`, // assembunny_plus-0.0.3: r"[0-9]" - `(?m)^Minion (\S*) did not respond\. No job will be sent\.$`, // salt-compressor-0.4.0: r"(?m)^Minion (\S*) did not respond\. No job will be sent\.$" - `</?[^>]+?>`, // sabisabi-0.4.1: r"</?[^>]+?>" - `\([^)]*\)`, // sabisabi-0.4.1: r"\([^)]*\)" - "@import \"([^\"]*)\";", // sassers-0.13.5-h28: "@import \"([^\"]*)\";" - `[A-Za-z\d-]{1,63}$`, // shadowsocks-0.6.2: r"[A-Za-z\d-]{1,63}$" - "[abc]+", // shkeleton-0.1.5: "[abc]+" - `([^A-Za-z0-9_\-.,:/@\n])`, // shellwords-0.1.0: r"([^A-Za-z0-9_\-.,:/@\n])" - `\n`, // shellwords-0.1.0: r"\n" - "(?P<num>[0-9]+)(?P<units>[dhms])", // shush-0.1.5: "(?P<num>[0-9]+)(?P<units>[dhms])" - `(?:Chrome|CrMo|CriOS)/([.0-9]+)`, // woothee-0.8.0: r"(?:Chrome|CrMo|CriOS)/([.0-9]+)" - `Vivaldi/([.0-9]+)`, // woothee-0.8.0: r"Vivaldi/([.0-9]+)" - `Firefox/([.0-9]+)`, // woothee-0.8.0: r"Firefox/([.0-9]+)" - `^Mozilla/[.0-9]+ \((?:Mobile|Tablet);(?:.*;)? rv:([.0-9]+)\) Gecko/[.0-9]+ Firefox/[.0-9]+$`, // woothee-0.8.0: r"^Mozilla/[.0-9]+ \((?:Mobile|Tablet);(?:.*;)? rv:([.0-9]+)\) Gecko/[.0-9]+ Firefox/[.0-9]+$" - `FxiOS/([.0-9]+)`, // woothee-0.8.0: r"FxiOS/([.0-9]+)" - `\(([^;)]+);FOMA;`, // woothee-0.8.0: r"\(([^;)]+);FOMA;" - `jig browser[^;]+; ([^);]+)`, // woothee-0.8.0: r"jig browser[^;]+; ([^);]+)" - `(?i)rss(?:reader|bar|[-_ /;()]|[ +]*/)`, // woothee-0.8.0: r"(?i)rss(?:reader|bar|[-_ /;()]|[ +]*/)" - `(?i)(?:bot|crawler|spider)(?:[-_ ./;@()]|$)`, // woothee-0.8.0: r"(?i)(?:bot|crawler|spider)(?:[-_ ./;@()]|$)" - `(?i)(?:feed|web) ?parser`, // woothee-0.8.0: r"(?i)(?:feed|web) ?parser" - `(?i)watch ?dog`, // woothee-0.8.0: r"(?i)watch ?dog" - `Edge/([.0-9]+)`, // woothee-0.8.0: r"Edge/([.0-9]+)" - `MSIE ([.0-9]+);`, // woothee-0.8.0: r"MSIE ([.0-9]+);" - `Version/([.0-9]+)`, // woothee-0.8.0: r"Version/([.0-9]+)" - `Opera[/ ]([.0-9]+)`, // woothee-0.8.0: r"Opera[/ ]([.0-9]+)" - `OPR/([.0-9]+)`, // woothee-0.8.0: r"OPR/([.0-9]+)" - `Version/([.0-9]+)`, // woothee-0.8.0: r"Version/([.0-9]+)" - `(?:SoftBank|Vodafone|J-PHONE)/[.0-9]+/([^ /;()]+)`, // woothee-0.8.0: r"(?:SoftBank|Vodafone|J-PHONE)/[.0-9]+/([^ /;()]+)" - `Trident/([.0-9]+);`, // woothee-0.8.0: r"Trident/([.0-9]+);" - ` rv:([.0-9]+)`, // woothee-0.8.0: r" rv:([.0-9]+)" - `IEMobile/([.0-9]+);`, // woothee-0.8.0: r"IEMobile/([.0-9]+);" - `(?:WILLCOM|DDIPOCKET);[^/]+/([^ /;()]+)`, // woothee-0.8.0: r"(?:WILLCOM|DDIPOCKET);[^/]+/([^ /;()]+)" - `Windows ([ .a-zA-Z0-9]+)[;\\)]`, // woothee-0.8.0: r"Windows ([ .a-zA-Z0-9]+)[;\\)]" - `^Phone(?: OS)? ([.0-9]+)`, // woothee-0.8.0: r"^Phone(?: OS)? ([.0-9]+)" - `iP(hone;|ad;|od) .*like Mac OS X`, // woothee-0.8.0: r"iP(hone;|ad;|od) .*like Mac OS X" - `Version/([.0-9]+)`, // woothee-0.8.0: r"Version/([.0-9]+)" - `rv:(\d+\.\d+\.\d+)`, // woothee-0.8.0: r"rv:(\d+\.\d+\.\d+)" - `FreeBSD ([^;\)]+);`, // woothee-0.8.0: r"FreeBSD ([^;\)]+);" - `CrOS ([^\)]+)\)`, // woothee-0.8.0: r"CrOS ([^\)]+)\)" - `Android[- ](\d+\.\d+(?:\.\d+)?)`, // woothee-0.8.0: r"Android[- ](\d+\.\d+(?:\.\d+)?)" - `PSP \(PlayStation Portable\); ([.0-9]+)\)`, // woothee-0.8.0: r"PSP \(PlayStation Portable\); ([.0-9]+)\)" - `PLAYSTATION 3;? ([.0-9]+)\)`, // woothee-0.8.0: r"PLAYSTATION 3;? ([.0-9]+)\)" - `PlayStation Vita ([.0-9]+)\)`, // woothee-0.8.0: r"PlayStation Vita ([.0-9]+)\)" - `PlayStation 4 ([.0-9]+)\)`, // woothee-0.8.0: r"PlayStation 4 ([.0-9]+)\)" - `BB10(?:.+)Version/([.0-9]+) `, // woothee-0.8.0: r"BB10(?:.+)Version/([.0-9]+) " - `BlackBerry(?:\d+)/([.0-9]+) `, // woothee-0.8.0: r"BlackBerry(?:\d+)/([.0-9]+) " - `; CPU(?: iPhone)? OS (\d+_\d+(?:_\d+)?) like Mac OS X`, // woothee-0.8.0: r"; CPU(?: iPhone)? OS (\d+_\d+(?:_\d+)?) like Mac OS X" - `Mac OS X (10[._]\d+(?:[._]\d+)?)(?:\)|;)`, // woothee-0.8.0: r"Mac OS X (10[._]\d+(?:[._]\d+)?)(?:\)|;)" - `^(?:Apache-HttpClient/|Jakarta Commons-HttpClient/|Java/)`, // woothee-0.8.0: r"^(?:Apache-HttpClient/|Jakarta Commons-HttpClient/|Java/)" - `[- ]HttpClient(/|$)`, // woothee-0.8.0: r"[- ]HttpClient(/|$)" - `^(?:PHP|WordPress|CakePHP|PukiWiki|PECL::HTTP)(?:/| |$)`, // woothee-0.8.0: r"^(?:PHP|WordPress|CakePHP|PukiWiki|PECL::HTTP)(?:/| |$)" - `(?:PEAR HTTP_Request|HTTP_Request)(?: class|2)`, // woothee-0.8.0: r"(?:PEAR HTTP_Request|HTTP_Request)(?: class|2)" - `(?:Rome Client |UnwindFetchor/|ia_archiver |Summify |PostRank/)`, // woothee-0.8.0: r"(?:Rome Client |UnwindFetchor/|ia_archiver |Summify |PostRank/)" - `Sleipnir/([.0-9]+)`, // woothee-0.8.0: r"Sleipnir/([.0-9]+)" - `@@[a-z|A-Z|\d]+@@`, // word_replace-0.0.3: r"@@[a-z|A-Z|\d]+@@" - `\w+`, // wordcount-0.1.0: r"\w+" - "^([^=]+)=(.*)$", // just-0.3.12: "^([^=]+)=(.*)$" - `:[a-zA-Z_]+?:`, // emote-0.1.0: r":[a-zA-Z_]+?:" - `:([a-zA-Z0-9_+-]+):`, // emojicons-1.0.1: r":([a-zA-Z0-9_+-]+):" - `git-codecommit\.([a-z0-9-]+)\.amazonaws\.com`, // git2_codecommit-0.1.2: r"git-codecommit\.([a-z0-9-]+)\.amazonaws\.com" - `^submodule\.(?P<name>.*)\.(?P<key>[^=]*)=(?P<value>.*)$`, // git-workarea-3.1.2: r"^submodule\.(?P<name>.*)\.(?P<key>[^=]*)=(?P<value>.*)$" - `^(?P<command>git-(?:receive|upload)-pack) '(?P<path>.+)'$`, // git-shell-enforce-directory-1.0.0: r"^(?P<command>git-(?:receive|upload)-pack) '(?P<path>.+)'$" - `[ \n]:(.*?):`, // git-journal-1.6.3: r"[ \n]:(.*?):" - `^git@(?P<host>[[:alnum:]\._-]+):(?P<path>[[:alnum:]\._\-/]+).git$`, // git-find-0.3.2: r"^git@(?P<host>[[:alnum:]\._-]+):(?P<path>[[:alnum:]\._\-/]+).git$" - `private_token=\w{20}`, // gitlab-api-0.6.0: r"private_token=\w{20}" - "^(http://|https://)", // td-client-0.7.0: "^(http://|https://)" - `--(?P<type>[a-zA-Z]+)-- (?P<contents>.*)`, // karaconv-0.3.0: r"--(?P<type>[a-zA-Z]+)-- (?P<contents>.*)" - `(?P<comp>et al\.)(?:\.)`, // katana-1.0.2: r"(?P<comp>et al\.)(?:\.)" - `\.{3}`, // katana-1.0.2: r"\.{3}" - `(?P<number>[0-9]+)\.(?P<decimal>[0-9]+)`, // katana-1.0.2: r"(?P<number>[0-9]+)\.(?P<decimal>[0-9]+)" - `\s\.(?P<nums>[0-9]+)`, // katana-1.0.2: r"\s\.(?P<nums>[0-9]+)" - `(?:[A-Za-z]\.){2,}`, // katana-1.0.2: r"(?:[A-Za-z]\.){2,}" - `(?P<init>[A-Z])(?P<point>\.)`, // katana-1.0.2: r"(?P<init>[A-Z])(?P<point>\.)" - `(?P<title>[A-Z][a-z]{1,3})(\.)`, // katana-1.0.2: r"(?P<title>[A-Z][a-z]{1,3})(\.)" - `&==&(?P<p>[.!?])`, // katana-1.0.2: r"&==&(?P<p>[.!?])" - `&\^&(?P<p>[.!?])`, // katana-1.0.2: r"&\^&(?P<p>[.!?])" - `&\*\*&(?P<p>[.!?])`, // katana-1.0.2: r"&\*\*&(?P<p>[.!?])" - `&=&(?P<p>[.!?])`, // katana-1.0.2: r"&=&(?P<p>[.!?])" - `&##&(?P<p>[.!?])`, // katana-1.0.2: r"&##&(?P<p>[.!?])" - `&\$&(?P<p>[.!?])`, // katana-1.0.2: r"&\$&(?P<p>[.!?])" - `@(?:_|\d+(?:/\d+(?:-\d+)?)?)`, // kailua_syntax-1.1.0: r"@(?:_|\d+(?:/\d+(?:-\d+)?)?)" - `<(\d+)>`, // kailua_syntax-1.1.0: r"<(\d+)>" - `\((\d+),(\d+),(\d+),(\d+),(\d+),(\d+)\)`, // ftp-3.0.1: r"\((\d+),(\d+),(\d+),(\d+),(\d+),(\d+)\)" - `\b(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})\b`, // ftp-3.0.1: r"\b(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})\b" - `\s+(\d+)\s*$`, // ftp-3.0.1: r"\s+(\d+)\s*$" - `<countryCode>(.*?)</countryCode>`, // vat-0.1.0: r"<countryCode>(.*?)</countryCode>" - `<vatNumber>(.*?)</vatNumber>`, // vat-0.1.0: r"<vatNumber>(.*?)</vatNumber>" - `<name>(.*?)</name>`, // vat-0.1.0: r"<name>(.*?)</name>" - `<address>(?s)(.*?)(?-s)</address>`, // vat-0.1.0: r"<address>(?s)(.*?)(?-s)</address>" - `<valid>(true|false)</valid>`, // vat-0.1.0: r"<valid>(true|false)</valid>" - `^ATU\d{8}$`, // vat-0.1.0: r"^ATU\d{8}$" - `^BE0?\d{9, 10}$`, // vat-0.1.0: r"^BE0?\d{9, 10}$" - `^BG\d{9,10}$`, // vat-0.1.0: r"^BG\d{9,10}$" - `^HR\d{11}$`, // vat-0.1.0: r"^HR\d{11}$" - `^CY\d{8}[A-Z]$`, // vat-0.1.0: r"^CY\d{8}[A-Z]$" - `^CZ\d{8,10}$`, // vat-0.1.0: r"^CZ\d{8,10}$" - `^DK\d{8}$`, // vat-0.1.0: r"^DK\d{8}$" - `^EE\d{9}$`, // vat-0.1.0: r"^EE\d{9}$" - `^FI\d{8}$`, // vat-0.1.0: r"^FI\d{8}$" - `^FR[A-HJ-NP-Z0-9][A-HJ-NP-Z0-9]\d{9}$`, // vat-0.1.0: r"^FR[A-HJ-NP-Z0-9][A-HJ-NP-Z0-9]\d{9}$" - `^DE\d{9}$`, // vat-0.1.0: r"^DE\d{9}$" - `^EL\d{9}$`, // vat-0.1.0: r"^EL\d{9}$" - `^HU\d{8}$`, // vat-0.1.0: r"^HU\d{8}$" - `^IE\d[A-Z0-9\+\*]\d{5}[A-Z]{1,2}$`, // vat-0.1.0: r"^IE\d[A-Z0-9\+\*]\d{5}[A-Z]{1,2}$" - `^IT\d{11}$`, // vat-0.1.0: r"^IT\d{11}$" - `^LV\d{11}$`, // vat-0.1.0: r"^LV\d{11}$" - `^LT(\d{9}|\d{12})$`, // vat-0.1.0: r"^LT(\d{9}|\d{12})$" - `^LU\d{8}$`, // vat-0.1.0: r"^LU\d{8}$" - `^MT\d{8}$`, // vat-0.1.0: r"^MT\d{8}$" - `^NL\d{9}B\d{2}$`, // vat-0.1.0: r"^NL\d{9}B\d{2}$" - `^PL\d{10}$`, // vat-0.1.0: r"^PL\d{10}$" - `^PT\d{9}$`, // vat-0.1.0: r"^PT\d{9}$" - `^RO\d{2,10}$`, // vat-0.1.0: r"^RO\d{2,10}$" - `^SK\d{10}$`, // vat-0.1.0: r"^SK\d{10}$" - `^SI\d{8}$`, // vat-0.1.0: r"^SI\d{8}$" - `^ES[A-Z0-9]\d{7}[A-Z0-9]$`, // vat-0.1.0: r"^ES[A-Z0-9]\d{7}[A-Z0-9]$" - `^SE\d{10}01$`, // vat-0.1.0: r"^SE\d{10}01$" - `^(GB(GD|HA)\d{3}|GB\d{9}|GB\d{12})$`, // vat-0.1.0: r"^(GB(GD|HA)\d{3}|GB\d{9}|GB\d{12})$" - `\{\{(.*)\}\}`, // eve-0.1.1: r"\{\{(.*)\}\}" - "^mio", // egc-0.1.2: "^mio" - "", // pew-0.2.3: "" - "", // pew-0.2.3: "" - "y", // mob-0.4.3: "y" - "@([a-z]+)", // lit-0.2.8: "@([a-z]+)" - "([A-Z-]+):(.*)", // lit-0.2.8: "([A-Z-]+):(.*)" - "^[a-zA-Z_][a-zA-Z0-9_]*$", // lit-0.2.8: "^[a-zA-Z_][a-zA-Z0-9_]*$" - `\d+\.\d+\.\d+`, // avm-1.0.1: r"\d+\.\d+\.\d+" - `\d+\.\d+\.\d+`, // avm-1.0.1: r"\d+\.\d+\.\d+" - `^Vec<(.+)>$`, // orm-0.2.0: r"^Vec<(.+)>$" - `\\(\r\n|\n\r|\n|\r)`, // sgf-0.1.5: r"\\(\r\n|\n\r|\n|\r)" - `\\(.)`, // sgf-0.1.5: r"\\(.)" - `\r\n|\n\r|\n|\r`, // sgf-0.1.5: r"\r\n|\n\r|\n|\r" - `([\]\\:])`, // sgf-0.1.5: r"([\]\\:])" - "^Bearer realm=\"(.+?)\",service=\"(.+?)\",scope=\"(.+?)\"$", // dok-0.2.0: "^Bearer realm=\"(.+?)\",service=\"(.+?)\",scope=\"(.+?)\"$" - `([+-]?\s*\d+[dD]\d+|[+-]?\s*\d+)`, // d20-0.1.0: r"([+-]?\s*\d+[dD]\d+|[+-]?\s*\d+)" - "E", // dvb-0.3.0: "E" - "^F", // dvb-0.3.0: "^F" - "^S", // dvb-0.3.0: "^S" - `Change-Id: (I[a-f0-9]{40})$`, // ger-0.2.0: r"Change-Id: (I[a-f0-9]{40})$" - `(refs|ref|fix|fixes|close|closes)\s+([A-Z]{2,5}-[0-9]{1,5})$`, // ger-0.2.0: r"(refs|ref|fix|fixes|close|closes)\s+([A-Z]{2,5}-[0-9]{1,5})$" - `(\d+)(\.(\d+))?(\.(\d+))?(.*)`, // n5-0.2.1: r"(\d+)(\.(\d+))?(\.(\d+))?(.*)" - `[A-Za-z0-9]`, // po-0.1.4: r"[A-Za-z0-9]" - "path is (‘|')?([^’'\n]*)(’|')?", // carnix-0.8.5: "path is (‘|')?([^’'\n]*)(’|')?" - `^(\S*) (\d*)\.(\d*)\.(\d*)(-(\S*))?(.*)?`, // carnix-0.8.5: r"^(\S*) (\d*)\.(\d*)\.(\d*)(-(\S*))?(.*)?" - `(\d*)\.(\d*)\.(\d*)(-(\S*))?`, // carnix-0.8.5: r"(\d*)\.(\d*)\.(\d*)(-(\S*))?" - `(\S*)-(\d*)\.(\d*)\.(\d*)(-(\S*))?`, // carnix-0.8.5: r"(\S*)-(\d*)\.(\d*)\.(\d*)(-(\S*))?" - `^# CaseFolding-(\d+)\.(\d+)\.(\d+).txt$`, // caseless-0.2.1: r"^# CaseFolding-(\d+)\.(\d+)\.(\d+).txt$" - `^([0-9A-F]+); [CF]; ([0-9A-F ]+);`, // caseless-0.2.1: r"^([0-9A-F]+); [CF]; ([0-9A-F ]+);" - "\r?\n\r?\n", // cabot-0.2.0: "\r?\n\r?\n" - "\r?\n", // cabot-0.2.0: "\r?\n" - `^600`, // card-validate-2.2.1: r"^600" - `^5019`, // card-validate-2.2.1: r"^5019" - `^4`, // card-validate-2.2.1: r"^4" - `^(5[1-5]|2[2-7])`, // card-validate-2.2.1: r"^(5[1-5]|2[2-7])" - `^3[47]`, // card-validate-2.2.1: r"^3[47]" - `^3[0689]`, // card-validate-2.2.1: r"^3[0689]" - `^6([045]|22)`, // card-validate-2.2.1: r"^6([045]|22)" - `^(62|88)`, // card-validate-2.2.1: r"^(62|88)" - `^35`, // card-validate-2.2.1: r"^35" - `^[0-9]+$`, // card-validate-2.2.1: r"^[0-9]+$" - `\d{1,} passed.*filtered out`, // cargo-testify-0.3.0: r"\d{1,} passed.*filtered out" - `error(:|\[).*`, // cargo-testify-0.3.0: r"error(:|\[).*" - `<(.*?)>`, // cargo-wix-0.0.5: r"<(.*?)>" - `<(.*?)>`, // cargo-wix-0.0.5: r"<(.*?)>" - `<(.*?)>`, // cargo-wix-0.0.5: r"<(.*?)>" - `<(.*?)>`, // cargo-wix-0.0.5: r"<(.*?)>" - `(?m)^incremental: re-using (\d+) out of (\d+) modules$`, // cargo-incremental-0.1.23: r"(?m)^incremental: re-using (\d+) out of (\d+) modules$" - "(?m)(warning|error): (.*)\n --> ([^:]:\\d+:\\d+)$", // cargo-incremental-0.1.23: "(?m)(warning|error): (.*)\n --> ([^:]:\\d+:\\d+)$" - `(?m)^test (.*) \.\.\. (\w+)`, // cargo-incremental-0.1.23: r"(?m)^test (.*) \.\.\. (\w+)" - `(?m)(\d+) passed; (\d+) failed; (\d+) ignored; \d+ measured`, // cargo-incremental-0.1.23: r"(?m)(\d+) passed; (\d+) failed; (\d+) ignored; \d+ measured" - `^[^-]+-[0-9a-f]+\.js$`, // cargo-testjs-0.1.2: r"^[^-]+-[0-9a-f]+\.js$" - `\s*//`, // cargo-tarpaulin-0.6.2: r"\s*//" - `/\*`, // cargo-tarpaulin-0.6.2: r"/\*" - `\*/`, // cargo-tarpaulin-0.6.2: r"\*/" - `^fo`, // cargo-culture-kit-0.1.0: r"^fo" - "\\s+", // cargo-screeps-0.1.3: "\\s+" - "`(\\S+) v([0-9.]+)", // cargo-brew-0.1.4: r"`(\S+) v([0-9.]+)" - "^\\[.+\\]", // cargo-release-0.10.2: "^\\[.+\\]" - "^\\[\\[.+\\]\\]", // cargo-release-0.10.2: "^\\[\\[.+\\]\\]" - `^https://github.com/([-_0-9a-zA-Z]+)/([-_0-9a-zA-Z]+)(/|.git)?$`, // cargo-edit-0.3.0-beta.1: r"^https://github.com/([-_0-9a-zA-Z]+)/([-_0-9a-zA-Z]+)(/|.git)?$" - `^https://gitlab.com/([-_0-9a-zA-Z]+)/([-_0-9a-zA-Z]+)(/|.git)?$`, // cargo-edit-0.3.0-beta.1: r"^https://gitlab.com/([-_0-9a-zA-Z]+)/([-_0-9a-zA-Z]+)(/|.git)?$" - ".*", // cargo-disassemble-0.1.1: ".*" - `(?m)(?P<symbol>_ZN[0-9]+.*E)`, // cargo-demangle-0.1.2: r"(?m)(?P<symbol>_ZN[0-9]+.*E)" - `^\s*\}(?:\)*;?|\s*else\s*\{)$`, // cargo-coverage-annotations-0.1.5: r"^\s*\}(?:\)*;?|\s*else\s*\{)$" - "[\u001b\u009b][\\[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-PRZcf-nqry=><]", // cargo-urlcrate-1.0.1: "[\u{001b}\u{009b}][\\[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-PRZcf-nqry=><]" - `^\s*\*( |$)`, // cargo-script-0.2.8: r"^\s*\*( |$)" - `^(\s+)`, // cargo-script-0.2.8: r"^(\s+)" - `/\*|\*/`, // cargo-script-0.2.8: r"/\*|\*/" - `^\s*//!`, // cargo-script-0.2.8: r"^\s*//!" - `^#![^\[].*?(\r\n|\n)`, // cargo-script-0.2.8: r"^#![^\[].*?(\r\n|\n)" - `cargo-install-update\.exe-v.+`, // cargo-update-1.5.2: r"cargo-install-update\.exe-v.+" - `^<(?:(int|uint|str|float|path):)?([\w_][a-zA-Z0-9_]*)>$`, // canteen-0.4.1: r"^<(?:(int|uint|str|float|path):)?([\w_][a-zA-Z0-9_]*)>$" - `(.)([A-Z])`, // thruster-cli-0.1.3: r"(.)([A-Z])" - "([Z]+)$", // thieves-cant-0.1.0: "([Z]+)$" - `^@\S+/\S+`, // codeowners-0.1.3: r"^@\S+/\S+" - `^@\S+`, // codeowners-0.1.3: r"^@\S+" - `^\S+@\S+`, // codeowners-0.1.3: r"^\S+@\S+" - `^b0000 {21} complete 20[-0-9T:+]+\s +\d+s\n$`, // conserve-0.4.2: r"^b0000 {21} complete 20[-0-9T:+]+\s +\d+s\n$" - `(?P<greeting>\S+?) (?P<name>\S+?)$`, // commodore-0.3.0: r"(?P<greeting>\S+?) (?P<name>\S+?)$" - "([ \\t]*)```haskell([\\s\\S]*?)```", // corollary-0.3.0: r"([ \t]*)```haskell([\s\S]*?)```" - `\b((?:a|b|t)\d*)\b`, // corollary-0.3.0: r"\b((?:a|b|t)\d*)\b" - "NB", // colorizex-0.1.3: "NB" - `(?i)\[[a-z0-9_-]+\]`, // colorstring-0.0.1: r"(?i)\[[a-z0-9_-]+\]" - `^(?i)(\[[a-z0-9_-]+\])+`, // colorstring-0.0.1: r"^(?i)(\[[a-z0-9_-]+\])+" - "name:(.+)", // cosmogony-0.3.0: "name:(.+)" - `(?m:^ {0,3}\[[^\]]+\]:.+$)`, // cobalt-bin-0.12.1: r"(?m:^ {0,3}\[[^\]]+\]:.+$)" - `[^\p{L}\p{M}\p{N}\p{Pc} -]`, // comrak-0.2.12: r"[^\p{L}\p{M}\p{N}\p{Pc} -]" - "", // content-blocker-0.2.3: "" - "(?i)hi", // content-blocker-0.2.3: "(?i)hi" - "http[s]?://domain.org", // content-blocker-0.2.3: "http[s]?://domain.org" - "(?i)http[s]?://domain.org", // content-blocker-0.2.3: "(?i)http[s]?://domain.org" - "http://domain.org", // content-blocker-0.2.3: "http://domain.org" - "http://domain.org", // content-blocker-0.2.3: "http://domain.org" - "ad.html", // content-blocker-0.2.3: "ad.html" - "ad.html", // content-blocker-0.2.3: "ad.html" - "http://domain.org", // content-blocker-0.2.3: "http://domain.org" - "http://domain.org/nocookies.sjs", // content-blocker-0.2.3: "http://domain.org/nocookies.sjs" - "http://domain.org/nocookies.sjs", // content-blocker-0.2.3: "http://domain.org/nocookies.sjs" - "http://domain.org/hideme.jpg", // content-blocker-0.2.3: "http://domain.org/hideme.jpg" - "http://domain.org/ok.html", // content-blocker-0.2.3: "http://domain.org/ok.html" - "http://domain.org/ok.html\\?except_this=1", // content-blocker-0.2.3: "http://domain.org/ok.html\\?except_this=1" - "[A-Za-z0-9=]", // victoria-dom-0.1.2: "[A-Za-z0-9=]" - `^nsq://`, // numbat-1.0.0: r"^nsq://" - `[\s\t\r\n]`, // airkorea-0.1.2: r"[\s\t\r\n]" - `([\{\[,])|([\}\]])`, // airkorea-0.1.2: r"([\{\[,])|([\}\]])" - `[^.\d]+$`, // airkorea-0.1.2: r"[^.\d]+$" - // rofl-0.0.1: r"\b" - `--------- beginning of.*`, // rogcat-0.2.15: r"--------- beginning of.*" - `a|e|i|o|u`, // rogcat-0.2.15: r"a|e|i|o|u" - `^(\d+)([kMG])$`, // rogcat-0.2.15: r"^(\d+)([kMG])$" - "\\.([A-Za-z0-9]{2,4})$", // media_filename-0.1.4: "\\.([A-Za-z0-9]{2,4})$" - "([0-9]{3,4}p|[0-9]{3,4}x[0-9]{3,4})", // media_filename-0.1.4: "([0-9]{3,4}p|[0-9]{3,4}x[0-9]{3,4})" - "(?:^\\[([^]]+)\\]|- ?([^-]+)$)", // media_filename-0.1.4: "(?:^\\[([^]]+)\\]|- ?([^-]+)$)" - "(?:[eE]([0-9]{2,3})|[^0-9A-Za-z]([0-9]{2,3})(?:v[0-9])?[^0-9A-Za-z])", // media_filename-0.1.4: "(?:[eE]([0-9]{2,3})|[^0-9A-Za-z]([0-9]{2,3})(?:v[0-9])?[^0-9A-Za-z])" - "[sS]([0-9]{1,2})", // media_filename-0.1.4: "[sS]([0-9]{1,2})" - "((?i)(?:PPV.)?[HP]DTV|(?:HD)?CAM|BRRIP|[^a-z]TS[^a-z]|(?:PPV )?WEB.?DL(?: DVDRip)?|HDRip|DVDRip|CamRip|W[EB]BRip|BluRay|BD|DVD|DvDScr|hdtv)", // media_filename-0.1.4: "((?i)(?:PPV.)?[HP]DTV|(?:HD)?CAM|BRRIP|[^a-z]TS[^a-z]|(?:PPV )?WEB.?DL(?: DVDRip)?|HDRip|DVDRip|CamRip|W[EB]BRip|BluRay|BD|DVD|DvDScr|hdtv)" - "((19[0-9]|20[01])[0-9])", // media_filename-0.1.4: "((19[0-9]|20[01])[0-9])" - "((?i)xvid|x264|h\\.?264)", // media_filename-0.1.4: "((?i)xvid|x264|h\\.?264)" - "((?i)MP3|DD5\\.?1|Dual[- ]Audio|LiNE|DTS|AAC(?:\\.?2\\.0)?|AC3(?:\\.5\\.1)?)", // media_filename-0.1.4: "((?i)MP3|DD5\\.?1|Dual[- ]Audio|LiNE|DTS|AAC(?:\\.?2\\.0)?|AC3(?:\\.5\\.1)?)" - "\\[([0-9A-F]{8})\\]", // media_filename-0.1.4: "\\[([0-9A-F]{8})\\]" - `(\d+)[xX](\d+)`, // termimage-0.3.2: r"(\d+)[xX](\d+)" - `.*(\d{4}-\d{2}-\d{2}).*`, // teensy-0.1.0: r".*(\d{4}-\d{2}-\d{2}).*" - `<@(.+)>`, // telescreen-0.1.3: r"<@(.+)>" - `^(\d+)`, // tempus_fugit-0.4.4: r"^(\d+)" - "(\\?|\\.|\\*|\\[|\\]|\\(|\\)|\\^|\\$)", // fselect-0.4.1: "(\\?|\\.|\\*|\\[|\\]|\\(|\\)|\\^|\\$)" - "(%|_|\\?|\\.|\\*|\\[|\\]|\\(|\\)|\\^|\\$)", // fselect-0.4.1: "(%|_|\\?|\\.|\\*|\\[|\\]|\\(|\\)|\\^|\\$)" - `^([A-Z]+)(?:\s(.+))?\s*`, // fs_eventbridge-0.1.0: r"^([A-Z]+)(?:\s(.+))?\s*" - `(\w{1,2})\[(.+?)\]`, // joseki-0.0.1: r"(\w{1,2})\[(.+?)\]" - `(?i)in (\d+) (second|minute|hour|day|week)s?`, // tweetr-0.2.1: r"(?i)in (\d+) (second|minute|hour|day|week)s?" - "^([0-9])+", // bullet_core-0.1.1: "^(?u:[0-9])+" - "^([0-9])+(\\.)([0-9])+", // bullet_core-0.1.1: "^(?u:[0-9])+(?u:\\.)(?u:[0-9])+" - "^([A-Za-zª-ªµ-µº-ºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬ-ˬˮ-ˮͰ-ʹͶ-ͷͺ-ͽͿ-ͿΆ-ΆΈ-ΊΌ-ΌΎ-ΡΣ-ϵϷ-ҁҊ-ԯԱ-Ֆՙ-ՙա-ևא-תװ-ײؠ-يٮ-ٯٱ-ۓە-ەۥ-ۦۮ-ۯۺ-ۼۿ-ۿܐ-ܐܒ-ܯݍ-ޥޱ-ޱߊ-ߪߴ-ߵߺ-ߺࠀ-ࠕࠚ-ࠚࠤ-ࠤࠨ-ࠨࡀ-ࡘࢠ-ࢴऄ-हऽ-ऽॐ-ॐक़-ॡॱ-ঀঅ-ঌএ-ঐও-নপ-রল-লশ-হঽ-ঽৎ-ৎড়-ঢ়য়-ৡৰ-ৱਅ-ਊਏ-ਐਓ-ਨਪ-ਰਲ-ਲ਼ਵ-ਸ਼ਸ-ਹਖ਼-ੜਫ਼-ਫ਼ੲ-ੴઅ-ઍએ-ઑઓ-નપ-રલ-ળવ-હઽ-ઽૐ-ૐૠ-ૡૹ-ૹଅ-ଌଏ-ଐଓ-ନପ-ରଲ-ଳଵ-ହଽ-ଽଡ଼-ଢ଼ୟ-ୡୱ-ୱஃ-ஃஅ-ஊஎ-ஐஒ-கங-சஜ-ஜஞ-டண-தந-பம-ஹௐ-ௐఅ-ఌఎ-ఐఒ-నప-హఽ-ఽౘ-ౚౠ-ౡಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹಽ-ಽೞ-ೞೠ-ೡೱ-ೲഅ-ഌഎ-ഐഒ-ഺഽ-ഽൎ-ൎൟ-ൡൺ-ൿඅ-ඖක-නඳ-රල-ලව-ෆก-ะา-ำเ-ๆກ-ຂຄ-ຄງ-ຈຊ-ຊຍ-ຍດ-ທນ-ຟມ-ຣລ-ລວ-ວສ-ຫອ-ະາ-ຳຽ-ຽເ-ໄໆ-ໆໜ-ໟༀ-ༀཀ-ཇཉ-ཬྈ-ྌက-ဪဿ-ဿၐ-ၕၚ-ၝၡ-ၡၥ-ၦၮ-ၰၵ-ႁႎ-ႎႠ-ჅჇ-ჇჍ-Ⴭა-ჺჼ-ቈቊ-ቍቐ-ቖቘ-ቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀ-ዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚᎀ-ᎏᎠ-Ᏽᏸ-ᏽᐁ-ᙬᙯ-ᙿᚁ-ᚚᚠ-ᛪᛱ-ᛸᜀ-ᜌᜎ-ᜑᜠ-ᜱᝀ-ᝑᝠ-ᝬᝮ-ᝰក-ឳៗ-ៗៜ-ៜᠠ-ᡷᢀ-ᢨᢪ-ᢪᢰ-ᣵᤀ-ᤞᥐ-ᥭᥰ-ᥴᦀ-ᦫᦰ-ᧉᨀ-ᨖᨠ-ᩔᪧ-ᪧᬅ-ᬳᭅ-ᭋᮃ-ᮠᮮ-ᮯᮺ-ᯥᰀ-ᰣᱍ-ᱏᱚ-ᱽᳩ-ᳬᳮ-ᳱᳵ-ᳶᴀ-ᶿḀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙ-ὙὛ-ὛὝ-ὝὟ-ώᾀ-ᾴᾶ-ᾼι-ιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼⁱ-ⁱⁿ-ⁿₐ-ₜℂ-ℂℇ-ℇℊ-ℓℕ-ℕℙ-ℝℤ-ℤΩ-Ωℨ-ℨK-ℭℯ-ℹℼ-ℿⅅ-ⅉⅎ-ⅎↃ-ↄⰀ-Ⱞⰰ-ⱞⱠ-ⳤⳫ-ⳮⳲ-ⳳⴀ-ⴥⴧ-ⴧⴭ-ⴭⴰ-ⵧⵯ-ⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞⸯ-ⸯ々-〆〱-〵〻-〼ぁ-ゖゝ-ゟァ-ヺー-ヿㄅ-ㄭㄱ-ㆎㆠ-ㆺㇰ-ㇿ㐀-䶵一-鿕ꀀ-ꒌꓐ-ꓽꔀ-ꘌꘐ-ꘟꘪ-ꘫꙀ-ꙮꙿ-ꚝꚠ-ꛥꜗ-ꜟꜢ-ꞈꞋ-ꞭꞰ-ꞷꟷ-ꠁꠃ-ꠅꠇ-ꠊꠌ-ꠢꡀ-ꡳꢂ-ꢳꣲ-ꣷꣻ-ꣻꣽ-ꣽꤊ-ꤥꤰ-ꥆꥠ-ꥼꦄ-ꦲꧏ-ꧏꧠ-ꧤꧦ-ꧯꧺ-ꧾꨀ-ꨨꩀ-ꩂꩄ-ꩋꩠ-ꩶꩺ-ꩺꩾ-ꪯꪱ-ꪱꪵ-ꪶꪹ-ꪽꫀ-ꫀꫂ-ꫂꫛ-ꫝꫠ-ꫪꫲ-ꫴꬁ-ꬆꬉ-ꬎꬑ-ꬖꬠ-ꬦꬨ-ꬮꬰ-ꭚꭜ-ꭥꭰ-ꯢ가-힣ힰ-ퟆퟋ-ퟻ豈-舘並-龎ff-stﬓ-ﬗיִ-יִײַ-ﬨשׁ-זּטּ-לּמּ-מּנּ-סּףּ-פּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷻﹰ-ﹴﹶ-ﻼA-Za-zヲ-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ𐀀-𐀋𐀍-𐀦𐀨-𐀺𐀼-𐀽𐀿-𐁍𐁐-𐁝𐂀-𐃺𐊀-𐊜𐊠-𐋐𐌀-𐌟𐌰-𐍀𐍂-𐍉𐍐-𐍵𐎀-𐎝𐎠-𐏃𐏈-𐏏𐐀-𐒝𐔀-𐔧𐔰-𐕣𐘀-𐜶𐝀-𐝕𐝠-𐝧𐠀-𐠅𐠈-𐠈𐠊-𐠵𐠷-𐠸𐠼-𐠼𐠿-𐡕𐡠-𐡶𐢀-𐢞𐣠-𐣲𐣴-𐣵𐤀-𐤕𐤠-𐤹𐦀-𐦷𐦾-𐦿𐨀-𐨀𐨐-𐨓𐨕-𐨗𐨙-𐨳𐩠-𐩼𐪀-𐪜𐫀-𐫇𐫉-𐫤𐬀-𐬵𐭀-𐭕𐭠-𐭲𐮀-𐮑𐰀-𐱈𐲀-𐲲𐳀-𐳲𑀃-𑀷𑂃-𑂯𑃐-𑃨𑄃-𑄦𑅐-𑅲𑅶-𑅶𑆃-𑆲𑇁-𑇄𑇚-𑇚𑇜-𑇜𑈀-𑈑𑈓-𑈫𑊀-𑊆𑊈-𑊈𑊊-𑊍𑊏-𑊝𑊟-𑊨𑊰-𑋞𑌅-𑌌𑌏-𑌐𑌓-𑌨𑌪-𑌰𑌲-𑌳𑌵-𑌹𑌽-𑌽𑍐-𑍐𑍝-𑍡𑒀-𑒯𑓄-𑓅𑓇-𑓇𑖀-𑖮𑗘-𑗛𑘀-𑘯𑙄-𑙄𑚀-𑚪𑜀-𑜙𑢠-𑣟𑣿-𑣿𑫀-𑫸𒀀-𒎙𒒀-𒕃𓀀-𓐮𔐀-𔙆𖠀-𖨸𖩀-𖩞𖫐-𖫭𖬀-𖬯𖭀-𖭃𖭣-𖭷𖭽-𖮏𖼀-𖽄𖽐-𖽐𖾓-𖾟𛀀-𛀁𛰀-𛱪𛱰-𛱼𛲀-𛲈𛲐-𛲙𝐀-𝑔𝑖-𝒜𝒞-𝒟𝒢-𝒢𝒥-𝒦𝒩-𝒬𝒮-𝒹𝒻-𝒻𝒽-𝓃𝓅-𝔅𝔇-𝔊𝔍-𝔔𝔖-𝔜𝔞-𝔹𝔻-𝔾𝕀-𝕄𝕆-𝕆𝕊-𝕐𝕒-𝚥𝚨-𝛀𝛂-𝛚𝛜-𝛺𝛼-𝜔𝜖-𝜴𝜶-𝝎𝝐-𝝮𝝰-𝞈𝞊-𝞨𝞪-𝟂𝟄-𝟋𞠀-𞣄𞸀-𞸃𞸅-𞸟𞸡-𞸢𞸤-𞸤𞸧-𞸧𞸩-𞸲𞸴-𞸷𞸹-𞸹𞸻-𞸻𞹂-𞹂𞹇-𞹇𞹉-𞹉𞹋-𞹋𞹍-𞹏𞹑-𞹒𞹔-𞹔𞹗-𞹗𞹙-𞹙𞹛-𞹛𞹝-𞹝𞹟-𞹟𞹡-𞹢𞹤-𞹤𞹧-𞹪𞹬-𞹲𞹴-𞹷𞹹-𞹼𞹾-𞹾𞺀-𞺉𞺋-𞺛𞺡-𞺣𞺥-𞺩𞺫-𞺻𠀀-𪛖𪜀-𫜴𫝀-𫠝𫠠-𬺡丽-𪘀])+", // bullet_core-0.1.1: "^(?u:[A-Za-zª-ªµ-µº-ºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬ-ˬˮ-ˮͰ-ʹͶ-ͷͺ-ͽͿ-ͿΆ-ΆΈ-ΊΌ-ΌΎ-ΡΣ-ϵϷ-ҁҊ-ԯԱ-Ֆՙ-ՙա-ևא-תװ-ײؠ-يٮ-ٯٱ-ۓە-ەۥ-ۦۮ-ۯۺ-ۼۿ-ۿܐ-ܐܒ-ܯݍ-ޥޱ-ޱߊ-ߪߴ-ߵߺ-ߺࠀ-ࠕࠚ-ࠚࠤ-ࠤࠨ-ࠨࡀ-ࡘࢠ-ࢴऄ-हऽ-ऽॐ-ॐक़-ॡॱ-ঀঅ-ঌএ-ঐও-নপ-রল-লশ-হঽ-ঽৎ-ৎড়-ঢ়য়-ৡৰ-ৱਅ-ਊਏ-ਐਓ-ਨਪ-ਰਲ-ਲ਼ਵ-ਸ਼ਸ-ਹਖ਼-ੜਫ਼-ਫ਼ੲ-ੴઅ-ઍએ-ઑઓ-નપ-રલ-ળવ-હઽ-ઽૐ-ૐૠ-ૡૹ-ૹଅ-ଌଏ-ଐଓ-ନପ-ରଲ-ଳଵ-ହଽ-ଽଡ଼-ଢ଼ୟ-ୡୱ-ୱஃ-ஃஅ-ஊஎ-ஐஒ-கங-சஜ-ஜஞ-டண-தந-பம-ஹௐ-ௐఅ-ఌఎ-ఐఒ-నప-హఽ-ఽౘ-ౚౠ-ౡಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹಽ-ಽೞ-ೞೠ-ೡೱ-ೲഅ-ഌഎ-ഐഒ-ഺഽ-ഽൎ-ൎൟ-ൡൺ-ൿඅ-ඖක-නඳ-රල-ලව-ෆก-ะา-ำเ-ๆກ-ຂຄ-ຄງ-ຈຊ-ຊຍ-ຍດ-ທນ-ຟມ-ຣລ-ລວ-ວສ-ຫອ-ະາ-ຳຽ-ຽເ-ໄໆ-ໆໜ-ໟༀ-ༀཀ-ཇཉ-ཬྈ-ྌက-ဪဿ-ဿၐ-ၕၚ-ၝၡ-ၡၥ-ၦၮ-ၰၵ-ႁႎ-ႎႠ-ჅჇ-ჇჍ-Ⴭა-ჺჼ-ቈቊ-ቍቐ-ቖቘ-ቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀ-ዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚᎀ-ᎏᎠ-Ᏽᏸ-ᏽᐁ-ᙬᙯ-ᙿᚁ-ᚚᚠ-ᛪᛱ-ᛸᜀ-ᜌᜎ-ᜑᜠ-ᜱᝀ-ᝑᝠ-ᝬᝮ-ᝰក-ឳៗ-ៗៜ-ៜᠠ-ᡷᢀ-ᢨᢪ-ᢪᢰ-ᣵᤀ-ᤞᥐ-ᥭᥰ-ᥴᦀ-ᦫᦰ-ᧉᨀ-ᨖᨠ-ᩔᪧ-ᪧᬅ-ᬳᭅ-ᭋᮃ-ᮠᮮ-ᮯᮺ-ᯥᰀ-ᰣᱍ-ᱏᱚ-ᱽᳩ-ᳬᳮ-ᳱᳵ-ᳶᴀ-ᶿḀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙ-ὙὛ-ὛὝ-ὝὟ-ώᾀ-ᾴᾶ-ᾼι-ιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼⁱ-ⁱⁿ-ⁿₐ-ₜℂ-ℂℇ-ℇℊ-ℓℕ-ℕℙ-ℝℤ-ℤΩ-Ωℨ-ℨK-ℭℯ-ℹℼ-ℿⅅ-ⅉⅎ-ⅎↃ-ↄⰀ-Ⱞⰰ-ⱞⱠ-ⳤⳫ-ⳮⳲ-ⳳⴀ-ⴥⴧ-ⴧⴭ-ⴭⴰ-ⵧⵯ-ⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞⸯ-ⸯ々-〆〱-〵〻-〼ぁ-ゖゝ-ゟァ-ヺー-ヿㄅ-ㄭㄱ-ㆎㆠ-ㆺㇰ-ㇿ㐀-䶵一-鿕ꀀ-ꒌꓐ-ꓽꔀ-ꘌꘐ-ꘟꘪ-ꘫꙀ-ꙮꙿ-ꚝꚠ-ꛥꜗ-ꜟꜢ-ꞈꞋ-ꞭꞰ-ꞷꟷ-ꠁꠃ-ꠅꠇ-ꠊꠌ-ꠢꡀ-ꡳꢂ-ꢳꣲ-ꣷꣻ-ꣻꣽ-ꣽꤊ-ꤥꤰ-ꥆꥠ-ꥼꦄ-ꦲꧏ-ꧏꧠ-ꧤꧦ-ꧯꧺ-ꧾꨀ-ꨨꩀ-ꩂꩄ-ꩋꩠ-ꩶꩺ-ꩺꩾ-ꪯꪱ-ꪱꪵ-ꪶꪹ-ꪽꫀ-ꫀꫂ-ꫂꫛ-ꫝꫠ-ꫪꫲ-ꫴꬁ-ꬆꬉ-ꬎꬑ-ꬖꬠ-ꬦꬨ-ꬮꬰ-ꭚꭜ-ꭥꭰ-ꯢ가-힣ힰ-ퟆퟋ-ퟻ豈-舘並-龎ff-stﬓ-ﬗיִ-יִײַ-ﬨשׁ-זּטּ-לּמּ-מּנּ-סּףּ-פּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷻﹰ-ﹴﹶ-ﻼA-Za-zヲ-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ𐀀-𐀋𐀍-𐀦𐀨-𐀺𐀼-𐀽𐀿-𐁍𐁐-𐁝𐂀-𐃺𐊀-𐊜𐊠-𐋐𐌀-𐌟𐌰-𐍀𐍂-𐍉𐍐-𐍵𐎀-𐎝𐎠-𐏃𐏈-𐏏𐐀-𐒝𐔀-𐔧𐔰-𐕣𐘀-𐜶𐝀-𐝕𐝠-𐝧𐠀-𐠅𐠈-𐠈𐠊-𐠵𐠷-𐠸𐠼-𐠼𐠿-𐡕𐡠-𐡶𐢀-𐢞𐣠-𐣲𐣴-𐣵𐤀-𐤕𐤠-𐤹𐦀-𐦷𐦾-𐦿𐨀-𐨀𐨐-𐨓𐨕-𐨗𐨙-𐨳𐩠-𐩼𐪀-𐪜𐫀-𐫇𐫉-𐫤𐬀-𐬵𐭀-𐭕𐭠-𐭲𐮀-𐮑𐰀-𐱈𐲀-𐲲𐳀-𐳲𑀃-𑀷𑂃-𑂯𑃐-𑃨𑄃-𑄦𑅐-𑅲𑅶-𑅶𑆃-𑆲𑇁-𑇄𑇚-𑇚𑇜-𑇜𑈀-𑈑𑈓-𑈫𑊀-𑊆𑊈-𑊈𑊊-𑊍𑊏-𑊝𑊟-𑊨𑊰-𑋞𑌅-𑌌𑌏-𑌐𑌓-𑌨𑌪-𑌰𑌲-𑌳𑌵-𑌹𑌽-𑌽𑍐-𑍐𑍝-𑍡𑒀-𑒯𑓄-𑓅𑓇-𑓇𑖀-𑖮𑗘-𑗛𑘀-𑘯𑙄-𑙄𑚀-𑚪𑜀-𑜙𑢠-𑣟𑣿-𑣿𑫀-𑫸𒀀-𒎙𒒀-𒕃𓀀-𓐮𔐀-𔙆𖠀-𖨸𖩀-𖩞𖫐-𖫭𖬀-𖬯𖭀-𖭃𖭣-𖭷𖭽-𖮏𖼀-𖽄𖽐-𖽐𖾓-𖾟𛀀-𛀁𛰀-𛱪𛱰-𛱼𛲀-𛲈𛲐-𛲙𝐀-𝑔𝑖-𝒜𝒞-𝒟𝒢-𝒢𝒥-𝒦𝒩-𝒬𝒮-𝒹𝒻-𝒻𝒽-𝓃𝓅-𝔅𝔇-𝔊𝔍-𝔔𝔖-𝔜𝔞-𝔹𝔻-𝔾𝕀-𝕄𝕆-𝕆𝕊-𝕐𝕒-𝚥𝚨-𝛀𝛂-𝛚𝛜-𝛺𝛼-𝜔𝜖-𝜴𝜶-𝝎𝝐-𝝮𝝰-𝞈𝞊-𝞨𝞪-𝟂𝟄-𝟋𞠀-𞣄𞸀-𞸃𞸅-𞸟𞸡-𞸢𞸤-𞸤𞸧-𞸧𞸩-𞸲𞸴-𞸷𞸹-𞸹𞸻-𞸻𞹂-𞹂𞹇-𞹇𞹉-𞹉𞹋-𞹋𞹍-𞹏𞹑-𞹒𞹔-𞹔𞹗-𞹗𞹙-𞹙𞹛-𞹛𞹝-𞹝𞹟-𞹟𞹡-𞹢𞹤-𞹤𞹧-𞹪𞹬-𞹲𞹴-𞹷𞹹-𞹼𞹾-𞹾𞺀-𞺉𞺋-𞺛𞺡-𞺣𞺥-𞺩𞺫-𞺻𠀀-𪛖𪜀-𫜴𫝀-𫠝𫠠-𬺡丽-𪘀])+" - "^(d/d)(([A-Za-zª-ªµ-µº-ºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬ-ˬˮ-ˮͰ-ʹͶ-ͷͺ-ͽͿ-ͿΆ-ΆΈ-ΊΌ-ΌΎ-ΡΣ-ϵϷ-ҁҊ-ԯԱ-Ֆՙ-ՙա-ևא-תװ-ײؠ-يٮ-ٯٱ-ۓە-ەۥ-ۦۮ-ۯۺ-ۼۿ-ۿܐ-ܐܒ-ܯݍ-ޥޱ-ޱߊ-ߪߴ-ߵߺ-ߺࠀ-ࠕࠚ-ࠚࠤ-ࠤࠨ-ࠨࡀ-ࡘࢠ-ࢴऄ-हऽ-ऽॐ-ॐक़-ॡॱ-ঀঅ-ঌএ-ঐও-নপ-রল-লশ-হঽ-ঽৎ-ৎড়-ঢ়য়-ৡৰ-ৱਅ-ਊਏ-ਐਓ-ਨਪ-ਰਲ-ਲ਼ਵ-ਸ਼ਸ-ਹਖ਼-ੜਫ਼-ਫ਼ੲ-ੴઅ-ઍએ-ઑઓ-નપ-રલ-ળવ-હઽ-ઽૐ-ૐૠ-ૡૹ-ૹଅ-ଌଏ-ଐଓ-ନପ-ରଲ-ଳଵ-ହଽ-ଽଡ଼-ଢ଼ୟ-ୡୱ-ୱஃ-ஃஅ-ஊஎ-ஐஒ-கங-சஜ-ஜஞ-டண-தந-பம-ஹௐ-ௐఅ-ఌఎ-ఐఒ-నప-హఽ-ఽౘ-ౚౠ-ౡಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹಽ-ಽೞ-ೞೠ-ೡೱ-ೲഅ-ഌഎ-ഐഒ-ഺഽ-ഽൎ-ൎൟ-ൡൺ-ൿඅ-ඖක-නඳ-රල-ලව-ෆก-ะา-ำเ-ๆກ-ຂຄ-ຄງ-ຈຊ-ຊຍ-ຍດ-ທນ-ຟມ-ຣລ-ລວ-ວສ-ຫອ-ະາ-ຳຽ-ຽເ-ໄໆ-ໆໜ-ໟༀ-ༀཀ-ཇཉ-ཬྈ-ྌက-ဪဿ-ဿၐ-ၕၚ-ၝၡ-ၡၥ-ၦၮ-ၰၵ-ႁႎ-ႎႠ-ჅჇ-ჇჍ-Ⴭა-ჺჼ-ቈቊ-ቍቐ-ቖቘ-ቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀ-ዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚᎀ-ᎏᎠ-Ᏽᏸ-ᏽᐁ-ᙬᙯ-ᙿᚁ-ᚚᚠ-ᛪᛱ-ᛸᜀ-ᜌᜎ-ᜑᜠ-ᜱᝀ-ᝑᝠ-ᝬᝮ-ᝰក-ឳៗ-ៗៜ-ៜᠠ-ᡷᢀ-ᢨᢪ-ᢪᢰ-ᣵᤀ-ᤞᥐ-ᥭᥰ-ᥴᦀ-ᦫᦰ-ᧉᨀ-ᨖᨠ-ᩔᪧ-ᪧᬅ-ᬳᭅ-ᭋᮃ-ᮠᮮ-ᮯᮺ-ᯥᰀ-ᰣᱍ-ᱏᱚ-ᱽᳩ-ᳬᳮ-ᳱᳵ-ᳶᴀ-ᶿḀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙ-ὙὛ-ὛὝ-ὝὟ-ώᾀ-ᾴᾶ-ᾼι-ιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼⁱ-ⁱⁿ-ⁿₐ-ₜℂ-ℂℇ-ℇℊ-ℓℕ-ℕℙ-ℝℤ-ℤΩ-Ωℨ-ℨK-ℭℯ-ℹℼ-ℿⅅ-ⅉⅎ-ⅎↃ-ↄⰀ-Ⱞⰰ-ⱞⱠ-ⳤⳫ-ⳮⳲ-ⳳⴀ-ⴥⴧ-ⴧⴭ-ⴭⴰ-ⵧⵯ-ⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞⸯ-ⸯ々-〆〱-〵〻-〼ぁ-ゖゝ-ゟァ-ヺー-ヿㄅ-ㄭㄱ-ㆎㆠ-ㆺㇰ-ㇿ㐀-䶵一-鿕ꀀ-ꒌꓐ-ꓽꔀ-ꘌꘐ-ꘟꘪ-ꘫꙀ-ꙮꙿ-ꚝꚠ-ꛥꜗ-ꜟꜢ-ꞈꞋ-ꞭꞰ-ꞷꟷ-ꠁꠃ-ꠅꠇ-ꠊꠌ-ꠢꡀ-ꡳꢂ-ꢳꣲ-ꣷꣻ-ꣻꣽ-ꣽꤊ-ꤥꤰ-ꥆꥠ-ꥼꦄ-ꦲꧏ-ꧏꧠ-ꧤꧦ-ꧯꧺ-ꧾꨀ-ꨨꩀ-ꩂꩄ-ꩋꩠ-ꩶꩺ-ꩺꩾ-ꪯꪱ-ꪱꪵ-ꪶꪹ-ꪽꫀ-ꫀꫂ-ꫂꫛ-ꫝꫠ-ꫪꫲ-ꫴꬁ-ꬆꬉ-ꬎꬑ-ꬖꬠ-ꬦꬨ-ꬮꬰ-ꭚꭜ-ꭥꭰ-ꯢ가-힣ힰ-ퟆퟋ-ퟻ豈-舘並-龎ff-stﬓ-ﬗיִ-יִײַ-ﬨשׁ-זּטּ-לּמּ-מּנּ-סּףּ-פּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷻﹰ-ﹴﹶ-ﻼA-Za-zヲ-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ𐀀-𐀋𐀍-𐀦𐀨-𐀺𐀼-𐀽𐀿-𐁍𐁐-𐁝𐂀-𐃺𐊀-𐊜𐊠-𐋐𐌀-𐌟𐌰-𐍀𐍂-𐍉𐍐-𐍵𐎀-𐎝𐎠-𐏃𐏈-𐏏𐐀-𐒝𐔀-𐔧𐔰-𐕣𐘀-𐜶𐝀-𐝕𐝠-𐝧𐠀-𐠅𐠈-𐠈𐠊-𐠵𐠷-𐠸𐠼-𐠼𐠿-𐡕𐡠-𐡶𐢀-𐢞𐣠-𐣲𐣴-𐣵𐤀-𐤕𐤠-𐤹𐦀-𐦷𐦾-𐦿𐨀-𐨀𐨐-𐨓𐨕-𐨗𐨙-𐨳𐩠-𐩼𐪀-𐪜𐫀-𐫇𐫉-𐫤𐬀-𐬵𐭀-𐭕𐭠-𐭲𐮀-𐮑𐰀-𐱈𐲀-𐲲𐳀-𐳲𑀃-𑀷𑂃-𑂯𑃐-𑃨𑄃-𑄦𑅐-𑅲𑅶-𑅶𑆃-𑆲𑇁-𑇄𑇚-𑇚𑇜-𑇜𑈀-𑈑𑈓-𑈫𑊀-𑊆𑊈-𑊈𑊊-𑊍𑊏-𑊝𑊟-𑊨𑊰-𑋞𑌅-𑌌𑌏-𑌐𑌓-𑌨𑌪-𑌰𑌲-𑌳𑌵-𑌹𑌽-𑌽𑍐-𑍐𑍝-𑍡𑒀-𑒯𑓄-𑓅𑓇-𑓇𑖀-𑖮𑗘-𑗛𑘀-𑘯𑙄-𑙄𑚀-𑚪𑜀-𑜙𑢠-𑣟𑣿-𑣿𑫀-𑫸𒀀-𒎙𒒀-𒕃𓀀-𓐮𔐀-𔙆𖠀-𖨸𖩀-𖩞𖫐-𖫭𖬀-𖬯𖭀-𖭃𖭣-𖭷𖭽-𖮏𖼀-𖽄𖽐-𖽐𖾓-𖾟𛀀-𛀁𛰀-𛱪𛱰-𛱼𛲀-𛲈𛲐-𛲙𝐀-𝑔𝑖-𝒜𝒞-𝒟𝒢-𝒢𝒥-𝒦𝒩-𝒬𝒮-𝒹𝒻-𝒻𝒽-𝓃𝓅-𝔅𝔇-𝔊𝔍-𝔔𝔖-𝔜𝔞-𝔹𝔻-𝔾𝕀-𝕄𝕆-𝕆𝕊-𝕐𝕒-𝚥𝚨-𝛀𝛂-𝛚𝛜-𝛺𝛼-𝜔𝜖-𝜴𝜶-𝝎𝝐-𝝮𝝰-𝞈𝞊-𝞨𝞪-𝟂𝟄-𝟋𞠀-𞣄𞸀-𞸃𞸅-𞸟𞸡-𞸢𞸤-𞸤𞸧-𞸧𞸩-𞸲𞸴-𞸷𞸹-𞸹𞸻-𞸻𞹂-𞹂𞹇-𞹇𞹉-𞹉𞹋-𞹋𞹍-𞹏𞹑-𞹒𞹔-𞹔𞹗-𞹗𞹙-𞹙𞹛-𞹛𞹝-𞹝𞹟-𞹟𞹡-𞹢𞹤-𞹤𞹧-𞹪𞹬-𞹲𞹴-𞹷𞹹-𞹼𞹾-𞹾𞺀-𞺉𞺋-𞺛𞺡-𞺣𞺥-𞺩𞺫-𞺻𠀀-𪛖𪜀-𫜴𫝀-𫠝𫠠-𬺡丽-𪘀])+)", // bullet_core-0.1.1: "^(?u:d/d)((?u:[A-Za-zª-ªµ-µº-ºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬ-ˬˮ-ˮͰ-ʹͶ-ͷͺ-ͽͿ-ͿΆ-ΆΈ-ΊΌ-ΌΎ-ΡΣ-ϵϷ-ҁҊ-ԯԱ-Ֆՙ-ՙա-ևא-תװ-ײؠ-يٮ-ٯٱ-ۓە-ەۥ-ۦۮ-ۯۺ-ۼۿ-ۿܐ-ܐܒ-ܯݍ-ޥޱ-ޱߊ-ߪߴ-ߵߺ-ߺࠀ-ࠕࠚ-ࠚࠤ-ࠤࠨ-ࠨࡀ-ࡘࢠ-ࢴऄ-हऽ-ऽॐ-ॐक़-ॡॱ-ঀঅ-ঌএ-ঐও-নপ-রল-লশ-হঽ-ঽৎ-ৎড়-ঢ়য়-ৡৰ-ৱਅ-ਊਏ-ਐਓ-ਨਪ-ਰਲ-ਲ਼ਵ-ਸ਼ਸ-ਹਖ਼-ੜਫ਼-ਫ਼ੲ-ੴઅ-ઍએ-ઑઓ-નપ-રલ-ળવ-હઽ-ઽૐ-ૐૠ-ૡૹ-ૹଅ-ଌଏ-ଐଓ-ନପ-ରଲ-ଳଵ-ହଽ-ଽଡ଼-ଢ଼ୟ-ୡୱ-ୱஃ-ஃஅ-ஊஎ-ஐஒ-கங-சஜ-ஜஞ-டண-தந-பம-ஹௐ-ௐఅ-ఌఎ-ఐఒ-నప-హఽ-ఽౘ-ౚౠ-ౡಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹಽ-ಽೞ-ೞೠ-ೡೱ-ೲഅ-ഌഎ-ഐഒ-ഺഽ-ഽൎ-ൎൟ-ൡൺ-ൿඅ-ඖක-නඳ-රල-ලව-ෆก-ะา-ำเ-ๆກ-ຂຄ-ຄງ-ຈຊ-ຊຍ-ຍດ-ທນ-ຟມ-ຣລ-ລວ-ວສ-ຫອ-ະາ-ຳຽ-ຽເ-ໄໆ-ໆໜ-ໟༀ-ༀཀ-ཇཉ-ཬྈ-ྌက-ဪဿ-ဿၐ-ၕၚ-ၝၡ-ၡၥ-ၦၮ-ၰၵ-ႁႎ-ႎႠ-ჅჇ-ჇჍ-Ⴭა-ჺჼ-ቈቊ-ቍቐ-ቖቘ-ቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀ-ዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚᎀ-ᎏᎠ-Ᏽᏸ-ᏽᐁ-ᙬᙯ-ᙿᚁ-ᚚᚠ-ᛪᛱ-ᛸᜀ-ᜌᜎ-ᜑᜠ-ᜱᝀ-ᝑᝠ-ᝬᝮ-ᝰក-ឳៗ-ៗៜ-ៜᠠ-ᡷᢀ-ᢨᢪ-ᢪᢰ-ᣵᤀ-ᤞᥐ-ᥭᥰ-ᥴᦀ-ᦫᦰ-ᧉᨀ-ᨖᨠ-ᩔᪧ-ᪧᬅ-ᬳᭅ-ᭋᮃ-ᮠᮮ-ᮯᮺ-ᯥᰀ-ᰣᱍ-ᱏᱚ-ᱽᳩ-ᳬᳮ-ᳱᳵ-ᳶᴀ-ᶿḀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙ-ὙὛ-ὛὝ-ὝὟ-ώᾀ-ᾴᾶ-ᾼι-ιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼⁱ-ⁱⁿ-ⁿₐ-ₜℂ-ℂℇ-ℇℊ-ℓℕ-ℕℙ-ℝℤ-ℤΩ-Ωℨ-ℨK-ℭℯ-ℹℼ-ℿⅅ-ⅉⅎ-ⅎↃ-ↄⰀ-Ⱞⰰ-ⱞⱠ-ⳤⳫ-ⳮⳲ-ⳳⴀ-ⴥⴧ-ⴧⴭ-ⴭⴰ-ⵧⵯ-ⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞⸯ-ⸯ々-〆〱-〵〻-〼ぁ-ゖゝ-ゟァ-ヺー-ヿㄅ-ㄭㄱ-ㆎㆠ-ㆺㇰ-ㇿ㐀-䶵一-鿕ꀀ-ꒌꓐ-ꓽꔀ-ꘌꘐ-ꘟꘪ-ꘫꙀ-ꙮꙿ-ꚝꚠ-ꛥꜗ-ꜟꜢ-ꞈꞋ-ꞭꞰ-ꞷꟷ-ꠁꠃ-ꠅꠇ-ꠊꠌ-ꠢꡀ-ꡳꢂ-ꢳꣲ-ꣷꣻ-ꣻꣽ-ꣽꤊ-ꤥꤰ-ꥆꥠ-ꥼꦄ-ꦲꧏ-ꧏꧠ-ꧤꧦ-ꧯꧺ-ꧾꨀ-ꨨꩀ-ꩂꩄ-ꩋꩠ-ꩶꩺ-ꩺꩾ-ꪯꪱ-ꪱꪵ-ꪶꪹ-ꪽꫀ-ꫀꫂ-ꫂꫛ-ꫝꫠ-ꫪꫲ-ꫴꬁ-ꬆꬉ-ꬎꬑ-ꬖꬠ-ꬦꬨ-ꬮꬰ-ꭚꭜ-ꭥꭰ-ꯢ가-힣ힰ-ퟆퟋ-ퟻ豈-舘並-龎ff-stﬓ-ﬗיִ-יִײַ-ﬨשׁ-זּטּ-לּמּ-מּנּ-סּףּ-פּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷻﹰ-ﹴﹶ-ﻼA-Za-zヲ-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ𐀀-𐀋𐀍-𐀦𐀨-𐀺𐀼-𐀽𐀿-𐁍𐁐-𐁝𐂀-𐃺𐊀-𐊜𐊠-𐋐𐌀-𐌟𐌰-𐍀𐍂-𐍉𐍐-𐍵𐎀-𐎝𐎠-𐏃𐏈-𐏏𐐀-𐒝𐔀-𐔧𐔰-𐕣𐘀-𐜶𐝀-𐝕𐝠-𐝧𐠀-𐠅𐠈-𐠈𐠊-𐠵𐠷-𐠸𐠼-𐠼𐠿-𐡕𐡠-𐡶𐢀-𐢞𐣠-𐣲𐣴-𐣵𐤀-𐤕𐤠-𐤹𐦀-𐦷𐦾-𐦿𐨀-𐨀𐨐-𐨓𐨕-𐨗𐨙-𐨳𐩠-𐩼𐪀-𐪜𐫀-𐫇𐫉-𐫤𐬀-𐬵𐭀-𐭕𐭠-𐭲𐮀-𐮑𐰀-𐱈𐲀-𐲲𐳀-𐳲𑀃-𑀷𑂃-𑂯𑃐-𑃨𑄃-𑄦𑅐-𑅲𑅶-𑅶𑆃-𑆲𑇁-𑇄𑇚-𑇚𑇜-𑇜𑈀-𑈑𑈓-𑈫𑊀-𑊆𑊈-𑊈𑊊-𑊍𑊏-𑊝𑊟-𑊨𑊰-𑋞𑌅-𑌌𑌏-𑌐𑌓-𑌨𑌪-𑌰𑌲-𑌳𑌵-𑌹𑌽-𑌽𑍐-𑍐𑍝-𑍡𑒀-𑒯𑓄-𑓅𑓇-𑓇𑖀-𑖮𑗘-𑗛𑘀-𑘯𑙄-𑙄𑚀-𑚪𑜀-𑜙𑢠-𑣟𑣿-𑣿𑫀-𑫸𒀀-𒎙𒒀-𒕃𓀀-𓐮𔐀-𔙆𖠀-𖨸𖩀-𖩞𖫐-𖫭𖬀-𖬯𖭀-𖭃𖭣-𖭷𖭽-𖮏𖼀-𖽄𖽐-𖽐𖾓-𖾟𛀀-𛀁𛰀-𛱪𛱰-𛱼𛲀-𛲈𛲐-𛲙𝐀-𝑔𝑖-𝒜𝒞-𝒟𝒢-𝒢𝒥-𝒦𝒩-𝒬𝒮-𝒹𝒻-𝒻𝒽-𝓃𝓅-𝔅𝔇-𝔊𝔍-𝔔𝔖-𝔜𝔞-𝔹𝔻-𝔾𝕀-𝕄𝕆-𝕆𝕊-𝕐𝕒-𝚥𝚨-𝛀𝛂-𝛚𝛜-𝛺𝛼-𝜔𝜖-𝜴𝜶-𝝎𝝐-𝝮𝝰-𝞈𝞊-𝞨𝞪-𝟂𝟄-𝟋𞠀-𞣄𞸀-𞸃𞸅-𞸟𞸡-𞸢𞸤-𞸤𞸧-𞸧𞸩-𞸲𞸴-𞸷𞸹-𞸹𞸻-𞸻𞹂-𞹂𞹇-𞹇𞹉-𞹉𞹋-𞹋𞹍-𞹏𞹑-𞹒𞹔-𞹔𞹗-𞹗𞹙-𞹙𞹛-𞹛𞹝-𞹝𞹟-𞹟𞹡-𞹢𞹤-𞹤𞹧-𞹪𞹬-𞹲𞹴-𞹷𞹹-𞹼𞹾-𞹾𞺀-𞺉𞺋-𞺛𞺡-𞺣𞺥-𞺩𞺫-𞺻𠀀-𪛖𪜀-𫜴𫝀-𫠝𫠠-𬺡丽-𪘀])+)" - "^(\\()", // bullet_core-0.1.1: "^(?u:\\()" - "^(\\))", // bullet_core-0.1.1: "^(?u:\\))" - "^(\\*)", // bullet_core-0.1.1: "^(?u:\\*)" - "^(\\+)", // bullet_core-0.1.1: "^(?u:\\+)" - "^(,)", // bullet_core-0.1.1: "^(?u:,)" - "^(\\-)", // bullet_core-0.1.1: "^(?u:\\-)" - "^(/)", // bullet_core-0.1.1: "^(?u:/)" - "^(\\[)", // bullet_core-0.1.1: "^(?u:\\[)" - "^(\\])", // bullet_core-0.1.1: "^(?u:\\])" - "^(\\^)", // bullet_core-0.1.1: "^(?u:\\^)" - "^(·)", // bullet_core-0.1.1: "^(?u:·)" - "//+", // actix-web-0.6.13: "//+" - "//+", // actix-web-0.6.13: "//+" - `(\S*) .* (\S*) (REACHABLE|STALE|DELAY)`, // althea_kernel_interface-0.1.0: r"(\S*) .* (\S*) (REACHABLE|STALE|DELAY)" - `-s (.*) --ip6-dst (.*)/.* bcnt = (.*)`, // althea_kernel_interface-0.1.0: r"-s (.*) --ip6-dst (.*)/.* bcnt = (.*)" - `\buci(?:\s|$)`, // alcibiades-0.3.0: r"\buci(?:\s|$)" - `\A[a-z0-9._=-]+\z`, // ruma-identifiers-0.11.0: r"\A[a-z0-9._=-]+\z" - `/rusqbins/((?i)[A-F0-9]{8}\-[A-F0-9]{4}\-4[A-F0-9]{3}\-[89AB][A-F0-9]{3}\-[A-F0-9]{12})$`, // rusqbin-0.2.3: r"/rusqbins/((?i)[A-F0-9]{8}\-[A-F0-9]{4}\-4[A-F0-9]{3}\-[89AB][A-F0-9]{3}\-[A-F0-9]{12})$" - `/rusqbins/((?i)[A-F0-9]{8}\-[A-F0-9]{4}\-4[A-F0-9]{3}\-[89AB][A-F0-9]{3}\-[A-F0-9]{12})/requests/?$`, // rusqbin-0.2.3: r"/rusqbins/((?i)[A-F0-9]{8}\-[A-F0-9]{4}\-4[A-F0-9]{3}\-[89AB][A-F0-9]{3}\-[A-F0-9]{12})/requests/?$" - `^(nightly|beta|stable)(?:-(\d{4}-\d{2}-\d{2}))?$`, // rust-install-0.0.4: r"^(nightly|beta|stable)(?:-(\d{4}-\d{2}-\d{2}))?$" - "^+(.*)\r\n", // rust_inbox-0.0.5: "^+(.*)\r\n" - `^\* CAPABILITY (.*)\r\n`, // rust_inbox-0.0.5: r"^\* CAPABILITY (.*)\r\n" - `^([a-zA-Z0-9]+) (OK|NO|BAD)(.*)`, // rust_inbox-0.0.5: r"^([a-zA-Z0-9]+) (OK|NO|BAD)(.*)" - `^\* (\d+) EXISTS\r\n`, // rust_inbox-0.0.5: r"^\* (\d+) EXISTS\r\n" - `^\* (\d+) RECENT\r\n`, // rust_inbox-0.0.5: r"^\* (\d+) RECENT\r\n" - `^\* FLAGS (.+)\r\n`, // rust_inbox-0.0.5: r"^\* FLAGS (.+)\r\n" - `^\* OK \[UNSEEN (\d+)\](.*)\r\n`, // rust_inbox-0.0.5: r"^\* OK \[UNSEEN (\d+)\](.*)\r\n" - `^\* OK \[UIDVALIDITY (\d+)\](.*)\r\n`, // rust_inbox-0.0.5: r"^\* OK \[UIDVALIDITY (\d+)\](.*)\r\n" - `^\* OK \[UIDNEXT (\d+)\](.*)\r\n`, // rust_inbox-0.0.5: r"^\* OK \[UIDNEXT (\d+)\](.*)\r\n" - `^\* OK \[PERMANENTFLAGS (.+)\](.*)\r\n`, // rust_inbox-0.0.5: r"^\* OK \[PERMANENTFLAGS (.+)\](.*)\r\n" - `^[a-z]+ (\d+)$`, // rustml-0.0.7: r"^[a-z]+ (\d+)$" - `^[a-z]+ (\d+)$`, // rustml-0.0.7: r"^[a-z]+ (\d+)$" - `^[a-z]+ (\d+)$`, // rustml-0.0.7: r"^[a-z]+ (\d+)$" - `([^\\](\\\\)*)\\[\n\r][[:space:]]*`, // rustfmt-0.10.0: r"([^\\](\\\\)*)\\[\n\r][[:space:]]*" - `(^\s*$)|(^\s*//\s*rustfmt-[^:]+:\s*\S+)`, // rustfmt-core-0.4.0: r"(^\s*$)|(^\s*//\s*rustfmt-[^:]+:\s*\S+)" - "^## `([^`]+)`", // rustfmt-core-0.4.0: r"^## `([^`]+)`" - `([^\\](\\\\)*)\\[\n\r][[:space:]]*`, // rustfmt-core-0.4.0: r"([^\\](\\\\)*)\\[\n\r][[:space:]]*" - `\s;`, // rustfmt-core-0.4.0: r"\s;" - `^(0x)?([:digit:]+)$`, // rust-enum-derive-0.4.0: r"^(0x)?([:digit:]+)$" - `^([:digit:]+)[:space:]*<<[:space:]*([:digit:]+)$`, // rust-enum-derive-0.4.0: r"^([:digit:]+)[:space:]*<<[:space:]*([:digit:]+)$" - `^[:space:]*([[:alnum:]_]+)([:space:]*=[:space:]*([:graph:]+))?[:space:]*,`, // rust-enum-derive-0.4.0: r"^[:space:]*([[:alnum:]_]+)([:space:]*=[:space:]*([:graph:]+))?[:space:]*," - `^#define[:space:]+([:graph:]+)[:space:]+([:graph:]+)`, // rust-enum-derive-0.4.0: r"^#define[:space:]+([:graph:]+)[:space:]+([:graph:]+)" - `^\s*pub mod (.+);$`, // rustsourcebundler-0.2.0: r"^\s*pub mod (.+);$" - `^\s*pub mod (.+);$`, // rustsourcebundler-0.2.0: r"^\s*pub mod (.+);$" - `([^\\](\\\\)*)\\[\n\r][[:space:]]*`, // rustfmt-nightly-0.8.2: r"([^\\](\\\\)*)\\[\n\r][[:space:]]*" - `\s;`, // rustfmt-nightly-0.8.2: r"\s;" - `(?s)(.*?)([ \t\r\n]*)(\{\{(\{?\S?\s*?[\w\.\s]*.*?\s*?\}?)\}\})([ \t\r\n]*)`, // rustache-0.1.0: r"(?s)(.*?)([ \t\r\n]*)(\{\{(\{?\S?\s*?[\w\.\s]*.*?\s*?\}?)\}\})([ \t\r\n]*)" - `_ZN[\$\._[:alnum:]]*`, // rustfilt-0.2.0: r"_ZN[\$\._[:alnum:]]*" - `(?s)(.*?)([ \t\r\n]*)(\{\{(\{?\S?\s*?[\w\.\s]*.*?\s*?\}?)\}\})([ \t\r\n]*)`, // rustache-lists-0.1.2: r"(?s)(.*?)([ \t\r\n]*)(\{\{(\{?\S?\s*?[\w\.\s]*.*?\s*?\}?)\}\})([ \t\r\n]*)" - "(.+)=(.+)", // rural-0.7.3: "(.+)=(.+)" - "(.*):(.+)", // rural-0.7.3: "(.*):(.+)" - "(.+):=(.+)", // rural-0.7.3: "(.+):=(.+)" - "(.*)==(.+)", // rural-0.7.3: "(.*)==(.+)" - `^\[([^\]]+)\]$`, // rusoto_credential-0.11.0: r"^\[([^\]]+)\]$" - "([:blank:]*)$", // rumblebars-0.3.0: "([:blank:]*)$" - "(\r?\n)[:blank:]*(\\{\\{~?[#!/](?:\\}?[^}])*\\}\\})[:blank:]*(:?\r?\n)?\\z", // rumblebars-0.3.0: "(\r?\n)[:blank:]*(\\{\\{~?[#!/](?:\\}?[^}])*\\}\\})[:blank:]*(:?\r?\n)?\\z" - "(\r?\n[:blank:]*)(\\{\\{~?>(?:\\}?[^}])*\\}\\})[:blank:]*(:?\r?\n)?\\z", // rumblebars-0.3.0: "(\r?\n[:blank:]*)(\\{\\{~?>(?:\\}?[^}])*\\}\\})[:blank:]*(:?\r?\n)?\\z" - "((?:[:blank:]|\r?\n)*)(\r?\n)[:blank:]*$", // rumblebars-0.3.0: "((?:[:blank:]|\r?\n)*)(\r?\n)[:blank:]*$" - "^([:blank:]*\r?\n)(.*)", // rumblebars-0.3.0: "^([:blank:]*\r?\n)(.*)" - `(?P<stamp>[\d-]*)_hello`, // diesel_cli-1.3.1: r"(?P<stamp>[\d-]*)_hello" - `(\d+)s`, // dishub-0.1.1: r"(\d+)s" - `\n`, // spreadsheet_textconv-0.1.0: r"\n" - `\r`, // spreadsheet_textconv-0.1.0: r"\r" - `\t`, // spreadsheet_textconv-0.1.0: r"\t" - `DELAY (-?\d+)ms`, // split_aud-0.1.0: r"DELAY (-?\d+)ms" - `Trim\((\d+), ?(\d+)\)`, // split_aud-0.1.0: r"Trim\((\d+), ?(\d+)\)" - `spotify:[a-z]+:[a-zA-Z0-9]+`, // spotrust-0.0.5: r"spotify:[a-z]+:[a-zA-Z0-9]+" - `[^\x00-\x7F]`, // spaceslugs-0.1.0: r"[^\x00-\x7F]" - `[']+`, // spaceslugs-0.1.0: r"[']+" - `\W+`, // spaceslugs-0.1.0: r"\W+" - `[ ]+`, // spaceslugs-0.1.0: r"[ ]+" - "PHPSESSID=([0-9a-f]+)", // space_email_api-0.1.1: "PHPSESSID=([0-9a-f]+)" - "[^0-9.,]", // lorikeet-0.7.0: "[^0-9.,]" - `^(?:\b|(-)?)(\p{L})?((?:(?:\d{1,3}[\.,])+\d{3})|\d+)(?:[\.,](\d{2}))?\b$`, // claude-0.3.0: r"^(?:\b|(-)?)(\p{Currency_Symbol})?((?:(?:\d{1,3}[\.,])+\d{3})|\d+)(?:[\.,](\d{2}))?\b$" - `<%=\s*(.+?)\s*%>`, // clam-0.1.6: r"<%=\s*(.+?)\s*%>" - `(\s)`, // classifier-0.0.3: r"(\s)" - `(-----BEGIN .*-----\n)((?:(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)*\n)+)(-----END .*-----)`, // click-0.3.2: r"(-----BEGIN .*-----\n)((?:(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)*\n)+)(-----END .*-----)" - `-----BEGIN PRIVATE KEY-----`, // click-0.3.2: r"-----BEGIN PRIVATE KEY-----" - `#([A-Z3a-z]*):(.*)`, // ultrastar-txt-0.1.2: r"#([A-Z3a-z]*):(.*)" - "^-\\s?(-?[0-9]+)\\s*$", // ultrastar-txt-0.1.2: "^-\\s?(-?[0-9]+)\\s*$" - "^-\\s?(-?[0-9]+)\\s+(-?[0-9]+)", // ultrastar-txt-0.1.2: "^-\\s?(-?[0-9]+)\\s+(-?[0-9]+)" - "^(.)\\s*(-?[0-9]+)\\s+(-?[0-9]+)\\s+(-?[0-9]+)\\s?(.*)", // ultrastar-txt-0.1.2: "^(.)\\s*(-?[0-9]+)\\s+(-?[0-9]+)\\s+(-?[0-9]+)\\s?(.*)" - "^P\\s?(-?[0-9]+)", // ultrastar-txt-0.1.2: "^P\\s?(-?[0-9]+)" - `^template\.add($|\..+$)`, // db-accelerate-2.0.0: r"^template\.add($|\..+$)" - `^template\.sub($|\..+$)`, // db-accelerate-2.0.0: r"^template\.sub($|\..+$)" - `(\d+)([cegps])`, // sterling-0.3.0: r"(\d+)([cegps])" - `[^\w]`, // stache-0.2.0: r"[^\w]" - "\"([<>]?)([xcbB\\?hHiIlLqQfdspP]*)\"", // strukt-0.1.0: "\"([<>]?)([xcbB\\?hHiIlLqQfdspP]*)\"" - `^STEAM_([0-4]):([0-1]):([0-9]{1,10})$`, // steamid-ng-0.3.1: r"^STEAM_([0-4]):([0-1]):([0-9]{1,10})$" - `^\[([AGMPCgcLTIUai]):([0-4]):([0-9]{1,10})(:([0-9]+))?\]$`, // steamid-ng-0.3.1: r"^\[([AGMPCgcLTIUai]):([0-4]):([0-9]{1,10})(:([0-9]+))?\]$" - `^\w+`, // strscan-0.1.1: r"^\w+" - `^\s+`, // strscan-0.1.1: r"^\s+" - `^\w+`, // strscan-0.1.1: r"^\w+" - `^\s+`, // strscan-0.1.1: r"^\s+" - `^(\w+)\s+`, // strscan-0.1.1: r"^(\w+)\s+" - `^([a-zA-Z0-9\.-]+)(?:\s+(\d+))$`, // tk-carbon-0.2.0: r"^([a-zA-Z0-9\.-]+)(?:\s+(\d+))$" - `^([a-zA-Z0-9\.-]+)(?:\s+(\d+))$`, // tk-carbon-0.2.0: r"^([a-zA-Z0-9\.-]+)(?:\s+(\d+))$" - `extern\s+crate\s+([a-z0-9_]+)\s*;(\s*//(.+))?`, // evalrs-0.0.10: r"extern\s+crate\s+([a-z0-9_]+)\s*;(\s*//(.+))?" - `(?m)^# `, // evalrs-0.0.10: r"(?m)^# " - `(?m)^\s*fn +main *\( *\)`, // evalrs-0.0.10: r"(?m)^\s*fn +main *\( *\)" - `(extern\s+crate\s+[a-z0-9_]+\s*;)`, // evalrs-0.0.10: r"(extern\s+crate\s+[a-z0-9_]+\s*;)" - "(.*)_t([0-9]+)", // gate_build-0.5.0: "(.*)_t([0-9]+)" - `[^\P{P}-]|\s+-\s+`, // rake-0.1.1: r"[^\P{P}-]|\s+-\s+" - `^.*(?:(?:youtu\.be/|v/|vi/|u/w/|embed/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*`, // rafy-0.2.1: r"^.*(?:(?:youtu\.be/|v/|vi/|u/w/|embed/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*" - `^(?P<protocol>.*?)://(?P<public_key>.*?):(?P<secret_key>.*?)@(?P<host>.*?)/(?P<path>.*/)?(?P<project_id>.*)$`, // raven-0.2.1: r"^(?P<protocol>.*?)://(?P<public_key>.*?):(?P<secret_key>.*?)@(?P<host>.*?)/(?P<path>.*/)?(?P<project_id>.*)$" - `\{[[:space:]]*[^{}]*[[:space:]]*\}`, // rargs-0.2.0: r"\{[[:space:]]*[^{}]*[[:space:]]*\}" - `^\{[[:space:]]*(?P<name>[[:word:]]*)[[:space:]]*\}$`, // rargs-0.2.0: r"^\{[[:space:]]*(?P<name>[[:word:]]*)[[:space:]]*\}$" - `^\{[[:space:]]*(?P<num>-?\d+)[[:space:]]*\}$`, // rargs-0.2.0: r"^\{[[:space:]]*(?P<num>-?\d+)[[:space:]]*\}$" - `^\{(?P<left>-?\d*)?\.\.(?P<right>-?\d*)?(?::(?P<sep>.*))?\}$`, // rargs-0.2.0: r"^\{(?P<left>-?\d*)?\.\.(?P<right>-?\d*)?(?::(?P<sep>.*))?\}$" - `(.*?)[[:space:]]+|(.*?)$`, // rargs-0.2.0: r"(.*?)[[:space:]]+|(.*?)$" - `[a-zA-Z0-9]{8}`, // indradb-lib-0.15.0: r"[a-zA-Z0-9]{8}" - `::`, // fungi-lang-0.1.50: r"::" - "/hello/(?P<name>[a-zA-Z]+)", // nickel-0.10.1: "/hello/(?P<name>[a-zA-Z]+)" - "/hello/(?P<name>[a-zA-Z]+)", // nickel-0.10.1: "/hello/(?P<name>[a-zA-Z]+)" - `\{(\w+)\}`, // pact_verifier-0.4.0: r"\{(\w+)\}" - "application/.*json", // pact_matching-0.4.1: "application/.*json" - "application/json.*", // pact_matching-0.4.1: "application/json.*" - "application/.*xml", // pact_matching-0.4.1: "application/.*xml" - "([\"'\\(\\[\\{{<\u201c])(\\s*)(.+?)(\\s*)([\"'\\)\\]\\}}>\u201d])", // pangu-0.2.0: "([\"'\\(\\[\\{{<\u{201c}])(\\s*)(.+?)(\\s*)([\"'\\)\\]\\}}>\u{201d}])" - "([\\(\\[\\{{<\u201c]+)(\\s*)(.+?)(\\s*)([\\)\\]\\}}>\u201d]+)", // pangu-0.2.0: "([\\(\\[\\{{<\u{201c}]+)(\\s*)(.+?)(\\s*)([\\)\\]\\}}>\u{201d}]+)" - `\{-[\s\S]*?-\}`, // parser-haskell-0.2.0: r"\{-[\s\S]*?-\}" - `(?m);+\s*$`, // parser-haskell-0.2.0: r"(?m);+\s*$" - `(?m)^#(if|ifn?def|endif|else|include|elif).*`, // parser-haskell-0.2.0: r"(?m)^#(if|ifn?def|endif|else|include|elif).*" - `'([^'\\]|\\[A-Z]{1,3}|\\.)'`, // parser-haskell-0.2.0: r"'([^'\\]|\\[A-Z]{1,3}|\\.)'" - `forall\s+(.*?)\.`, // parser-haskell-0.2.0: r"forall\s+(.*?)\." - "\\s{2,}", // html2md-0.2.1: "\\s{2,}" - "\\n{2,}", // html2md-0.2.1: "\\n{2,}" - "(?m)(\\S) $", // html2md-0.2.1: "(?m)(\\S) $" - "(?m)^[-*] ", // html2md-0.2.1: "(?m)^[-*] " - `#.*$`, // ovpnfile-0.1.2: r"#.*$" - `^<(\S+)>`, // ovpnfile-0.1.2: r"^<(\S+)>" - `^</(\S+)>`, // ovpnfile-0.1.2: r"^</(\S+)>" - `#([:xdigit:]{2})([:xdigit:]{2})([:xdigit:]{2})`, // screenruster-saver-fractal-0.1.1: r"#([:xdigit:]{2})([:xdigit:]{2})([:xdigit:]{2})" - `rgb\((?: *(\d{1,3}),)(?: *(\d{1,3}),)(?: *(\d{1,3}))\)`, // scarlet-0.2.2: r"rgb\((?: *(\d{1,3}),)(?: *(\d{1,3}),)(?: *(\d{1,3}))\)" - `^([\w:]+)<(.+)>$`, // cpp_to_rust_generator-0.2.0: r"^([\w:]+)<(.+)>$" - `^type-parameter-(\d+)-(\d+)$`, // cpp_to_rust_generator-0.2.0: r"^type-parameter-(\d+)-(\d+)$" - `^([\w~]+)<[^<>]+>$`, // cpp_to_rust_generator-0.2.0: r"^([\w~]+)<[^<>]+>$" - `(signals|Q_SIGNALS)\s*:`, // cpp_to_rust_generator-0.2.0: r"(signals|Q_SIGNALS)\s*:" - `(slots|Q_SLOTS)\s*:`, // cpp_to_rust_generator-0.2.0: r"(slots|Q_SLOTS)\s*:" - `(public|protected|private)\s*:`, // cpp_to_rust_generator-0.2.0: r"(public|protected|private)\s*:" - `^([\w:]+)<(.+)>$`, // cpp_to_rust-0.5.3: r"^([\w:]+)<(.+)>$" - `^type-parameter-(\d+)-(\d+)$`, // cpp_to_rust-0.5.3: r"^type-parameter-(\d+)-(\d+)$" - `^([\w~]+)<[^<>]+>$`, // cpp_to_rust-0.5.3: r"^([\w~]+)<[^<>]+>$" - `(signals|Q_SIGNALS)\s*:`, // cpp_to_rust-0.5.3: r"(signals|Q_SIGNALS)\s*:" - `(slots|Q_SLOTS)\s*:`, // cpp_to_rust-0.5.3: r"(slots|Q_SLOTS)\s*:" - `(public|protected|private)\s*:`, // cpp_to_rust-0.5.3: r"(public|protected|private)\s*:" - "(\\d{2}\\.\\d{2}\\.\\d{2}) (\\d{2}:\\d{2}:\\d{2}) (.*)", // fritzbox_logs-0.2.0: "(\\d{2}\\.\\d{2}\\.\\d{2}) (\\d{2}:\\d{2}:\\d{2}) (.*)" - `mxc://(?P<server>[^/]+)/(?P<media>.+)`, // fractal-matrix-api-3.29.0: r"mxc://(?P<server>[^/]+)/(?P<media>.+)" - `^api-[a-zA-Z0-9]{32}$`, // smtp2go-0.1.4: r"^api-[a-zA-Z0-9]{32}$" - `^[-a-zA-Z0-9_=@,.;]+$`, // pusher-0.3.1: r"^[-a-zA-Z0-9_=@,.;]+$" - `\A\d+\.\d+\z`, // pusher-0.3.1: r"\A\d+\.\d+\z" - `^\.(.+?) +?(.+)$`, // bakervm-0.9.0: r"^\.(.+?) +?(.+)$" - `^\.([^\s]+)$`, // bakervm-0.9.0: r"^\.([^\s]+)$" - `^include! +([^\s]+)$`, // bakervm-0.9.0: r"^include! +([^\s]+)$" - `^@(\d+)$`, // bakervm-0.9.0: r"^@(\d+)$" - `^true|false$`, // bakervm-0.9.0: r"^true|false$" - `^(-?\d+)?\.[0-9]+$`, // bakervm-0.9.0: r"^(-?\d+)?\.[0-9]+$" - `^(-?\d+)?$`, // bakervm-0.9.0: r"^(-?\d+)?$" - `^#([0-9abcdefABCDEF]{6})$`, // bakervm-0.9.0: r"^#([0-9abcdefABCDEF]{6})$" - `^'(.)'$`, // bakervm-0.9.0: r"^'(.)'$" - `^\$vi\((\d+)\)$`, // bakervm-0.9.0: r"^\$vi\((\d+)\)$" - `^\$key\((\d+)\)$`, // bakervm-0.9.0: r"^\$key\((\d+)\)$" - "(?P<type>[A-Z^']+) (?P<route>[^']+) HTTP/(?P<http>[^']+)", // banana-0.0.2: "(?P<type>[A-Z^']+) (?P<route>[^']+) HTTP/(?P<http>[^']+)" - `[A-F0-9]{8}`, // serial-key-2.0.0: r"[A-F0-9]{8}" - // serde-hjson-0.8.1: "[\\\\\"\x00-\x1f\x7f-\u{9f}\u{00ad}\u{0600}-\u{0604}\u{070f}\u{17b4}\u{17b5}\u{200c}-\u{200f}\u{2028}-\u{202f}\u{2060}-\u{206f}\u{feff}\u{fff0}-\u{ffff}]" - // serde-hjson-0.8.1: "[\x00-\x1f\x7f-\u{9f}\u{00ad}\u{0600}-\u{0604}\u{070f}\u{17b4}\u{17b5}\u{200c}-\u{200f}\u{2028}-\u{202f}\u{2060}-\u{206f}\u{feff}\u{fff0}-\u{ffff}]" - // serde-hjson-0.8.1: "'''|[\x00-\x09\x0b\x0c\x0e-\x1f\x7f-\u{9f}\u{00ad}\u{0600}-\u{0604}\u{070f}\u{17b4}\u{17b5}\u{200c}-\u{200f}\u{2028}-\u{202f}\u{2060}-\u{206f}\u{feff}\u{fff0}-\u{ffff}]" - `/todos/(?P<id>\d+)`, // serde-odbc-0.1.0: r"/todos/(?P<id>\d+)" - `^(?:_<)?([a-zA-Z0-9_]+?)(?:\.\.|::)`, // sentry-0.6.0: r"^(?:_<)?([a-zA-Z0-9_]+?)(?:\.\.|::)" - `[^a-zA-Z0 -]+`, // sentiment-0.1.1: r"[^a-zA-Z0 -]+" - ` {2,}`, // sentiment-0.1.1: r" {2,}" - `(?m)//.*`, // verilog-0.0.1: r"(?m)//.*" - "(?P<robot>C3PO)", // verex-0.2.2: "(?P<robot>C3PO)" - ">|<|\"|&", // handlebars-0.32.4: ">|<|\"|&" - `^\w+-\w+-[0123456789]{4}$`, // haikunator-0.1.2: r"^\w+-\w+-[0123456789]{4}$" - `^\w+@\w+@[0123456789]{4}$`, // haikunator-0.1.2: r"^\w+@\w+@[0123456789]{4}$" - `^\w+-\w+-[0123456789abcdef]{4}$`, // haikunator-0.1.2: r"^\w+-\w+-[0123456789abcdef]{4}$" - `^\w+-\w+-[0123456789忠犬ハチ公]{10}$`, // haikunator-0.1.2: r"^\w+-\w+-[0123456789忠犬ハチ公]{10}$" - `^\w+-\w+$`, // haikunator-0.1.2: r"^\w+-\w+$" - `^\w+-\w+-[foo]{4}$`, // haikunator-0.1.2: r"^\w+-\w+-[foo]{4}$" - `^\w+-\w+-[0123456789忠犬ハチ公]{5}$`, // haikunator-0.1.2: r"^\w+-\w+-[0123456789忠犬ハチ公]{5}$" - `(.*)`, // bobbin-cli-0.8.3: r"(.*)" - `rustc (.*)`, // bobbin-cli-0.8.3: r"rustc (.*)" - `cargo (.*)`, // bobbin-cli-0.8.3: r"cargo (.*)" - `xargo (.*)\n`, // bobbin-cli-0.8.3: r"xargo (.*)\n" - `Open On-Chip Debugger (.*)`, // bobbin-cli-0.8.3: r"Open On-Chip Debugger (.*)" - `arm-none-eabi-gcc \(GNU Tools for ARM Embedded Processors[^\)]*\) (.*)`, // bobbin-cli-0.8.3: r"arm-none-eabi-gcc \(GNU Tools for ARM Embedded Processors[^\)]*\) (.*)" - `(?m).*\nBasic Open Source SAM-BA Application \(BOSSA\) Version (.*)\n`, // bobbin-cli-0.8.3: r"(?m).*\nBasic Open Source SAM-BA Application \(BOSSA\) Version (.*)\n" - `(?m)SEGGER J-Link Commander (.*)\n`, // bobbin-cli-0.8.3: r"(?m)SEGGER J-Link Commander (.*)\n" - `(?m)Teensy Loader, Command Line, Version (.*)\n`, // bobbin-cli-0.8.3: r"(?m)Teensy Loader, Command Line, Version (.*)\n" - `dfu-util (.*)\n`, // bobbin-cli-0.8.3: r"dfu-util (.*)\n" - `^/static/[\w.]+$`, // borsholder-0.9.1: r"^/static/[\w.]+$" - `^/timeline/([0-9]+)$`, // borsholder-0.9.1: r"^/timeline/([0-9]+)$" - "\u001B\\[[\\d;]*[^\\d;]", // fblog-1.0.1: "\u{001B}\\[[\\d;]*[^\\d;]" - "\u001B\\[[\\d;]*[^\\d;]", // fblog-1.0.1: "\u{001B}\\[[\\d;]*[^\\d;]" - `^\[\d+\]$`, // toml-query-0.6.0: r"^\[\d+\]$" - ` (?P<key>[^\s]+):(?P<value>[^\s^/]+)`, // todo-txt-1.1.0: r" (?P<key>[^\s]+):(?P<value>[^\s^/]+)" - `\band\b`, // findr-0.1.5: r"\band\b" - `\bor\b`, // findr-0.1.5: r"\bor\b" - `\bnot\b`, // findr-0.1.5: r"\bnot\b" - `.*?\.(a|la|lo|o|ll|keter|bc|dyn_o|out|d|rlib|crate|min\.js|hi|dyn_hi|S|jsexe|webapp|js\.externs|ibc|toc|aux|fdb_latexmk|fls|egg-info|whl|js_a|js_hi|jld|ji|js_o|so.*|dump-.*|vmb|crx|orig|elmo|elmi|pyc|mod|p_hi|p_o|prof|tix)$`, // file-sniffer-3.0.1: r".*?\.(a|la|lo|o|ll|keter|bc|dyn_o|out|d|rlib|crate|min\.js|hi|dyn_hi|S|jsexe|webapp|js\.externs|ibc|toc|aux|fdb_latexmk|fls|egg-info|whl|js_a|js_hi|jld|ji|js_o|so.*|dump-.*|vmb|crx|orig|elmo|elmi|pyc|mod|p_hi|p_o|prof|tix)$" - `.*?\.(stats|conf|h|cache.*|dat|pc|info)$`, // file-sniffer-3.0.1: r".*?\.(stats|conf|h|cache.*|dat|pc|info)$" - `.*?\.(exe|a|la|o|ll|keter|bc|dyn_o|out|d|rlib|crate|min\.js|hi|dyn_hi|jsexe|webapp|js\.externs|ibc|toc|aux|fdb_latexmk|fls|egg-info|whl|js_a|js_hi|jld|ji|js_o|so.*|dump-.*|vmb|crx|orig|elmo|elmi|pyc|mod|p_hi|p_o|prof|tix)$`, // file-sniffer-3.0.1: r".*?\.(exe|a|la|o|ll|keter|bc|dyn_o|out|d|rlib|crate|min\.js|hi|dyn_hi|jsexe|webapp|js\.externs|ibc|toc|aux|fdb_latexmk|fls|egg-info|whl|js_a|js_hi|jld|ji|js_o|so.*|dump-.*|vmb|crx|orig|elmo|elmi|pyc|mod|p_hi|p_o|prof|tix)$" - `.*?\.(stats|conf|h|cache.*)$`, // file-sniffer-3.0.1: r".*?\.(stats|conf|h|cache.*)$" - `(\.git|\.pijul|_darcs|\.hg)$`, // file-sniffer-3.0.1: r"(\.git|\.pijul|_darcs|\.hg)$" - "test", // file_logger-0.1.0: "test" - `foo`, // file_scanner-0.2.0: r"foo" - `a+b`, // file_scanner-0.2.0: r"a+b" - `a[ab]*b`, // file_scanner-0.2.0: r"a[ab]*b" - `\s+`, // file_scanner-0.2.0: r"\s+" - `\s+`, // file_scanner-0.2.0: r"\s+" - `^\s*([^\s]+) %cellsplit<\d+>$`, // cellsplit-0.2.1: r"^\s*([^\s]+) %cellsplit<\d+>$" - `^\s*([^\s]+) %cellsplit<\d+>$`, // cellsplit-0.2.1: r"^\s*([^\s]+) %cellsplit<\d+>$" - `^[+\-]?[0-9]+`, // aterm-0.20.0: r"^[+\-]?[0-9]+" - `^[+\-]?[0-9]+\.[0-9]*([eE][+\-]?[0-9]+)?`, // aterm-0.20.0: r"^[+\-]?[0-9]+\.[0-9]*([eE][+\-]?[0-9]+)?" - `^[*] OK`, // atarashii_imap-0.3.0: r"^[*] OK" - `FLAGS\s\((.+)\)`, // atarashii_imap-0.3.0: r"FLAGS\s\((.+)\)" - `\[PERMANENTFLAGS\s\((.+)\)\]`, // atarashii_imap-0.3.0: r"\[PERMANENTFLAGS\s\((.+)\)\]" - `\[UIDVALIDITY\s(\d+)\]`, // atarashii_imap-0.3.0: r"\[UIDVALIDITY\s(\d+)\]" - `(\d+)\sEXISTS`, // atarashii_imap-0.3.0: r"(\d+)\sEXISTS" - `(\d+)\sRECENT`, // atarashii_imap-0.3.0: r"(\d+)\sRECENT" - `\[UNSEEN\s(\d+)\]`, // atarashii_imap-0.3.0: r"\[UNSEEN\s(\d+)\]" - `\[UIDNEXT\s(\d+)\]`, // atarashii_imap-0.3.0: r"\[UIDNEXT\s(\d+)\]" - `\\(\{|\})`, // editorconfig-1.0.0: r"\\(\{|\})" - `(^|[^\\])\\\|`, // editorconfig-1.0.0: r"(^|[^\\])\\\|" - `\[([^\]]*)$`, // editorconfig-1.0.0: r"\[([^\]]*)$" - `\[(.*/.*)\]`, // editorconfig-1.0.0: r"\[(.*/.*)\]" - `\{(-?\d+\\\.\\\.-?\d+)\}`, // editorconfig-1.0.0: r"\{(-?\d+\\\.\\\.-?\d+)\}" - `\{([^,]+)\}`, // editorconfig-1.0.0: r"\{([^,]+)\}" - `\{(([^\}].*)?(,|\|)(.*[^\\])?)\}`, // editorconfig-1.0.0: r"\{(([^\}].*)?(,|\|)(.*[^\\])?)\}" - `^/`, // editorconfig-1.0.0: r"^/" - `(^|[^\\])(\{|\})`, // editorconfig-1.0.0: r"(^|[^\\])(\{|\})" - "^#!.*\n", // edmunge-1.0.0: "^#!.*\n" - `\\N\{(.*?)(?:\}|$)`, // unicode_names2_macros-0.2.0: r"\\N\{(.*?)(?:\}|$)" - `^--- (?P<filename>[^\t\n]+)(?:\t(?P<timestamp>[^\n]+))?`, // unidiff-0.2.1: r"^--- (?P<filename>[^\t\n]+)(?:\t(?P<timestamp>[^\n]+))?" - `^\+\+\+ (?P<filename>[^\t\n]+)(?:\t(?P<timestamp>[^\n]+))?`, // unidiff-0.2.1: r"^\+\+\+ (?P<filename>[^\t\n]+)(?:\t(?P<timestamp>[^\n]+))?" - `^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@[ ]?(.*)`, // unidiff-0.2.1: r"^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@[ ]?(.*)" - `^(?P<line_type>[- \n\+\\]?)(?P<value>.*)`, // unidiff-0.2.1: r"^(?P<line_type>[- \n\+\\]?)(?P<value>.*)" - "/?(?P<zoom>[0-9]?[0-9])/(?P<x>[0-9]{1,10})/(?P<y>[0-9]{1,10})(\\.[a-zA-Z]{3,4})?$", // slippy-map-tiles-0.13.1: "/?(?P<zoom>[0-9]?[0-9])/(?P<x>[0-9]{1,10})/(?P<y>[0-9]{1,10})(\\.[a-zA-Z]{3,4})?$" - `^(?P<minlon>-?[0-9]{1,3}(\.[0-9]{1,10})?) (?P<minlat>-?[0-9]{1,3}(\.[0-9]{1,10})?) (?P<maxlon>-?[0-9]{1,3}(\.[0-9]{1,10})?) (?P<maxlat>-?[0-9]{1,3}(\.[0-9]{1,10})?)$`, // slippy-map-tiles-0.13.1: r"^(?P<minlon>-?[0-9]{1,3}(\.[0-9]{1,10})?) (?P<minlat>-?[0-9]{1,3}(\.[0-9]{1,10})?) (?P<maxlon>-?[0-9]{1,3}(\.[0-9]{1,10})?) (?P<maxlat>-?[0-9]{1,3}(\.[0-9]{1,10})?)$" - `^(?P<minlon>-?[0-9]{1,3}(\.[0-9]{1,10})?),(?P<minlat>-?[0-9]{1,3}(\.[0-9]{1,10})?),(?P<maxlon>-?[0-9]{1,3}(\.[0-9]{1,10})?),(?P<maxlat>-?[0-9]{1,3}(\.[0-9]{1,10})?)$`, // slippy-map-tiles-0.13.1: r"^(?P<minlon>-?[0-9]{1,3}(\.[0-9]{1,10})?),(?P<minlat>-?[0-9]{1,3}(\.[0-9]{1,10})?),(?P<maxlon>-?[0-9]{1,3}(\.[0-9]{1,10})?),(?P<maxlat>-?[0-9]{1,3}(\.[0-9]{1,10})?)$" - `^https?://(.+?):1400/xml`, // sonos-0.1.2: r"^https?://(.+?):1400/xml" - `^[a-z]{2}$`, // validator_derive-0.7.0: r"^[a-z]{2}$" - `[a-z]{2}`, // validator_derive-0.7.0: r"[a-z]{2}" - `[a-z]{2}`, // validator_derive-0.7.0: r"[a-z]{2}" - `one of \d+ options`, // nginx-config-0.8.0: r"one of \d+ options" - `[\s,]`, // waltz-0.4.0: r"[\s,]" - `^aws_access_key_id = (.*)`, // warheadhateus-0.2.1: r"^aws_access_key_id = (.*)" - `^aws_secret_access_key = (.*)`, // warheadhateus-0.2.1: r"^aws_secret_access_key = (.*)" - `^aws_access_key_id = (.*)`, // warheadhateus-0.2.1: r"^aws_access_key_id = (.*)" - `^aws_secret_access_key = (.*)`, // warheadhateus-0.2.1: r"^aws_secret_access_key = (.*)" - "([\u4E00-\u9FD5a-zA-Z0-9+#&\\._%]+)", // jieba-rs-0.2.2: r"([\u{4E00}-\u{9FD5}a-zA-Z0-9+#&\._%]+)" - `(\r\n|\s)`, // jieba-rs-0.2.2: r"(\r\n|\s)" - "([\u4E00-\u9FD5]+)", // jieba-rs-0.2.2: "([\u{4E00}-\u{9FD5}]+)" - `[^a-zA-Z0-9+#\n]`, // jieba-rs-0.2.2: r"[^a-zA-Z0-9+#\n]" - "([\u4E00-\u9FD5]+)", // jieba-rs-0.2.2: r"([\u{4E00}-\u{9FD5}]+)" - `([a-zA-Z0-9]+(?:.\d+)?%?)`, // jieba-rs-0.2.2: r"([a-zA-Z0-9]+(?:.\d+)?%?)" - `Span\([0-9 ,]*\)`, // lalrpop-0.15.2: r"Span\([0-9 ,]*\)" - `Span\([0-9 ,]*\)`, // lalrpop-snap-0.15.2: r"Span\([0-9 ,]*\)" - `[\S]+`, // nlp-tokenize-0.1.0: r"[\S]+" - "[[:xdigit:]][70]", // kbgpg-0.1.2: "[[:xdigit:]][70]" - `^((?P<address>.*):)?(?P<port>\d+)$`, // cdbd-0.1.1: r"^((?P<address>.*):)?(?P<port>\d+)$" - `[\w\s=+-/]+\((\{(.|\n)*\})\);?`, // mbutiles-0.1.1: r"[\w\s=+-/]+\((\{(.|\n)*\})\);?" - `^-\d+(?:ms|s|m|h|d|w|y)?$`, // extrahop-0.2.5: r"^-\d+(?:ms|s|m|h|d|w|y)?$" - "^((?:.*)-)?ss(0|[1-9][0-9]*)\\.pip$", // pippin-0.1.0: "^((?:.*)-)?ss(0|[1-9][0-9]*)\\.pip$" - "^((?:.*)-)?ss(0|[1-9][0-9]*)-cl(0|[1-9][0-9]*)\\.piplog$", // pippin-0.1.0: "^((?:.*)-)?ss(0|[1-9][0-9]*)-cl(0|[1-9][0-9]*)\\.piplog$" - "^((?:.*)-)?ss(0|[1-9][0-9]*)\\.pip$", // pippin-0.1.0: "^((?:.*)-)?ss(0|[1-9][0-9]*)\\.pip$" - "^((?:.*)-)?ss(0|[1-9][0-9]*)-cl(0|[1-9][0-9]*)\\.piplog$", // pippin-0.1.0: "^((?:.*)-)?ss(0|[1-9][0-9]*)-cl(0|[1-9][0-9]*)\\.piplog$" - "^.*pn(0|[1-9][0-9]*)(-ss(0|[1-9][0-9]*)(\\.pip|-cl(0|[1-9][0-9]*)\\.piplog))?$", // pippin-0.1.0: "^.*pn(0|[1-9][0-9]*)(-ss(0|[1-9][0-9]*)(\\.pip|-cl(0|[1-9][0-9]*)\\.piplog))?$" - "^(.*)-ss(?:0|[1-9][0-9]*)(?:\\.pip|-cl(?:0|[1-9][0-9]*)\\.piplog)$", // pippin-0.1.0: "^(.*)-ss(?:0|[1-9][0-9]*)(?:\\.pip|-cl(?:0|[1-9][0-9]*)\\.piplog)$" - `(?i)[āáǎàēéěèōóǒòīíǐìūúǔùüǘǚǜńň]`, // pinyin-0.3.0: r"(?i)[āáǎàēéěèōóǒòīíǐìūúǔùüǘǚǜńň]" - `([aeoiuvnm])([0-4])$`, // pinyin-0.3.0: r"([aeoiuvnm])([0-4])$" - `(?P<value>\d+)(?P<units>[a-z])`, // duration-parser-0.2.0: r"(?P<value>\d+)(?P<units>[a-z])" - `^\d+\D?$`, // dutree-0.2.7: r"^\d+\D?$" - `^[A-Za-z0-9]*$`, // djangohashers-0.3.0: r"^[A-Za-z0-9]*$" - `^[A-Z][A-Z0-9]{2,}$`, // rtag-0.3.5: r"^[A-Z][A-Z0-9]{2,}$" - `^http://www\.emusic\.com`, // rtag-0.3.5: r"^http://www\.emusic\.com" - `^[A-Z][A-Z0-9]{2,}`, // rtag-0.3.5: r"^[A-Z][A-Z0-9]{2,}" - `(^[\x{0}|\x{feff}|\x{fffe}]*|[\x{0}|\x{feff}|\x{fffe}]*$)`, // rtag-0.3.5: r"(^[\x{0}|\x{feff}|\x{fffe}]*|[\x{0}|\x{feff}|\x{fffe}]*$)" - `(\d+)[xX](\d+)`, // rtow-0.1.0: r"(\d+)[xX](\d+)" - `\$([a-zA-Z0-9_]+)`, // pleingres-sql-plugin-0.1.0: r"\$([a-zA-Z0-9_]+)" - "[\\n]+", // dono-2.0.0: "[\\n]+" - "(?m)^\\n", // dono-2.0.0: "(?m)^\\n" - "(?m)^\\n", // dono-2.0.0: "(?m)^\\n" - `^[0-9A-Za-z\+/]{43}=\.ed25519$`, // ssb-common-0.3.0: r"^[0-9A-Za-z\+/]{43}=\.ed25519$" - `^[0-9A-Za-z\+/]{86}==\.ed25519$`, // ssb-common-0.3.0: r"^[0-9A-Za-z\+/]{86}==\.ed25519$" - `^[0-9A-Za-z\+/]{43}=\.sha256$`, // ssb-common-0.3.0: r"^[0-9A-Za-z\+/]{43}=\.sha256$" - `^(?P<major>\d+)\.(?P<minor>\d+)(?:\.(?P<patch>\d+))?(?:(?P<pre0>[a-z]+)(?P<pre1>\d*))?$`, // mozversion-0.1.3: r"^(?P<major>\d+)\.(?P<minor>\d+)(?:\.(?P<patch>\d+))?(?:(?P<pre0>[a-z]+)(?P<pre1>\d*))?$" - `^(\d+)\.(\d+)$`, // monger-0.5.6: r"^(\d+)\.(\d+)$" - `^[rv]2\.6`, // mongo_rub-0.0.2: r"^[rv]2\.6" - "body value", // flow-0.3.5: "body value" - "start marker", // flow-0.3.5: "start marker" - "end marker", // flow-0.3.5: "end marker" - "body value", // flow-0.3.5: "body value" - "^([A-Za-z/ ]+): (.*)", // vobsub-0.2.3: "^([A-Za-z/ ]+): (.*)" - `#([^\s=]+)*`, // voidmap-1.1.2: r"#([^\s=]+)*" - `#(\S+)*`, // voidmap-1.1.2: r"#(\S+)*" - `#prio=(\d+)`, // voidmap-1.1.2: r"#prio=(\d+)" - `\[(\S+)\]`, // voidmap-1.1.2: r"\[(\S+)\]" - `#limit=(\d+)`, // voidmap-1.1.2: r"#limit=(\d+)" - `#tagged=(\S+)`, // voidmap-1.1.2: r"#tagged=(\S+)" - `#rev\b`, // voidmap-1.1.2: r"#rev\b" - `#done\b`, // voidmap-1.1.2: r"#done\b" - `#open\b`, // voidmap-1.1.2: r"#open\b" - `#since=(\S+)`, // voidmap-1.1.2: r"#since=(\S+)" - `#until=(\S+)`, // voidmap-1.1.2: r"#until=(\S+)" - `#plot=(\S+)`, // voidmap-1.1.2: r"#plot=(\S+)" - `#n=(\d+)`, // voidmap-1.1.2: r"#n=(\d+)" - `(\S+)`, // voidmap-1.1.2: r"(\S+)" - `(?P<y>\d+)y`, // voidmap-1.1.2: r"(?P<y>\d+)y" - `(?P<m>\d+)m`, // voidmap-1.1.2: r"(?P<m>\d+)m" - `(?P<w>\d+)w`, // voidmap-1.1.2: r"(?P<w>\d+)w" - `(?P<d>\d+)d`, // voidmap-1.1.2: r"(?P<d>\d+)d" - `(?P<h>\d+)h`, // voidmap-1.1.2: r"(?P<h>\d+)h" - `C-(.)`, // voidmap-1.1.2: r"C-(.)" - `^\.\./qt[^/]+/`, // qt_generator-0.2.0: r"^\.\./qt[^/]+/" - "(href|src)=\"([^\"]*)\"", // qt_generator-0.2.0: "(href|src)=\"([^\"]*)\"" - `[01]{5}`, // kryptos-0.6.1: r"[01]{5}" - "data_batch_[1-5].bin", // cifar_10_loader-0.2.0: "data_batch_[1-5].bin" - "test_batch.bin", // cifar_10_loader-0.2.0: "test_batch.bin" - `^\d+.\d+s$`, // circadian-0.6.0: r"^\d+.\d+s$" - `^\d+:\d+$`, // circadian-0.6.0: r"^\d+:\d+$" - `^\d+:\d+m$`, // circadian-0.6.0: r"^\d+:\d+m$" - `!!`, // cicada-0.8.1: r"!!" - "^([^`]*)`([^`]+)`(.*)$", // cicada-0.8.1: r"^([^`]*)`([^`]+)`(.*)$" - `\*+`, // cicada-0.8.1: r"\*+" - `([^\$]*)\$\{?([A-Za-z0-9\?\$_]+)\}?(.*)`, // cicada-0.8.1: r"([^\$]*)\$\{?([A-Za-z0-9\?\$_]+)\}?(.*)" - `^ *alias +([a-zA-Z0-9_\.-]+)=(.*)$`, // cicada-0.8.1: r"^ *alias +([a-zA-Z0-9_\.-]+)=(.*)$" - `hi`, // vterm-sys-0.1.0: r"hi" - `.*?\t`, // skim-0.5.0: r".*?\t" - `.*?[\t ]`, // skim-0.5.0: r".*?[\t ]" - `(\{-?[0-9.,q]*?})`, // skim-0.5.0: r"(\{-?[0-9.,q]*?})" - `[ \t\n]+`, // skim-0.5.0: r"[ \t\n]+" - `[ \t\n]+`, // skim-0.5.0: r"[ \t\n]+" - `([^ |]+( +\| +[^ |]*)+)|( +)`, // skim-0.5.0: r"([^ |]+( +\| +[^ |]*)+)|( +)" - ` +\| +`, // skim-0.5.0: r" +\| +" - `^(?P<left>-?\d+)?(?P<sep>\.\.)?(?P<right>-?\d+)?$`, // skim-0.5.0: r"^(?P<left>-?\d+)?(?P<sep>\.\.)?(?P<right>-?\d+)?$" - ",", // skim-0.5.0: "," - ".*?,", // skim-0.5.0: ".*?," - ".*?,", // skim-0.5.0: ".*?," - ",", // skim-0.5.0: "," - `\x1B\[(?:([0-9]+;[0-9]+[Hf])|([0-9]+[ABCD])|(s|u|2J|K)|([0-9;]*m)|(=[0-9]+[hI]))`, // skim-0.5.0: r"\x1B\[(?:([0-9]+;[0-9]+[Hf])|([0-9]+[ABCD])|(s|u|2J|K)|([0-9;]*m)|(=[0-9]+[hI]))" - `[-_./]\z`, // egg-mode-text-1.14.7: r"[-_./]\z" - "^[ \t\r\n\x0c]*[#!]", // java-properties-1.1.1: "^[ \t\r\n\x0c]*[#!]" - `^[ \t\x0c]*[#!][^\r\n]*$`, // java-properties-1.1.1: r"^[ \t\x0c]*[#!][^\r\n]*$" - `^([ \t\x0c]*[:=][ \t\x0c]*|[ \t\x0c]+)$`, // java-properties-1.1.1: r"^([ \t\x0c]*[:=][ \t\x0c]*|[ \t\x0c]+)$" - `:.+\.`, // ipaddress-0.1.2: r":.+\." - `\.`, // ipaddress-0.1.2: r"\." - `:`, // ipaddress-0.1.2: r":" - `v(\d+)\.(\d+)\.(\d+)`, // iptables-0.2.2: r"v(\d+)\.(\d+)\.(\d+)" - `^([^-]+)-(.*)\.dat\.gz$`, // rsure-0.8.1: r"^([^-]+)-(.*)\.dat\.gz$" - "^(.*?)(<=|<|==|>=|>)(.*?)$", // rs-jsonpath-0.1.0: "^(.*?)(<=|<|==|>=|>)(.*?)$" - `(\n|^)(\w+):([\n\w\W]+?)(\n(?:\w)|(\n\]))`, // oatie-0.3.0: r"(\n|^)(\w+):([\n\w\W]+?)(\n(?:\w)|(\n\]))" - "#.*$", // weld-0.2.0: "#.*$" - `^[A-Za-z$_][A-Za-z0-9$_]*$`, // weld-0.2.0: r"^[A-Za-z$_][A-Za-z0-9$_]*$" - `^[0-9]+[cC]$`, // weld-0.2.0: r"^[0-9]+[cC]$" - `^0b[0-1]+[cC]$`, // weld-0.2.0: r"^0b[0-1]+[cC]$" - `^0x[0-9a-fA-F]+[cC]$`, // weld-0.2.0: r"^0x[0-9a-fA-F]+[cC]$" - `^[0-9]+$`, // weld-0.2.0: r"^[0-9]+$" - `^0b[0-1]+$`, // weld-0.2.0: r"^0b[0-1]+$" - `^0x[0-9a-fA-F]+$`, // weld-0.2.0: r"^0x[0-9a-fA-F]+$" - `^[0-9]+[lL]$`, // weld-0.2.0: r"^[0-9]+[lL]$" - `^0b[0-1]+[lL]$`, // weld-0.2.0: r"^0b[0-1]+[lL]$" - `^0x[0-9a-fA-F]+[lL]$`, // weld-0.2.0: r"^0x[0-9a-fA-F]+[lL]$" - "([(, ])enum\\b", // webgl_generator-0.1.0: "([(, ])enum\\b" - "\\bAcquireResourcesCallback\\b", // webgl_generator-0.1.0: "\\bAcquireResourcesCallback\\b" - `^(\d+)(,(\d+))?([acd]).*$`, // weave-0.2.0: r"^(\d+)(,(\d+))?([acd]).*$" - `<BinaryState>(\d)(\|-?\d+)*</BinaryState>`, // wemo-0.0.12: r"<BinaryState>(\d)(\|-?\d+)*</BinaryState>" - `(http[s]?://[^\s]+)`, // webscale-0.9.4: r"(http[s]?://[^\s]+)" - `^\d+.*$`, // svgrep-1.1.0: r"^\d+.*$" - `^[\pL\pN]+$`, // ignore-0.4.2: r"^[\pL\pN]+$" - `^([A-Za-z][0-9A-Za-z_]*)?$`, // ommui_string_patterns-0.1.2: r"^([A-Za-z][0-9A-Za-z_]*)?$" - `^(\S+(?:.*\S)?)?$`, // ommui_string_patterns-0.1.2: r"^(\S+(?:.*\S)?)?$" - "^(?P<min>[0-9]{1,10})(:(?P<max>[0-9]{1,10}))?$", // opcua-types-0.3.0: "^(?P<min>[0-9]{1,10})(:(?P<max>[0-9]{1,10}))?$" - `^(ns=(?P<ns>[0-9]+);)?(?P<t>[isgb])=(?P<v>.+)$`, // opcua-types-0.3.0: r"^(ns=(?P<ns>[0-9]+);)?(?P<t>[isgb])=(?P<v>.+)$" - `^(.+?)\s*:\s*(.+)$`, // open_read_later-1.1.1: r"^(.+?)\s*:\s*(.+)$" - `^.*(?:(?:youtu\.be/|v/|vi/|u/w/|embed/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*`, // youtube-downloader-0.1.0: r"^.*(?:(?:youtu\.be/|v/|vi/|u/w/|embed/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*" - ".", // yobot-0.1.1: "." - `.`, // yobot-0.1.1: r"." - `.+`, // yobot-0.1.1: r".+" - `.`, // yobot-0.1.1: r"." - `foo`, // ubiquity-0.1.5: r"foo" - `/target/`, // ubiquity-0.1.5: r"/target/" - `.DS_Store`, // ubiquity-0.1.5: r".DS_Store" - `//.*`, // qasm-1.0.0: r"//.*" - `\{\{ *([a-z\._]+) *\}\}`, // drill-0.3.5: r"\{\{ *([a-z\._]+) *\}\}" - `^([^\]\[]+)`, // queryst-2.0.0: r"^([^\]\[]+)" - `(\[[^\]\[]*\])`, // queryst-2.0.0: r"(\[[^\]\[]*\])" - `^/(\w+)$`, // qui-vive-0.1.0: r"^/(\w+)$" - `^/key$`, // qui-vive-0.1.0: r"^/key$" - `^/key/(\w+)$`, // qui-vive-0.1.0: r"^/key/(\w+)$" - `^/url$`, // qui-vive-0.1.0: r"^/url$" - `^/url/(\w+)$`, // qui-vive-0.1.0: r"^/url/(\w+)$" - `^/inv$`, // qui-vive-0.1.0: r"^/inv$" - `^/inv/(\w+)$`, // qui-vive-0.1.0: r"^/inv/(\w+)$" - // subdiff-0.1.0: r"\b" - `^(\d+)/(\d+)$`, // substudy-0.4.5: r"^(\d+)/(\d+)$" - `\s+`, // substudy-0.4.5: r"\s+" - `<[a-z/][^>]*>`, // substudy-0.4.5: r"<[a-z/][^>]*>" - `(\([^)]*\)|♪[^♪]*♪|[A-Z]{2,} ?:)`, // substudy-0.4.5: r"(\([^)]*\)|♪[^♪]*♪|[A-Z]{2,} ?:)" - `\s+`, // substudy-0.4.5: r"\s+" - `^(\d(-| )?){9}(x|X|\d|(\d(-| )?){3}\d)$`, // isbnid-0.1.3: r"^(\d(-| )?){9}(x|X|\d|(\d(-| )?){3}\d)$" - `[^0-9X]`, // isbnid-0.1.3: r"[^0-9X]" - `Intel\(r\) SPMD Program Compiler \(ispc\), (\d+\.\d+\.\d+)`, // ispc-0.3.5: r"Intel\(r\) SPMD Program Compiler \(ispc\), (\d+\.\d+\.\d+)" -} - -func TestStringMatching(t *testing.T) { - t.Parallel() - - for _, expr := range crateRegexps { - t.Run(expr, func(t *testing.T) { - re, err := regexp.Compile(expr) - if err != nil { - t.Fatalf("failed to compile %q: %v", expr, err) - } - - Check(t, func(t *T) { - s := StringMatching(expr).Draw(t, "s").(string) - if !re.MatchString(s) { - t.Fatalf("%q does not match %q", s, expr) - } - }) - }) - } -} - -func TestSliceOfBytesMatching(t *testing.T) { - t.Parallel() - - for _, expr := range crateRegexps { - t.Run(expr, func(t *testing.T) { - re, err := regexp.Compile(expr) - if err != nil { - t.Fatalf("failed to compile %q: %v", expr, err) - } - - Check(t, func(t *T) { - s := SliceOfBytesMatching(expr).Draw(t, "s").([]byte) - if !re.Match(s) { - t.Fatalf("%q does not match %q", s, expr) - } - }) - }) - } -} diff --git a/vendor/pgregory.net/rapid/shrink.go b/vendor/pgregory.net/rapid/shrink.go index 6112156..52a1589 100644 --- a/vendor/pgregory.net/rapid/shrink.go +++ b/vendor/pgregory.net/rapid/shrink.go @@ -31,7 +31,7 @@ const ( labelSortGroups = "sort_groups" ) -func shrink(tb tb, rec recordedBits, err *testError, prop func(*T)) ([]uint64, *testError) { +func shrink(tb tb, deadline time.Time, rec recordedBits, err *testError, prop func(*T)) ([]uint64, *testError) { rec.prune() s := &shrinker{ @@ -44,7 +44,7 @@ func shrink(tb tb, rec recordedBits, err *testError, prop func(*T)) ([]uint64, * cache: map[string]struct{}{}, } - buf, err := s.shrink() + buf, err := s.shrink(deadline) if flags.debugvis { name := fmt.Sprintf("vis-%v.html", strings.Replace(tb.Name(), "/", "_", -1)) @@ -75,14 +75,14 @@ type shrinker struct { hits int } -func (s *shrinker) debugf(verbose_ bool, format string, args ...interface{}) { +func (s *shrinker) debugf(verbose_ bool, format string, args ...any) { if flags.debug && (!verbose_ || flags.verbose) { s.tb.Helper() s.tb.Logf("[shrink] "+format, args...) } } -func (s *shrinker) shrink() (buf []uint64, err *testError) { +func (s *shrinker) shrink(deadline time.Time) (buf []uint64, err *testError) { defer func() { if r := recover(); r != nil { buf, err = s.rec.data, r.(*testError) @@ -90,7 +90,6 @@ func (s *shrinker) shrink() (buf []uint64, err *testError) { }() i := 0 - deadline := time.Now().Add(flags.shrinkTime) for shrinks := -1; s.shrinks > shrinks && time.Now().Before(deadline); i++ { shrinks = s.shrinks @@ -246,7 +245,7 @@ func (s *shrinker) removeGroupSpans(deadline time.Time) { } } -func (s *shrinker) accept(buf []uint64, label string, format string, args ...interface{}) bool { +func (s *shrinker) accept(buf []uint64, label string, format string, args ...any) bool { if compareData(buf, s.rec.data) >= 0 { return false } diff --git a/vendor/pgregory.net/rapid/shrink_test.go b/vendor/pgregory.net/rapid/shrink_test.go deleted file mode 100644 index 1373360..0000000 --- a/vendor/pgregory.net/rapid/shrink_test.go +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright 2019 Gregory Petrosyan <gregory.petrosyan@gmail.com> -// -// 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 https://mozilla.org/MPL/2.0/. - -package rapid - -import ( - "fmt" - "math" - "math/bits" - "sort" - "strconv" - "testing" -) - -const shrinkTestRuns = 10 - -func TestShrink_IntCmp(t *testing.T) { - t.Parallel() - - ref := []struct { - gt bool - a int - b int - eq bool - }{ - {true, 1000000, 1000001, false}, - {true, -1000000, 0, false}, - {true, 0, 0, true}, - {false, 1000000, 0, false}, - {false, -1000000, -1000001, false}, - {false, 0, 0, true}, - } - - for _, r := range ref { - t.Run(fmt.Sprintf("%v", r), func(t *testing.T) { - checkShrink(t, func(t *T) { - i := Int().Draw(t, "i").(int) - if ((r.gt && i > r.a) || (!r.gt && i < r.a)) || (r.eq && i == r.a) { - t.Fail() - } - }, r.b) - }) - } -} - -func TestShrink_FloatCmp(t *testing.T) { - t.Parallel() - - type cmp struct { - gt bool - a float64 - b float64 - eq bool - } - - ref := []cmp{ - {true, 1000000, 1000000.5, false}, - {true, math.Pi, 3.5, false}, - {true, 1, 1, true}, - {true, -1000000, 1, false}, - {false, -1000000, -1000000.5, false}, - {false, -math.E, -2.75, false}, - } - if *flaky { - ref = append(ref, cmp{false, 0, -1, true}) // sometimes we end up at exactly 0 - } - - for _, r := range ref { - t.Run(fmt.Sprintf("%v", r), func(t *testing.T) { - checkShrink(t, func(t *T) { - f := Float64().Draw(t, "f").(float64) - if ((r.gt && f > r.a) || (!r.gt && f < r.a)) || (r.eq && f == r.a) { - t.Fail() - } - }, r.b) - }) - } -} - -func TestShrink_IntSliceNElemsGt(t *testing.T) { - t.Parallel() - - checkShrink(t, func(t *T) { - s := SliceOf(Int()).Draw(t, "s").([]int) - n := 0 - for _, i := range s { - if i > 1000000 { - n++ - } - } - if n > 1 { - t.Fail() - } - }, []int{1000001, 1000001}) -} - -func TestShrink_IntSliceElemGe(t *testing.T) { - t.Parallel() - - checkShrink(t, func(t *T) { - s := SliceOfN(Int(), 1, -1).Draw(t, "s").([]int) - ix := IntRange(0, len(s)-1).Draw(t, "ix").(int) - - if s[ix] >= 100 { - t.Fail() - } - }, []int{100}, 0) -} - -func TestShrink_IntSliceElemSpanGe(t *testing.T) { - t.Parallel() - - checkShrink(t, func(t *T) { - s := SliceOfN(Int(), 4, -1).Draw(t, "s").([]int) - if len(s)%3 == 1 && s[len(s)-1] >= 100 { - t.Fail() - } - }, []int{0, 0, 0, 100}) -} - -func TestShrink_IntSliceNoDuplicates(t *testing.T) { - t.Parallel() - - checkShrink(t, func(t *T) { - s := SliceOfN(IntMin(1), 5, -1).Draw(t, "s").([]int) - sort.Ints(s) - last := 0 - for _, i := range s { - if i == last { - return - } - last = i - } - t.Fail() - }, []int{1, 2, 3, 4, 5}) -} - -func TestShrink_String(t *testing.T) { - t.Parallel() - - checkShrink(t, func(t *T) { - s1 := String().Draw(t, "s1").(string) - s2 := String().Draw(t, "s2").(string) - if len(s1) > len(s2) { - t.Fail() - } - }, "?", "") -} - -func TestShrink_StringOfBytes(t *testing.T) { - t.Parallel() - - checkShrink(t, func(t *T) { - s1 := StringOf(Byte()).Draw(t, "s1").(string) - s2 := StringOf(Byte()).Draw(t, "s2").(string) - if len(s1) > len(s2) { - t.Fail() - } - }, "\x00", "") -} - -func TestMinimize_UnsetBits(t *testing.T) { - t.Parallel() - - Check(t, func(t *T) { - mask := Uint64Range(0, math.MaxUint64).Draw(t, "mask").(uint64) - best := minimize(math.MaxUint64, func(x uint64, s string) bool { return x&mask == mask }) - if best != mask { - t.Fatalf("unset to %v instead of %v", bin(best), bin(mask)) - } - }) -} - -func TestMinimize_SortBits(t *testing.T) { - t.Parallel() - - Check(t, func(t *T) { - u := Uint64Range(0, math.MaxUint64).Draw(t, "u").(uint64) - n := bits.OnesCount64(u) - v := uint64(1<<uint(n) - 1) - - best := minimize(u, func(x uint64, s string) bool { return bits.OnesCount64(x) == n }) - if best != v { - t.Fatalf("minimized to %v instead of %v (%v bits set)", bin(best), bin(v), n) - } - }) -} - -func TestMinimize_LowerBound(t *testing.T) { - t.Parallel() - - Check(t, func(t *T) { - min := Uint64().Draw(t, "min").(uint64) - u := Uint64Min(min).Draw(t, "u").(uint64) - - best := minimize(u, func(x uint64, s string) bool { return x >= min }) - if best != min { - t.Fatalf("found %v instead of %v", best, min) - } - }) -} - -func checkShrink(t *testing.T, prop func(*T), draws ...value) { - t.Helper() - - for i := 0; i < shrinkTestRuns; i++ { - t.Run(strconv.Itoa(i), func(t *testing.T) { - t.Helper() - - _, _, seed, buf, err1, err2 := doCheck(t, "", 100, baseSeed(), prop) - if seed != 0 && err1 == nil && err2 == nil { - t.Fatalf("shrink test did not fail (seed %v)", seed) - } - if traceback(err1) != traceback(err2) { - t.Fatalf("flaky shrink test (seed %v)\nTraceback (%v):\n%vOriginal traceback (%v):\n%v", seed, err2, traceback(err2), err1, traceback(err1)) - } - - nt := newT(t, newBufBitStream(buf, false), false, nil, draws...) - _ = checkOnce(nt, prop) - if nt.draws != len(draws) { - t.Fatalf("different number of draws: %v vs expected %v", nt.draws, len(draws)) - } - }) - } -} - -func bin(u uint64) string { - return "0b" + strconv.FormatUint(u, 2) -} diff --git a/vendor/pgregory.net/rapid/statemachine.go b/vendor/pgregory.net/rapid/statemachine.go index 527d88f..40069d2 100644 --- a/vendor/pgregory.net/rapid/statemachine.go +++ b/vendor/pgregory.net/rapid/statemachine.go @@ -9,128 +9,101 @@ package rapid import ( "reflect" "sort" + "testing" ) const ( - actionLabel = "action" - validActionTries = 100 // hack, but probably good enough for now - - initMethodName = "Init" + actionLabel = "action" + validActionTries = 100 // hack, but probably good enough for now checkMethodName = "Check" - cleanupMethodName = "Cleanup" - - noValidActionsMsg = "can't find a valid action" + noValidActionsMsg = "can't find a valid (non-skipped) action" ) -type StateMachine interface { - // Check is ran after every action and should contain invariant checks. - // - // Other public methods are treated as follows: - // - Init(t *rapid.T), if present, is ran at the beginning of each test case - // to initialize the state machine instance; - // - Cleanup(), if present, is called at the end of each test case; - // - All other public methods should have a form ActionName(t *rapid.T) - // and are used as possible actions. At least one action has to be specified. - // - Check(*T) -} - -// Run is a convenience function for defining "state machine" tests, -// to be run by Check or MakeCheck. -// -// State machine test is a pattern for testing stateful systems that looks -// like this: +// Repeat executes a random sequence of actions (often called a "state machine" test). +// actions[""], if set, is executed before/after every other action invocation +// and should only contain invariant checking code. // -// m := new(StateMachineType) -// m.Init(t) // optional -// defer m.Cleanup() // optional -// m.Check(t) -// for { -// m.RandomAction(t) -// m.Check(t) -// } -// -// Run synthesizes such test from the type of m, which must be a pointer. -// Note that for each test case, new state machine instance is created -// via reflection; any data inside m is ignored. -func Run(m StateMachine) func(*T) { - typ := reflect.TypeOf(m) +// For complex state machines, it can be more convenient to specify actions as +// methods of a special state machine type. In this case, [StateMachineActions] +// can be used to create an actions map from state machine methods using reflection. +func (t *T) Repeat(actions map[string]func(*T)) { + t.Helper() - return func(t *T) { - t.Helper() + check := func(*T) {} + actionKeys := make([]string, 0, len(actions)) + for key, action := range actions { + if key != "" { + actionKeys = append(actionKeys, key) + } else { + check = action + } + } + if len(actionKeys) == 0 { + return + } + sort.Strings(actionKeys) - repeat := newRepeat(0, flags.steps, maxInt) + steps := flags.steps + if testing.Short() { + steps /= 2 + } - sm := newStateMachine(typ) - if sm.init != nil { - sm.init(t) - t.failOnError() - } - if sm.cleanup != nil { - defer sm.cleanup() - } + repeat := newRepeat(-1, -1, float64(steps), "Repeat") + sm := stateMachine{ + check: check, + actionKeys: SampledFrom(actionKeys), + actions: actions, + } - sm.check(t) - t.failOnError() - for repeat.more(t.s, typ.String()) { - ok := sm.executeAction(t) - if ok { - sm.check(t) - t.failOnError() - } else { - repeat.reject() - } + sm.check(t) + t.failOnError() + for repeat.more(t.s) { + ok := sm.executeAction(t) + if ok { + sm.check(t) + t.failOnError() + } else { + repeat.reject() } } } -type stateMachine struct { - init func(*T) - cleanup func() - check func(*T) - actionKeys *Generator - actions map[string]func(*T) +type StateMachine interface { + // Check is ran after every action and should contain invariant checks. + // + // All other public methods should have a form ActionName(t *rapid.T) + // and are used as possible actions. At least one action has to be specified. + Check(*T) } -func newStateMachine(typ reflect.Type) *stateMachine { - assertf(typ.Kind() == reflect.Ptr, "state machine type should be a pointer, not %v", typ.Kind()) - +// StateMachineActions creates an actions map for [*T.Repeat] +// from methods of a [StateMachine] type instance using reflection. +func StateMachineActions(sm StateMachine) map[string]func(*T) { var ( - v = reflect.New(typ.Elem()) - n = typ.NumMethod() - init func(*T) - cleanup func() - actionKeys []string - actions = map[string]func(*T){} + v = reflect.ValueOf(sm) + t = v.Type() + n = t.NumMethod() ) + actions := make(map[string]func(*T), n) for i := 0; i < n; i++ { - name := typ.Method(i).Name + name := t.Method(i).Name m, ok := v.Method(i).Interface().(func(*T)) - if ok { - if name == initMethodName { - init = m - } else if name != checkMethodName { - actionKeys = append(actionKeys, name) - actions[name] = m - } - } else if name == cleanupMethodName { - m, ok := v.Method(i).Interface().(func()) - assertf(ok, "method %v should have type func(), not %v", cleanupMethodName, v.Method(i).Type()) - cleanup = m + if ok && name != checkMethodName { + actions[name] = m } } - assertf(len(actions) > 0, "state machine of type %v has no actions specified", typ) - sort.Strings(actionKeys) + assertf(len(actions) > 0, "state machine of type %v has no actions specified", t) + actions[""] = sm.Check - return &stateMachine{ - init: init, - cleanup: cleanup, - check: v.Interface().(StateMachine).Check, - actionKeys: SampledFrom(actionKeys), - actions: actions, - } + return actions +} + +type stateMachine struct { + check func(*T) + actionKeys *Generator[string] + actions map[string]func(*T) } func (sm *stateMachine) executeAction(t *T) bool { @@ -138,7 +111,7 @@ func (sm *stateMachine) executeAction(t *T) bool { for n := 0; n < validActionTries; n++ { i := t.s.beginGroup(actionLabel, false) - action := sm.actions[sm.actionKeys.Draw(t, "action").(string)] + action := sm.actions[sm.actionKeys.Draw(t, "action")] invalid, skipped := runAction(t, action) t.s.endGroup(i, false) diff --git a/vendor/pgregory.net/rapid/statemachine_test.go b/vendor/pgregory.net/rapid/statemachine_test.go deleted file mode 100644 index 30206b1..0000000 --- a/vendor/pgregory.net/rapid/statemachine_test.go +++ /dev/null @@ -1,270 +0,0 @@ -// Copyright 2019 Gregory Petrosyan <gregory.petrosyan@gmail.com> -// -// 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 https://mozilla.org/MPL/2.0/. - -package rapid - -import "testing" - -// https://github.com/leanovate/gopter/blob/master/commands/example_circularqueue_test.go -var gopterBug = false - -// https://pkg.go.dev/github.com/leanovate/gopter/commands?tab=doc#example-package-BuggyCounter -type buggyCounter struct { - n int -} - -func (c *buggyCounter) Get() int { - return c.n -} - -func (c *buggyCounter) Inc() { - c.n++ -} - -func (c *buggyCounter) Dec() { - if c.n > 3 { - c.n -= 2 - } else { - c.n-- - } -} - -func (c *buggyCounter) Reset() { - c.n = 0 -} - -type counterMachine struct { - c buggyCounter - incs int - decs int -} - -func (m *counterMachine) Inc(_ *T) { - m.c.Inc() - m.incs++ -} - -func (m *counterMachine) Dec(_ *T) { - m.c.Dec() - m.decs++ -} - -func (m *counterMachine) Reset(_ *T) { - m.c.Reset() - m.incs = 0 - m.decs = 0 -} - -func (m *counterMachine) Check(t *T) { - if m.c.Get() != m.incs-m.decs { - t.Fatalf("counter value is %v with %v incs and %v decs", m.c.Get(), m.incs, m.decs) - } -} - -func TestStateMachine_Counter(t *testing.T) { - t.Parallel() - - checkShrink(t, Run(&counterMachine{}), - "Inc", "Inc", "Inc", "Inc", - "Dec", - ) -} - -type haltingMachine struct { - a []int - b []int - c []int -} - -func (m *haltingMachine) Check(t *T) { - if len(m.a) > 3 || len(m.b) > 3 || len(m.c) > 3 { - t.Fatalf("too many elements: %v, %v, %v", len(m.a), len(m.b), len(m.c)) - } -} - -func (m *haltingMachine) A(t *T) { - if len(m.a) == 3 { - t.SkipNow() - } - - m.a = append(m.a, Int().Draw(t, "a").(int)) -} - -func (m *haltingMachine) B(t *T) { - if len(m.b) == 3 { - t.SkipNow() - } - - m.b = append(m.b, Int().Draw(t, "b").(int)) -} - -func (m *haltingMachine) C(t *T) { - if len(m.c) == 3 { - t.SkipNow() - } - - m.c = append(m.c, Int().Draw(t, "c").(int)) -} - -func TestStateMachine_Halting(t *testing.T) { - t.Parallel() - - a := []value{"A", 0, "A", 0, "A", 0} - for i := 0; i < 100; i++ { - a = append(a, "A") // TODO proper shrinking of "stuck" state machines - } - - checkShrink(t, Run(&haltingMachine{}), a...) -} - -// https://www.cs.tufts.edu/~nr/cs257/archive/john-hughes/quviq-testing.pdf -type buggyQueue struct { - buf []int - in int - out int -} - -func newBuggyQueue(size int) *buggyQueue { - return &buggyQueue{ - buf: make([]int, size+1), - } -} - -func (q *buggyQueue) Get() int { - n := q.buf[q.out] - q.out = (q.out + 1) % len(q.buf) - return n -} - -func (q *buggyQueue) Put(i int) { - if gopterBug && q.in == 4 && i > 0 { - q.buf[len(q.buf)-1] *= i - } - - q.buf[q.in] = i - q.in = (q.in + 1) % len(q.buf) -} - -func (q *buggyQueue) Size() int { - if gopterBug { - return (q.in - q.out + len(q.buf)) % len(q.buf) - } else { - return (q.in - q.out) % len(q.buf) - } -} - -type queueMachine struct { - q *buggyQueue - state []int - size int -} - -func (m *queueMachine) Init(t *T) { - size := IntRange(1, 1000).Draw(t, "size").(int) - m.q = newBuggyQueue(size) - m.size = size -} - -func (m *queueMachine) Get(t *T) { - if m.q.Size() == 0 { - t.Skip("queue empty") - } - - n := m.q.Get() - if n != m.state[0] { - t.Fatalf("got invalid value: %v vs expected %v", n, m.state[0]) - } - m.state = m.state[1:] -} - -func (m *queueMachine) Put(t *T) { - if m.q.Size() == m.size { - t.Skip("queue full") - } - - n := Int().Draw(t, "n").(int) - m.q.Put(n) - m.state = append(m.state, n) -} - -func (m *queueMachine) Check(t *T) { - if m.q.Size() != len(m.state) { - t.Fatalf("queue size mismatch: %v vs expected %v", m.q.Size(), len(m.state)) - } -} - -func TestStateMachine_Queue(t *testing.T) { - t.Parallel() - - checkShrink(t, Run(&queueMachine{}), - 1, - "Put", 0, - "Get", - "Put", 0, - ) -} - -type garbageMachine struct { - a []int - b []int -} - -func (m *garbageMachine) AddA(t *T) { - if len(m.b) < 3 { - t.Skip("too early") - } - - n := Int().Draw(t, "a").(int) - m.a = append(m.a, n) -} - -func (m *garbageMachine) AddB(t *T) { - n := Int().Draw(t, "b").(int) - m.b = append(m.b, n) -} - -func (m *garbageMachine) Whatever1(t *T) { - b := Bool().Draw(t, "whatever 1/1").(bool) - if b { - t.Skip("arbitrary decision") - } - - Float64().Draw(t, "whatever 1/2") -} - -func (m *garbageMachine) Whatever2(t *T) { - SliceOfDistinct(Int(), nil).Draw(t, "whatever 2") -} - -func (m *garbageMachine) Whatever3(t *T) { - OneOf(SliceOf(Byte()), MapOf(Int(), String())).Draw(t, "whatever 3") -} - -func (m *garbageMachine) Check(t *T) { - if len(m.a) > len(m.b) { - t.Fatalf("`a` has outgrown `b`: %v vs %v", len(m.a), len(m.b)) - } -} - -func TestStateMachine_DiscardGarbage(t *testing.T) { - t.Parallel() - - checkShrink(t, Run(&garbageMachine{}), - "AddB", 0, - "AddB", 0, - "AddB", 0, - "AddA", 0, - "AddA", 0, - "AddA", 0, - "AddA", 0, - ) -} - -func BenchmarkCheckQueue(b *testing.B) { - for i := 0; i < b.N; i++ { - _, _, _, _, _, _ = doCheck(b, "", 100, baseSeed(), Run(&queueMachine{})) - } -} diff --git a/vendor/pgregory.net/rapid/strings.go b/vendor/pgregory.net/rapid/strings.go index 302eea4..7948e04 100644 --- a/vendor/pgregory.net/rapid/strings.go +++ b/vendor/pgregory.net/rapid/strings.go @@ -9,7 +9,7 @@ package rapid import ( "bytes" "fmt" - "reflect" + "math" "regexp" "regexp/syntax" "strings" @@ -19,11 +19,8 @@ import ( ) var ( - stringType = reflect.TypeOf("") - byteSliceType = reflect.TypeOf([]byte(nil)) - defaultRunes = []rune{ - '?', + 'A', 'a', '?', '~', '!', '@', '#', '$', '%', '^', '&', '*', '_', '-', '+', '=', '.', ',', ':', ';', ' ', '\t', '\r', '\n', @@ -73,15 +70,18 @@ type compiledRegexp struct { re *regexp.Regexp } -func Rune() *Generator { +// Rune creates a rune generator. Rune is equivalent to [RuneFrom] with default set of runes and tables. +func Rune() *Generator[rune] { return runesFrom(true, defaultRunes, defaultTables...) } -func RuneFrom(runes []rune, tables ...*unicode.RangeTable) *Generator { +// RuneFrom creates a rune generator from provided runes and tables. +// RuneFrom panics if both runes and tables are empty. RuneFrom panics if tables contain an empty table. +func RuneFrom(runes []rune, tables ...*unicode.RangeTable) *Generator[rune] { return runesFrom(false, runes, tables...) } -func runesFrom(default_ bool, runes []rune, tables ...*unicode.RangeTable) *Generator { +func runesFrom(default_ bool, runes []rune, tables ...*unicode.RangeTable) *Generator[rune] { if len(tables) == 0 { assertf(len(runes) > 0, "at least one rune should be specified") } @@ -103,7 +103,7 @@ func runesFrom(default_ bool, runes []rune, tables ...*unicode.RangeTable) *Gene assertf(len(tables_[i]) > 0, "empty *unicode.RangeTable %v", i) } - return newGenerator(&runeGen{ + return newGenerator[rune](&runeGen{ die: newLoadedDie(weights), runes: runes, tables: tables_, @@ -126,11 +126,7 @@ func (g *runeGen) String() string { } } -func (g *runeGen) type_() reflect.Type { - return int32Type -} - -func (g *runeGen) value(t *T) value { +func (g *runeGen) value(t *T) rune { n := g.die.roll(t.s) runes := g.runes @@ -143,106 +139,112 @@ func (g *runeGen) value(t *T) value { return runes[genIndex(t.s, len(runes), true)] } -func String() *Generator { +// String is a shorthand for [StringOf]([Rune]()). +func String() *Generator[string] { return StringOf(anyRuneGen) } -func StringN(minRunes int, maxRunes int, maxLen int) *Generator { +// StringN is a shorthand for [StringOfN]([Rune](), minRunes, maxRunes, maxLen). +func StringN(minRunes int, maxRunes int, maxLen int) *Generator[string] { return StringOfN(anyRuneGen, minRunes, maxRunes, maxLen) } -func StringOf(elem *Generator) *Generator { +// StringOf is a shorthand for [StringOfN](elem, -1, -1, -1). +func StringOf(elem *Generator[rune]) *Generator[string] { return StringOfN(elem, -1, -1, -1) } -func StringOfN(elem *Generator, minElems int, maxElems int, maxLen int) *Generator { - assertValidRange(minElems, maxElems) - assertf(elem.type_() == int32Type || elem.type_() == uint8Type, "element generator should generate runes or bytes, not %v", elem.type_()) - assertf(maxLen < 0 || maxLen >= maxElems, "maximum length (%v) should not be less than maximum number of elements (%v)", maxLen, maxElems) +// StringOfN creates a UTF-8 string generator. +// If minRunes >= 0, generated strings have minimum minRunes runes. +// If maxRunes >= 0, generated strings have maximum maxRunes runes. +// If maxLen >= 0, generates strings have maximum length of maxLen. +// StringOfN panics if maxRunes >= 0 and minRunes > maxRunes. +// StringOfN panics if maxLen >= 0 and maxLen < maxRunes. +func StringOfN(elem *Generator[rune], minRunes int, maxRunes int, maxLen int) *Generator[string] { + assertValidRange(minRunes, maxRunes) + assertf(maxLen < 0 || maxLen >= maxRunes, "maximum length (%v) should not be less than maximum number of runes (%v)", maxLen, maxRunes) - return newGenerator(&stringGen{ + return newGenerator[string](&stringGen{ elem: elem, - minElems: minElems, - maxElems: maxElems, + minRunes: minRunes, + maxRunes: maxRunes, maxLen: maxLen, }) } type stringGen struct { - elem *Generator - minElems int - maxElems int + elem *Generator[rune] + minRunes int + maxRunes int maxLen int } func (g *stringGen) String() string { if g.elem == anyRuneGen { - if g.minElems < 0 && g.maxElems < 0 && g.maxLen < 0 { + if g.minRunes < 0 && g.maxRunes < 0 && g.maxLen < 0 { return "String()" } else { - return fmt.Sprintf("StringN(minRunes=%v, maxRunes=%v, maxLen=%v)", g.minElems, g.maxElems, g.maxLen) + return fmt.Sprintf("StringN(minRunes=%v, maxRunes=%v, maxLen=%v)", g.minRunes, g.maxRunes, g.maxLen) } } else { - if g.minElems < 0 && g.maxElems < 0 && g.maxLen < 0 { + if g.minRunes < 0 && g.maxRunes < 0 && g.maxLen < 0 { return fmt.Sprintf("StringOf(%v)", g.elem) } else { - return fmt.Sprintf("StringOfN(%v, minElems=%v, maxElems=%v, maxLen=%v)", g.elem, g.minElems, g.maxElems, g.maxLen) + return fmt.Sprintf("StringOfN(%v, minRunes=%v, maxRunes=%v, maxLen=%v)", g.elem, g.minRunes, g.maxRunes, g.maxLen) } } } -func (g *stringGen) type_() reflect.Type { - return stringType -} - -func (g *stringGen) value(t *T) value { - repeat := newRepeat(g.minElems, g.maxElems, -1) +func (g *stringGen) value(t *T) string { + repeat := newRepeat(g.minRunes, g.maxRunes, -1, g.elem.String()) var b strings.Builder b.Grow(repeat.avg()) - if g.elem.type_() == int32Type { - maxLen := g.maxLen - if maxLen < 0 { - maxLen = maxInt - } + maxLen := g.maxLen + if maxLen < 0 { + maxLen = math.MaxInt + } - for repeat.more(t.s, g.elem.String()) { - r := g.elem.value(t).(rune) - n := utf8.RuneLen(r) + for repeat.more(t.s) { + r := g.elem.value(t) + n := utf8.RuneLen(r) - if n < 0 || b.Len()+n > maxLen { - repeat.reject() - } else { - b.WriteRune(r) - } - } - } else { - for repeat.more(t.s, g.elem.String()) { - b.WriteByte(g.elem.value(t).(byte)) + if n < 0 || b.Len()+n > maxLen { + repeat.reject() + } else { + b.WriteRune(r) } } return b.String() } -func StringMatching(expr string) *Generator { - return matching(expr, true) -} +// StringMatching creates a UTF-8 string generator matching the provided [syntax.Perl] regular expression. +func StringMatching(expr string) *Generator[string] { + compiled, err := compileRegexp(expr) + assertf(err == nil, "%v", err) -func SliceOfBytesMatching(expr string) *Generator { - return matching(expr, false) + return newGenerator[string](®expStringGen{ + regexpGen{ + expr: expr, + syn: compiled.syn, + re: compiled.re, + }, + }) } -func matching(expr string, str bool) *Generator { +// SliceOfBytesMatching creates a UTF-8 byte slice generator matching the provided [syntax.Perl] regular expression. +func SliceOfBytesMatching(expr string) *Generator[[]byte] { compiled, err := compileRegexp(expr) assertf(err == nil, "%v", err) - return newGenerator(®expGen{ - str: str, - expr: expr, - syn: compiled.syn, - re: compiled.re, + return newGenerator[[]byte](®expSliceGen{ + regexpGen{ + expr: expr, + syn: compiled.syn, + re: compiled.re, + }, }) } @@ -251,58 +253,49 @@ type runeWriter interface { } type regexpGen struct { - str bool expr string syn *syntax.Regexp re *regexp.Regexp } +type regexpStringGen struct{ regexpGen } +type regexpSliceGen struct{ regexpGen } -func (g *regexpGen) String() string { - if g.str { - return fmt.Sprintf("StringMatching(%q)", g.expr) - } else { - return fmt.Sprintf("SliceOfBytesMatching(%q)", g.expr) - } +func (g *regexpStringGen) String() string { + return fmt.Sprintf("StringMatching(%q)", g.expr) } - -func (g *regexpGen) type_() reflect.Type { - if g.str { - return stringType - } else { - return byteSliceType - } +func (g *regexpSliceGen) String() string { + return fmt.Sprintf("SliceOfBytesMatching(%q)", g.expr) } -func (g *regexpGen) maybeString(t *T) value { +func (g *regexpStringGen) maybeString(t *T) (string, bool) { b := &strings.Builder{} g.build(b, g.syn, t) v := b.String() if g.re.MatchString(v) { - return v + return v, true } else { - return nil + return "", false } } -func (g *regexpGen) maybeSlice(t *T) value { +func (g *regexpSliceGen) maybeSlice(t *T) ([]byte, bool) { b := &bytes.Buffer{} g.build(b, g.syn, t) v := b.Bytes() if g.re.Match(v) { - return v + return v, true } else { - return nil + return nil, false } } -func (g *regexpGen) value(t *T) value { - if g.str { - return find(g.maybeString, t, small) - } else { - return find(g.maybeSlice, t, small) - } +func (g *regexpStringGen) value(t *T) string { + return find(g.maybeString, t, small) +} +func (g *regexpSliceGen) value(t *T) []byte { + return find(g.maybeSlice, t, small) } func (g *regexpGen) build(w runeWriter, re *syntax.Regexp, t *T) { @@ -326,7 +319,7 @@ func (g *regexpGen) build(w runeWriter, re *syntax.Regexp, t *T) { case syntax.OpAnyCharNotNL: sub = anyRuneGenNoNL } - r := sub.value(t).(rune) + r := sub.value(t) _, _ = w.WriteRune(maybeFoldCase(t.s, r, re.Flags)) case syntax.OpBeginLine, syntax.OpEndLine, syntax.OpBeginText, syntax.OpEndText, @@ -344,8 +337,8 @@ func (g *regexpGen) build(w runeWriter, re *syntax.Regexp, t *T) { case syntax.OpQuest: min, max = 0, 1 } - repeat := newRepeat(min, max, -1) - for repeat.more(t.s, regexpName(re.Sub[0])) { + repeat := newRepeat(min, max, -1, regexpName(re.Sub[0])) + for repeat.more(t.s) { g.build(w, re.Sub[0], t) } case syntax.OpConcat: @@ -375,20 +368,28 @@ func maybeFoldCase(s bitStream, r rune, flags syntax.Flags) rune { return r } -func expandRangeTable(t *unicode.RangeTable, key interface{}) []rune { +func expandRangeTable(t *unicode.RangeTable, key any) []rune { cached, ok := expandedTables.Load(key) if ok { return cached.([]rune) } - var ret []rune + n := 0 + for _, r := range t.R16 { + n += int(r.Hi-r.Lo)/int(r.Stride) + 1 + } + for _, r := range t.R32 { + n += int(r.Hi-r.Lo)/int(r.Stride) + 1 + } + + ret := make([]rune, 0, n) for _, r := range t.R16 { - for i := r.Lo; i <= r.Hi; i += r.Stride { + for i := uint32(r.Lo); i <= uint32(r.Hi); i += uint32(r.Stride) { ret = append(ret, rune(i)) } } for _, r := range t.R32 { - for i := r.Lo; i <= r.Hi; i += r.Stride { + for i := uint64(r.Lo); i <= uint64(r.Hi); i += uint64(r.Stride) { ret = append(ret, rune(i)) } } @@ -431,14 +432,16 @@ func regexpName(re *syntax.Regexp) string { return s } -func charClassGen(re *syntax.Regexp) *Generator { +func charClassGen(re *syntax.Regexp) *Generator[rune] { cached, ok := charClassGens.Load(regexpName(re)) if ok { - return cached.(*Generator) + return cached.(*Generator[rune]) } - t := &unicode.RangeTable{} + t := &unicode.RangeTable{R32: make([]unicode.Range32, 0, len(re.Rune)/2)} for i := 0; i < len(re.Rune); i += 2 { + // not a valid unicode.Range32, since it requires that Lo and Hi must always be >= 1<<16 + // however, we don't really care, since the only use of these ranges is as input to expandRangeTable t.R32 = append(t.R32, unicode.Range32{ Lo: uint32(re.Rune[i]), Hi: uint32(re.Rune[i+1]), @@ -446,7 +449,7 @@ func charClassGen(re *syntax.Regexp) *Generator { }) } - g := newGenerator(&runeGen{ + g := newGenerator[rune](&runeGen{ die: newLoadedDie([]int{1}), tables: [][]rune{expandRangeTable(t, regexpName(re))}, }) diff --git a/vendor/pgregory.net/rapid/strings_example_test.go b/vendor/pgregory.net/rapid/strings_example_test.go deleted file mode 100644 index f231acb..0000000 --- a/vendor/pgregory.net/rapid/strings_example_test.go +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2020 Gregory Petrosyan <gregory.petrosyan@gmail.com> -// -// 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 https://mozilla.org/MPL/2.0/. - -// String generation depends on the Unicode tables, which change with Go versions: -// +build go1.14 - -package rapid_test - -import ( - "fmt" - "unicode" - - "pgregory.net/rapid" -) - -func ExampleRune() { - gen := rapid.Rune() - - for i := 0; i < 25; i++ { - if i%5 == 0 { - fmt.Println() - } else { - fmt.Print(" ") - } - fmt.Printf("%q", gen.Example(i)) - } - // Output: - // '\\' '\ufeff' '?' '~' '-' - // '0' '$' '!' '`' '\ue05d' - // '"' '&' '#' '\u0604' 'A' - // '&' '茞' '@' '#' '|' - // '⊙' '𝩔' '$' '҈' '\r' -} - -func ExampleRuneFrom() { - gens := []*rapid.Generator{ - rapid.RuneFrom([]rune{'A', 'B', 'C'}), - rapid.RuneFrom(nil, unicode.Cyrillic, unicode.Greek), - rapid.RuneFrom([]rune{'⌘'}, &unicode.RangeTable{ - R32: []unicode.Range32{{0x1F600, 0x1F64F, 1}}, - }), - } - - for _, gen := range gens { - for i := 0; i < 5; i++ { - if i > 0 { - fmt.Print(" ") - } - fmt.Printf("%q", gen.Example(i)) - } - fmt.Println() - } - // Output: - // 'A' 'A' 'A' 'B' 'A' - // 'Ͱ' 'Ѥ' 'Ͱ' 'ͱ' 'Ϳ' - // '😀' '⌘' '😀' '😁' '😋' -} - -func ExampleString() { - gen := rapid.String() - - for i := 0; i < 5; i++ { - fmt.Printf("%q\n", gen.Example(i)) - } - // Output: - // "\\߾⃝!/?Ⱥ֍" - // "\u2006𑨷" - // "?﹩\u0603ᾢ" - // ".*%:<%৲" - // "" -} - -func ExampleStringOf() { - gen := rapid.StringOf(rapid.RuneFrom(nil, unicode.Tibetan)) - - for i := 0; i < 5; i++ { - fmt.Printf("%q\n", gen.Example(i)) - } - // Output: - // "༁༭༇ཬ༆༐༖ༀྸ༁༆༎ༀ༁ཱི༂༨ༀ༂" - // "༂༁ༀ༂༴ༀ༁ྵ" - // "ༀ༴༁༅ན༃༁༎ྼ༄༽" - // "༎༂༎ༀༀༀཌྷ༂ༀྥ" - // "" -} - -func ExampleStringN() { - gen := rapid.StringN(5, 5, -1) - - for i := 0; i < 5; i++ { - fmt.Printf("%q\n", gen.Example(i)) - } - // Output: - // "\\߾⃝!/" - // "\u2006𑨷%\v\ufeff" - // "?﹩\u0603ᾢÉ" - // ".*%:<" - // ":?\"~¤" -} - -func ExampleStringOfN() { - gen := rapid.StringOfN(rapid.ByteRange(65, 90), 5, 5, -1) - - for i := 0; i < 5; i++ { - fmt.Printf("%q\n", gen.Example(i)) - } - // Output: - // "AXYHC" - // "ESAAC" - // "AUGWT" - // "BRIOX" - // "LYATZ" -} - -func ExampleStringMatching() { - gen := rapid.StringMatching(`\(?([0-9]{3})\)?([ .-]?)([0-9]{3})([ .-]?)([0-9]{4})`) - - for i := 0; i < 5; i++ { - fmt.Printf("%q\n", gen.Example(i)) - } - // Output: - // "(532) 649-9610" - // "901)-5783983" - // "914.444.1575" - // "(316 696.3584" - // "816)0861080" -} - -func ExampleSliceOfBytesMatching() { - gen := rapid.SliceOfBytesMatching(`[CAGT]+`) - - for i := 0; i < 5; i++ { - fmt.Printf("%q\n", gen.Example(i)) - } - // Output: - // "CCTTGAGAGCGATACGGAAG" - // "GCAGAACT" - // "AACCGTCGAG" - // "GGGAAAAGAT" - // "AGTG" -} diff --git a/vendor/pgregory.net/rapid/strings_external_test.go b/vendor/pgregory.net/rapid/strings_external_test.go deleted file mode 100644 index 15391a2..0000000 --- a/vendor/pgregory.net/rapid/strings_external_test.go +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2019 Gregory Petrosyan <gregory.petrosyan@gmail.com> -// -// 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 https://mozilla.org/MPL/2.0/. - -package rapid_test - -import ( - "strconv" - "testing" - "unicode" - "unicode/utf8" - - . "pgregory.net/rapid" -) - -func TestStringExamples(t *testing.T) { - g := StringN(10, -1, -1) - - for i := 0; i < 100; i++ { - s := g.Example().(string) - t.Log(len(s), s) - } -} - -func TestRegexpExamples(t *testing.T) { - g := StringMatching("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$") - - for i := 0; i < 100; i++ { - s := g.Example().(string) - t.Log(len(s), s) - } -} - -func TestStringOfRunesIsUTF8(t *testing.T) { - t.Parallel() - - gens := []*Generator{ - String(), - StringN(2, 10, -1), - StringOf(Rune()), - StringOfN(Rune(), 2, 10, -1), - StringOf(RuneFrom(nil, unicode.Cyrillic)), - StringOf(RuneFrom([]rune{'a', 'b', 'c'})), - } - - for _, g := range gens { - t.Run(g.String(), MakeCheck(func(t *T) { - s := g.Draw(t, "s").(string) - if !utf8.ValidString(s) { - t.Fatalf("invalid UTF-8 string: %q", s) - } - })) - } -} - -func TestStringRuneCountLimits(t *testing.T) { - t.Parallel() - - genFuncs := []func(i, j int) *Generator{ - func(i, j int) *Generator { return StringN(i, j, -1) }, - func(i, j int) *Generator { return StringOfN(Rune(), i, j, -1) }, - } - - for i, gf := range genFuncs { - t.Run(strconv.Itoa(i), MakeCheck(func(t *T) { - minRunes := IntRange(0, 256).Draw(t, "minRunes").(int) - maxRunes := IntMin(minRunes).Draw(t, "maxRunes").(int) - - s := gf(minRunes, maxRunes).Draw(t, "s").(string) - n := utf8.RuneCountInString(s) - if n < minRunes { - t.Fatalf("got string with %v runes with lower limit %v", n, minRunes) - } - if n > maxRunes { - t.Fatalf("got string with %v runes with upper limit %v", n, maxRunes) - } - })) - } -} - -func TestStringNMaxLen(t *testing.T) { - t.Parallel() - - genFuncs := []func(int) *Generator{ - func(i int) *Generator { return StringN(-1, -1, i) }, - func(i int) *Generator { return StringOfN(Rune(), -1, -1, i) }, - func(i int) *Generator { return StringOfN(Byte(), -1, i, -1) }, - } - - for i, gf := range genFuncs { - t.Run(strconv.Itoa(i), MakeCheck(func(t *T) { - maxLen := IntMin(0).Draw(t, "maxLen").(int) - s := gf(maxLen).Draw(t, "s").(string) - if len(s) > maxLen { - t.Fatalf("got string of length %v with maxLen %v", len(s), maxLen) - } - })) - } -} diff --git a/vendor/pgregory.net/rapid/utils.go b/vendor/pgregory.net/rapid/utils.go index 3499aaf..0c54314 100644 --- a/vendor/pgregory.net/rapid/utils.go +++ b/vendor/pgregory.net/rapid/utils.go @@ -24,7 +24,7 @@ func bitmask64(n uint) uint64 { } func genFloat01(s bitStream) float64 { - return float64(s.drawBits(53)) / (1 << 53) + return float64(s.drawBits(53)) * 0x1.0p-53 } func genGeom(s bitStream, p float64) uint64 { @@ -207,14 +207,15 @@ type repeat struct { rejected bool rejections int forceStop bool + label string } -func newRepeat(minCount int, maxCount int, avgCount float64) *repeat { +func newRepeat(minCount int, maxCount int, avgCount float64, label string) *repeat { if minCount < 0 { minCount = 0 } if maxCount < 0 { - maxCount = maxInt + maxCount = math.MaxInt } if avgCount < 0 { avgCount = float64(minCount) + math.Min(math.Max(float64(minCount), small), (float64(maxCount)-float64(minCount))/2) @@ -226,6 +227,7 @@ func newRepeat(minCount int, maxCount int, avgCount float64) *repeat { avgCount: avgCount, pContinue: 1 - 1/(1+avgCount-float64(minCount)), // TODO was no -minCount intentional? group: -1, + label: label + repeatLabel, } } @@ -233,12 +235,12 @@ func (r *repeat) avg() int { return int(math.Ceil(r.avgCount)) } -func (r *repeat) more(s bitStream, label string) bool { +func (r *repeat) more(s bitStream) bool { if r.group >= 0 { s.endGroup(r.group, r.rejected) } - r.group = s.beginGroup(label+repeatLabel, true) + r.group = s.beginGroup(r.label, true) r.rejected = false pCont := r.pContinue diff --git a/vendor/pgregory.net/rapid/utils_test.go b/vendor/pgregory.net/rapid/utils_test.go deleted file mode 100644 index b62e73d..0000000 --- a/vendor/pgregory.net/rapid/utils_test.go +++ /dev/null @@ -1,346 +0,0 @@ -// Copyright 2019 Gregory Petrosyan <gregory.petrosyan@gmail.com> -// -// 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 https://mozilla.org/MPL/2.0/. - -package rapid - -import ( - "flag" - "fmt" - "math" - "math/bits" - "sort" - "strconv" - "strings" - "testing" -) - -var flaky = flag.Bool("flaky", false, "run flaky tests") - -func createRandomBitStream(t *testing.T) bitStream { - t.Helper() - - seed := baseSeed() - t.Logf("random seed %v", seed) - - return newRandomBitStream(seed, false) -} - -func TestGenFloat01(t *testing.T) { - t.Parallel() - - s1 := &bufBitStream{buf: []uint64{0}} - f1 := genFloat01(s1) - if f1 != 0 { - t.Errorf("got %v instead of 0", f1) - } - - s2 := &bufBitStream{buf: []uint64{math.MaxUint64}} - f2 := genFloat01(s2) - if f2 == 1 { - t.Errorf("got impossible 1") - } -} - -func TestGenGeom(t *testing.T) { - t.Parallel() - - s1 := &bufBitStream{buf: []uint64{0}} - i1 := genGeom(s1, 0.1) - if i1 != 0 { - t.Errorf("got %v instead of 0 for 0.1", i1) - } - - s2 := &bufBitStream{buf: []uint64{0}} - i2 := genGeom(s2, 1) - if i2 != 0 { - t.Errorf("got %v instead of 0 for 1", i2) - } -} - -func TestGenGeomMean(t *testing.T) { - t.Parallel() - if !*flaky { - t.Skip("flaky") - } - - s := newRandomBitStream(baseSeed(), false) - - for i := 0; i < 100; i++ { - t.Run(strconv.Itoa(i), func(t *testing.T) { - p := genFloat01(s) - - var geoms []uint64 - for i := 0; i < 10000; i++ { - geoms = append(geoms, genGeom(s, p)) - } - - avg := 0.0 - for _, f := range geoms { - avg += float64(f) - } - avg /= float64(len(geoms)) - - mean := (1 - p) / p - if math.Abs(avg-mean) > 0.5 { // true science - t.Fatalf("for p=%v geom avg=%v vs expected mean=%v", p, avg, mean) - } - }) - } -} - -func TestUintsExamplesHist(t *testing.T) { - s := newRandomBitStream(baseSeed(), false) - - for _, n := range []int{2, 3, 4, 5, 6, 8, 16, 32, 64} { - t.Run(strconv.Itoa(n), func(t *testing.T) { - var lines []string - for i := 0; i < 50; i++ { - n, _, _ := genUintN(s, bitmask64(uint(n)), true) - b := bits.Len64(n) - l := fmt.Sprintf("% 24d %s % 3d", n, strings.Repeat("*", b)+strings.Repeat(" ", 64-b), b) - lines = append(lines, l) - } - - sort.Strings(lines) - t.Log("\n" + strings.Join(lines, "\n")) - }) - } -} - -func ensureWithin3Sigma(t *testing.T, ctx interface{}, y int, n int, p float64) { - t.Helper() - - mu := float64(n) * p - s := math.Sqrt(float64(n) * p * (1 - p)) - - if float64(y) < mu-3*s || float64(y) > mu+3*s { - if ctx != nil { - t.Errorf("for %v: got %v out of %v (p %v, mu %v, stddev %v)", ctx, y, n, p, mu, s) - } else { - t.Errorf("got %v out of %v (p %v, mu %v, stddev %v)", y, n, p, mu, s) - } - } -} - -func TestGenUintN(t *testing.T) { - t.Parallel() - if !*flaky { - t.Skip("flaky") - } - - s := createRandomBitStream(t) - max := []uint64{0, 1, 2, 5, 13} - - for _, m := range max { - r := make([]int, m+1) - n := 1000 - for i := 0; i < n; i++ { - u, _, _ := genUintN(s, m, false) - r[u]++ - } - - for u := range r { - ensureWithin3Sigma(t, m, r[u], n, 1/float64(m+1)) - } - } -} - -func TestGenUintRange(t *testing.T) { - t.Parallel() - if !*flaky { - t.Skip("flaky") - } - - s := createRandomBitStream(t) - ranges := [][]uint64{ - {0, 0}, - {0, 1}, - {0, 2}, - {1, 1}, - {1, 3}, - {3, 7}, - {math.MaxUint64 - 3, math.MaxUint64}, - {math.MaxUint64 - 1, math.MaxUint64}, - {math.MaxUint64, math.MaxUint64}, - } - - for _, r := range ranges { - m := map[uint64]int{} - n := 1000 - for i := 0; i < n; i++ { - u, _, _ := genUintRange(s, r[0], r[1], false) - m[u]++ - } - - for u := range m { - if u < r[0] || u > r[1] { - t.Errorf("%v out of range [%v, %v]", u, r[0], r[1]) - } - ensureWithin3Sigma(t, fmt.Sprintf("%v from %v", u, r), m[u], n, 1/float64(r[1]-r[0]+1)) - } - } -} - -func TestGenIntRange(t *testing.T) { - t.Parallel() - if !*flaky { - t.Skip("flaky") - } - - s := createRandomBitStream(t) - ranges := [][]int64{ - {0, 0}, - {0, 1}, - {0, 2}, - {1, 1}, - {1, 3}, - {3, 7}, - {math.MaxInt64 - 3, math.MaxInt64}, - {math.MaxInt64 - 1, math.MaxInt64}, - {math.MaxInt64, math.MaxInt64}, - {-1, -1}, - {-2, -1}, - {-3, 0}, - {-1, 1}, - {-1, 3}, - {-3, 7}, - {-7, -3}, - {math.MinInt64, math.MinInt64 + 3}, - {math.MinInt64, math.MinInt64 + 1}, - {math.MinInt64, math.MinInt64}, - } - - for _, r := range ranges { - m := map[int64]int{} - n := 1000 - for i := 0; i < n; i++ { - u, _, _ := genIntRange(s, r[0], r[1], false) - m[u]++ - } - - for u := range m { - if u < r[0] || u > r[1] { - t.Errorf("%v out of range [%v, %v]", u, r[0], r[1]) - } - ensureWithin3Sigma(t, fmt.Sprintf("%v from %v", u, r), m[u], n, 1/float64(r[1]-r[0]+1)) - } - } -} - -func TestFlipBiasedCoin(t *testing.T) { - t.Parallel() - if !*flaky { - t.Skip("flaky") - } - - s := createRandomBitStream(t) - ps := []float64{0, 0.3, 0.5, 0.7, 1} - - for _, p := range ps { - n := 1000 - y := 0 - for i := 0; i < n; i++ { - if flipBiasedCoin(s, p) { - y++ - } - } - - ensureWithin3Sigma(t, p, y, n, p) - } -} - -func TestLoadedDie(t *testing.T) { - t.Parallel() - if !*flaky { - t.Skip("flaky") - } - - s := createRandomBitStream(t) - weights := [][]int{ - {1}, - {1, 2}, - {3, 2, 1}, - {1, 2, 4, 2, 1}, - } - - for _, ws := range weights { - d := newLoadedDie(ws) - n := 1000 - r := make([]int, len(ws)) - - for i := 0; i < n; i++ { - r[d.roll(s)]++ - } - - total := 0 - for _, w := range ws { - total += w - } - - for i, w := range ws { - ensureWithin3Sigma(t, ws, r[i], n, float64(w)/float64(total)) - } - } -} - -func TestRepeat(t *testing.T) { - t.Parallel() - if !*flaky { - t.Skip("flaky") - } - - s := createRandomBitStream(t) - mmas := [][3]int{ - {0, 0, 0}, - {0, 1, 0}, - {0, 1, 1}, - {1, 1, 1}, - {3, 3, 3}, - {3, 7, 3}, - {3, 7, 5}, - {3, 7, 7}, - {0, 10, 5}, - {1, 10, 6}, - {0, 50, 5}, - {1, 50, 6}, - {1000, math.MaxInt32, 1000 + 1}, - {1000, math.MaxInt32, 1000 + 2}, - {1000, math.MaxInt32, 1000 + 7}, - {1000, math.MaxInt32, 1000 + 13}, - {1000, math.MaxInt32, 1000 + 100}, - {1000, math.MaxInt32, 1000 + 1000}, - } - - for _, mma := range mmas { - min, max, avg := mma[0], mma[1], mma[2] - - n := 5000 - c := make([]int, n) - for i := 0; i < n; i++ { - r := newRepeat(min, max, float64(avg)) - for r.more(s, "") { - c[i]++ - } - - if c[i] < min || c[i] > max { - t.Errorf("got %v tries with bounds [%v, %v]", c[i], min, max) - } - } - - if min == 1000 && max == math.MaxInt32 { - mu := float64(0) - for _, e := range c { - mu += float64(e) - } - mu /= float64(len(c)) - - diff := math.Abs(mu - float64(avg)) - if diff > 0.5 { // true science - t.Errorf("real avg %v vs desired %v, diff %v (%v tries)", mu, avg, diff, n) - } - } - } -} diff --git a/vendor/pgregory.net/rapid/vis_test.go b/vendor/pgregory.net/rapid/vis_test.go deleted file mode 100644 index 839b69a..0000000 --- a/vendor/pgregory.net/rapid/vis_test.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2019 Gregory Petrosyan <gregory.petrosyan@gmail.com> -// -// 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 https://mozilla.org/MPL/2.0/. - -package rapid - -import ( - "math" - "os" - "testing" -) - -func TestDataVis(t *testing.T) { - t.Parallel() - - f, err := os.Create("vis-test.html") - if err != nil { - t.Fatalf("failed to create vis html file: %v", err) - - } - defer func() { _ = f.Close() }() - - data := []uint64{ - 0, - 0x55, - 0xaa, - math.MaxUint8, - 0x5555, - 0xaaaa, - math.MaxUint16, - 0x55555555, - 0xaaaaaaaa, - math.MaxUint32, - 0x5555555555555555, - 0xaaaaaaaaaaaaaaaa, - math.MaxUint64, - } - - groups := []groupInfo{ - {begin: 0, end: 13, label: ""}, - {begin: 1, end: 1 + 3, label: "8-bit"}, - {begin: 3, end: 4, label: "0xff", discard: true}, - {begin: 4, end: 4 + 3, label: "16-bit"}, - {begin: 7, end: 13, label: "big integers"}, - {begin: 7, end: 7 + 3, label: "32-bit"}, - {begin: 10, end: 10 + 3, label: "64-bit"}, - } - - rd := []recordedBits{ - {data: data, groups: groups}, - } - - g := SliceOf(SliceOf(Uint().Filter(func(i uint) bool { return i%2 == 1 }))).Filter(func(s [][]uint) bool { return len(s) > 0 }) - for { - s := newRandomBitStream(baseSeed(), true) - _, err := recoverValue(g, newT(nil, s, false, nil)) - if err != nil && !err.isInvalidData() { - t.Errorf("unexpected error %v", err) - } - - rd = append(rd, recordedBits{data: s.data, groups: s.groups}) - - if err == nil { - break - } - } - - err = visWriteHTML(f, "test", rd) - if err != nil { - t.Errorf("visWriteHTML error: %v", err) - } -} From 401c49614e7c1d4b52d0c380c1ccf4cffc25cbe1 Mon Sep 17 00:00:00 2001 From: ValentinMontmirail <valentin.montmirail@se.com> Date: Sun, 21 Jan 2024 17:25:20 +0100 Subject: [PATCH 6/6] Update all packages to their latest versions. We still need a baseline image on 1.21+ --- Makefile | 2 +- cmd/modbus-cli/main.go | 4 ++-- go.sum | 8 -------- rtuclient_prop_test.go | 6 +++--- tcpclient_prop_test.go | 8 ++++---- 5 files changed, 10 insertions(+), 18 deletions(-) diff --git a/Makefile b/Makefile index 15a4048..40638b2 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ GO_FILES := $(shell find . -type f -name "*.go") GO_BUILD := CGO_ENABLED=0 go build -ldflags "-w -s" -GO_TOOLS := public.ecr.aws/gridx/base-images:modbus-dev-1.19.latest +GO_TOOLS := public.ecr.aws/gridx/base-images:modbus-dev-1.21.latest DOCKER_RUN := docker run --init --rm -v $$PWD:/go/src/github.com/grid-x/modbus -w /go/src/github.com/grid-x/modbus GO_RUN := ${DOCKER_RUN} ${GO_TOOLS} bash -c diff --git a/cmd/modbus-cli/main.go b/cmd/modbus-cli/main.go index b315703..c2366ca 100644 --- a/cmd/modbus-cli/main.go +++ b/cmd/modbus-cli/main.go @@ -6,7 +6,6 @@ import ( "flag" "fmt" "io" - "log" "log/slog" "math" "net/url" @@ -96,7 +95,8 @@ func main() { os.Exit(-1) } if err := handler.Connect(); err != nil { - log.Fatal(err) + logger.Error(err.Error()) + os.Exit(-1) } defer handler.Close() diff --git a/go.sum b/go.sum index 9b3498f..845532a 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +1,6 @@ -github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/grid-x/serial v0.0.0-20191104121038-e24bc9bf6f08 h1:syBxnRYnSPUDdkdo5U4sy2roxBPQDjNiw4od7xlsABQ= -github.com/grid-x/serial v0.0.0-20191104121038-e24bc9bf6f08/go.mod h1:kdOd86/VGFWRrtkNwf1MPk0u1gIjc4Y7R2j7nhwc7Rk= github.com/grid-x/serial v0.0.0-20211107191517-583c7356b3aa h1:Rsn6ARgNkXrsXJIzhkE4vQr5Gbx2LvtEMv4BJOK4LyU= github.com/grid-x/serial v0.0.0-20211107191517-583c7356b3aa/go.mod h1:kdOd86/VGFWRrtkNwf1MPk0u1gIjc4Y7R2j7nhwc7Rk= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -pgregory.net/rapid v0.4.7 h1:MTNRktPuv5FNqOO151TM9mDTa+XHcX6ypYeISDVD14g= -pgregory.net/rapid v0.4.7/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU= pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= diff --git a/rtuclient_prop_test.go b/rtuclient_prop_test.go index 4da20a6..3cccf5b 100644 --- a/rtuclient_prop_test.go +++ b/rtuclient_prop_test.go @@ -10,12 +10,12 @@ import ( func TestRTUEncodeDecode(t *testing.T) { rapid.Check(t, func(t *rapid.T) { packager := &rtuPackager{ - SlaveID: rapid.Byte().Draw(t, "SlaveID").(byte), + SlaveID: rapid.Byte().Draw(t, "SlaveID"), } pdu := &ProtocolDataUnit{ - FunctionCode: rapid.Byte().Draw(t, "FunctionCode").(byte), - Data: rapid.SliceOf(rapid.Byte()).Draw(t, "Data").([]byte), + FunctionCode: rapid.Byte().Draw(t, "FunctionCode"), + Data: rapid.SliceOf(rapid.Byte()).Draw(t, "Data"), } raw, err := packager.Encode(pdu) diff --git a/tcpclient_prop_test.go b/tcpclient_prop_test.go index 87f18e2..530dd88 100644 --- a/tcpclient_prop_test.go +++ b/tcpclient_prop_test.go @@ -10,13 +10,13 @@ import ( func TestTCPEncodeDecode(t *testing.T) { rapid.Check(t, func(t *rapid.T) { packager := &tcpPackager{ - transactionID: rapid.Uint32().Draw(t, "transactionID").(uint32), - SlaveID: rapid.Byte().Draw(t, "SlaveID").(byte), + transactionID: rapid.Uint32().Draw(t, "transactionID"), + SlaveID: rapid.Byte().Draw(t, "SlaveID"), } pdu := &ProtocolDataUnit{ - FunctionCode: rapid.Byte().Draw(t, "FunctionCode").(byte), - Data: rapid.SliceOf(rapid.Byte()).Draw(t, "Data").([]byte), + FunctionCode: rapid.Byte().Draw(t, "FunctionCode"), + Data: rapid.SliceOf(rapid.Byte()).Draw(t, "Data"), } raw, err := packager.Encode(pdu)