From 341f034e3a013f65a293eb1ad2709bd826504f7d Mon Sep 17 00:00:00 2001 From: Benedikt Bongartz Date: Mon, 22 Nov 2021 21:18:05 +0100 Subject: [PATCH 01/51] feat(cmd/modbus-cli): support writing single coils --- cmd/modbus-cli/main.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/cmd/modbus-cli/main.go b/cmd/modbus-cli/main.go index 2eba9b0..6517091 100644 --- a/cmd/modbus-cli/main.go +++ b/cmd/modbus-cli/main.go @@ -134,6 +134,20 @@ func exec( switch fnCode { case 0x01: result, err = client.ReadCoils(uint16(register), uint16(quantity)) + case 0x05: + const ( + coilOn uint16 = 0xFF00 + coilOff uint16 = 0x0000 + ) + v := uint16(wval) + if v != coilOn && v != coilOff { + err = fmt.Errorf( + "illegal: expect %X to request the output to be on or %X to be off, got: %X", + coilOn, coilOff, v, + ) + return + } + result, err = client.WriteSingleCoil(uint16(register), v) case 0x06: max := float64(math.MaxUint16) if wval > max || wval < 0 { From cbbbae8be5964f65b3631130ec7e2a5b65bad9c7 Mon Sep 17 00:00:00 2001 From: andig Date: Thu, 23 Dec 2021 14:34:49 +0100 Subject: [PATCH 02/51] Add connect delay --- tcpclient.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tcpclient.go b/tcpclient.go index 1582e89..e241ca1 100644 --- a/tcpclient.go +++ b/tcpclient.go @@ -131,6 +131,8 @@ type tcpTransporter struct { LinkRecoveryTimeout time.Duration // Recovery timeout if the protocol is malformed, e.g. wrong transaction ID ProtocolRecoveryTimeout time.Duration + // Silent period after successful connection + ConnectDelay time.Duration // Transmission logger Logger logger @@ -273,6 +275,9 @@ func (mb *tcpTransporter) connect() error { return err } mb.conn = conn + + // silent period + time.Sleep(mb.ConnectDelay) } return nil } From 689bbd9c062361284b6b664644bc592b2625b6d1 Mon Sep 17 00:00:00 2001 From: andig Date: Thu, 23 Dec 2021 14:39:11 +0100 Subject: [PATCH 03/51] Use current go idioms --- rtuclient.go | 2 +- tcpclient.go | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/rtuclient.go b/rtuclient.go index 224c70f..615d95c 100644 --- a/rtuclient.go +++ b/rtuclient.go @@ -174,7 +174,7 @@ func readIncrementally(slaveID, functionCode byte, r io.Reader, deadline time.Ti if r == nil { return nil, fmt.Errorf("reader is nil") } - buf := make([]byte, 1, 1) + buf := make([]byte, 1) _, err := io.ReadAtLeast(r, buf, 1) if err != nil { return nil, err diff --git a/tcpclient.go b/tcpclient.go index 1582e89..0179865 100644 --- a/tcpclient.go +++ b/tcpclient.go @@ -191,17 +191,17 @@ func (mb *tcpTransporter) Send(aduRequest []byte) (aduResponse []byte, err error } } if _, ok := err.(ErrTCPHeaderLength); !ok { - if mb.ProtocolRecoveryTimeout > 0 && recoveryDeadline.Sub(time.Now()) > 0 { + if mb.ProtocolRecoveryTimeout > 0 && time.Until(recoveryDeadline) > 0 { continue // TCP header OK but modbus frame not } return // no time left, report error } - if mb.LinkRecoveryTimeout == 0 || recoveryDeadline.Sub(time.Now()) < 0 { + if mb.LinkRecoveryTimeout == 0 || time.Until(recoveryDeadline) < 0 { return // TCP header not OK, but no time left, report error } // Read attempt failed } else if (err != io.EOF && err != io.ErrUnexpectedEOF) || - mb.LinkRecoveryTimeout == 0 || recoveryDeadline.Sub(time.Now()) < 0 { + mb.LinkRecoveryTimeout == 0 || time.Until(recoveryDeadline) < 0 { return } mb.logf("modbus: close connection and retry, because of %v", err) @@ -335,8 +335,8 @@ func (mb *tcpTransporter) closeIdle() { if mb.IdleTimeout <= 0 { return } - idle := time.Now().Sub(mb.lastActivity) - if idle >= mb.IdleTimeout { + + if idle := time.Since(mb.lastActivity); idle >= mb.IdleTimeout { mb.logf("modbus: closing connection due to idle timeout: %v", idle) mb.close() } From 66aa1fd879b31add3dae1330ecf43507657743b9 Mon Sep 17 00:00:00 2001 From: Georg Reinke Date: Mon, 10 Jan 2022 16:06:31 +0100 Subject: [PATCH 04/51] fix: print original function code for exceptions Fixes #46. --- modbus.go | 2 +- test/client.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modbus.go b/modbus.go index cb0c1ec..f83d1d5 100644 --- a/modbus.go +++ b/modbus.go @@ -89,7 +89,7 @@ func (e *Error) Error() string { default: name = "unknown" } - return fmt.Sprintf("modbus: exception '%v' (%s), function '%v'", e.ExceptionCode, name, e.FunctionCode) + return fmt.Sprintf("modbus: exception '%v' (%s), function '%v'", e.ExceptionCode, name, e.FunctionCode&0x7F) } // ProtocolDataUnit (PDU) is independent of underlying communication layers. diff --git a/test/client.go b/test/client.go index 9433b61..6635129 100644 --- a/test/client.go +++ b/test/client.go @@ -132,7 +132,7 @@ func ClientTestReadFIFOQueue(t *testing.T, client modbus.Client) { results, err := client.ReadFIFOQueue(address) // Server not implemented if err != nil { - AssertEquals(t, "modbus: exception '1' (illegal function), function '152'", err.Error()) + AssertEquals(t, "modbus: exception '1' (illegal function), function '24'", err.Error()) } else { AssertEquals(t, 0, len(results)) } From 9ebd2ff8301e9370014e11fec2b305f41f3f0b85 Mon Sep 17 00:00:00 2001 From: Georg Reinke Date: Tue, 8 Feb 2022 10:01:57 +0100 Subject: [PATCH 05/51] fix: retry reading query response on transaction id mismatch This was previously attempted with the flushAll call, but this doesn't seem to be reliable in practice - since it sets the read timeout to time.Now(), this means that in practice the Read in most cases actually returns directly without attempting to read. Instead of trying to extend the deadline for the flushing read by some arbitrary small value, this fix handles the problem at a different place: if we detect a transaction ID mismatch by 1, we simply ignore the response and assume that a matching response will follow. This required some refactoring of the reading code since just retrying the read without another write wasn't easily possible. --- tcpclient.go | 113 ++++++++++++++++++++++++++++------------------ tcpclient_test.go | 63 ++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 45 deletions(-) diff --git a/tcpclient.go b/tcpclient.go index 0b0b9b3..d92c70f 100644 --- a/tcpclient.go +++ b/tcpclient.go @@ -143,6 +143,15 @@ type tcpTransporter struct { lastActivity time.Time } +// helper value to signify what to do in Send +type readResult int + +const ( + readResultDone readResult = iota + readResultRetry + readResultCloseRetry +) + // Send sends data to server and ensures response length is greater than header length. func (mb *tcpTransporter) Send(aduRequest []byte) (aduResponse []byte, err error) { mb.mu.Lock() @@ -157,15 +166,6 @@ func (mb *tcpTransporter) Send(aduRequest []byte) (aduResponse []byte, err error return } - // If an answer to a previously timed-out request is already in teh buffer, this will result - // in a transaction ID mismatch from which we will never recover. To prevent this, just - // flush any previous reponses before launching the next poll. That's throwing away - // possibly useful data, but the previous request was already satisfied with a timeout - // error so that probably makes the most sense here. - - // Be aware that this call resets the read deadline. - mb.flushAll() - // Set timer to close when idle mb.lastActivity = time.Now() mb.startCloseTimer() @@ -182,7 +182,26 @@ func (mb *tcpTransporter) Send(aduRequest []byte) (aduResponse []byte, err error if _, err = mb.conn.Write(aduRequest); err != nil { return } - // Read header first + + var res readResult + aduResponse, res, err = mb.readResponse(aduRequest, data[:], recoveryDeadline) + switch res { + case readResultDone: + return + case readResultRetry: + continue + } + + mb.logf("modbus: close connection and retry, because of %v", err) + + mb.close() + time.Sleep(mb.LinkRecoveryTimeout) + } +} + +func (mb *tcpTransporter) readResponse(aduRequest []byte, data []byte, recoveryDeadline time.Time) (aduResponse []byte, res readResult, err error) { + // res is readResultDone by default, which either means we succeeded or err contains the fatal error + for { if _, err = io.ReadFull(mb.conn, data[:tcpHeaderSize]); err == nil { aduResponse, err = mb.processResponse(data[:]) if err == nil { @@ -192,24 +211,44 @@ func (mb *tcpTransporter) Send(aduRequest []byte) (aduResponse []byte, err error return // everything is OK } } - if _, ok := err.(ErrTCPHeaderLength); !ok { + switch v := err.(type) { + case ErrTCPHeaderLength: + if mb.LinkRecoveryTimeout > 0 && time.Until(recoveryDeadline) > 0 { + // TCP header not OK - retry with another query + res = readResultRetry + return + } + // no time left, report error + return + case errTransactionIDMismatch: if mb.ProtocolRecoveryTimeout > 0 && time.Until(recoveryDeadline) > 0 { - continue // TCP header OK but modbus frame not + if v.got == v.expected-1 { + // most likely, we simply had a timeout for the earlier query and now read the (late) response. Ignore it + // and assume that the response will come *without* sending another query. (If we send another query + // with transactionId X+1 here, we would again get a transactionMismatchError if the response to + // transactionId X is already in the buffer). + continue + } + // some other mismatch, still in time - retry with another query + res = readResultRetry + return + } + return // no time left, report error + default: + if mb.ProtocolRecoveryTimeout > 0 && time.Until(recoveryDeadline) > 0 { + // TCP header OK but modbus frame not - retry with another query + res = readResultRetry + return } return // no time left, report error } - if mb.LinkRecoveryTimeout == 0 || time.Until(recoveryDeadline) < 0 { - return // TCP header not OK, but no time left, report error - } - // Read attempt failed } else if (err != io.EOF && err != io.ErrUnexpectedEOF) || mb.LinkRecoveryTimeout == 0 || time.Until(recoveryDeadline) < 0 { return } - mb.logf("modbus: close connection and retry, because of %v", err) - - mb.close() - time.Sleep(mb.LinkRecoveryTimeout) + // any other error, but recovery deadline isn't reached yet - close and retry + res = readResultCloseRetry + return } } @@ -235,12 +274,20 @@ func (mb *tcpTransporter) processResponse(data []byte) (aduResponse []byte, err return } +type errTransactionIDMismatch struct { + got, expected uint16 +} + +func (e errTransactionIDMismatch) Error() string { + return fmt.Sprintf("modbus: response transaction id '%v' does not match request '%v'", e.got, e.expected) +} + func verify(aduRequest []byte, aduResponse []byte) (err error) { // Transaction id responseVal := binary.BigEndian.Uint16(aduResponse) requestVal := binary.BigEndian.Uint16(aduRequest) if responseVal != requestVal { - err = fmt.Errorf("modbus: response transaction id '%v' does not match request '%v'", responseVal, requestVal) + err = errTransactionIDMismatch{got: responseVal, expected: requestVal} return } // Protocol id @@ -346,27 +393,3 @@ func (mb *tcpTransporter) closeIdle() { mb.close() } } - -// flushAll implements a non-blocking read flush. Be warned it resets -// the read deadline. -func (mb *tcpTransporter) flushAll() (int, error) { - if err := mb.conn.SetReadDeadline(time.Now()); err != nil { - return 0, err - } - - count := 0 - buffer := make([]byte, 1024) - - for { - n, err := mb.conn.Read(buffer) - - if err != nil { - return count + n, err - } else if n > 0 { - count = count + n - } else { - // didn't flush any new bytes, return - return count, err - } - } -} diff --git a/tcpclient_test.go b/tcpclient_test.go index 63a215e..7fa5c2d 100644 --- a/tcpclient_test.go +++ b/tcpclient_test.go @@ -93,6 +93,69 @@ func TestErrTCPHeaderLength_Error(t *testing.T) { _ = ErrTCPHeaderLength(1000).Error() } +func TestTCPTransactionMismatchRetry(t *testing.T) { + ln, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + defer ln.Close() + + done := make(chan struct{}) + defer close(done) + data := []byte{0xCA, 0xFE} + go func() { + conn, err := ln.Accept() + if err != nil { + t.Error(err) + return + } + defer conn.Close() + time.Sleep(1 * time.Second) + packager := &tcpPackager{SlaveID: 0} + pdu := &ProtocolDataUnit{ + FunctionCode: FuncCodeReadInputRegisters, + Data: append([]byte{0x02}, data...), + } + data1, err := packager.Encode(pdu) + if err != nil { + t.Error(err) + return + } + // encoding same PDU twice will increment the transaction id + data2, err := packager.Encode(pdu) + if err != nil { + t.Error(err) + return + } + if _, err := conn.Write(data1); err != nil { + t.Error(err) + return + } + if _, err := conn.Write(data2); err != nil { + t.Error(err) + return + } + // keep the connection open until the main routine is finished + <-done + }() + handler := NewTCPClientHandler(ln.Addr().String()) + handler.Timeout = 1 * time.Second + handler.ProtocolRecoveryTimeout = 50 * time.Millisecond + client := NewClient(handler) + resp, err := client.ReadInputRegisters(0, 1) + opError, ok := err.(*net.OpError) + if !ok || !opError.Timeout() { + t.Fatalf("expected timeout error, got %q", err) + } + resp, err = client.ReadInputRegisters(0, 1) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(resp, data) { + t.Fatalf("got wrong response: got %q wanted %q", resp, data) + } +} + func BenchmarkTCPEncoder(b *testing.B) { encoder := tcpPackager{ SlaveID: 10, From f1937e7495d8a53dc3e088b945501477981df965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlo=20Nie=C3=9Fen?= Date: Thu, 14 Apr 2022 11:10:23 +0200 Subject: [PATCH 06/51] feat(tcpclient): added skipping of transaction mismatches by more than one packet --- tcpclient.go | 12 +++++++++++- tcpclient_test.go | 18 +++++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/tcpclient.go b/tcpclient.go index d92c70f..767d6ce 100644 --- a/tcpclient.go +++ b/tcpclient.go @@ -141,6 +141,9 @@ type tcpTransporter struct { conn net.Conn closeTimer *time.Timer lastActivity time.Time + + lastAttemptedTransactionID uint16 + lastSuccessfulTransactionID uint16 } // helper value to signify what to do in Send @@ -183,10 +186,14 @@ func (mb *tcpTransporter) Send(aduRequest []byte) (aduResponse []byte, err error return } + mb.lastAttemptedTransactionID = binary.BigEndian.Uint16(aduRequest) var res readResult aduResponse, res, err = mb.readResponse(aduRequest, data[:], recoveryDeadline) switch res { case readResultDone: + if err == nil { + mb.lastSuccessfulTransactionID = binary.BigEndian.Uint16(aduResponse) + } return case readResultRetry: continue @@ -222,7 +229,10 @@ func (mb *tcpTransporter) readResponse(aduRequest []byte, data []byte, recoveryD return case errTransactionIDMismatch: if mb.ProtocolRecoveryTimeout > 0 && time.Until(recoveryDeadline) > 0 { - if v.got == v.expected-1 { + // the first condition check for a normal transaction id mismatch. The second part of the condition check for a wrap-around. If a wraparound is + // detected (last attempt is smaller than last success), the id can be higher than the last success or lower than the last attempt, but not both + if (v.got > mb.lastSuccessfulTransactionID && v.got < mb.lastAttemptedTransactionID) || + (mb.lastAttemptedTransactionID < mb.lastSuccessfulTransactionID && (v.got > mb.lastSuccessfulTransactionID || v.got < mb.lastAttemptedTransactionID)) { // most likely, we simply had a timeout for the earlier query and now read the (late) response. Ignore it // and assume that the response will come *without* sending another query. (If we send another query // with transactionId X+1 here, we would again get a transactionMismatchError if the response to diff --git a/tcpclient_test.go b/tcpclient_test.go index 7fa5c2d..0871083 100644 --- a/tcpclient_test.go +++ b/tcpclient_test.go @@ -110,7 +110,8 @@ func TestTCPTransactionMismatchRetry(t *testing.T) { return } defer conn.Close() - time.Sleep(1 * time.Second) + // ensure that answer is only written after second read attempt failed + time.Sleep(2500 * time.Millisecond) packager := &tcpPackager{SlaveID: 0} pdu := &ProtocolDataUnit{ FunctionCode: FuncCodeReadInputRegisters, @@ -127,6 +128,12 @@ func TestTCPTransactionMismatchRetry(t *testing.T) { t.Error(err) return } + // encoding same PDU twice will increment the transaction id + data3, err := packager.Encode(pdu) + if err != nil { + t.Error(err) + return + } if _, err := conn.Write(data1); err != nil { t.Error(err) return @@ -135,6 +142,10 @@ func TestTCPTransactionMismatchRetry(t *testing.T) { t.Error(err) return } + if _, err := conn.Write(data3); err != nil { + t.Error(err) + return + } // keep the connection open until the main routine is finished <-done }() @@ -148,6 +159,11 @@ func TestTCPTransactionMismatchRetry(t *testing.T) { t.Fatalf("expected timeout error, got %q", err) } resp, err = client.ReadInputRegisters(0, 1) + opError, ok = err.(*net.OpError) + if !ok || !opError.Timeout() { + t.Fatalf("expected timeout error, got %q", err) + } + resp, err = client.ReadInputRegisters(0, 1) if err != nil { t.Fatal(err) } From 6d61d5fbec071f4bee5151d122343706b1a42e56 Mon Sep 17 00:00:00 2001 From: andig Date: Mon, 23 May 2022 14:10:07 +0200 Subject: [PATCH 07/51] Validate result length againt request --- client.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/client.go b/client.go index 533b773..5c6e3b1 100644 --- a/client.go +++ b/client.go @@ -125,6 +125,10 @@ func (mb *client) ReadHoldingRegisters(address, quantity uint16) (results []byte err = fmt.Errorf("modbus: response data size '%v' does not match count '%v'", length, count) return } + if count != 2*int(quantity) { + err = fmt.Errorf("modbus: response data size '%v' does not match request quantity '%v'", length, quantity) + return + } results = response.Data[1:] return } @@ -156,6 +160,10 @@ func (mb *client) ReadInputRegisters(address, quantity uint16) (results []byte, err = fmt.Errorf("modbus: response data size '%v' does not match count '%v'", length, count) return } + if count != 2*int(quantity) { + err = fmt.Errorf("modbus: response data size '%v' does not match request quantity '%v'", length, quantity) + return + } results = response.Data[1:] return } From 8b2c9ef9f9cabe3582f8418eab34e4d124b0f099 Mon Sep 17 00:00:00 2001 From: andig Date: Mon, 29 Aug 2022 10:29:39 +0200 Subject: [PATCH 08/51] Wrap open error with device name for context --- serial.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/serial.go b/serial.go index ce4c93e..a910b95 100644 --- a/serial.go +++ b/serial.go @@ -5,6 +5,7 @@ package modbus import ( + "fmt" "io" "sync" "time" @@ -45,7 +46,7 @@ func (mb *serialPort) connect() error { if mb.port == nil { port, err := serial.Open(&mb.Config) if err != nil { - return err + return fmt.Errorf("could not open %s: %w", mb.Config.Address, err) } mb.port = port } @@ -93,8 +94,8 @@ func (mb *serialPort) closeIdle() { if mb.IdleTimeout <= 0 { return } - idle := time.Now().Sub(mb.lastActivity) - if idle >= mb.IdleTimeout { + + if idle := time.Since(mb.lastActivity); idle >= mb.IdleTimeout { mb.logf("modbus: closing connection due to idle timeout: %v", idle) mb.close() } From 9486cf6e83bc0c5d590b9ef54b2a0847c3a7985d Mon Sep 17 00:00:00 2001 From: andig Date: Sun, 6 Nov 2022 14:30:13 +0100 Subject: [PATCH 09/51] Use time.Since --- serial.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/serial.go b/serial.go index ce4c93e..29160b5 100644 --- a/serial.go +++ b/serial.go @@ -93,8 +93,8 @@ func (mb *serialPort) closeIdle() { if mb.IdleTimeout <= 0 { return } - idle := time.Now().Sub(mb.lastActivity) - if idle >= mb.IdleTimeout { + + if idle := time.Since(mb.lastActivity); idle >= mb.IdleTimeout { mb.logf("modbus: closing connection due to idle timeout: %v", idle) mb.close() } From ca87e58682ded1f766f85083274e73d783201f6c Mon Sep 17 00:00:00 2001 From: andig Date: Sun, 6 Nov 2022 14:33:09 +0100 Subject: [PATCH 10/51] Fix staticcheck findings (yoda questions, result not used) --- asciiclient_test.go | 2 +- crc_test.go | 2 +- lrc_test.go | 2 +- rtuclient_test.go | 2 +- tcpclient_test.go | 8 ++++---- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/asciiclient_test.go b/asciiclient_test.go index 4679f86..1502066 100644 --- a/asciiclient_test.go +++ b/asciiclient_test.go @@ -37,7 +37,7 @@ func TestASCIIDecoding(t *testing.T) { t.Fatal(err) } - if 3 != pdu.FunctionCode { + if pdu.FunctionCode != 3 { t.Fatalf("Function code: expected %v, actual %v", 15, pdu.FunctionCode) } expected := []byte{0x13, 0x89, 0, 0x0A} diff --git a/crc_test.go b/crc_test.go index e24b3b7..557f3f6 100644 --- a/crc_test.go +++ b/crc_test.go @@ -13,7 +13,7 @@ func TestCRC(t *testing.T) { crc.reset() crc.pushBytes([]byte{0x02, 0x07}) - if 0x1241 != crc.value() { + if crc.value() != 0x1241 { t.Fatalf("crc expected %v, actual %v", 0x1241, crc.value()) } } diff --git a/lrc_test.go b/lrc_test.go index 785f204..e036c5a 100644 --- a/lrc_test.go +++ b/lrc_test.go @@ -13,7 +13,7 @@ func TestLRC(t *testing.T) { lrc.reset().pushByte(0x01).pushByte(0x03) lrc.pushBytes([]byte{0x01, 0x0A}) - if 0xF1 != lrc.value() { + if lrc.value() != 0xF1 { t.Fatalf("lrc expected %v, actual %v", 0xF1, lrc.value()) } } diff --git a/rtuclient_test.go b/rtuclient_test.go index 815b613..1585de6 100644 --- a/rtuclient_test.go +++ b/rtuclient_test.go @@ -38,7 +38,7 @@ func TestRTUDecoding(t *testing.T) { t.Fatal(err) } - if 16 != pdu.FunctionCode { + if pdu.FunctionCode != 16 { t.Fatalf("Function code: expected %v, actual %v", 16, pdu.FunctionCode) } expected := []byte{0x8A, 0x00, 0x00, 0x03} diff --git a/tcpclient_test.go b/tcpclient_test.go index 0871083..3854951 100644 --- a/tcpclient_test.go +++ b/tcpclient_test.go @@ -40,7 +40,7 @@ func TestTCPDecoding(t *testing.T) { t.Fatal(err) } - if 3 != pdu.FunctionCode { + if pdu.FunctionCode != 3 { t.Fatalf("Function code: expected %v, actual %v", 3, pdu.FunctionCode) } expected := []byte{0, 120, 0, 3} @@ -153,17 +153,17 @@ func TestTCPTransactionMismatchRetry(t *testing.T) { handler.Timeout = 1 * time.Second handler.ProtocolRecoveryTimeout = 50 * time.Millisecond client := NewClient(handler) - resp, err := client.ReadInputRegisters(0, 1) + _, err = client.ReadInputRegisters(0, 1) opError, ok := err.(*net.OpError) if !ok || !opError.Timeout() { t.Fatalf("expected timeout error, got %q", err) } - resp, err = client.ReadInputRegisters(0, 1) + _, err = client.ReadInputRegisters(0, 1) opError, ok = err.(*net.OpError) if !ok || !opError.Timeout() { t.Fatalf("expected timeout error, got %q", err) } - resp, err = client.ReadInputRegisters(0, 1) + resp, err := client.ReadInputRegisters(0, 1) if err != nil { t.Fatal(err) } From 8cf18f1816012f6dcfc7a736a88909f898bd71af Mon Sep 17 00:00:00 2001 From: andig Date: Sun, 6 Nov 2022 14:51:57 +0100 Subject: [PATCH 11/51] Align implementation with RTU over TCP client --- ascii_over_tcp_client.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ascii_over_tcp_client.go b/ascii_over_tcp_client.go index 040de20..3aea9f2 100644 --- a/ascii_over_tcp_client.go +++ b/ascii_over_tcp_client.go @@ -35,16 +35,16 @@ type asciiTCPTransporter struct { } func (mb *asciiTCPTransporter) Send(aduRequest []byte) (aduResponse []byte, err error) { - mb.tcpTransporter.mu.Lock() - defer mb.tcpTransporter.mu.Unlock() + mb.mu.Lock() + defer mb.mu.Unlock() // Make sure port is connected - if err = mb.tcpTransporter.connect(); err != nil { + if err = mb.connect(); err != nil { return } // Start the timer to close when idle - mb.tcpTransporter.lastActivity = time.Now() - mb.tcpTransporter.startCloseTimer() + mb.lastActivity = time.Now() + mb.startCloseTimer() // Set write and read timeout var timeout time.Time if mb.Timeout > 0 { @@ -55,7 +55,7 @@ func (mb *asciiTCPTransporter) Send(aduRequest []byte) (aduResponse []byte, err } // Send the request - mb.tcpTransporter.logf("modbus: send %q\n", aduRequest) + mb.logf("modbus: send %q\n", aduRequest) if _, err = mb.conn.Write(aduRequest); err != nil { return } @@ -79,6 +79,6 @@ func (mb *asciiTCPTransporter) Send(aduRequest []byte) (aduResponse []byte, err } } aduResponse = data[:length] - mb.tcpTransporter.logf("modbus: recv %q\n", aduResponse) + mb.logf("modbus: recv %q\n", aduResponse) return } From b2c4d954d7e184f6269035490b5b1e6bcde2aa71 Mon Sep 17 00:00:00 2001 From: andig Date: Sun, 6 Nov 2022 15:43:35 +0100 Subject: [PATCH 12/51] Simplify initialising length --- ascii_over_tcp_client.go | 3 +-- asciiclient.go | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/ascii_over_tcp_client.go b/ascii_over_tcp_client.go index 3aea9f2..cc263e4 100644 --- a/ascii_over_tcp_client.go +++ b/ascii_over_tcp_client.go @@ -60,9 +60,8 @@ func (mb *asciiTCPTransporter) Send(aduRequest []byte) (aduResponse []byte, err return } // Get the response - var n int + var n, length int var data [asciiMaxSize]byte - length := 0 for { if n, err = mb.conn.Read(data[length:]); err != nil { return diff --git a/asciiclient.go b/asciiclient.go index 30ff7d5..94a85f0 100644 --- a/asciiclient.go +++ b/asciiclient.go @@ -193,9 +193,8 @@ func (mb *asciiSerialTransporter) Send(aduRequest []byte) (aduResponse []byte, e return } // Get the response - var n int + var n, length int var data [asciiMaxSize]byte - length := 0 for { if n, err = mb.port.Read(data[length:]); err != nil { return From 63789f0f7b3a056071a5a0f1b3ea9ba34dd6cb6e Mon Sep 17 00:00:00 2001 From: andig Date: Sun, 6 Nov 2022 15:57:52 +0100 Subject: [PATCH 13/51] Check recovery deadline only once --- tcpclient.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tcpclient.go b/tcpclient.go index 767d6ce..336460e 100644 --- a/tcpclient.go +++ b/tcpclient.go @@ -218,9 +218,15 @@ func (mb *tcpTransporter) readResponse(aduRequest []byte, data []byte, recoveryD return // everything is OK } } + + // no time left, report error + if time.Since(recoveryDeadline) >= 0 { + return + } + switch v := err.(type) { case ErrTCPHeaderLength: - if mb.LinkRecoveryTimeout > 0 && time.Until(recoveryDeadline) > 0 { + if mb.LinkRecoveryTimeout > 0 { // TCP header not OK - retry with another query res = readResultRetry return @@ -228,7 +234,7 @@ func (mb *tcpTransporter) readResponse(aduRequest []byte, data []byte, recoveryD // no time left, report error return case errTransactionIDMismatch: - if mb.ProtocolRecoveryTimeout > 0 && time.Until(recoveryDeadline) > 0 { + if mb.ProtocolRecoveryTimeout > 0 { // the first condition check for a normal transaction id mismatch. The second part of the condition check for a wrap-around. If a wraparound is // detected (last attempt is smaller than last success), the id can be higher than the last success or lower than the last attempt, but not both if (v.got > mb.lastSuccessfulTransactionID && v.got < mb.lastAttemptedTransactionID) || @@ -245,7 +251,7 @@ func (mb *tcpTransporter) readResponse(aduRequest []byte, data []byte, recoveryD } return // no time left, report error default: - if mb.ProtocolRecoveryTimeout > 0 && time.Until(recoveryDeadline) > 0 { + if mb.ProtocolRecoveryTimeout > 0 { // TCP header OK but modbus frame not - retry with another query res = readResultRetry return From 2ff11345d1353e3da9e1e8b403de50c2aa8aaf78 Mon Sep 17 00:00:00 2001 From: Marco Trettner Date: Fri, 18 Nov 2022 07:07:07 +0100 Subject: [PATCH 14/51] fix(cmd/modbus-cli): use correct register length as byte length divided by 2 when writing multiple registers --- cmd/modbus-cli/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/modbus-cli/main.go b/cmd/modbus-cli/main.go index 2eba9b0..ce0cf4c 100644 --- a/cmd/modbus-cli/main.go +++ b/cmd/modbus-cli/main.go @@ -173,7 +173,7 @@ func exec( buf = make([]byte, 8) w.PutFloat64(buf, float64(wval)) } - result, err = client.WriteMultipleRegisters(uint16(register), uint16(len(buf)), buf) + result, err = client.WriteMultipleRegisters(uint16(register), uint16(len(buf))/2, buf) case 0x04: result, err = client.ReadInputRegisters(uint16(register), uint16(quantity)) case 0x03: From ff6c484bd8454ed2111442c58dc4daacc45e6407 Mon Sep 17 00:00:00 2001 From: Benedikt Bongartz Date: Sat, 19 Nov 2022 01:03:33 +0100 Subject: [PATCH 15/51] feat(cmd/modbus-cli): any non-zero value will lead to single coil on Signed-off-by: Benedikt Bongartz --- cmd/modbus-cli/main.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/cmd/modbus-cli/main.go b/cmd/modbus-cli/main.go index 6517091..5997d15 100644 --- a/cmd/modbus-cli/main.go +++ b/cmd/modbus-cli/main.go @@ -139,13 +139,9 @@ func exec( coilOn uint16 = 0xFF00 coilOff uint16 = 0x0000 ) - v := uint16(wval) - if v != coilOn && v != coilOff { - err = fmt.Errorf( - "illegal: expect %X to request the output to be on or %X to be off, got: %X", - coilOn, coilOff, v, - ) - return + v := coilOff + if wval > 0 { + v = coilOn } result, err = client.WriteSingleCoil(uint16(register), v) case 0x06: From 64ad9271d533d994160d2418998e58d5306baad7 Mon Sep 17 00:00:00 2001 From: Marco Trettner Date: Fri, 25 Nov 2022 11:58:36 +0100 Subject: [PATCH 16/51] feat(modbus-cli): add option to print out different encoding options --- cmd/modbus-cli/main.go | 112 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 109 insertions(+), 3 deletions(-) diff --git a/cmd/modbus-cli/main.go b/cmd/modbus-cli/main.go index 4edc10b..ee52df7 100644 --- a/cmd/modbus-cli/main.go +++ b/cmd/modbus-cli/main.go @@ -46,7 +46,7 @@ func main() { quantity = flag.Int("quantity", 2, "register quantity, length in bytes") ignoreCRCError = flag.Bool("ignore-crc", false, "ignore crc") eType = flag.String("type-exec", "uint16", "") - pType = flag.String("type-parse", "raw", "") + pType = flag.String("type-parse", "raw", "type to parse the register result. Use 'raw' if you want to see the raw bits and bytes. Use 'all' if you want to decode the result to different commonly used formats.") writeValue = flag.Float64("write-value", math.MaxFloat64, "") parseBigEndian = flag.Bool("order-parse-bigendian", true, "t: big, f: little") execBigEndian = flag.Bool("order-exec-bigendian", true, "t: big, f: little") @@ -103,11 +103,15 @@ func main() { } var res string - if *pType == "raw" { + switch *pType { + case "raw": res, err = resultToRawString(result, int(startReg)) - } else { + case "all": + res, err = resultToAllString(result) + default: res, err = resultToString(result, po, *pType) } + if err != nil { logger.Fatal(err) } @@ -207,6 +211,108 @@ func resultToRawString(r []byte, startReg int) (string, error) { return res, nil } +func resultToAllString(result []byte) (string, error) { + switch len(result) { + case 2: + bigUint16, err := resultToString(result, binary.BigEndian, "uint16") + if err != nil { + return "", err + } + bigInt16, err := resultToString(result, binary.BigEndian, "int16") + if err != nil { + return "", err + } + littleUint16, err := resultToString(result, binary.LittleEndian, "uint16") + if err != nil { + return "", err + } + littleInt16, err := resultToString(result, binary.LittleEndian, "int16") + if err != nil { + return "", err + } + + return strings.Join([]string{ + fmt.Sprintf("INT16 - Big Endian (AB): %s", bigInt16), + fmt.Sprintf("INT16 - Little Endian (BA): %s", littleInt16), + fmt.Sprintf("UINT16 - Big Endian (AB): %s", bigUint16), + fmt.Sprintf("UINT16 - Little Endian (BA): %s", littleUint16), + }, "\n"), nil + case 4: + bigUint32, err := resultToString(result, binary.BigEndian, "uint32") + if err != nil { + return "", err + } + bigInt32, err := resultToString(result, binary.BigEndian, "int32") + if err != nil { + return "", err + } + bigFloat32, err := resultToString(result, binary.BigEndian, "float32") + if err != nil { + return "", err + } + littleUint32, err := resultToString(result, binary.LittleEndian, "uint32") + if err != nil { + return "", err + } + littleInt32, err := resultToString(result, binary.LittleEndian, "int32") + if err != nil { + return "", err + } + littleFloat32, err := resultToString(result, binary.LittleEndian, "float32") + if err != nil { + return "", err + } + + // flip result + result := []byte{result[1], result[0], result[3], result[2]} + + midBigUint32, err := resultToString(result, binary.BigEndian, "uint32") + if err != nil { + return "", err + } + midBigInt32, err := resultToString(result, binary.BigEndian, "int32") + if err != nil { + return "", err + } + midBigFloat32, err := resultToString(result, binary.BigEndian, "float32") + if err != nil { + return "", err + } + midLittleUint32, err := resultToString(result, binary.LittleEndian, "uint32") + if err != nil { + return "", err + } + midLittleInt32, err := resultToString(result, binary.LittleEndian, "int32") + if err != nil { + return "", err + } + midLittleFloat32, err := resultToString(result, binary.LittleEndian, "float32") + if err != nil { + return "", err + } + + return strings.Join([]string{ + fmt.Sprintf("INT32 - Big Endian (ABCD): %s", bigInt32), + fmt.Sprintf("INT32 - Little Endian (DCBA): %s", littleInt32), + fmt.Sprintf("INT32 - Mid-Big Endian (BADC): %s", midBigInt32), + fmt.Sprintf("INT32 - Mid-Little Endian (CDAB): %s", midLittleInt32), + "", + fmt.Sprintf("UINT32 - Big Endian (ABCD): %s", bigUint32), + fmt.Sprintf("UINT32 - Little Endian (DCBA): %s", littleUint32), + fmt.Sprintf("UINT32 - Mid-Big Endian (BADC): %s", midBigUint32), + fmt.Sprintf("UINT32 - Mid-Little Endian (CDAB): %s", midLittleUint32), + "", + fmt.Sprintf("Float32 - Big Endian (ABCD): %s", bigFloat32), + fmt.Sprintf("Float32 - Little Endian (DCBA): %s", littleFloat32), + fmt.Sprintf("Float32 - Mid-Big Endian (BADC): %s", midBigFloat32), + fmt.Sprintf("Float32 - Mid-Little Endian (CDAB): %s", midLittleFloat32), + }, "\n"), nil + + default: + return "", fmt.Errorf("can't convert data with length %d", len(result)) + } +} + func resultToString(r []byte, order binary.ByteOrder, varType string) (string, error) { switch varType { case "string": From 05892cd3c3b92d4e88ae2b920ec2246d41fe9530 Mon Sep 17 00:00:00 2001 From: Marco Trettner Date: Mon, 19 Dec 2022 10:23:54 +0100 Subject: [PATCH 17/51] refactor(cmd/modbus-cli): use tabwriter to format the output for all encodings --- cmd/modbus-cli/main.go | 57 ++++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/cmd/modbus-cli/main.go b/cmd/modbus-cli/main.go index ee52df7..cdf4b37 100644 --- a/cmd/modbus-cli/main.go +++ b/cmd/modbus-cli/main.go @@ -12,6 +12,7 @@ import ( "net/url" "os" "strings" + "text/tabwriter" "time" "github.com/grid-x/modbus" @@ -212,6 +213,9 @@ func resultToRawString(r []byte, startReg int) (string, error) { } func resultToAllString(result []byte) (string, error) { + buf := new(bytes.Buffer) + w := tabwriter.NewWriter(buf, 0, 0, 2, ' ', 0) + switch len(result) { case 2: bigUint16, err := resultToString(result, binary.BigEndian, "uint16") @@ -231,12 +235,18 @@ func resultToAllString(result []byte) (string, error) { return "", err } - return strings.Join([]string{ - fmt.Sprintf("INT16 - Big Endian (AB): %s", bigInt16), - fmt.Sprintf("INT16 - Little Endian (BA): %s", littleInt16), - fmt.Sprintf("UINT16 - Big Endian (AB): %s", bigUint16), - fmt.Sprintf("UINT16 - Little Endian (BA): %s", littleUint16), - }, "\n"), nil + fmt.Fprintf(w, "INT16\tBig Endian (AB):\t%s\t\n", bigInt16) + fmt.Fprintf(w, "INT16\tLittle Endian (BA):\t%s\t\n", littleInt16) + fmt.Fprintln(w, "\t") + fmt.Fprintf(w, "UINT16\tBig Endian (AB):\t%s\t\n", bigUint16) + fmt.Fprintf(w, "UINT16\tLittle Endian (BA):\t%s\t\n", littleUint16) + + err = w.Flush() + if err != nil { + return "", err + } + + return buf.String(), nil case 4: bigUint32, err := resultToString(result, binary.BigEndian, "uint32") if err != nil { @@ -291,23 +301,26 @@ func resultToAllString(result []byte) (string, error) { return "", err } - return strings.Join([]string{ - fmt.Sprintf("INT32 - Big Endian (ABCD): %s", bigInt32), - fmt.Sprintf("INT32 - Little Endian (DCBA): %s", littleInt32), - fmt.Sprintf("INT32 - Mid-Big Endian (BADC): %s", midBigInt32), - fmt.Sprintf("INT32 - Mid-Little Endian (CDAB): %s", midLittleInt32), - "", - fmt.Sprintf("UINT32 - Big Endian (ABCD): %s", bigUint32), - fmt.Sprintf("UINT32 - Little Endian (DCBA): %s", littleUint32), - fmt.Sprintf("UINT32 - Mid-Big Endian (BADC): %s", midBigUint32), - fmt.Sprintf("UINT32 - Mid-Little Endian (CDAB): %s", midLittleUint32), - "", - fmt.Sprintf("Float32 - Big Endian (ABCD): %s", bigFloat32), - fmt.Sprintf("Float32 - Little Endian (DCBA): %s", littleFloat32), - fmt.Sprintf("Float32 - Mid-Big Endian (BADC): %s", midBigFloat32), - fmt.Sprintf("Float32 - Mid-Little Endian (CDAB): %s", midLittleFloat32), - }, "\n"), nil + fmt.Fprintf(w, "INT32\tBig Endian (ABCD):\t%s\t\n", bigInt32) + fmt.Fprintf(w, "INT32\tLittle Endian (DCBA):\t%s\t\n", littleInt32) + fmt.Fprintf(w, "INT32\tMid-Big Endian (BADC):\t%s\t\n", midBigInt32) + fmt.Fprintf(w, "INT32\tMid-Little Endian (CDAB):\t%s\t\n", midLittleInt32) + fmt.Fprintln(w, "\t") + fmt.Fprintf(w, "UINT32\tBig Endian (ABCD):\t%s\t\n", bigUint32) + fmt.Fprintf(w, "UINT32\tLittle Endian (DCBA):\t%s\t\n", littleUint32) + fmt.Fprintf(w, "UINT32\tMid-Big Endian (BADC):\t%s\t\n", midBigUint32) + fmt.Fprintf(w, "UINT32\tMid-Little Endian (CDAB):\t%s\t\n", midLittleUint32) + fmt.Fprintln(w, "\t") + fmt.Fprintf(w, "Float32\tBig Endian (ABCD):\t%s\t\n", bigFloat32) + fmt.Fprintf(w, "Float32\tLittle Endian (DCBA):\t%s\t\n", littleFloat32) + fmt.Fprintf(w, "Float32\tMid-Big Endian (BADC):\t%s\t\n", midBigFloat32) + fmt.Fprintf(w, "Float32\tMid-Little Endian (CDAB):\t%s\t\n", midLittleFloat32) + err = w.Flush() + if err != nil { + return "", err + } + return buf.String(), nil default: return "", fmt.Errorf("can't convert data with length %d", len(result)) } From 63091d022fec1532430ed707c48f3f3098082bdd Mon Sep 17 00:00:00 2001 From: Marco Trettner Date: Wed, 8 Mar 2023 09:17:03 +0100 Subject: [PATCH 18/51] chore(Makefile): use go 1.19 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 77f970b..25a347e 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 := registry.gridx.de/gridx/base-images:modbus-dev-1.15.latest +GO_TOOLS := public.ecr.aws/gridx/base-images:modbus-dev-1.19.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 From 237139950d94555ba462d0c1659b736d4204528f Mon Sep 17 00:00:00 2001 From: Marco Trettner Date: Wed, 8 Mar 2023 08:27:41 +0100 Subject: [PATCH 19/51] fix(cmd/modbus-cli): fix byte write function to also work with floats --- cmd/modbus-cli/main.go | 43 ++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/cmd/modbus-cli/main.go b/cmd/modbus-cli/main.go index cdf4b37..2737d30 100644 --- a/cmd/modbus-cli/main.go +++ b/cmd/modbus-cli/main.go @@ -432,33 +432,44 @@ func newHandler(o option) (modbus.ClientHandler, error) { return nil, fmt.Errorf("unsupported scheme: %s", u.Scheme) } -type binaryWriter interface { - PutUint32(b []byte, v uint32) - PutUint16(b []byte, v uint16) - PutFloat32(b []byte, v float32) - PutFloat64(b []byte, v float64) -} - func newWriter(o binary.ByteOrder) *writer { - return &writer{o} + return &writer{order: o} } type writer struct { - binary.ByteOrder + order binary.ByteOrder +} + +func (w *writer) ToUint16(v uint16) []byte { + var buf bytes.Buffer + w.to(&buf, v) + b, _ := io.ReadAll(&buf) + return b +} + +func (w *writer) ToUint32(v uint32) []byte { + var buf bytes.Buffer + w.to(&buf, v) + b, _ := io.ReadAll(&buf) + return b } -func (w *writer) PutFloat32(b []byte, v float32) { - buf := bytes.NewBuffer(b) - w.to(buf, v) +func (w *writer) ToFloat32(v float32) []byte { + var buf bytes.Buffer + w.to(&buf, v) + b, _ := io.ReadAll(&buf) + return b } -func (w *writer) PutFloat64(b []byte, v float64) { - buf := bytes.NewBuffer(b) - w.to(buf, v) +func (w *writer) ToFloat64(v float64) []byte { + var buf bytes.Buffer + w.to(&buf, v) + b, _ := io.ReadAll(&buf) + return b } func (w *writer) to(buf io.Writer, f interface{}) { - if err := binary.Write(buf, w.ByteOrder, f); err != nil { + if err := binary.Write(buf, w.order, f); err != nil { panic(fmt.Sprintf("binary.Write failed: %s", err.Error())) } } From c9ee23bb6d9810cbe8431f56757de6ecabf23e8c Mon Sep 17 00:00:00 2001 From: Marco Trettner Date: Wed, 8 Mar 2023 08:29:47 +0100 Subject: [PATCH 20/51] feat(cmd/modbus-cli): extend cli tool to add a forcerd order in the form of "AB", "BA", "ABCD", "DCBA", "BADC" or "CDAB. This can be used for 1 or 2 registers and will overwrite the order. Also, added unit tests to test the new conversion function --- cmd/modbus-cli/main.go | 174 ++++++++++++++-------- cmd/modbus-cli/main_test.go | 285 ++++++++++++++++++++++++++++++++++++ 2 files changed, 394 insertions(+), 65 deletions(-) create mode 100644 cmd/modbus-cli/main_test.go diff --git a/cmd/modbus-cli/main.go b/cmd/modbus-cli/main.go index 2737d30..7a4e8d9 100644 --- a/cmd/modbus-cli/main.go +++ b/cmd/modbus-cli/main.go @@ -42,17 +42,19 @@ func main() { flag.BoolVar(&opt.rtu.rs485.rxDuringTx, "rs485-rxDuringTx", false, "Allow bidirectional rx during tx") var ( - register = flag.Int("register", -1, "") - fnCode = flag.Int("fn-code", 0x03, "fn") - quantity = flag.Int("quantity", 2, "register quantity, length in bytes") - ignoreCRCError = flag.Bool("ignore-crc", false, "ignore crc") - eType = flag.String("type-exec", "uint16", "") - pType = flag.String("type-parse", "raw", "type to parse the register result. Use 'raw' if you want to see the raw bits and bytes. Use 'all' if you want to decode the result to different commonly used formats.") - writeValue = flag.Float64("write-value", math.MaxFloat64, "") - parseBigEndian = flag.Bool("order-parse-bigendian", true, "t: big, f: little") - execBigEndian = flag.Bool("order-exec-bigendian", true, "t: big, f: little") - filename = flag.String("filename", "", "") - logframe = flag.Bool("log-frame", false, "prints received and send modbus frame to stdout") + register = flag.Int("register", -1, "") + fnCode = flag.Int("fn-code", 0x03, "fn") + quantity = flag.Int("quantity", 2, "register quantity, length in bytes") + ignoreCRCError = flag.Bool("ignore-crc", false, "ignore crc") + eType = flag.String("type-exec", "uint16", "") + pType = flag.String("type-parse", "raw", "type to parse the register result. Use 'raw' if you want to see the raw bits and bytes. Use 'all' if you want to decode the result to different commonly used formats.") + writeValue = flag.Float64("write-value", math.MaxFloat64, "") + readParseOrder = flag.String("read-parse-order", "", "order to parse the register that was read out. Valid values: [AB, BA, ABCD, DCBA, BADC, CDAB]. Can only be used for 16bit (1 register) and 32bit (2 registers). If used, it will overwrite the big-endian or little-endian parameter.") + writeParseOrder = flag.String("write-exec-order", "", "order to execute the register(s) that should be written to. Valid values: [AB, BA, ABCD, DCBA, BADC, CDAB]. Can only be used for 16bit (1 register) and 32bit (2 registers). If used, it will overwrite the big-endian or little-endian parameter.") + parseBigEndian = flag.Bool("order-parse-bigendian", true, "t: big, f: little") + execBigEndian = flag.Bool("order-exec-bigendian", true, "t: big, f: little") + filename = flag.String("filename", "", "") + logframe = flag.Bool("log-frame", false, "prints received and send modbus frame to stdout") ) flag.Parse() @@ -96,7 +98,7 @@ func main() { client := modbus.NewClient(handler) - result, err := exec(client, eo, *register, *fnCode, *writeValue, *eType, *quantity) + 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) } else if err != nil { @@ -110,7 +112,7 @@ func main() { case "all": res, err = resultToAllString(result) default: - res, err = resultToString(result, po, *pType) + res, err = resultToString(result, po, *readParseOrder, *pType) } if err != nil { @@ -130,12 +132,15 @@ func main() { func exec( client modbus.Client, o binary.ByteOrder, + forcedOrder string, register int, fnCode int, wval float64, etype string, quantity int, -) (result []byte, err error) { +) ([]byte, error) { + var err error + var result []byte switch fnCode { case 0x01: result, err = client.ReadCoils(uint16(register), uint16(quantity)) @@ -153,40 +158,14 @@ func exec( max := float64(math.MaxUint16) if wval > max || wval < 0 { err = fmt.Errorf("overflow: %f does not fit into datatype uint16", wval) - return + break } result, err = client.WriteSingleRegister(uint16(register), uint16(wval)) case 0x10: - w := newWriter(o) var buf []byte - switch etype { - case "uint16": - max := float64(math.MaxUint16) - if wval > max || wval < 0 { - err = fmt.Errorf("overflow: %f does not fit into datatype %s", wval, etype) - return - } - buf = make([]byte, 2) - w.PutUint16(buf, uint16(wval)) - case "uint32": - max := float64(math.MaxUint32) - if wval > max || wval < 0 { - err = fmt.Errorf("overflow: %f does not fit into datatype %s", wval, etype) - return - } - buf = make([]byte, 4) - w.PutUint32(buf, uint32(wval)) - case "float32": - max := float64(math.MaxFloat32) - if wval > max || wval < 0 { - err = fmt.Errorf("overflow: %f does not fit into datatype %s", wval, etype) - return - } - buf = make([]byte, 4) - w.PutFloat32(buf, float32(wval)) - case "float64": - buf = make([]byte, 8) - w.PutFloat64(buf, float64(wval)) + buf, err = convertToBytes(etype, o, forcedOrder, wval) + if err != nil { + break } result, err = client.WriteMultipleRegisters(uint16(register), uint16(len(buf))/2, buf) case 0x04: @@ -196,7 +175,58 @@ func exec( default: err = fmt.Errorf("function code %d is unsupported", fnCode) } - return + return result, err +} + +func convertToBytes(eType string, order binary.ByteOrder, forcedOrder string, val float64) ([]byte, error) { + fo := strings.ToUpper(forcedOrder) + switch fo { + case "": + // nothing is forced + case "AB", "ABCD", "BADC": + order = binary.BigEndian + case "BA", "DCBA", "CDAB": + order = binary.LittleEndian + default: + return nil, fmt.Errorf("forced order %s not known", strings.ToUpper(forcedOrder)) + } + + w := newWriter(order) + var buf []byte + var err error + switch eType { + case "uint16": + max := float64(math.MaxUint16) + if val > max || val < 0 { + err = fmt.Errorf("overflow: %f does not fit into datatype %s", val, eType) + break + } + buf = w.ToUint16(uint16(val)) + case "uint32": + max := float64(math.MaxUint32) + if val > max || val < 0 { + err = fmt.Errorf("overflow: %f does not fit into datatype %s", val, eType) + break + } + buf = w.ToUint32(uint32(val)) + case "float32": + max := float64(math.MaxFloat32) + min := -float64(math.MaxFloat32) + if val > max || val < min { + err = fmt.Errorf("overflow: %f does not fit into datatype %s", val, eType) + break + } + buf = w.ToFloat32(float32(val)) + case "float64": + buf = w.ToFloat64(float64(val)) + } + + // flip bytes when CDAB or BADC are used (and we have 4 bytes) + if fo == "CDAB" || fo == "BADC" && len(buf) == 4 { + buf = []byte{buf[1], buf[0], buf[3], buf[2]} + } + + return buf, err } func resultToFile(r []byte, filename string) error { @@ -218,19 +248,19 @@ func resultToAllString(result []byte) (string, error) { switch len(result) { case 2: - bigUint16, err := resultToString(result, binary.BigEndian, "uint16") + bigUint16, err := resultToString(result, binary.BigEndian, "", "uint16") if err != nil { return "", err } - bigInt16, err := resultToString(result, binary.BigEndian, "int16") + bigInt16, err := resultToString(result, binary.BigEndian, "", "int16") if err != nil { return "", err } - littleUint16, err := resultToString(result, binary.LittleEndian, "uint16") + littleUint16, err := resultToString(result, binary.LittleEndian, "", "uint16") if err != nil { return "", err } - littleInt16, err := resultToString(result, binary.LittleEndian, "int16") + littleInt16, err := resultToString(result, binary.LittleEndian, "", "int16") if err != nil { return "", err } @@ -248,55 +278,52 @@ func resultToAllString(result []byte) (string, error) { return buf.String(), nil case 4: - bigUint32, err := resultToString(result, binary.BigEndian, "uint32") + bigUint32, err := resultToString(result, binary.BigEndian, "", "uint32") if err != nil { return "", err } - bigInt32, err := resultToString(result, binary.BigEndian, "int32") + bigInt32, err := resultToString(result, binary.BigEndian, "", "int32") if err != nil { return "", err } - bigFloat32, err := resultToString(result, binary.BigEndian, "float32") + bigFloat32, err := resultToString(result, binary.BigEndian, "", "float32") if err != nil { return "", err } - littleUint32, err := resultToString(result, binary.LittleEndian, "uint32") + littleUint32, err := resultToString(result, binary.LittleEndian, "", "uint32") if err != nil { return "", err } - littleInt32, err := resultToString(result, binary.LittleEndian, "int32") + littleInt32, err := resultToString(result, binary.LittleEndian, "", "int32") if err != nil { return "", err } - littleFloat32, err := resultToString(result, binary.LittleEndian, "float32") + littleFloat32, err := resultToString(result, binary.LittleEndian, "", "float32") if err != nil { return "", err } - // flip result - result := []byte{result[1], result[0], result[3], result[2]} - - midBigUint32, err := resultToString(result, binary.BigEndian, "uint32") + midBigUint32, err := resultToString(result, binary.BigEndian, "BADC", "uint32") if err != nil { return "", err } - midBigInt32, err := resultToString(result, binary.BigEndian, "int32") + midBigInt32, err := resultToString(result, binary.BigEndian, "BADC", "int32") if err != nil { return "", err } - midBigFloat32, err := resultToString(result, binary.BigEndian, "float32") + midBigFloat32, err := resultToString(result, binary.BigEndian, "BADC", "float32") if err != nil { return "", err } - midLittleUint32, err := resultToString(result, binary.LittleEndian, "uint32") + midLittleUint32, err := resultToString(result, binary.LittleEndian, "CDAB", "uint32") if err != nil { return "", err } - midLittleInt32, err := resultToString(result, binary.LittleEndian, "int32") + midLittleInt32, err := resultToString(result, binary.LittleEndian, "CDAB", "int32") if err != nil { return "", err } - midLittleFloat32, err := resultToString(result, binary.LittleEndian, "float32") + midLittleFloat32, err := resultToString(result, binary.LittleEndian, "CDAB", "float32") if err != nil { return "", err } @@ -326,7 +353,24 @@ func resultToAllString(result []byte) (string, error) { } } -func resultToString(r []byte, order binary.ByteOrder, varType string) (string, error) { +func resultToString(r []byte, order binary.ByteOrder, forcedOrder string, varType string) (string, error) { + fo := strings.ToUpper(forcedOrder) + switch fo { + case "": + // nothing is forced + case "AB", "ABCD", "BADC": + order = binary.BigEndian + case "BA", "DCBA", "CDAB": + order = binary.LittleEndian + default: + return "", fmt.Errorf("forced order %s not known", strings.ToUpper(forcedOrder)) + } + + if fo == "CDAB" || fo == "BADC" && len(r) == 4 { + // flip result + r = []byte{r[1], r[0], r[3], r[2]} + } + switch varType { case "string": return string(r), nil diff --git a/cmd/modbus-cli/main_test.go b/cmd/modbus-cli/main_test.go new file mode 100644 index 0000000..aea3040 --- /dev/null +++ b/cmd/modbus-cli/main_test.go @@ -0,0 +1,285 @@ +package main + +import ( + "encoding/binary" + "math" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestConvertBytes(t *testing.T) { + type testCase struct { + name string + eType string + order binary.ByteOrder + val float64 + expected []byte + expectError bool + } + + tests := []testCase{ + // UInt 16 + { + name: "convert uint16 - be - valid value", + eType: "uint16", + order: binary.BigEndian, + val: 42, + expected: []byte{0x00, 0x2A}, + }, + { + name: "convert uint16 - le - valid value", + eType: "uint16", + order: binary.LittleEndian, + val: 42, + expected: []byte{0x2A, 0x00}, + }, + { + name: "convert uint16 - be - max", + eType: "uint16", + order: binary.BigEndian, + val: math.MaxUint16, + expected: []byte{0xFF, 0xFF}, + }, + { + name: "convert uint16 - le - max", + eType: "uint16", + order: binary.LittleEndian, + val: math.MaxUint16, + expected: []byte{0xFF, 0xFF}, + }, + { + name: "convert uint16 - negative value", + eType: "uint16", + order: binary.LittleEndian, + val: -42, + expectError: true, + }, + // UInt32 + { + name: "convert uint32 - be - valid value", + eType: "uint32", + order: binary.BigEndian, + val: 42, + expected: []byte{0x00, 0x00, 0x00, 0x2A}, + }, + { + name: "convert uint32 - le - valid value", + eType: "uint32", + order: binary.LittleEndian, + val: 42, + expected: []byte{0x2A, 0x00, 0x00, 0x00}, + }, + { + name: "convert uint32 - be - max", + eType: "uint32", + order: binary.BigEndian, + val: math.MaxUint32, + expected: []byte{0xFF, 0xFF, 0xFF, 0xFF}, + }, + { + name: "convert uint32 - le - max", + eType: "uint32", + order: binary.LittleEndian, + val: math.MaxUint32, + expected: []byte{0xFF, 0xFF, 0xFF, 0xFF}, + }, + { + name: "convert uint32 - negative value", + eType: "uint32", + order: binary.LittleEndian, + val: -42, + expectError: true, + }, + // Float32 + { + name: "convert float32 - be - valid value", + eType: "float32", + order: binary.BigEndian, + val: 42, + expected: []byte{0x42, 0x28, 0x00, 0x00}, + }, + { + name: "convert float32 - le - valid value", + eType: "float32", + order: binary.LittleEndian, + val: 42, + expected: []byte{0x00, 0x00, 0x28, 0x42}, + }, + { + name: "convert float32 - be - max", + eType: "float32", + order: binary.BigEndian, + val: math.MaxFloat32, + expected: []byte{0x7F, 0x7F, 0xFF, 0xFF}, + }, + { + name: "convert float32 - le - max", + eType: "float32", + order: binary.LittleEndian, + val: math.MaxFloat32, + expected: []byte{0xFF, 0xFF, 0x7F, 0x7F}, + }, + { + name: "convert float32 - negative value", + eType: "float32", + order: binary.BigEndian, + val: -42, + expected: []byte{0xC2, 0x28, 0x00, 0x00}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + bytes, err := convertToBytes(tc.eType, tc.order, "", tc.val) + if err != nil && tc.expectError == false { + t.Errorf("exepcted no error but got %v", err) + return + } + + if tc.expectError && err == nil { + t.Error("expected an error but didn't get one") + return + } + + // when in error, we don't need to compare anything + if err != nil { + return + } + + if !cmp.Equal(bytes, tc.expected) { + t.Errorf("expected %v but got %v. Diff: %v", tc.expected, bytes, cmp.Diff(tc.expected, bytes)) + } + + }) + } +} + +func TestForcedOrder(t *testing.T) { + type testCase struct { + name string + eType string + order binary.ByteOrder + forcedOrder string + val float64 + expected []byte + expectError bool + } + + tests := []testCase{ + // UInt 16 + { + name: "convert uint16, AB", + eType: "uint16", + order: binary.LittleEndian, // this should be overwritten by the forced order + forcedOrder: "AB", + val: 42, + expected: []byte{0x00, 0x2A}, + }, + { + name: "convert uint16, BA", + eType: "uint16", + order: binary.BigEndian, // this should be overwritten by the forced order + forcedOrder: "BA", + val: 42, + expected: []byte{0x2A, 0x00}, + }, + // UInt 32 + { + name: "convert uint32, ABCD", + eType: "uint32", + order: binary.LittleEndian, // this should be overwritten by the forced order + forcedOrder: "ABCD", + val: 42, + expected: []byte{0x00, 0x00, 0x00, 0x2A}, + }, + { + name: "convert uint32, DCBA", + eType: "uint32", + order: binary.BigEndian, // this should be overwritten by the forced order + forcedOrder: "DCBA", + val: 42, + expected: []byte{0x2A, 0x00, 0x00, 0x00}, + }, + { + name: "convert uint32, BADC", + eType: "uint32", + order: binary.BigEndian, // this should be overwritten by the forced order + forcedOrder: "BADC", + val: 42, + expected: []byte{0x00, 0x00, 0x2A, 0x00}, + }, + { + name: "convert uint32, CDAB", + eType: "uint32", + order: binary.LittleEndian, // this should be overwritten by the forced order + forcedOrder: "CDAB", + val: 42, + expected: []byte{0x00, 0x2A, 0x00, 0x00}, + }, + // Float 32 - TBD + { + name: "convert uint32, ABCD", + eType: "uint32", + order: binary.LittleEndian, // this should be overwritten by the forced order + forcedOrder: "ABCD", + val: 42, + expected: []byte{0x00, 0x00, 0x00, 0x2A}, + }, + { + name: "convert uint32, DCBA", + eType: "uint32", + order: binary.BigEndian, // this should be overwritten by the forced order + forcedOrder: "DCBA", + val: 42, + expected: []byte{0x2A, 0x00, 0x00, 0x00}, + }, + { + name: "convert uint32, BADC", + eType: "uint32", + order: binary.BigEndian, // this should be overwritten by the forced order + forcedOrder: "BADC", + val: 42, + expected: []byte{0x00, 0x00, 0x2A, 0x00}, + }, + { + name: "convert uint32, CDAB", + eType: "uint32", + order: binary.LittleEndian, // this should be overwritten by the forced order + forcedOrder: "CDAB", + val: 42, + expected: []byte{0x00, 0x2A, 0x00, 0x00}, + }, + // Error cases + { + name: "invalid forced order", + forcedOrder: "CDBA", + expectError: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + bytes, err := convertToBytes(tc.eType, tc.order, tc.forcedOrder, tc.val) + if err != nil && tc.expectError == false { + t.Errorf("exepcted no error but got %v", err) + return + } + + if tc.expectError && err == nil { + t.Error("expected an error but didn't get one") + return + } + + // when in error, we don't need to compare anything + if err != nil { + return + } + + if !cmp.Equal(bytes, tc.expected) { + t.Errorf("expected %v but got %v. Diff: %v", tc.expected, bytes, cmp.Diff(tc.expected, bytes)) + } + + }) + } +} From 44180bf8a92afc33fbdf0ab39600d5741be65890 Mon Sep 17 00:00:00 2001 From: Marco Trettner Date: Wed, 8 Mar 2023 08:41:34 +0100 Subject: [PATCH 21/51] feat(Makefile): add test for the modbus-cli tool --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 25a347e..0e3f575 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,7 @@ test: diagslave -m tcp -p 5020 & diagslave -m enc -p 5021 & go test -run TCP -v $(shell glide nv) socat -d -d pty,raw,echo=0 pty,raw,echo=0 & diagslave -m rtu /dev/pts/1 & go test -run RTU -v $(shell glide nv) socat -d -d pty,raw,echo=0 pty,raw,echo=0 & diagslave -m ascii /dev/pts/3 & go test -run ASCII -v $(shell glide nv) + go test -v -count=1 github.com/grid-x/modbus/cmd/modbus-cli .PHONY: lint lint: From 02a89c306c47ba154dba0f53aef849da31a3a2a9 Mon Sep 17 00:00:00 2001 From: Hendrik Steidl <837420+hsteidl@users.noreply.github.com> Date: Wed, 5 Apr 2023 13:44:01 +0200 Subject: [PATCH 22/51] fix(modbus/tcpclient): fix errTransactionIDMismatch with no ProtocolRecoveryTimeout set References to https://gridx.zendesk.com/agent/tickets/1647 --- tcpclient.go | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/tcpclient.go b/tcpclient.go index 336460e..57b3ffd 100644 --- a/tcpclient.go +++ b/tcpclient.go @@ -68,12 +68,13 @@ func (mb *tcpPackager) SetSlave(slaveID byte) { } // Encode adds modbus application protocol header: -// Transaction identifier: 2 bytes -// Protocol identifier: 2 bytes -// Length: 2 bytes -// Unit identifier: 1 byte -// Function code: 1 byte -// Data: n bytes +// +// Transaction identifier: 2 bytes +// Protocol identifier: 2 bytes +// Length: 2 bytes +// Unit identifier: 1 byte +// Function code: 1 byte +// Data: n bytes func (mb *tcpPackager) Encode(pdu *ProtocolDataUnit) (adu []byte, err error) { adu = make([]byte, tcpHeaderSize+1+len(pdu.Data)) @@ -100,10 +101,11 @@ func (mb *tcpPackager) Verify(aduRequest []byte, aduResponse []byte) error { } // Decode extracts PDU from TCP frame: -// Transaction identifier: 2 bytes -// Protocol identifier: 2 bytes -// Length: 2 bytes -// Unit identifier: 1 byte +// +// Transaction identifier: 2 bytes +// Protocol identifier: 2 bytes +// Length: 2 bytes +// Unit identifier: 1 byte func (mb *tcpPackager) Decode(adu []byte) (pdu *ProtocolDataUnit, err error) { // Read length value in the header length := binary.BigEndian.Uint16(adu[4:]) @@ -234,18 +236,18 @@ func (mb *tcpTransporter) readResponse(aduRequest []byte, data []byte, recoveryD // no time left, report error return case errTransactionIDMismatch: + // the first condition check for a normal transaction id mismatch. The second part of the condition check for a wrap-around. If a wraparound is + // detected (last attempt is smaller than last success), the id can be higher than the last success or lower than the last attempt, but not both + if (v.got > mb.lastSuccessfulTransactionID && v.got < mb.lastAttemptedTransactionID) || + (mb.lastAttemptedTransactionID < mb.lastSuccessfulTransactionID && (v.got > mb.lastSuccessfulTransactionID || v.got < mb.lastAttemptedTransactionID)) { + // most likely, we simply had a timeout for the earlier query and now read the (late) response. Ignore it + // and assume that the response will come *without* sending another query. (If we send another query + // with transactionId X+1 here, we would again get a transactionMismatchError if the response to + // transactionId X is already in the buffer). + continue + } if mb.ProtocolRecoveryTimeout > 0 { - // the first condition check for a normal transaction id mismatch. The second part of the condition check for a wrap-around. If a wraparound is - // detected (last attempt is smaller than last success), the id can be higher than the last success or lower than the last attempt, but not both - if (v.got > mb.lastSuccessfulTransactionID && v.got < mb.lastAttemptedTransactionID) || - (mb.lastAttemptedTransactionID < mb.lastSuccessfulTransactionID && (v.got > mb.lastSuccessfulTransactionID || v.got < mb.lastAttemptedTransactionID)) { - // most likely, we simply had a timeout for the earlier query and now read the (late) response. Ignore it - // and assume that the response will come *without* sending another query. (If we send another query - // with transactionId X+1 here, we would again get a transactionMismatchError if the response to - // transactionId X is already in the buffer). - continue - } - // some other mismatch, still in time - retry with another query + // some other mismatch, still in time and protocol may recover - retry with another query res = readResultRetry return } From 5dd11fc4e65c640608d54805110d9f535aeef3bf Mon Sep 17 00:00:00 2001 From: Hendrik Steidl <837420+hsteidl@users.noreply.github.com> Date: Wed, 12 Apr 2023 14:09:56 +0200 Subject: [PATCH 23/51] docs(*): make go doc comments compliant with `gofmt` by replacing double spaces with bullet points --- asciiclient.go | 15 ++-- client.go | 187 +++++++++++++++++++++++++++++-------------------- rtuclient.go | 15 ++-- 3 files changed, 126 insertions(+), 91 deletions(-) diff --git a/asciiclient.go b/asciiclient.go index 94a85f0..aadfd32 100644 --- a/asciiclient.go +++ b/asciiclient.go @@ -54,13 +54,14 @@ func (mb *asciiPackager) SetSlave(slaveID byte) { mb.SlaveID = slaveID } -// Encode encodes PDU in a ASCII frame: -// Start : 1 char -// Address : 2 chars -// Function : 2 chars -// Data : 0 up to 2x252 chars -// LRC : 2 chars -// End : 2 chars +// Encode encodes PDU in an ASCII frame: +// +// Start : 1 char +// Address : 2 chars +// Function : 2 chars +// Data : 0 up to 2x252 chars +// LRC : 2 chars +// End : 2 chars func (mb *asciiPackager) Encode(pdu *ProtocolDataUnit) (adu []byte, err error) { var buf bytes.Buffer diff --git a/client.go b/client.go index 533b773..28fed28 100644 --- a/client.go +++ b/client.go @@ -37,13 +37,16 @@ func NewClient2(packager Packager, transporter Transporter) Client { } // Request: -// Function code : 1 byte (0x01) -// Starting address : 2 bytes -// Quantity of coils : 2 bytes +// +// Function code : 1 byte (0x01) +// Starting address : 2 bytes +// Quantity of coils : 2 bytes +// // Response: -// Function code : 1 byte (0x01) -// Byte count : 1 byte -// Coil status : N* bytes (=N or N+1) +// +// Function code : 1 byte (0x01) +// Byte count : 1 byte +// Coil status : N* bytes (=N or N+1) func (mb *client) ReadCoils(address, quantity uint16) (results []byte, err error) { if quantity < 1 || quantity > 2000 { err = fmt.Errorf("modbus: quantity '%v' must be between '%v' and '%v',", quantity, 1, 2000) @@ -68,13 +71,16 @@ func (mb *client) ReadCoils(address, quantity uint16) (results []byte, err error } // Request: -// Function code : 1 byte (0x02) -// Starting address : 2 bytes -// Quantity of inputs : 2 bytes +// +// Function code : 1 byte (0x02) +// Starting address : 2 bytes +// Quantity of inputs : 2 bytes +// // Response: -// Function code : 1 byte (0x02) -// Byte count : 1 byte -// Input status : N* bytes (=N or N+1) +// +// Function code : 1 byte (0x02) +// Byte count : 1 byte +// Input status : N* bytes (=N or N+1) func (mb *client) ReadDiscreteInputs(address, quantity uint16) (results []byte, err error) { if quantity < 1 || quantity > 2000 { err = fmt.Errorf("modbus: quantity '%v' must be between '%v' and '%v',", quantity, 1, 2000) @@ -99,13 +105,16 @@ func (mb *client) ReadDiscreteInputs(address, quantity uint16) (results []byte, } // Request: -// Function code : 1 byte (0x03) -// Starting address : 2 bytes -// Quantity of registers : 2 bytes +// +// Function code : 1 byte (0x03) +// Starting address : 2 bytes +// Quantity of registers : 2 bytes +// // Response: -// Function code : 1 byte (0x03) -// Byte count : 1 byte -// Register value : Nx2 bytes +// +// Function code : 1 byte (0x03) +// Byte count : 1 byte +// Register value : Nx2 bytes func (mb *client) ReadHoldingRegisters(address, quantity uint16) (results []byte, err error) { if quantity < 1 || quantity > 125 { err = fmt.Errorf("modbus: quantity '%v' must be between '%v' and '%v',", quantity, 1, 125) @@ -130,13 +139,16 @@ func (mb *client) ReadHoldingRegisters(address, quantity uint16) (results []byte } // Request: -// Function code : 1 byte (0x04) -// Starting address : 2 bytes -// Quantity of registers : 2 bytes +// +// Function code : 1 byte (0x04) +// Starting address : 2 bytes +// Quantity of registers : 2 bytes +// // Response: -// Function code : 1 byte (0x04) -// Byte count : 1 byte -// Input registers : N bytes +// +// Function code : 1 byte (0x04) +// Byte count : 1 byte +// Input registers : N bytes func (mb *client) ReadInputRegisters(address, quantity uint16) (results []byte, err error) { if quantity < 1 || quantity > 125 { err = fmt.Errorf("modbus: quantity '%v' must be between '%v' and '%v',", quantity, 1, 125) @@ -161,13 +173,16 @@ func (mb *client) ReadInputRegisters(address, quantity uint16) (results []byte, } // Request: -// Function code : 1 byte (0x05) -// Output address : 2 bytes -// Output value : 2 bytes +// +// Function code : 1 byte (0x05) +// Output address : 2 bytes +// Output value : 2 bytes +// // Response: -// Function code : 1 byte (0x05) -// Output address : 2 bytes -// Output value : 2 bytes +// +// Function code : 1 byte (0x05) +// Output address : 2 bytes +// Output value : 2 bytes func (mb *client) WriteSingleCoil(address, value uint16) (results []byte, err error) { // The requested ON/OFF state can only be 0xFF00 and 0x0000 if value != 0xFF00 && value != 0x0000 { @@ -202,13 +217,16 @@ func (mb *client) WriteSingleCoil(address, value uint16) (results []byte, err er } // Request: -// Function code : 1 byte (0x06) -// Register address : 2 bytes -// Register value : 2 bytes +// +// Function code : 1 byte (0x06) +// Register address : 2 bytes +// Register value : 2 bytes +// // Response: -// Function code : 1 byte (0x06) -// Register address : 2 bytes -// Register value : 2 bytes +// +// Function code : 1 byte (0x06) +// Register address : 2 bytes +// Register value : 2 bytes func (mb *client) WriteSingleRegister(address, value uint16) (results []byte, err error) { request := ProtocolDataUnit{ FunctionCode: FuncCodeWriteSingleRegister, @@ -238,15 +256,18 @@ func (mb *client) WriteSingleRegister(address, value uint16) (results []byte, er } // Request: -// Function code : 1 byte (0x0F) -// Starting address : 2 bytes -// Quantity of outputs : 2 bytes -// Byte count : 1 byte -// Outputs value : N* bytes +// +// Function code : 1 byte (0x0F) +// Starting address : 2 bytes +// Quantity of outputs : 2 bytes +// Byte count : 1 byte +// Outputs value : N* bytes +// // Response: -// Function code : 1 byte (0x0F) -// Starting address : 2 bytes -// Quantity of outputs : 2 bytes +// +// Function code : 1 byte (0x0F) +// Starting address : 2 bytes +// Quantity of outputs : 2 bytes func (mb *client) WriteMultipleCoils(address, quantity uint16, value []byte) (results []byte, err error) { if quantity < 1 || quantity > 1968 { err = fmt.Errorf("modbus: quantity '%v' must be between '%v' and '%v',", quantity, 1, 1968) @@ -280,15 +301,18 @@ func (mb *client) WriteMultipleCoils(address, quantity uint16, value []byte) (re } // Request: -// Function code : 1 byte (0x10) -// Starting address : 2 bytes -// Quantity of outputs : 2 bytes -// Byte count : 1 byte -// Registers value : N* bytes +// +// Function code : 1 byte (0x10) +// Starting address : 2 bytes +// Quantity of outputs : 2 bytes +// Byte count : 1 byte +// Registers value : N* bytes +// // Response: -// Function code : 1 byte (0x10) -// Starting address : 2 bytes -// Quantity of registers : 2 bytes +// +// Function code : 1 byte (0x10) +// Starting address : 2 bytes +// Quantity of registers : 2 bytes func (mb *client) WriteMultipleRegisters(address, quantity uint16, value []byte) (results []byte, err error) { if quantity < 1 || quantity > 123 { err = fmt.Errorf("modbus: quantity '%v' must be between '%v' and '%v',", quantity, 1, 123) @@ -322,15 +346,18 @@ func (mb *client) WriteMultipleRegisters(address, quantity uint16, value []byte) } // Request: -// Function code : 1 byte (0x16) -// Reference address : 2 bytes -// AND-mask : 2 bytes -// OR-mask : 2 bytes +// +// Function code : 1 byte (0x16) +// Reference address : 2 bytes +// AND-mask : 2 bytes +// OR-mask : 2 bytes +// // Response: -// Function code : 1 byte (0x16) -// Reference address : 2 bytes -// AND-mask : 2 bytes -// OR-mask : 2 bytes +// +// Function code : 1 byte (0x16) +// Reference address : 2 bytes +// AND-mask : 2 bytes +// OR-mask : 2 bytes func (mb *client) MaskWriteRegister(address, andMask, orMask uint16) (results []byte, err error) { request := ProtocolDataUnit{ FunctionCode: FuncCodeMaskWriteRegister, @@ -365,17 +392,20 @@ func (mb *client) MaskWriteRegister(address, andMask, orMask uint16) (results [] } // Request: -// Function code : 1 byte (0x17) -// Read starting address : 2 bytes -// Quantity to read : 2 bytes -// Write starting address: 2 bytes -// Quantity to write : 2 bytes -// Write byte count : 1 byte -// Write registers value : N* bytes +// +// Function code : 1 byte (0x17) +// Read starting address : 2 bytes +// Quantity to read : 2 bytes +// Write starting address: 2 bytes +// Quantity to write : 2 bytes +// Write byte count : 1 byte +// Write registers value : N* bytes +// // Response: -// Function code : 1 byte (0x17) -// Byte count : 1 byte -// Read registers value : Nx2 bytes +// +// Function code : 1 byte (0x17) +// Byte count : 1 byte +// Read registers value : Nx2 bytes func (mb *client) ReadWriteMultipleRegisters(readAddress, readQuantity, writeAddress, writeQuantity uint16, value []byte) (results []byte, err error) { if readQuantity < 1 || readQuantity > 125 { err = fmt.Errorf("modbus: quantity to read '%v' must be between '%v' and '%v',", readQuantity, 1, 125) @@ -403,14 +433,17 @@ func (mb *client) ReadWriteMultipleRegisters(readAddress, readQuantity, writeAdd } // Request: -// Function code : 1 byte (0x18) -// FIFO pointer address : 2 bytes +// +// Function code : 1 byte (0x18) +// FIFO pointer address : 2 bytes +// // Response: -// Function code : 1 byte (0x18) -// Byte count : 2 bytes -// FIFO count : 2 bytes -// FIFO count : 2 bytes (<=31) -// FIFO value register : Nx2 bytes +// +// Function code : 1 byte (0x18) +// Byte count : 2 bytes +// FIFO count : 2 bytes +// FIFO count : 2 bytes (<=31) +// FIFO value register : Nx2 bytes func (mb *client) ReadFIFOQueue(address uint16) (results []byte, err error) { request := ProtocolDataUnit{ FunctionCode: FuncCodeReadFIFOQueue, diff --git a/rtuclient.go b/rtuclient.go index 615d95c..be38d44 100644 --- a/rtuclient.go +++ b/rtuclient.go @@ -74,11 +74,12 @@ func (mb *rtuPackager) SetSlave(slaveID byte) { mb.SlaveID = slaveID } -// Encode encodes PDU in a RTU frame: -// Slave Address : 1 byte -// Function : 1 byte -// Data : 0 up to 252 bytes -// CRC : 2 byte +// Encode encodes PDU in an RTU frame: +// +// Slave Address : 1 byte +// Function : 1 byte +// Data : 0 up to 252 bytes +// CRC : 2 byte func (mb *rtuPackager) Encode(pdu *ProtocolDataUnit) (adu []byte, err error) { length := len(pdu.Data) + 4 if length > rtuMaxSize { @@ -274,8 +275,8 @@ func (mb *rtuSerialTransporter) Send(aduRequest []byte) (aduResponse []byte, err if _, err = mb.port.Write(aduRequest); err != nil { return } - //function := aduRequest[1] - //functionFail := aduRequest[1] & 0x80 + // function := aduRequest[1] + // functionFail := aduRequest[1] & 0x80 bytesToRead := calculateResponseLength(aduRequest) time.Sleep(mb.calculateDelay(len(aduRequest) + bytesToRead)) From c8847a4526ca310ddbeb4581b08d9e877be7d553 Mon Sep 17 00:00:00 2001 From: andig Date: Mon, 10 Apr 2023 23:06:48 +0200 Subject: [PATCH 24/51] Simplify logger implementation --- asciiclient.go | 8 -------- rtuclient.go | 8 -------- 2 files changed, 16 deletions(-) diff --git a/asciiclient.go b/asciiclient.go index aadfd32..9adfa02 100644 --- a/asciiclient.go +++ b/asciiclient.go @@ -34,7 +34,6 @@ func NewASCIIClientHandler(address string) *ASCIIClientHandler { handler.Address = address handler.Timeout = serialTimeout handler.IdleTimeout = serialIdleTimeout - handler.serialPort.Logger = handler // expose the logger return handler } @@ -167,13 +166,6 @@ func (mb *asciiPackager) Decode(adu []byte) (pdu *ProtocolDataUnit, err error) { // asciiSerialTransporter implements Transporter interface. type asciiSerialTransporter struct { serialPort - Logger logger -} - -func (mb *asciiSerialTransporter) Printf(format string, v ...interface{}) { - if mb.Logger != nil { - mb.Logger.Printf(format, v...) - } } func (mb *asciiSerialTransporter) Send(aduRequest []byte) (aduResponse []byte, err error) { diff --git a/rtuclient.go b/rtuclient.go index be38d44..5a9967a 100644 --- a/rtuclient.go +++ b/rtuclient.go @@ -54,7 +54,6 @@ func NewRTUClientHandler(address string) *RTUClientHandler { handler.Address = address handler.Timeout = serialTimeout handler.IdleTimeout = serialIdleTimeout - handler.serialPort.Logger = handler // expose the logger return handler } @@ -139,13 +138,6 @@ func (mb *rtuPackager) Decode(adu []byte) (pdu *ProtocolDataUnit, err error) { // rtuSerialTransporter implements Transporter interface. type rtuSerialTransporter struct { serialPort - Logger logger -} - -func (mb *rtuSerialTransporter) Printf(format string, v ...interface{}) { - if mb.Logger != nil { - mb.Logger.Printf(format, v...) - } } // InvalidLengthError is returned by readIncrementally when the modbus response would overflow buffer From 8f3641c523a7d791adf1eac351ba4ae20218c816 Mon Sep 17 00:00:00 2001 From: Marco Trettner Date: Thu, 4 May 2023 09:46:34 +0200 Subject: [PATCH 25/51] chore: ignore location for generated binaries by goreleaser --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index b1001a6..b44aeed 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ .idea/** + +dist/ From 98610f7692ad50be6576f3592b0d7c9602de316e Mon Sep 17 00:00:00 2001 From: Marco Trettner Date: Thu, 4 May 2023 09:46:41 +0200 Subject: [PATCH 26/51] feat(modbus-cli): add .goreleaser config for modbus-cli --- .goreleaser.yaml | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .goreleaser.yaml diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000..f6dc220 --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,40 @@ +builds: + - main: ./cmd/modbus-cli + env: + - CGO_ENABLED=0 + goos: + - linux + - windows + - darwin + goarch: + - amd64 + - arm64 + - arm + goarm: + - 7 + # Ignore ARM32 build for both macOS and Windows + ignore: + - goos: darwin + goarch: arm + - goos: windows + goarch: arm +archives: + - id: modbus-cli + name_template: >- + modbus-cli_ + {{- if eq .Os "darwin" }}mac + {{- else }}{{ .Os }}{{ end }}_{{ .Arch }} + format_overrides: + - goos: windows + format: zip +checksum: + name_template: 'checksums.txt' +snapshot: + name_template: "{{ incpatch .Version }}-next" +changelog: + sort: asc + filters: + exclude: + - '^docs:' + - '^test:' + From 9fd07c8398878555b2f14aca171a82d3b5ba5b0d Mon Sep 17 00:00:00 2001 From: Marco Trettner Date: Thu, 4 May 2023 14:13:10 +0200 Subject: [PATCH 27/51] feat(modbus-cli): add options in the Makefile to generate releases for modbus-cli locally or using a docker image --- Makefile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Makefile b/Makefile index 25a347e..4ef1a6d 100644 --- a/Makefile +++ b/Makefile @@ -22,6 +22,9 @@ lint: build: go build +release: + goreleaser release --skip-publish --skip-validate --clean + ci_test: ${GO_RUN} "make test" @@ -30,3 +33,6 @@ ci_lint: ci_build: ${GO_RUN} "make build" + +ci_release: + ${GO_RUN} "goreleaser release --skip-publish --skip-validate --rm-dist" \ No newline at end of file From 7e81d6e0fd68ab22acca9ddb13e65827e575b119 Mon Sep 17 00:00:00 2001 From: Marco Trettner Date: Thu, 4 May 2023 14:44:38 +0200 Subject: [PATCH 28/51] chore: refactor readme to use markdown format and add documentation for the Modbus CLI tool --- README.md | 110 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 84 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index ea60bc0..6852e36 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,28 @@ -go modbus -========= +# go modbus Fault-tolerant, fail-fast implementation of Modbus protocol in Go. -Supported functions -------------------- +# Supported functions + Bit access: -* Read Discrete Inputs -* Read Coils -* Write Single Coil -* Write Multiple Coils +- Read Discrete Inputs +- Read Coils +- Write Single Coil +- Write Multiple Coils 16-bit access: -* Read Input Registers -* Read Holding Registers -* Write Single Register -* Write Multiple Registers -* Read/Write Multiple Registers -* Mask Write Register -* Read FIFO Queue - -Supported formats ------------------ -* TCP -* Serial (RTU, ASCII) - -Usage ------ +- Read Input Registers +- Read Holding Registers +- Write Single Register +- Write Multiple Registers +- Read/Write Multiple Registers +- Mask Write Register +- Read FIFO Queue + +# Supported formats +- TCP +- Serial (RTU, ASCII) + +# Usage Basic usage: ```go // Modbus TCP @@ -73,6 +70,67 @@ client := modbus.NewClient(handler) results, err := client.ReadDiscreteInputs(15, 2) ``` -References ----------- -- [Modbus Specifications and Implementation Guides](http://www.modbus.org/specs.php) +# Modbus-CLI + +We offer a CLI tool to read/write registers. + +## Usage + +For simplicity, the following examples are all using Modbus TCP. +For Modbus RTU, replace the address field and use the `rtu-` arguments in order to use different baudrates, databits, etc. +```sh +./modbus-cli -address=rtu:///dev/ttyUSB0 -rtu-baudrate=57600 -rtu-stopbits=2 -rtu-parity=N -rtu-databits=8 ... +``` +### Reading Registers + +Read 1 register and get raw result +```sh +./modbus-cli -address=tcp://127.0.0.1:502 -quantity=1 -type-parse=raw -register=42 +``` + +Read 1 register and decode result as uint16 +```sh +./modbus-cli -address=tcp://127.0.0.1:502 -quantity=1 -type-parse=uint16 -register=42 +``` + +Read 1 register and get all possible decoded results +```sh +./modbus-cli -address=tcp://127.0.0.1:502 -quantity=1 -type-parse=all -register=42 +``` + +Read 2 registers and decode result as uint32 +```sh +./modbus-cli -address=tcp://127.0.0.1:502 -quantity=2 -type-parse=uint32 -register=42 +``` + +Read 2 registers and get all possible decoded results +```sh +./modbus-cli -address=tcp://127.0.0.1:502 -quantity=2 -type-parse=all -register=42 +``` + +Reading multiple registers is only possible in the raw format +```sh +./modbus-cli -address=tcp://127.0.0.1:502 -quantity=16 -type-parse=raw -register=42 +``` + +### Writing Registers + +Write 1 register +```sh +./modbus-cli -address=tcp://127.0.0.1:502 -fn-code=0x06 -type-exec=uint16 -register=42 -write-value=7 +``` + +Write 2 registers +```sh +./modbus-cli -address=tcp://127.0.0.1:502 -fn-code=0x10 -type-exec=uint32 -register=42 -write-value=7 +``` + +## Release + +To release the Modbus-CLI tool, run either `make release` if you have installed `goreleaser` or `make ci_release`. +The generated files can be found in the directory in the `dist` directory. + +Take the `.tar.gz` and `.zip` files and create a new GitHub release. + +# References +- [Modbus Specifications and Implementation Guides](http://www.modbus.org/specs.php) From 552a7c7a619a558fb928e8caf9f9ed130305775c Mon Sep 17 00:00:00 2001 From: andig Date: Mon, 10 Apr 2023 19:06:40 +0200 Subject: [PATCH 29/51] Use embedded fields directly --- asciiclient.go | 14 +++++++------- rtuclient.go | 16 +++++++--------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/asciiclient.go b/asciiclient.go index aadfd32..b5d25e7 100644 --- a/asciiclient.go +++ b/asciiclient.go @@ -177,19 +177,19 @@ func (mb *asciiSerialTransporter) Printf(format string, v ...interface{}) { } func (mb *asciiSerialTransporter) Send(aduRequest []byte) (aduResponse []byte, err error) { - mb.serialPort.mu.Lock() - defer mb.serialPort.mu.Unlock() + mb.mu.Lock() + defer mb.mu.Unlock() // Make sure port is connected - if err = mb.serialPort.connect(); err != nil { + if err = mb.connect(); err != nil { return } // Start the timer to close when idle - mb.serialPort.lastActivity = time.Now() - mb.serialPort.startCloseTimer() + mb.lastActivity = time.Now() + mb.startCloseTimer() // Send the request - mb.serialPort.logf("modbus: send % x\n", aduRequest) + mb.logf("modbus: send % x\n", aduRequest) if _, err = mb.port.Write(aduRequest); err != nil { return } @@ -212,7 +212,7 @@ func (mb *asciiSerialTransporter) Send(aduRequest []byte) (aduResponse []byte, e } } aduResponse = data[:length] - mb.serialPort.logf("modbus: recv % x\n", aduResponse) + mb.logf("modbus: recv % x\n", aduResponse) return } diff --git a/rtuclient.go b/rtuclient.go index be38d44..fbf2854 100644 --- a/rtuclient.go +++ b/rtuclient.go @@ -161,12 +161,11 @@ func (e *InvalidLengthError) Error() string { // readIncrementally reads incrementally func readIncrementally(slaveID, functionCode byte, r io.Reader, deadline time.Time) ([]byte, error) { - n := 0 data := make([]byte, rtuMaxSize) state := stateSlaveID var length, toRead byte - crcCount := 0 + var n, crcCount int for { if time.Now().After(deadline) { // Possible that serialport may spew data @@ -255,7 +254,6 @@ func readIncrementally(slaveID, functionCode byte, r io.Reader, deadline time.Ti } } } - } func (mb *rtuSerialTransporter) Send(aduRequest []byte) (aduResponse []byte, err error) { @@ -263,15 +261,15 @@ func (mb *rtuSerialTransporter) Send(aduRequest []byte) (aduResponse []byte, err defer mb.mu.Unlock() // Make sure port is connected - if err = mb.serialPort.connect(); err != nil { + if err = mb.connect(); err != nil { return } // Start the timer to close when idle - mb.serialPort.lastActivity = time.Now() - mb.serialPort.startCloseTimer() + mb.lastActivity = time.Now() + mb.startCloseTimer() // Send the request - mb.serialPort.logf("modbus: send % x\n", aduRequest) + mb.logf("modbus: send % x\n", aduRequest) if _, err = mb.port.Write(aduRequest); err != nil { return } @@ -280,8 +278,8 @@ func (mb *rtuSerialTransporter) Send(aduRequest []byte) (aduResponse []byte, err bytesToRead := calculateResponseLength(aduRequest) time.Sleep(mb.calculateDelay(len(aduRequest) + bytesToRead)) - data, err := readIncrementally(aduRequest[0], aduRequest[1], mb.port, time.Now().Add(mb.serialPort.Config.Timeout)) - mb.serialPort.logf("modbus: recv % x\n", data[:]) + data, err := readIncrementally(aduRequest[0], aduRequest[1], mb.port, time.Now().Add(mb.Config.Timeout)) + mb.logf("modbus: recv % x\n", data[:]) aduResponse = data return } From 30f9d284f28fbca0ff87a80c4c0970d816d63d6a Mon Sep 17 00:00:00 2001 From: andig Date: Mon, 10 Apr 2023 19:09:12 +0200 Subject: [PATCH 30/51] Fix typo --- asciiclient.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asciiclient.go b/asciiclient.go index b5d25e7..8f379f5 100644 --- a/asciiclient.go +++ b/asciiclient.go @@ -231,7 +231,7 @@ func writeHex(buf *bytes.Buffer, value []byte) (err error) { return } -// readHex decodes hexa string to byte, e.g. "8C" => 0x8C. +// readHex decodes hex string to byte, e.g. "8C" => 0x8C. func readHex(data []byte) (value byte, err error) { var dst [1]byte if _, err = hex.Decode(dst[:], data[0:2]); err != nil { From aaeb0828017918552295f071a1712284dda2b28a Mon Sep 17 00:00:00 2001 From: ValentinMontmirail Date: Fri, 5 Jan 2024 21:32:21 +0100 Subject: [PATCH 31/51] 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 32/51] 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 33/51] 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 34/51] 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 35/51] 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 36/51] 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) From 79617db65f67b431901492846a2cb78b302a98e5 Mon Sep 17 00:00:00 2001 From: Hendrik Steidl <837420+hsteidl@users.noreply.github.com> Date: Fri, 2 Feb 2024 13:57:00 +0100 Subject: [PATCH 37/51] feat(*): bump to Go version 1.21 --- Makefile | 2 +- go.mod | 2 +- 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 | 168 +- .../google/go-cmp/cmp/compare_test.go | 2829 ----------------- .../go-cmp/cmp/example_reporter_test.go | 59 - .../google/go-cmp/cmp/example_test.go | 376 --- .../google/go-cmp/cmp/export_panic.go | 8 +- .../google/go-cmp/cmp/export_unsafe.go | 22 +- .../go-cmp/cmp/internal/diff/debug_disable.go | 2 +- .../go-cmp/cmp/internal/diff/debug_enable.go | 2 +- .../google/go-cmp/cmp/internal/diff/diff.go | 44 +- .../go-cmp/cmp/internal/diff/diff_test.go | 444 --- .../google/go-cmp/cmp/internal/flags/flags.go | 2 +- .../cmp/internal/flags/toolchain_legacy.go | 2 +- .../cmp/internal/flags/toolchain_recent.go | 2 +- .../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 | 157 + .../cmp/internal/value/pointer_purego.go | 12 +- .../cmp/internal/value/pointer_unsafe.go | 12 +- .../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 | 2 +- .../go-cmp/cmp/internal/value/zero_test.go | 52 - .../github.com/google/go-cmp/cmp/options.go | 60 +- .../google/go-cmp/cmp/options_test.go | 216 -- vendor/github.com/google/go-cmp/cmp/path.go | 80 +- vendor/github.com/google/go-cmp/cmp/report.go | 7 +- .../google/go-cmp/cmp/report_compare.go | 202 +- .../google/go-cmp/cmp/report_references.go | 264 ++ .../google/go-cmp/cmp/report_reflect.go | 296 +- .../google/go-cmp/cmp/report_slices.go | 356 ++- .../google/go-cmp/cmp/report_text.go | 88 +- .../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/modules.txt | 13 + .../rapid/.github/workflows/ci.yml | 32 - .../.github/workflows/codeql-analysis.yml | 71 - vendor/pgregory.net/rapid/README.md | 11 +- vendor/pgregory.net/rapid/TODO.md | 19 +- .../rapid/collections_example_test.go | 139 - .../rapid/collections_external_test.go | 157 - vendor/pgregory.net/rapid/collections_test.go | 28 - .../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_test.go | 105 - vendor/pgregory.net/rapid/engine.go | 14 +- 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 - .../rapid/floats_external_test.go | 145 - vendor/pgregory.net/rapid/floats_test.go | 120 - vendor/pgregory.net/rapid/generator.go | 16 +- vendor/pgregory.net/rapid/generator_test.go | 38 - vendor/pgregory.net/rapid/go.mod | 3 - .../rapid/integers_external_test.go | 245 -- vendor/pgregory.net/rapid/persist.go | 38 +- vendor/pgregory.net/rapid/persist_test.go | 45 - .../rapid/regexp_external_test.go | 1096 ------- vendor/pgregory.net/rapid/shrink_test.go | 232 -- .../pgregory.net/rapid/statemachine_test.go | 270 -- vendor/pgregory.net/rapid/strings.go | 6 +- .../rapid/strings_example_test.go | 144 - .../rapid/strings_external_test.go | 101 - vendor/pgregory.net/rapid/utils_test.go | 346 -- vendor/pgregory.net/rapid/vis_test.go | 74 - 84 files changed, 1587 insertions(+), 11323 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 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/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 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_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/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 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/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/go.mod b/go.mod index 7e1e99a..c0cf2a9 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/grid-x/modbus -go 1.13 +go 1.21 require ( github.com/google/go-cmp v0.5.6 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 <https://cla.developers.google.com/> 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..86d0903 100644 --- a/vendor/github.com/google/go-cmp/cmp/compare.go +++ b/vendor/github.com/google/go-cmp/cmp/compare.go @@ -1,11 +1,15 @@ // 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. +// 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: // @@ -22,8 +26,8 @@ // 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. +// by using an Ignore option (see cmpopts.IgnoreUnexported) or explicitly +// compared using the Exporter option. package cmp import ( @@ -62,8 +66,8 @@ import ( // // 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. +// (e.g., 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. @@ -80,7 +84,58 @@ import ( // 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) @@ -103,33 +158,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 +166,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 +178,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 +205,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 +218,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 +242,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) { @@ -342,7 +376,7 @@ func detectRaces(c chan<- reflect.Value, f reflect.Value, vs ...reflect.Value) { // 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). + // TODO(≥go1.10): 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() @@ -352,8 +386,10 @@ func sanitizeValue(v reflect.Value, t reflect.Type) reflect.Value { } 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 +408,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 +435,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 +526,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 +568,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}}}) 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} - // +: <invalid reflect.Value> -} 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_panic.go b/vendor/github.com/google/go-cmp/cmp/export_panic.go index abc3a1c..5ff0b42 100644 --- a/vendor/github.com/google/go-cmp/cmp/export_panic.go +++ b/vendor/github.com/google/go-cmp/cmp/export_panic.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. // +build purego @@ -8,8 +8,8 @@ package cmp import "reflect" -const supportAllowUnexported = false +const supportExporters = false -func retrieveUnexportedField(reflect.Value, reflect.StructField) reflect.Value { - panic("retrieveUnexportedField is not implemented") +func retrieveUnexportedField(reflect.Value, reflect.StructField, bool) reflect.Value { + panic("no support for forcibly accessing unexported fields") } diff --git a/vendor/github.com/google/go-cmp/cmp/export_unsafe.go b/vendor/github.com/google/go-cmp/cmp/export_unsafe.go index 59d4ee9..21eb548 100644 --- a/vendor/github.com/google/go-cmp/cmp/export_unsafe.go +++ b/vendor/github.com/google/go-cmp/cmp/export_unsafe.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. // +build !purego @@ -11,13 +11,25 @@ import ( "unsafe" ) -const supportAllowUnexported = true +const supportExporters = 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() +// 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/internal/diff/debug_disable.go b/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go index fe98dcc..1daaaac 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,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. // +build !cmp_debug 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..4b91dbc 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,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. // +build cmp_debug 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..bc196b1 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,6 +119,8 @@ 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. // @@ -177,6 +186,11 @@ 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. // @@ -194,20 +208,26 @@ func Difference(nx, ny int, f EqualFunc) (es EditScript) { // 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 +262,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 +304,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-- { 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 index 01aed0a..82d1d7f 100644 --- 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 @@ -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. // +build !go1.10 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 index c0b667f..8646f05 100644 --- 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 @@ -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. // +build go1.10 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..b6c12ce --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/internal/value/name.go @@ -0,0 +1,157 @@ +// 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" +) + +// 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. + + // 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_purego.go b/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go index 0a01c47..44f4a5a 100644 --- 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 @@ -1,6 +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. +// license that can be found in the LICENSE file. // +build purego @@ -21,3 +21,13 @@ func PointerOf(v reflect.Value) Pointer { // assumes that the GC implementation does not use a moving collector. return Pointer{v.Pointer(), v.Type()} } + +// IsNil reports whether the pointer is nil. +func (p Pointer) IsNil() bool { + return p.p == 0 +} + +// Uintptr returns the pointer as a uintptr. +func (p Pointer) Uintptr() uintptr { + return p.p +} 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_unsafe.go index da134ae..a605953 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_unsafe.go @@ -1,6 +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. +// license that can be found in the LICENSE file. // +build !purego @@ -24,3 +24,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/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 index 06a8ffd..9147a29 100644 --- a/vendor/github.com/google/go-cmp/cmp/internal/value/zero.go +++ b/vendor/github.com/google/go-cmp/cmp/internal/value/zero.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/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..e57b9eb 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 @@ -225,8 +225,23 @@ 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 { + // 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") @@ -360,9 +375,8 @@ 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 @@ -386,10 +400,24 @@ func (cm comparer) String() string { // // In other cases, the cmpopts.IgnoreUnexported option can be used to ignore // all unexported fields on specified struct types. -func AllowUnexported(types ...interface{}) Option { - if !supportAllowUnexported { - panic("AllowUnexported is not supported on purego builds, Google App Engine Standard, or GopherJS") +func Exporter(f func(reflect.Type) bool) Option { + if !supportExporters { + panic("Exporter is not supported on purego builds") } + return exporter(f) +} + +type exporter func(reflect.Type) bool + +func (exporter) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableOption { + panic("not implemented") +} + +// AllowUnexported returns an Options 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 { m := make(map[reflect.Type]bool) for _, typ := range types { t := reflect.TypeOf(typ) @@ -398,13 +426,7 @@ 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 @@ -436,6 +458,11 @@ 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,6 +473,7 @@ const ( reportByIgnore reportByMethod reportByFunc + reportByCycle ) // Reporter is an Option that can be passed to Equal. When Equal traverses 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..f01eff3 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,6 +10,8 @@ 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 @@ -41,7 +43,7 @@ type PathStep interface { // 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. + // 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 @@ -175,7 +177,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 addressible) field reflect.StructField // Field information } @@ -187,8 +190,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 @@ -207,6 +210,7 @@ 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 } @@ -301,6 +305,72 @@ func (tf Transform) Func() reflect.Value { return tf.trans.fnc } // 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..104bb30 100644 --- a/vendor/github.com/google/go-cmp/cmp/report_compare.go +++ b/vendor/github.com/google/go-cmp/cmp/report_compare.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 @@ -11,14 +11,6 @@ import ( "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 +63,66 @@ 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 { 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 +135,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 +150,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 +216,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,6 +235,11 @@ 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 @@ -186,23 +263,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 +311,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 +337,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() } - assert(len(recs) == 0) - return textWrap{"{", list, "}"} + if maxGroup.IsZero() { + assert(len(recs) == 0) + } else { + list.AppendEllipsis(maxGroup) + for len(keys) < len(list) { + keys = append(keys, reflect.Value{}) + } + } + 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..33f0357 100644 --- a/vendor/github.com/google/go-cmp/cmp/report_reflect.go +++ b/vendor/github.com/google/go-cmp/cmp/report_reflect.go @@ -1,17 +1,18 @@ // 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" ) @@ -20,14 +21,22 @@ type formatValueOptions struct { // 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 +53,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 +69,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,94 +152,140 @@ 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) { 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 supportExporters && !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() == reflect.TypeOf(byte(0)) { + 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)) + return opts.WithTypeMode(emitType).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)) - } - if opts.PrintAddresses { - ptr = formatPointer(v) + + // Check pointer for cycles. + ptrRef, visited := ptrs.Push(v) + if visited { + out = makeLeafReference(ptrRef, opts.PrintAddresses) + return &textWrap{Prefix: "&", Value: out} } + defer ptrs.Pop() + 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 @@ -197,19 +293,67 @@ func (opts formatOptions) FormatValue(v reflect.Value, m visitedPointers) (out t // 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 +371,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 +400,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..2ad3bc8 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 { @@ -53,7 +81,7 @@ 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 + 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 + isString = true case t.Kind() == reflect.Slice && t.Elem() == reflect.TypeOf(byte(0)): 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,26 @@ 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)) + isPureLinedText = efficiencyLines < 4*efficiencyBytes + } } // Format the string into printable records. @@ -106,9 +157,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 +166,87 @@ 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 != reflect.TypeOf(string("")) { + 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 +254,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 +271,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 +299,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 +314,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,12 +325,12 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode { } switch t.Kind() { case reflect.String: - out = textWrap{"strings.Join(", out, fmt.Sprintf(", %q)", delim)} + out = &textWrap{Prefix: "strings.Join(", Value: out, Suffix: fmt.Sprintf(", %q)", delim)} if t != reflect.TypeOf(string("")) { out = opts.FormatType(t, out) } case reflect.Slice: - out = textWrap{"bytes.Join(", out, fmt.Sprintf(", %q)", delim)} + out = &textWrap{Prefix: "bytes.Join(", Value: out, Suffix: fmt.Sprintf(", %q)", delim)} if t != reflect.TypeOf([]byte(nil)) { out = opts.FormatType(t, out) } @@ -225,8 +354,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 +374,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 +419,53 @@ 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 +475,35 @@ 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 +523,91 @@ 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 i := 0; i < nx && i < ny && eq(ix+i, iy+i); i++ { + numLeadingIdentical++ + } + for i := 0; i < nx && i < ny && eq(ix+nx-1-i, iy+ny-1-i); i++ { + 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..0fd46d7 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 } 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/modules.txt b/vendor/modules.txt new file mode 100644 index 0000000..6d0f4f9 --- /dev/null +++ b/vendor/modules.txt @@ -0,0 +1,13 @@ +# github.com/google/go-cmp v0.5.6 +## explicit; go 1.8 +github.com/google/go-cmp/cmp +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 +# github.com/grid-x/serial v0.0.0-20191104121038-e24bc9bf6f08 +## explicit; go 1.13 +github.com/grid-x/serial +# pgregory.net/rapid v0.4.7 +## explicit; go 1.12 +pgregory.net/rapid diff --git a/vendor/pgregory.net/rapid/.github/workflows/ci.yml b/vendor/pgregory.net/rapid/.github/workflows/ci.yml deleted file mode 100644 index 3f8f7d0..0000000 --- a/vendor/pgregory.net/rapid/.github/workflows/ci.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: CI -on: - push: - pull_request: - schedule: - - cron: "0 */13 * * *" -jobs: - ci: - name: CI - strategy: - matrix: - go: ['1.13', '1.14', '1.15'] - os: ['ubuntu-latest', 'windows-latest', 'macOS-latest'] - runs-on: ${{ matrix.os }} - steps: - - name: Set up Go - uses: actions/setup-go@v2 - with: - go-version: ${{ matrix.go }} - - - name: Check out code into the Go module directory - uses: actions/checkout@v2 - - - name: Run gofmt - run: test -z "$(go fmt .)" - shell: bash - - - name: Test - run: go test -race - - - name: Bench - run: go test -run=Benchmark -bench=. diff --git a/vendor/pgregory.net/rapid/.github/workflows/codeql-analysis.yml b/vendor/pgregory.net/rapid/.github/workflows/codeql-analysis.yml deleted file mode 100644 index b6dde6e..0000000 --- a/vendor/pgregory.net/rapid/.github/workflows/codeql-analysis.yml +++ /dev/null @@ -1,71 +0,0 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -name: "CodeQL" - -on: - push: - branches: [master] - pull_request: - # The branches below must be a subset of the branches above - branches: [master] - schedule: - - cron: '0 3 * * 1' - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - # Override automatic language detection by changing the below list - # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] - language: ['go'] - # Learn more... - # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection - - steps: - - name: Checkout repository - uses: actions/checkout@v2 - with: - # We must fetch at least the immediate parents so that if this is - # a pull request then we can checkout the head. - fetch-depth: 2 - - # If this run was triggered by a pull request event, then checkout - # the head of the pull request instead of the merge commit. - - run: git checkout HEAD^2 - if: ${{ github.event_name == 'pull_request' }} - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v1 - - # ℹ️ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - - #- run: | - # make bootstrap - # make release - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 diff --git a/vendor/pgregory.net/rapid/README.md b/vendor/pgregory.net/rapid/README.md index a0f9cbf..a205f97 100644 --- a/vendor/pgregory.net/rapid/README.md +++ b/vendor/pgregory.net/rapid/README.md @@ -23,12 +23,9 @@ a descendant of [QuickCheck](https://hackage.haskell.org/package/QuickCheck). [testify/require](https://pkg.go.dev/github.com/stretchr/testify/require) and [testify/assert](https://pkg.go.dev/github.com/stretchr/testify/assert) - Fully automatic minimization of failing test cases +- Persistence of minimized failing test cases - Support for state machine ("stateful" or "model-based") testing -- No dependencies outside of the Go standard library - -### Planned features - -- Automatic persistence of failing test cases +- No dependencies outside the Go standard library ## Examples @@ -91,6 +88,7 @@ Compared to [gopter](https://pkg.go.dev/github.com/leanovate/gopter), rapid: - has a much simpler API (queue test in [rapid](./example_statemachine_test.go) vs [gopter](https://github.com/leanovate/gopter/blob/master/commands/example_circularqueue_test.go)) - does not require any user code to minimize failing test cases +- persists minimized failing test cases to files for easy reproduction - generates biased data to explore "small" values and edge cases more thoroughly (inspired by [SmallCheck](https://hackage.haskell.org/package/smallcheck)) - enables interactive tests by allowing data generation and test code to arbitrarily intermix @@ -105,8 +103,7 @@ Compared to [testing/quick](https://golang.org/pkg/testing/quick/), rapid: ## Status -Rapid is alpha software. Important pieces of functionality are missing; -API breakage and bugs should be expected. +Rapid is preparing for stable 1.0 release. API breakage and bugs should be extremely rare. If rapid fails to find a bug you believe it should, or the failing test case that rapid reports does not look like a minimal one, diff --git a/vendor/pgregory.net/rapid/TODO.md b/vendor/pgregory.net/rapid/TODO.md index 0a0bbd1..c310af3 100644 --- a/vendor/pgregory.net/rapid/TODO.md +++ b/vendor/pgregory.net/rapid/TODO.md @@ -1,19 +1,14 @@ # TODO -## Docs - -- document every exported symbol -- doc.go paragraph about defining properties -- more examples - - HTTP API testing - - buffers? - - DI? -- tutorial? - -## Big things +## Pre-1.0 +- cleanly separate generation from the rest (so that people can use only the generation) +- `Any`/`Arbitrary` generator, + some kind of `DrawInto` API? - explicit examples support (based on `refDraws` shrink test machinery) -- failure persistence +- explicit settings support (to not depend on global environment) +- [go-fuzz](https://github.com/golang/proposal/blob/master/design/draft-fuzzing.md) integration +- decide what to do with FP NaNs etc. +- document every exported symbol? ## Generators 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 <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 ( - "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 <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 ( - "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 <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" - -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_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 <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 ( - "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("<nil>") - } else { - fmt.Println("(*int)", *v) - } - } - // Output: - // (*int) 1 - // (*int) -3 - // <nil> - // (*int) 590 - // <nil> -} 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 <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 ( - "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 <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" - -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_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 <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/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/engine.go b/vendor/pgregory.net/rapid/engine.go index 431a96a..aced4e7 100644 --- a/vendor/pgregory.net/rapid/engine.go +++ b/vendor/pgregory.net/rapid/engine.go @@ -47,6 +47,7 @@ type cmdline struct { checks int steps int failfile string + nofailfile bool seed uint64 log bool verbose bool @@ -59,6 +60,7 @@ 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.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") @@ -131,12 +133,12 @@ func checkTB(tb tb, prop func(*T)) { repr := fmt.Sprintf("-rapid.seed=%d", seed) if flags.failfile != "" && seed == 0 { repr = fmt.Sprintf("-rapid.failfile=%q", flags.failfile) - } else { + } else if !flags.nofailfile { failfile := failFileName(tb.Name()) out := captureTestOutput(tb, prop, buf) - err := saveFailFile(failfile, out, buf) + err := saveFailFile(failfile, rapidVersion, out, seed, buf) if err == nil { - repr = fmt.Sprintf("-rapid.failfile=%q", failfile) + repr = fmt.Sprintf("-rapid.failfile=%q (or -rapid.seed=%d)", failfile, seed) } else { tb.Logf("[rapid] %v", err) } @@ -195,11 +197,15 @@ func doCheck(tb tb, failfile string, checks int, seed uint64, prop func(*T)) (in 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) 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 <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 ( - "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 <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 ( - "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 <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 ( - "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 <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 ( - "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_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 <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 ( - "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 <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" - "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..72a9716 100644 --- a/vendor/pgregory.net/rapid/generator.go +++ b/vendor/pgregory.net/rapid/generator.go @@ -6,7 +6,10 @@ package rapid -import "reflect" +import ( + "reflect" + "sync" +) type value interface{} @@ -17,9 +20,10 @@ type generatorImpl interface { } type Generator struct { - impl generatorImpl - typ reflect.Type - str string + impl generatorImpl + typ reflect.Type + strOnce sync.Once + str string } func newGenerator(impl generatorImpl) *Generator { @@ -30,9 +34,9 @@ func newGenerator(impl generatorImpl) *Generator { } func (g *Generator) String() string { - if g.str == "" { + g.strOnce.Do(func() { g.str = g.impl.String() - } + }) return g.str } 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 <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 ( - "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_external_test.go b/vendor/pgregory.net/rapid/integers_external_test.go deleted file mode 100644 index 2eddf70..0000000 --- a/vendor/pgregory.net/rapid/integers_external_test.go +++ /dev/null @@ -1,245 +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 ( - "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/persist.go b/vendor/pgregory.net/rapid/persist.go index bdf5ff1..c0dece7 100644 --- a/vendor/pgregory.net/rapid/persist.go +++ b/vendor/pgregory.net/rapid/persist.go @@ -19,6 +19,8 @@ import ( ) const ( + rapidVersion = "v0.4.6" + persistDirMode = 0775 failfileTmpPattern = ".rapid-failfile-tmp-*" ) @@ -40,7 +42,7 @@ func failFileName(testName string) string { return fmt.Sprintf("%s-%s-%d.fail", kindaSafeFilename(testName), ts, os.Getpid()) } -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 { @@ -62,12 +64,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 +83,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 <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 ( - "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 <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 ( - "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<percent>.+)%.*\].*`, // markifier-0.1.0: r".*\[(?P<percent>.+)%.*\].*" - `(#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<k>[a-zA-Z_]+)`, // magnet_app-0.0.1: r":(?P<k>[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].*?>" - "<!--(.|\n)*?-->", // minifier-0.0.13: "<!--(.|\n)*?-->" - `<\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<relation>\([^)]+\))|(?P<prop>[a-zA-Z0-9_]+)`, // ggp-rs-0.1.2: r"(?P<relation>\([^)]+\))|(?P<prop>[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<nmsg>\d+) (?P<size>\d+)`, // pop3-rs-0.1.0: r"(?P<nmsg>\d+) (?P<size>\d+)" - `(?P<msgid>\d+) (?P<uidl>[\x21-\x7E]{1,70})`, // pop3-rs-0.1.0: r"(?P<msgid>\d+) (?P<uidl>[\x21-\x7E]{1,70})" - `(<.*>)\r\n$`, // pop3-rs-0.1.0: r"(<.*>)\r\n$" - `^(?P<status>\+OK|-ERR) (?P<statustext>.*)`, // pop3-rs-0.1.0: r"^(?P<status>\+OK|-ERR) (?P<statustext>.*)" - `^\.\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}:))" - `<value><string>([0-9.]*)</string></value>`, // generic-dns-update-1.1.4: r"<value><string>([0-9.]*)</string></value>" - `<int>([0-9]+)</int>`, // generic-dns-update-1.1.4: r"<int>([0-9]+)</int>" - `<int>([0-9]+)</int>`, // generic-dns-update-1.1.4: r"<int>([0-9]+)</int>" - `<boolean>([0-1]*)</boolean>`, // generic-dns-update-1.1.4: r"<boolean>([0-1]*)</boolean>" - `(\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<config>.*?)\*/`, // grimoire-0.1.0: r"(?s)/\*(?P<config>.*?)\*/" - `[\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<hex>[0-9a-fA-F][0-9a-fA-F])", // uritemplate-0.1.2: "%25(?P<hex>[0-9a-fA-F][0-9a-fA-F])" - "^package://(\\w+)/", // urdf-rs-0.4.2: "^package://(\\w+)/" - `(?P<key>[?&.])`, // url-match-0.1.7: r"(?P<key>[?&.])" - `:(?P<key>[a-zA-Z0-9_-]+)`, // url-match-0.1.7: r":(?P<key>[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<Block>\{db: \w+\}`, // hyperscan-0.1.6: r"RawDatabase<Block>\{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<day>\d{1,2})/(?P<month>\d{1,2})(/(?P<year>\d{4}|\d{2}))?`, // timmy-0.3.0: r"(?P<day>\d{1,2})/(?P<month>\d{1,2})(/(?P<year>\d{4}|\d{2}))?" - `(?P<n>\d+) (days?|ds?)(?P<ago>( ago)?)`, // timmy-0.3.0: r"(?P<n>\d+) (days?|ds?)(?P<ago>( ago)?)" - `(?P<hr>\d{2}):(?P<mins>\d{2})`, // timmy-0.3.0: r"(?P<hr>\d{2}):(?P<mins>\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<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{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_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_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..68da1c3 100644 --- a/vendor/pgregory.net/rapid/strings.go +++ b/vendor/pgregory.net/rapid/strings.go @@ -23,7 +23,7 @@ var ( byteSliceType = reflect.TypeOf([]byte(nil)) defaultRunes = []rune{ - '?', + 'A', 'a', '?', '~', '!', '@', '#', '$', '%', '^', '&', '*', '_', '-', '+', '=', '.', ',', ':', ';', ' ', '\t', '\r', '\n', @@ -383,12 +383,12 @@ func expandRangeTable(t *unicode.RangeTable, key interface{}) []rune { var ret []rune 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)) } } 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_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 f078006f0047a3a86e062764a4148610c3fa0884 Mon Sep 17 00:00:00 2001 From: Sven Rebhan <srebhan@influxdata.com> Date: Thu, 1 Feb 2024 14:17:33 +0100 Subject: [PATCH 38/51] Fix race in 'TestSerialCloseIdle()' --- serial_test.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/serial_test.go b/serial_test.go index 78fce0a..226ebb5 100644 --- a/serial_test.go +++ b/serial_test.go @@ -3,6 +3,7 @@ package modbus import ( "bytes" "io" + "sync/atomic" "testing" "time" ) @@ -10,11 +11,11 @@ import ( type nopCloser struct { io.ReadWriter - closed bool + closed atomic.Bool } func (n *nopCloser) Close() error { - n.closed = true + n.closed.Store(true) return nil } @@ -30,7 +31,9 @@ func TestSerialCloseIdle(t *testing.T) { s.startCloseTimer() time.Sleep(150 * time.Millisecond) - if !port.closed || s.port != nil { + s.mu.Lock() + defer s.mu.Unlock() + if !port.closed.Load() || s.port != nil { t.Fatalf("serial port is not closed when inactivity: %+v", port) } } From 63dc149136c610a092a6a0e355e3f4c445144da6 Mon Sep 17 00:00:00 2001 From: Sven Rebhan <srebhan@influxdata.com> Date: Thu, 1 Feb 2024 14:59:10 +0100 Subject: [PATCH 39/51] Fix race in 'TestTCPTransporter()' --- tcpclient_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tcpclient_test.go b/tcpclient_test.go index 3854951..4abb6f4 100644 --- a/tcpclient_test.go +++ b/tcpclient_test.go @@ -83,6 +83,8 @@ func TestTCPTransporter(t *testing.T) { t.Fatalf("unexpected response: %x", rsp) } time.Sleep(150 * time.Millisecond) + client.mu.Lock() + defer client.mu.Unlock() if client.conn != nil { t.Fatalf("connection is not closed: %+v", client.conn) } From 4237d62997afaac06f24d81128585c82c714b361 Mon Sep 17 00:00:00 2001 From: Stefan Nienhuis <stefan@nienhuisdevelopment.com> Date: Sun, 18 Feb 2024 16:25:19 +0100 Subject: [PATCH 40/51] feat: Implement RTU over UDP --- cmd/modbus-cli/main.go | 6 ++ rtu_over_udp_client.go | 146 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 rtu_over_udp_client.go diff --git a/cmd/modbus-cli/main.go b/cmd/modbus-cli/main.go index 7a4e8d9..39c69a8 100644 --- a/cmd/modbus-cli/main.go +++ b/cmd/modbus-cli/main.go @@ -472,7 +472,13 @@ func newHandler(o option) (modbus.ClientHandler, error) { h.ProtocolRecoveryTimeout = o.tcp.protocolRecoveryTimeout h.Logger = o.logger return h, nil + case "udp": + h := modbus.NewRTUOverUDPClientHandler(u.Host) + h.SlaveID = byte(o.slaveID) + h.Logger = o.logger + return h, nil } + return nil, fmt.Errorf("unsupported scheme: %s", u.Scheme) } diff --git a/rtu_over_udp_client.go b/rtu_over_udp_client.go new file mode 100644 index 0000000..2d07838 --- /dev/null +++ b/rtu_over_udp_client.go @@ -0,0 +1,146 @@ +package modbus + +import ( + "io" + "net" + "sync" +) + +// RTUOverUDPClientHandler implements Packager and Transporter interface. +type RTUOverUDPClientHandler struct { + rtuPackager + rtuUDPTransporter +} + +// NewRTUOverUDPClientHandler allocates and initializes a RTUOverUDPClientHandler. +func NewRTUOverUDPClientHandler(address string) *RTUOverUDPClientHandler { + handler := &RTUOverUDPClientHandler{} + handler.Address = address + return handler +} + +// RTUOverUDPClient creates RTU over UDP client with default handler and given connect string. +func RTUOverUDPClient(address string) Client { + handler := NewRTUOverUDPClientHandler(address) + return NewClient(handler) +} + +// rtuUDPTransporter implements Transporter interface. +type rtuUDPTransporter struct { + // Connect string + Address string + // Transmission logger + Logger logger + + // UDP connection + mu sync.Mutex + conn net.Conn +} + +// Send sends data to server and ensures adequate response for request type +func (mb *rtuUDPTransporter) Send(aduRequest []byte) (aduResponse []byte, err error) { + mb.mu.Lock() + defer mb.mu.Unlock() + + // Establish a new connection if not connected + if err = mb.connect(); err != nil { + return + } + + // Set write and read timeout + // var timeout time.Time + // if mb.Timeout > 0 { + // timeout = mb.lastActivity.Add(mb.Timeout) + // } + // if err = mb.conn.SetDeadline(timeout); err != nil { + // return + // } + + // Send the request + mb.logf("modbus: send % x\n", aduRequest) + if _, err = mb.conn.Write(aduRequest); err != nil { + return + } + function := aduRequest[1] + functionFail := aduRequest[1] & 0x80 + bytesToRead := calculateResponseLength(aduRequest) + + var n int + var n1 int + var data [rtuMaxSize]byte + //We first read the minimum length and then read either the full package + //or the error package, depending on the error status (byte 2 of the response) + n, err = io.ReadAtLeast(mb.conn, data[:], rtuMinSize) + if err != nil { + return + } + //if the function is correct + if data[1] == function { + //we read the rest of the bytes + if n < bytesToRead { + if bytesToRead > rtuMinSize && bytesToRead <= rtuMaxSize { + n1, err = io.ReadFull(mb.conn, data[n:bytesToRead]) + n += n1 + } + } + } else if data[1] == functionFail { + //for error we need to read 5 bytes + if n < rtuExceptionSize { + n1, err = io.ReadFull(mb.conn, data[n:rtuExceptionSize]) + } + n += n1 + } + + if err != nil { + return + } + aduResponse = data[:n] + mb.logf("modbus: recv % x\n", aduResponse) + return +} + +func (mb *rtuUDPTransporter) logf(format string, v ...interface{}) { + if mb.Logger != nil { + mb.Logger.Printf(format, v...) + } +} + +// Connect establishes a new connection to the address in Address. +func (mb *rtuUDPTransporter) Connect() error { + mb.mu.Lock() + defer mb.mu.Unlock() + + return mb.connect() +} + +// connect establishes a new connection to the address in Address. Caller must hold the mutex before calling this method. +// Since UDP is connectionless this does little more than setting up the connection object. +func (mb *rtuUDPTransporter) connect() error { + if mb.conn == nil { + dialer := net.Dialer{} + conn, err := dialer.Dial("udp", mb.Address) + if err != nil { + return err + } + mb.conn = conn + } + return nil +} + +// Close closes current connection. +func (mb *rtuUDPTransporter) Close() error { + mb.mu.Lock() + defer mb.mu.Unlock() + + return mb.close() +} + +// close closes current connection. Caller must hold the mutex before calling this method. +// Since UDP is connectionless this does little more than freeing up the connection object. +func (mb *rtuUDPTransporter) close() (err error) { + if mb.conn != nil { + err = mb.conn.Close() + mb.conn = nil + } + return +} From d0431e769eee1041e8da4524bccd5ccdc46a33d6 Mon Sep 17 00:00:00 2001 From: Stefan Nienhuis <stefan@nienhuisdevelopment.com> Date: Sun, 18 Feb 2024 16:29:44 +0100 Subject: [PATCH 41/51] docs: Document UDP protocol --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 6852e36..3ff3c00 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ Bit access: # Supported formats - TCP - Serial (RTU, ASCII) +- UDP # Usage Basic usage: @@ -81,6 +82,12 @@ For Modbus RTU, replace the address field and use the `rtu-` arguments in order ```sh ./modbus-cli -address=rtu:///dev/ttyUSB0 -rtu-baudrate=57600 -rtu-stopbits=2 -rtu-parity=N -rtu-databits=8 ... ``` + +For Modbus UDP, replace the address field with a UDP address. +```sh +./modbus-cli --address=udp://127.0.0.1:502 ... +``` + ### Reading Registers Read 1 register and get raw result From 47a626895b8998530ec7cc3f080efe04890c7e21 Mon Sep 17 00:00:00 2001 From: Stefan Nienhuis <stefan@nienhuisdevelopment.com> Date: Fri, 23 Feb 2024 15:39:01 +0100 Subject: [PATCH 42/51] fmt: Clean up RTU over UDP comments --- rtu_over_tcp_client.go | 10 +++++----- rtu_over_udp_client.go | 19 +++++-------------- 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/rtu_over_tcp_client.go b/rtu_over_tcp_client.go index d9ec19d..1a88080 100644 --- a/rtu_over_tcp_client.go +++ b/rtu_over_tcp_client.go @@ -68,15 +68,15 @@ func (mb *rtuTCPTransporter) Send(aduRequest []byte) (aduResponse []byte, err er var n int var n1 int var data [rtuMaxSize]byte - //We first read the minimum length and then read either the full package - //or the error package, depending on the error status (byte 2 of the response) + // We first read the minimum length and then read either the full package + // or the error package, depending on the error status (byte 2 of the response) n, err = io.ReadAtLeast(mb.conn, data[:], rtuMinSize) if err != nil { return } - //if the function is correct + // if the function is correct if data[1] == function { - //we read the rest of the bytes + // we read the rest of the bytes if n < bytesToRead { if bytesToRead > rtuMinSize && bytesToRead <= rtuMaxSize { n1, err = io.ReadFull(mb.conn, data[n:bytesToRead]) @@ -84,7 +84,7 @@ func (mb *rtuTCPTransporter) Send(aduRequest []byte) (aduResponse []byte, err er } } } else if data[1] == functionFail { - //for error we need to read 5 bytes + // for error we need to read 5 bytes if n < rtuExceptionSize { n1, err = io.ReadFull(mb.conn, data[n:rtuExceptionSize]) } diff --git a/rtu_over_udp_client.go b/rtu_over_udp_client.go index 2d07838..fa3f1b1 100644 --- a/rtu_over_udp_client.go +++ b/rtu_over_udp_client.go @@ -47,15 +47,6 @@ func (mb *rtuUDPTransporter) Send(aduRequest []byte) (aduResponse []byte, err er return } - // Set write and read timeout - // var timeout time.Time - // if mb.Timeout > 0 { - // timeout = mb.lastActivity.Add(mb.Timeout) - // } - // if err = mb.conn.SetDeadline(timeout); err != nil { - // return - // } - // Send the request mb.logf("modbus: send % x\n", aduRequest) if _, err = mb.conn.Write(aduRequest); err != nil { @@ -68,15 +59,15 @@ func (mb *rtuUDPTransporter) Send(aduRequest []byte) (aduResponse []byte, err er var n int var n1 int var data [rtuMaxSize]byte - //We first read the minimum length and then read either the full package - //or the error package, depending on the error status (byte 2 of the response) + // We first read the minimum length and then read either the full package + // or the error package, depending on the error status (byte 2 of the response) n, err = io.ReadAtLeast(mb.conn, data[:], rtuMinSize) if err != nil { return } - //if the function is correct + // if the function is correct if data[1] == function { - //we read the rest of the bytes + // we read the rest of the bytes if n < bytesToRead { if bytesToRead > rtuMinSize && bytesToRead <= rtuMaxSize { n1, err = io.ReadFull(mb.conn, data[n:bytesToRead]) @@ -84,7 +75,7 @@ func (mb *rtuUDPTransporter) Send(aduRequest []byte) (aduResponse []byte, err er } } } else if data[1] == functionFail { - //for error we need to read 5 bytes + // for error we need to read 5 bytes if n < rtuExceptionSize { n1, err = io.ReadFull(mb.conn, data[n:rtuExceptionSize]) } From 6e290f266aed865b73c46bb81cbebe59e25adf9f Mon Sep 17 00:00:00 2001 From: Stefan Nienhuis <stefan@nienhuisdevelopment.com> Date: Fri, 23 Feb 2024 15:48:01 +0100 Subject: [PATCH 43/51] fix: Add length checks to RTU over UDP --- rtu_over_udp_client.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/rtu_over_udp_client.go b/rtu_over_udp_client.go index fa3f1b1..6c5483e 100644 --- a/rtu_over_udp_client.go +++ b/rtu_over_udp_client.go @@ -1,11 +1,26 @@ package modbus import ( + "fmt" "io" "net" "sync" ) +// ErrADURequestLength informs about a wrong ADU request length. +type ErrADURequestLength int + +func (length ErrADURequestLength) Error() string { + return fmt.Sprintf("modbus: ADU request length '%d' must not be less than 2", length) +} + +// ErrADUResponseLength informs about a wrong ADU request length. +type ErrADUResponseLength int + +func (length ErrADUResponseLength) Error() string { + return fmt.Sprintf("modbus: ADU response length '%d' must not be less than 2", length) +} + // RTUOverUDPClientHandler implements Packager and Transporter interface. type RTUOverUDPClientHandler struct { rtuPackager @@ -42,6 +57,12 @@ func (mb *rtuUDPTransporter) Send(aduRequest []byte) (aduResponse []byte, err er mb.mu.Lock() defer mb.mu.Unlock() + // Check ADU request length + if len(aduRequest) < 2 { + err = ErrADURequestLength(len(aduRequest)) + return + } + // Establish a new connection if not connected if err = mb.connect(); err != nil { return @@ -65,6 +86,13 @@ func (mb *rtuUDPTransporter) Send(aduRequest []byte) (aduResponse []byte, err er if err != nil { return } + + // Check ADU response length + if len(data) < 2 { + err = ErrADUResponseLength(len(data)) + return + } + // if the function is correct if data[1] == function { // we read the rest of the bytes From 79d3faec2163f26f201ddf4d35f082d32154d4a6 Mon Sep 17 00:00:00 2001 From: Sven Rebhan <srebhan@influxdata.com> Date: Thu, 1 Feb 2024 13:55:14 +0100 Subject: [PATCH 44/51] Introduce custom error for invalid data-sizes --- client.go | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/client.go b/client.go index 6d4c383..2ababdb 100644 --- a/client.go +++ b/client.go @@ -9,6 +9,15 @@ import ( "fmt" ) +type DataSizeError struct { + Expected int + Actual int +} + +func (e *DataSizeError) Error() string { + return fmt.Sprintf("modbus: response data size '%d' does not match count '%d'", e.Actual, e.Expected) +} + // ClientHandler is the interface that groups the Packager and Transporter methods. type ClientHandler interface { Packager @@ -58,7 +67,7 @@ func (mb *client) ReadCoils(address, quantity uint16) (results []byte, err error count := int(response.Data[0]) length := len(response.Data) - 1 if count != length { - err = fmt.Errorf("modbus: response data size '%v' does not match count '%v'", length, count) + err = &DataSizeError{Expected: count, Actual: length} return } results = response.Data[1:] @@ -92,7 +101,7 @@ func (mb *client) ReadDiscreteInputs(address, quantity uint16) (results []byte, count := int(response.Data[0]) length := len(response.Data) - 1 if count != length { - err = fmt.Errorf("modbus: response data size '%v' does not match count '%v'", length, count) + err = &DataSizeError{Expected: count, Actual: length} return } results = response.Data[1:] @@ -126,7 +135,7 @@ func (mb *client) ReadHoldingRegisters(address, quantity uint16) (results []byte count := int(response.Data[0]) length := len(response.Data) - 1 if count != length { - err = fmt.Errorf("modbus: response data size '%v' does not match count '%v'", length, count) + err = &DataSizeError{Expected: count, Actual: length} return } if count != 2*int(quantity) { @@ -164,7 +173,7 @@ func (mb *client) ReadInputRegisters(address, quantity uint16) (results []byte, count := int(response.Data[0]) length := len(response.Data) - 1 if count != length { - err = fmt.Errorf("modbus: response data size '%v' does not match count '%v'", length, count) + err = &DataSizeError{Expected: count, Actual: length} return } if count != 2*int(quantity) { @@ -202,7 +211,7 @@ func (mb *client) WriteSingleCoil(address, value uint16) (results []byte, err er } // Fixed response length if len(response.Data) != 4 { - err = fmt.Errorf("modbus: response data size '%v' does not match expected '%v'", len(response.Data), 4) + err = &DataSizeError{Expected: 4, Actual: len(response.Data)} return } respValue := binary.BigEndian.Uint16(response.Data) @@ -241,7 +250,7 @@ func (mb *client) WriteSingleRegister(address, value uint16) (results []byte, er } // Fixed response length if len(response.Data) != 4 { - err = fmt.Errorf("modbus: response data size '%v' does not match expected '%v'", len(response.Data), 4) + err = &DataSizeError{Expected: 4, Actual: len(response.Data)} return } respValue := binary.BigEndian.Uint16(response.Data) @@ -286,7 +295,7 @@ func (mb *client) WriteMultipleCoils(address, quantity uint16, value []byte) (re } // Fixed response length if len(response.Data) != 4 { - err = fmt.Errorf("modbus: response data size '%v' does not match expected '%v'", len(response.Data), 4) + err = &DataSizeError{Expected: 4, Actual: len(response.Data)} return } respValue := binary.BigEndian.Uint16(response.Data) @@ -331,7 +340,7 @@ func (mb *client) WriteMultipleRegisters(address, quantity uint16, value []byte) } // Fixed response length if len(response.Data) != 4 { - err = fmt.Errorf("modbus: response data size '%v' does not match expected '%v'", len(response.Data), 4) + err = &DataSizeError{Expected: 4, Actual: len(response.Data)} return } respValue := binary.BigEndian.Uint16(response.Data) @@ -372,7 +381,7 @@ func (mb *client) MaskWriteRegister(address, andMask, orMask uint16) (results [] } // Fixed response length if len(response.Data) != 6 { - err = fmt.Errorf("modbus: response data size '%v' does not match expected '%v'", len(response.Data), 6) + err = &DataSizeError{Expected: 6, Actual: len(response.Data)} return } respValue := binary.BigEndian.Uint16(response.Data) @@ -428,7 +437,7 @@ func (mb *client) ReadWriteMultipleRegisters(readAddress, readQuantity, writeAdd } count := int(response.Data[0]) if count != (len(response.Data) - 1) { - err = fmt.Errorf("modbus: response data size '%v' does not match count '%v'", len(response.Data)-1, count) + err = &DataSizeError{Expected: count, Actual: len(response.Data) - 1} return } results = response.Data[1:] @@ -462,7 +471,7 @@ func (mb *client) ReadFIFOQueue(address uint16) (results []byte, err error) { } count := int(binary.BigEndian.Uint16(response.Data)) if count != (len(response.Data) - 1) { - err = fmt.Errorf("modbus: response data size '%v' does not match count '%v'", len(response.Data)-1, count) + err = &DataSizeError{Expected: count, Actual: len(response.Data) - 1} return } count = int(binary.BigEndian.Uint16(response.Data[2:])) From 0a13a3da40f543f3ba84a0e2158102aec477cb58 Mon Sep 17 00:00:00 2001 From: Sven Rebhan <srebhan@influxdata.com> Date: Thu, 1 Feb 2024 14:03:37 +0100 Subject: [PATCH 45/51] Return data if response is too large, fixes #52 --- client.go | 46 ++++++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/client.go b/client.go index 2ababdb..5115b51 100644 --- a/client.go +++ b/client.go @@ -68,9 +68,11 @@ func (mb *client) ReadCoils(address, quantity uint16) (results []byte, err error length := len(response.Data) - 1 if count != length { err = &DataSizeError{Expected: count, Actual: length} - return + if length < count { + return + } } - results = response.Data[1:] + results = response.Data[1 : count+1] return } @@ -102,9 +104,11 @@ func (mb *client) ReadDiscreteInputs(address, quantity uint16) (results []byte, length := len(response.Data) - 1 if count != length { err = &DataSizeError{Expected: count, Actual: length} - return + if length < count { + return + } } - results = response.Data[1:] + results = response.Data[1 : count+1] return } @@ -136,13 +140,15 @@ func (mb *client) ReadHoldingRegisters(address, quantity uint16) (results []byte length := len(response.Data) - 1 if count != length { err = &DataSizeError{Expected: count, Actual: length} - return + if length < count { + return + } } if count != 2*int(quantity) { err = fmt.Errorf("modbus: response data size '%v' does not match request quantity '%v'", length, quantity) return } - results = response.Data[1:] + results = response.Data[1 : count+1] return } @@ -174,13 +180,15 @@ func (mb *client) ReadInputRegisters(address, quantity uint16) (results []byte, length := len(response.Data) - 1 if count != length { err = &DataSizeError{Expected: count, Actual: length} - return + if length < count { + return + } } if count != 2*int(quantity) { err = fmt.Errorf("modbus: response data size '%v' does not match request quantity '%v'", length, quantity) return } - results = response.Data[1:] + results = response.Data[1 : count+1] return } @@ -436,11 +444,14 @@ func (mb *client) ReadWriteMultipleRegisters(readAddress, readQuantity, writeAdd return } count := int(response.Data[0]) - if count != (len(response.Data) - 1) { - err = &DataSizeError{Expected: count, Actual: len(response.Data) - 1} - return + length := len(response.Data) - 1 + if count != length { + err = &DataSizeError{Expected: count, Actual: length} + if length < count { + return + } } - results = response.Data[1:] + results = response.Data[1 : count+1] return } @@ -470,11 +481,14 @@ func (mb *client) ReadFIFOQueue(address uint16) (results []byte, err error) { return } count := int(binary.BigEndian.Uint16(response.Data)) - if count != (len(response.Data) - 1) { - err = &DataSizeError{Expected: count, Actual: len(response.Data) - 1} - return + length := len(response.Data) - 1 + if count != length { + err = &DataSizeError{Expected: count, Actual: length} + if length < count { + return + } } - count = int(binary.BigEndian.Uint16(response.Data[2:])) + count = int(binary.BigEndian.Uint16(response.Data[2 : count+2])) if count > 31 { err = fmt.Errorf("modbus: fifo count '%v' is greater than expected '%v'", count, 31) return From f14741f98e302d289df32306987eafc2d215e789 Mon Sep 17 00:00:00 2001 From: Sven Rebhan <srebhan@influxdata.com> Date: Thu, 1 Feb 2024 15:04:08 +0100 Subject: [PATCH 46/51] Comment new error type --- client.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client.go b/client.go index 5115b51..bdb9194 100644 --- a/client.go +++ b/client.go @@ -9,6 +9,8 @@ import ( "fmt" ) +// DataSizeError represents an error for invalid data-sizes i.e. for cases +// where the data-size does not match the expectation. type DataSizeError struct { Expected int Actual int From 828f9ade4663fe9f34e4fceb88cfe70a57d9f36f Mon Sep 17 00:00:00 2001 From: Sven Rebhan <srebhan@influxdata.com> Date: Mon, 12 Feb 2024 14:06:10 +0100 Subject: [PATCH 47/51] Make clear expected and actual are in bytes --- client.go | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/client.go b/client.go index bdb9194..0c8e1b6 100644 --- a/client.go +++ b/client.go @@ -12,12 +12,12 @@ import ( // DataSizeError represents an error for invalid data-sizes i.e. for cases // where the data-size does not match the expectation. type DataSizeError struct { - Expected int - Actual int + ExpectedBytes int + ActualBytes int } func (e *DataSizeError) Error() string { - return fmt.Sprintf("modbus: response data size '%d' does not match count '%d'", e.Actual, e.Expected) + return fmt.Sprintf("modbus: response data size '%d' does not match count '%d'", e.ActualBytes, e.ExpectedBytes) } // ClientHandler is the interface that groups the Packager and Transporter methods. @@ -69,7 +69,7 @@ func (mb *client) ReadCoils(address, quantity uint16) (results []byte, err error count := int(response.Data[0]) length := len(response.Data) - 1 if count != length { - err = &DataSizeError{Expected: count, Actual: length} + err = &DataSizeError{ExpectedBytes: count, ActualBytes: length} if length < count { return } @@ -105,7 +105,7 @@ func (mb *client) ReadDiscreteInputs(address, quantity uint16) (results []byte, count := int(response.Data[0]) length := len(response.Data) - 1 if count != length { - err = &DataSizeError{Expected: count, Actual: length} + err = &DataSizeError{ExpectedBytes: count, ActualBytes: length} if length < count { return } @@ -141,7 +141,7 @@ func (mb *client) ReadHoldingRegisters(address, quantity uint16) (results []byte count := int(response.Data[0]) length := len(response.Data) - 1 if count != length { - err = &DataSizeError{Expected: count, Actual: length} + err = &DataSizeError{ExpectedBytes: count, ActualBytes: length} if length < count { return } @@ -181,7 +181,7 @@ func (mb *client) ReadInputRegisters(address, quantity uint16) (results []byte, count := int(response.Data[0]) length := len(response.Data) - 1 if count != length { - err = &DataSizeError{Expected: count, Actual: length} + err = &DataSizeError{ExpectedBytes: count, ActualBytes: length} if length < count { return } @@ -221,7 +221,7 @@ func (mb *client) WriteSingleCoil(address, value uint16) (results []byte, err er } // Fixed response length if len(response.Data) != 4 { - err = &DataSizeError{Expected: 4, Actual: len(response.Data)} + err = &DataSizeError{ExpectedBytes: 4, ActualBytes: len(response.Data)} return } respValue := binary.BigEndian.Uint16(response.Data) @@ -260,7 +260,7 @@ func (mb *client) WriteSingleRegister(address, value uint16) (results []byte, er } // Fixed response length if len(response.Data) != 4 { - err = &DataSizeError{Expected: 4, Actual: len(response.Data)} + err = &DataSizeError{ExpectedBytes: 4, ActualBytes: len(response.Data)} return } respValue := binary.BigEndian.Uint16(response.Data) @@ -305,7 +305,7 @@ func (mb *client) WriteMultipleCoils(address, quantity uint16, value []byte) (re } // Fixed response length if len(response.Data) != 4 { - err = &DataSizeError{Expected: 4, Actual: len(response.Data)} + err = &DataSizeError{ExpectedBytes: 4, ActualBytes: len(response.Data)} return } respValue := binary.BigEndian.Uint16(response.Data) @@ -350,7 +350,7 @@ func (mb *client) WriteMultipleRegisters(address, quantity uint16, value []byte) } // Fixed response length if len(response.Data) != 4 { - err = &DataSizeError{Expected: 4, Actual: len(response.Data)} + err = &DataSizeError{ExpectedBytes: 4, ActualBytes: len(response.Data)} return } respValue := binary.BigEndian.Uint16(response.Data) @@ -391,7 +391,7 @@ func (mb *client) MaskWriteRegister(address, andMask, orMask uint16) (results [] } // Fixed response length if len(response.Data) != 6 { - err = &DataSizeError{Expected: 6, Actual: len(response.Data)} + err = &DataSizeError{ExpectedBytes: 6, ActualBytes: len(response.Data)} return } respValue := binary.BigEndian.Uint16(response.Data) @@ -448,7 +448,7 @@ func (mb *client) ReadWriteMultipleRegisters(readAddress, readQuantity, writeAdd count := int(response.Data[0]) length := len(response.Data) - 1 if count != length { - err = &DataSizeError{Expected: count, Actual: length} + err = &DataSizeError{ExpectedBytes: count, ActualBytes: length} if length < count { return } @@ -485,7 +485,7 @@ func (mb *client) ReadFIFOQueue(address uint16) (results []byte, err error) { count := int(binary.BigEndian.Uint16(response.Data)) length := len(response.Data) - 1 if count != length { - err = &DataSizeError{Expected: count, Actual: length} + err = &DataSizeError{ExpectedBytes: count, ActualBytes: length} if length < count { return } From 1334895a5c0c9bc30af4ac25f239a7f267dfb23e Mon Sep 17 00:00:00 2001 From: andig <cpuidle@gmx.de> Date: Fri, 3 May 2024 11:49:31 +0200 Subject: [PATCH 48/51] Return to minimal logger interface --- ascii_over_tcp_client.go | 4 +-- asciiclient.go | 4 +-- client.go | 5 ++++ cmd/modbus-cli/main.go | 4 +-- rtu_over_tcp_client.go | 4 +-- rtuclient.go | 4 +-- serial.go | 40 +++------------------------ tcpclient.go | 46 +++++--------------------------- test/asciiclient_test.go | 10 ++++++- test/rtu_over_tcp_client_test.go | 2 +- test/rtuclient_test.go | 2 +- test/tcpclient_test.go | 2 +- 12 files changed, 38 insertions(+), 89 deletions(-) diff --git a/ascii_over_tcp_client.go b/ascii_over_tcp_client.go index d45b008..f8a5805 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.Debug("modbus: send %q\n", aduRequest) + mb.Printf("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.Debug("modbus: recv %q\n", aduResponse) + mb.Printf("modbus: recv %q\n", aduResponse) return } diff --git a/asciiclient.go b/asciiclient.go index 0deffea..6cd0054 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.Debug("modbus: send % x\n", aduRequest) + mb.Printf("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.Debug("modbus: recv % x\n", aduResponse) + mb.Printf("modbus: recv % x\n", aduResponse) return } diff --git a/client.go b/client.go index 0c8e1b6..8148704 100644 --- a/client.go +++ b/client.go @@ -9,6 +9,11 @@ import ( "fmt" ) +// Logger is the interface to the required logging functions +type Logger interface { + Printf(format string, v ...interface{}) +} + // DataSizeError represents an error for invalid data-sizes i.e. for cases // where the data-size does not match the expectation. type DataSizeError struct { diff --git a/cmd/modbus-cli/main.go b/cmd/modbus-cli/main.go index 2c9c141..01306ca 100644 --- a/cmd/modbus-cli/main.go +++ b/cmd/modbus-cli/main.go @@ -238,7 +238,7 @@ func convertToBytes(eType string, order binary.ByteOrder, forcedOrder string, va } func resultToFile(r []byte, filename string) error { - return os.WriteFile(filename, r, 0644) + return os.WriteFile(filename, r, 0o644) } func resultToRawString(r []byte, startReg int) (string, error) { @@ -421,7 +421,7 @@ type option struct { slaveID int timeout time.Duration - logger *slog.Logger + logger Logger rtu struct { baudrate int diff --git a/rtu_over_tcp_client.go b/rtu_over_tcp_client.go index 5b28691..c7b2f70 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.Debug("modbus: send % x\n", aduRequest) + mb.Printf("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.Debug("modbus: recv % x\n", aduResponse) + mb.Printf("modbus: recv % x\n", aduResponse) return } diff --git a/rtuclient.go b/rtuclient.go index b4cf837..01cc2aa 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.Debug("modbus: send % x\n", aduRequest) + mb.Printf("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.Debug("modbus: recv % x\n", data[:]) + mb.Printf("modbus: recv % x\n", data[:]) aduResponse = data return } diff --git a/serial.go b/serial.go index 178c831..4329e92 100644 --- a/serial.go +++ b/serial.go @@ -5,10 +5,8 @@ package modbus import ( - "context" "fmt" "io" - "log/slog" "sync" "time" @@ -26,7 +24,7 @@ type serialPort struct { // Serial port configuration. serial.Config - Logger *slog.Logger + Logger Logger IdleTimeout time.Duration mu sync.Mutex @@ -71,39 +69,9 @@ func (mb *serialPort) close() (err error) { return } -func (mb *serialPort) Debug(format string, v ...interface{}) { +func (mb *serialPort) Printf(format string, v ...interface{}) { if mb.Logger != nil { - 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...) - } -} - -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...) + mb.Logger.Printf(format, v...) } } @@ -128,7 +96,7 @@ func (mb *serialPort) closeIdle() { } if idle := time.Since(mb.lastActivity); idle >= mb.IdleTimeout { - mb.Debug("modbus: closing connection due to idle timeout: %v", idle) + mb.Printf("modbus: closing connection due to idle timeout: %v", idle) mb.close() } } diff --git a/tcpclient.go b/tcpclient.go index dcb872f..82d3c70 100644 --- a/tcpclient.go +++ b/tcpclient.go @@ -5,11 +5,9 @@ package modbus import ( - "context" "encoding/binary" "fmt" "io" - "log/slog" "net" "sync" "sync/atomic" @@ -138,7 +136,7 @@ type tcpTransporter struct { // Silent period after successful connection ConnectDelay time.Duration // Transmission logger - Logger *slog.Logger + Logger Logger // TCP connection mu sync.Mutex @@ -185,7 +183,7 @@ func (mb *tcpTransporter) Send(aduRequest []byte) (aduResponse []byte, err error return } // Send data - mb.Debug("modbus: send % x", aduRequest) + mb.Printf("modbus: send % x", aduRequest) if _, err = mb.conn.Write(aduRequest); err != nil { return } @@ -203,7 +201,7 @@ func (mb *tcpTransporter) Send(aduRequest []byte) (aduResponse []byte, err error continue } - mb.Debug("modbus: close connection and retry, because of %v", err) + mb.Printf("modbus: close connection and retry, because of %v", err) mb.close() time.Sleep(mb.LinkRecoveryTimeout) @@ -218,7 +216,7 @@ func (mb *tcpTransporter) readResponse(aduRequest []byte, data []byte, recoveryD if err == nil { err = verify(aduRequest, aduResponse) if err == nil { - mb.Debug("modbus: recv % x\n", aduResponse) + mb.Printf("modbus: recv % x\n", aduResponse) return // everything is OK } } @@ -384,39 +382,9 @@ func (mb *tcpTransporter) flush(b []byte) (err error) { return } -func (mb *tcpTransporter) Debug(format string, v ...interface{}) { +func (mb *tcpTransporter) Printf(format string, v ...interface{}) { if mb.Logger != nil { - 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...) - } -} - -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...) + mb.Logger.Printf(format, v...) } } @@ -439,7 +407,7 @@ func (mb *tcpTransporter) closeIdle() { } if idle := time.Since(mb.lastActivity); idle >= mb.IdleTimeout { - mb.Debug("modbus: closing connection due to idle timeout: %v", idle) + mb.Printf("modbus: closing connection due to idle timeout: %v", idle) mb.close() } } diff --git a/test/asciiclient_test.go b/test/asciiclient_test.go index dbef4a2..5d2a2ab 100644 --- a/test/asciiclient_test.go +++ b/test/asciiclient_test.go @@ -16,6 +16,14 @@ const ( asciiDevice = "/dev/pts/2" ) +type debugAdapter struct { + *slog.Logger +} + +func (log *debugAdapter) Printf(msg string, args ...any) { + log.Logger.Debug(msg, args...) +} + func TestASCIIClient(t *testing.T) { // Diagslave does not support broadcast id. handler := modbus.NewASCIIClientHandler(asciiDevice) @@ -30,7 +38,7 @@ func TestASCIIClientAdvancedUsage(t *testing.T) { handler.Parity = "E" handler.StopBits = 1 handler.SlaveID = 12 - handler.Logger = slog.New(slog.NewJSONHandler(os.Stdout, nil)) + handler.Logger = &debugAdapter{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 402c38b..d097b80 100644 --- a/test/rtu_over_tcp_client_test.go +++ b/test/rtu_over_tcp_client_test.go @@ -28,7 +28,7 @@ func TestRTUOverTCPClientAdvancedUsage(t *testing.T) { handler := modbus.NewRTUOverTCPClientHandler(rtuOverTCPDevice) handler.Timeout = 5 * time.Second handler.SlaveID = 1 - handler.Logger = slog.New(slog.NewJSONHandler(os.Stdout, nil)) + handler.Logger = &debugAdapter{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 dddccb7..fd506ac 100644 --- a/test/rtuclient_test.go +++ b/test/rtuclient_test.go @@ -30,7 +30,7 @@ func TestRTUClientAdvancedUsage(t *testing.T) { handler.Parity = "E" handler.StopBits = 1 handler.SlaveID = 11 - handler.Logger = slog.New(slog.NewJSONHandler(os.Stdout, nil)) + handler.Logger = &debugAdapter{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 0363675..e1ef709 100644 --- a/test/tcpclient_test.go +++ b/test/tcpclient_test.go @@ -26,7 +26,7 @@ func TestTCPClientAdvancedUsage(t *testing.T) { handler := modbus.NewTCPClientHandler(tcpDevice) handler.Timeout = 5 * time.Second handler.SlaveID = 1 - handler.Logger = slog.New(slog.NewJSONHandler(os.Stdout, nil)) + handler.Logger = &debugAdapter{slog.New(slog.NewJSONHandler(os.Stdout, nil))} handler.Connect() defer handler.Close() From bbc42ffb2d0bcc1b45fb6bc47a5989bb591d1688 Mon Sep 17 00:00:00 2001 From: andig <cpuidle@gmx.de> Date: Fri, 3 May 2024 12:02:42 +0200 Subject: [PATCH 49/51] Prepare a full revert --- ascii_over_tcp_client.go | 4 ++-- asciiclient.go | 4 ++-- client.go | 2 +- cmd/modbus-cli/main.go | 2 +- rtu_over_tcp_client.go | 4 ++-- rtu_over_udp_client.go | 5 ++--- rtuclient.go | 4 ++-- serial.go | 4 ++-- tcpclient.go | 10 +++++----- 9 files changed, 19 insertions(+), 20 deletions(-) diff --git a/ascii_over_tcp_client.go b/ascii_over_tcp_client.go index f8a5805..cc263e4 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.Printf("modbus: send %q\n", aduRequest) + mb.logf("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.Printf("modbus: recv %q\n", aduResponse) + mb.logf("modbus: recv %q\n", aduResponse) return } diff --git a/asciiclient.go b/asciiclient.go index 6cd0054..d860fb3 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.Printf("modbus: send % x\n", aduRequest) + mb.logf("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.Printf("modbus: recv % x\n", aduResponse) + mb.logf("modbus: recv % x\n", aduResponse) return } diff --git a/client.go b/client.go index 8148704..e7919c8 100644 --- a/client.go +++ b/client.go @@ -11,7 +11,7 @@ import ( // Logger is the interface to the required logging functions type Logger interface { - Printf(format string, v ...interface{}) + Printf(format string, v ...any) } // DataSizeError represents an error for invalid data-sizes i.e. for cases diff --git a/cmd/modbus-cli/main.go b/cmd/modbus-cli/main.go index 01306ca..5538aef 100644 --- a/cmd/modbus-cli/main.go +++ b/cmd/modbus-cli/main.go @@ -421,7 +421,7 @@ type option struct { slaveID int timeout time.Duration - logger Logger + logger modbus.Logger rtu struct { baudrate int diff --git a/rtu_over_tcp_client.go b/rtu_over_tcp_client.go index c7b2f70..1a88080 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.Printf("modbus: send % x\n", aduRequest) + mb.logf("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.Printf("modbus: recv % x\n", aduResponse) + mb.logf("modbus: recv % x\n", aduResponse) return } diff --git a/rtu_over_udp_client.go b/rtu_over_udp_client.go index 2a6bb51..0e448ed 100644 --- a/rtu_over_udp_client.go +++ b/rtu_over_udp_client.go @@ -3,7 +3,6 @@ package modbus import ( "fmt" "io" - logger "log/slog" "net" "sync" ) @@ -46,7 +45,7 @@ type rtuUDPTransporter struct { // Connect string Address string // Transmission logger - Logger *logger.Logger + Logger Logger // UDP connection mu sync.Mutex @@ -121,7 +120,7 @@ func (mb *rtuUDPTransporter) Send(aduRequest []byte) (aduResponse []byte, err er func (mb *rtuUDPTransporter) logf(format string, v ...interface{}) { if mb.Logger != nil { - mb.Logger.Info(format, v...) + mb.Logger.Printf(format, v...) } } diff --git a/rtuclient.go b/rtuclient.go index 01cc2aa..5d8d065 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.Printf("modbus: send % x\n", aduRequest) + mb.logf("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.Printf("modbus: recv % x\n", data[:]) + mb.logf("modbus: recv % x\n", data[:]) aduResponse = data return } diff --git a/serial.go b/serial.go index 4329e92..3f3e53b 100644 --- a/serial.go +++ b/serial.go @@ -69,7 +69,7 @@ func (mb *serialPort) close() (err error) { return } -func (mb *serialPort) Printf(format string, v ...interface{}) { +func (mb *serialPort) logf(format string, v ...interface{}) { if mb.Logger != nil { mb.Logger.Printf(format, v...) } @@ -96,7 +96,7 @@ func (mb *serialPort) closeIdle() { } if idle := time.Since(mb.lastActivity); idle >= mb.IdleTimeout { - mb.Printf("modbus: closing connection due to idle timeout: %v", idle) + mb.logf("modbus: closing connection due to idle timeout: %v", idle) mb.close() } } diff --git a/tcpclient.go b/tcpclient.go index 82d3c70..d8f5da2 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.Printf("modbus: send % x", aduRequest) + mb.logf("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.Printf("modbus: close connection and retry, because of %v", err) + mb.logf("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.Printf("modbus: recv % x\n", aduResponse) + mb.logf("modbus: recv % x\n", aduResponse) return // everything is OK } } @@ -382,7 +382,7 @@ func (mb *tcpTransporter) flush(b []byte) (err error) { return } -func (mb *tcpTransporter) Printf(format string, v ...interface{}) { +func (mb *tcpTransporter) logf(format string, v ...interface{}) { if mb.Logger != nil { mb.Logger.Printf(format, v...) } @@ -407,7 +407,7 @@ func (mb *tcpTransporter) closeIdle() { } if idle := time.Since(mb.lastActivity); idle >= mb.IdleTimeout { - mb.Printf("modbus: closing connection due to idle timeout: %v", idle) + mb.logf("modbus: closing connection due to idle timeout: %v", idle) mb.close() } } From a602f3fc380b3a821bfa312502228fcd0ce22133 Mon Sep 17 00:00:00 2001 From: andig <cpuidle@gmx.de> Date: Fri, 3 May 2024 12:05:24 +0200 Subject: [PATCH 50/51] Split adapter --- cmd/modbus-cli/log.go | 11 +++++++++++ cmd/modbus-cli/main.go | 2 +- test/asciiclient_test.go | 8 -------- test/log.go | 11 +++++++++++ 4 files changed, 23 insertions(+), 9 deletions(-) create mode 100644 cmd/modbus-cli/log.go create mode 100644 test/log.go diff --git a/cmd/modbus-cli/log.go b/cmd/modbus-cli/log.go new file mode 100644 index 0000000..8d2350a --- /dev/null +++ b/cmd/modbus-cli/log.go @@ -0,0 +1,11 @@ +package main + +import "log/slog" + +type debugAdapter struct { + *slog.Logger +} + +func (log *debugAdapter) Printf(msg string, args ...any) { + log.Logger.Debug(msg, args...) +} diff --git a/cmd/modbus-cli/main.go b/cmd/modbus-cli/main.go index 5538aef..06b5a71 100644 --- a/cmd/modbus-cli/main.go +++ b/cmd/modbus-cli/main.go @@ -74,7 +74,7 @@ func main() { startReg := uint16(*register) if *logframe { - opt.logger = logger + opt.logger = &debugAdapter{logger} } var ( diff --git a/test/asciiclient_test.go b/test/asciiclient_test.go index 5d2a2ab..6375d13 100644 --- a/test/asciiclient_test.go +++ b/test/asciiclient_test.go @@ -16,14 +16,6 @@ const ( asciiDevice = "/dev/pts/2" ) -type debugAdapter struct { - *slog.Logger -} - -func (log *debugAdapter) Printf(msg string, args ...any) { - log.Logger.Debug(msg, args...) -} - func TestASCIIClient(t *testing.T) { // Diagslave does not support broadcast id. handler := modbus.NewASCIIClientHandler(asciiDevice) diff --git a/test/log.go b/test/log.go new file mode 100644 index 0000000..7b42f13 --- /dev/null +++ b/test/log.go @@ -0,0 +1,11 @@ +package test + +import "log/slog" + +type debugAdapter struct { + *slog.Logger +} + +func (log *debugAdapter) Printf(msg string, args ...any) { + log.Logger.Debug(msg, args...) +} From a743f131b6c48d66410aa4f204d02da400513d33 Mon Sep 17 00:00:00 2001 From: andig <cpuidle@gmx.de> Date: Fri, 3 May 2024 12:11:19 +0200 Subject: [PATCH 51/51] Simplify test --- test/asciiclient_test.go | 5 ++--- test/log.go | 11 ----------- test/rtu_over_tcp_client_test.go | 5 ++--- test/rtuclient_test.go | 5 ++--- test/tcpclient_test.go | 5 ++--- 5 files changed, 8 insertions(+), 23 deletions(-) delete mode 100644 test/log.go diff --git a/test/asciiclient_test.go b/test/asciiclient_test.go index 6375d13..81f9048 100644 --- a/test/asciiclient_test.go +++ b/test/asciiclient_test.go @@ -5,8 +5,7 @@ package test import ( - "log/slog" - "os" + "log" "testing" "github.com/grid-x/modbus" @@ -30,7 +29,7 @@ func TestASCIIClientAdvancedUsage(t *testing.T) { handler.Parity = "E" handler.StopBits = 1 handler.SlaveID = 12 - handler.Logger = &debugAdapter{slog.New(slog.NewJSONHandler(os.Stdout, nil))} + handler.Logger = log.Default() err := handler.Connect() if err != nil { t.Fatal(err) diff --git a/test/log.go b/test/log.go deleted file mode 100644 index 7b42f13..0000000 --- a/test/log.go +++ /dev/null @@ -1,11 +0,0 @@ -package test - -import "log/slog" - -type debugAdapter struct { - *slog.Logger -} - -func (log *debugAdapter) Printf(msg string, args ...any) { - log.Logger.Debug(msg, args...) -} diff --git a/test/rtu_over_tcp_client_test.go b/test/rtu_over_tcp_client_test.go index d097b80..295b59b 100644 --- a/test/rtu_over_tcp_client_test.go +++ b/test/rtu_over_tcp_client_test.go @@ -5,8 +5,7 @@ package test import ( - "log/slog" - "os" + "log" "testing" "time" @@ -28,7 +27,7 @@ func TestRTUOverTCPClientAdvancedUsage(t *testing.T) { handler := modbus.NewRTUOverTCPClientHandler(rtuOverTCPDevice) handler.Timeout = 5 * time.Second handler.SlaveID = 1 - handler.Logger = &debugAdapter{slog.New(slog.NewJSONHandler(os.Stdout, nil))} + handler.Logger = log.Default() handler.Connect() defer handler.Close() diff --git a/test/rtuclient_test.go b/test/rtuclient_test.go index fd506ac..dd15b63 100644 --- a/test/rtuclient_test.go +++ b/test/rtuclient_test.go @@ -5,8 +5,7 @@ package test import ( - "log/slog" - "os" + "log" "testing" "github.com/grid-x/modbus" @@ -30,7 +29,7 @@ func TestRTUClientAdvancedUsage(t *testing.T) { handler.Parity = "E" handler.StopBits = 1 handler.SlaveID = 11 - handler.Logger = &debugAdapter{slog.New(slog.NewJSONHandler(os.Stdout, nil))} + handler.Logger = log.Default() err := handler.Connect() if err != nil { t.Fatal(err) diff --git a/test/tcpclient_test.go b/test/tcpclient_test.go index e1ef709..d11be82 100644 --- a/test/tcpclient_test.go +++ b/test/tcpclient_test.go @@ -5,8 +5,7 @@ package test import ( - "log/slog" - "os" + "log" "testing" "time" @@ -26,7 +25,7 @@ func TestTCPClientAdvancedUsage(t *testing.T) { handler := modbus.NewTCPClientHandler(tcpDevice) handler.Timeout = 5 * time.Second handler.SlaveID = 1 - handler.Logger = &debugAdapter{slog.New(slog.NewJSONHandler(os.Stdout, nil))} + handler.Logger = log.Default() handler.Connect() defer handler.Close()