...

Source file src/github.com/onsi/gomega/format/format_test.go

Documentation: github.com/onsi/gomega/format

     1  package format_test
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  	"time"
     8  
     9  	. "github.com/onsi/ginkgo/v2"
    10  
    11  	. "github.com/onsi/gomega"
    12  	. "github.com/onsi/gomega/format"
    13  	"github.com/onsi/gomega/types"
    14  )
    15  
    16  //recursive struct
    17  
    18  const truncateHelpText = `
    19  Gomega truncated this representation as it exceeds 'format.MaxLength'.
    20  Consider having the object provide a custom 'GomegaStringer' representation
    21  or adjust the parameters in Gomega's 'format' package.
    22  
    23  Learn more here: https://onsi.github.io/gomega/#adjusting-output
    24  `
    25  
    26  type StringAlias string
    27  type ByteAlias []byte
    28  type IntAlias int
    29  
    30  type AStruct struct {
    31  	Exported string
    32  }
    33  
    34  type SimpleStruct struct {
    35  	Name        string
    36  	Enumeration int
    37  	Veritas     bool
    38  	Data        []byte
    39  	secret      uint32
    40  }
    41  
    42  type ComplexStruct struct {
    43  	Strings      []string
    44  	SimpleThings []*SimpleStruct
    45  	DataMaps     map[int]ByteAlias
    46  }
    47  
    48  type SecretiveStruct struct {
    49  	boolValue      bool
    50  	intValue       int
    51  	uintValue      uint
    52  	uintptrValue   uintptr
    53  	floatValue     float32
    54  	complexValue   complex64
    55  	chanValue      chan bool
    56  	funcValue      func()
    57  	pointerValue   *int
    58  	sliceValue     []string
    59  	byteSliceValue []byte
    60  	stringValue    string
    61  	arrValue       [3]int
    62  	byteArrValue   [3]byte
    63  	mapValue       map[string]int
    64  	structValue    AStruct
    65  	interfaceValue interface{}
    66  }
    67  
    68  type CustomFormatted struct {
    69  	Data  string
    70  	Count int
    71  }
    72  type NotCustomFormatted struct {
    73  	Data  string
    74  	Count int
    75  }
    76  
    77  type CustomError struct {
    78  	Details string
    79  }
    80  
    81  var _ error = &CustomError{}
    82  
    83  func (c *CustomError) Error() string {
    84  	return c.Details
    85  }
    86  
    87  func customFormatter(obj interface{}) (string, bool) {
    88  	cf, ok := obj.(CustomFormatted)
    89  	if !ok {
    90  		return "", false
    91  	}
    92  	return fmt.Sprintf("%s (%d)", cf.Data, cf.Count), true
    93  }
    94  
    95  type GoStringer struct {
    96  }
    97  
    98  func (g GoStringer) GoString() string {
    99  	return "go-string"
   100  }
   101  
   102  func (g GoStringer) String() string {
   103  	return "string"
   104  }
   105  
   106  type Stringer struct {
   107  }
   108  
   109  func (g Stringer) String() string {
   110  	return "string"
   111  }
   112  
   113  type gomegaStringer struct {
   114  }
   115  
   116  func (g gomegaStringer) GomegaString() string {
   117  	return "gomegastring"
   118  }
   119  
   120  type gomegaStringerLong struct {
   121  }
   122  
   123  func (g gomegaStringerLong) GomegaString() string {
   124  	return strings.Repeat("s", MaxLength*2)
   125  }
   126  
   127  type gomegaStringerMultiline struct {
   128  }
   129  
   130  func (g gomegaStringerMultiline) GomegaString() string {
   131  	return "A\nB\nC"
   132  }
   133  
   134  var _ = Describe("Format", func() {
   135  	match := func(typeRepresentation string, valueRepresentation string, args ...interface{}) types.GomegaMatcher {
   136  		if len(args) > 0 {
   137  			valueRepresentation = fmt.Sprintf(valueRepresentation, args...)
   138  		}
   139  		return Equal(fmt.Sprintf("%s<%s>: %s", Indent, typeRepresentation, valueRepresentation))
   140  	}
   141  
   142  	matchRegexp := func(typeRepresentation string, valueRepresentation string, args ...interface{}) types.GomegaMatcher {
   143  		if len(args) > 0 {
   144  			valueRepresentation = fmt.Sprintf(valueRepresentation, args...)
   145  		}
   146  		return MatchRegexp(fmt.Sprintf("%s<%s>: %s", Indent, typeRepresentation, valueRepresentation))
   147  	}
   148  
   149  	hashMatchingRegexp := func(entries ...string) string {
   150  		entriesSwitch := "(" + strings.Join(entries, "|") + ")"
   151  		arr := make([]string, len(entries))
   152  		for i := range arr {
   153  			arr[i] = entriesSwitch
   154  		}
   155  		return "{\\s*" + strings.Join(arr, ",\\s* ") + ",?\\s*}"
   156  	}
   157  
   158  	Describe("Message", func() {
   159  		Context("with only an actual value", func() {
   160  			BeforeEach(func() {
   161  				MaxLength = 4000
   162  			})
   163  
   164  			It("should print out an indented formatted representation of the value and the message", func() {
   165  				Expect(Message(3, "to be three.")).Should(Equal("Expected\n    <int>: 3\nto be three."))
   166  			})
   167  
   168  			It("should print out an indented formatted representation of the value and the message, and trucate it when too long", func() {
   169  				tooLong := strings.Repeat("s", MaxLength+1)
   170  				tooLongResult := strings.Repeat("s", MaxLength) + "...\n" + truncateHelpText
   171  				Expect(Message(tooLong, "to be truncated")).Should(Equal("Expected\n    <string>: " + tooLongResult + "\nto be truncated"))
   172  			})
   173  
   174  			It("should print out an indented formatted representation of the value and the message, and not trucate it when MaxLength = 0", func() {
   175  				MaxLength = 0
   176  				tooLong := strings.Repeat("s", MaxLength+1)
   177  				Expect(Message(tooLong, "to be truncated")).Should(Equal("Expected\n    <string>: " + tooLong + "\nto be truncated"))
   178  			})
   179  		})
   180  
   181  		Context("with an actual and an expected value", func() {
   182  			It("should print out an indented formatted representatino of both values, and the message", func() {
   183  				Expect(Message(3, "to equal", 4)).Should(Equal("Expected\n    <int>: 3\nto equal\n    <int>: 4"))
   184  			})
   185  		})
   186  	})
   187  
   188  	Describe("MessageWithDiff", func() {
   189  		It("shows the exact point where two long strings differ", func() {
   190  			stringWithB := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
   191  			stringWithZ := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaazaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
   192  
   193  			Expect(MessageWithDiff(stringWithB, "to equal", stringWithZ)).Should(Equal(expectedLongStringFailureMessage))
   194  		})
   195  
   196  		It("truncates the start of long strings that differ only at their end", func() {
   197  			stringWithB := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab"
   198  			stringWithZ := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz"
   199  
   200  			Expect(MessageWithDiff(stringWithB, "to equal", stringWithZ)).Should(Equal(expectedTruncatedStartStringFailureMessage))
   201  		})
   202  
   203  		It("truncates the start of long strings that differ only in length", func() {
   204  			smallString := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
   205  			largeString := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
   206  
   207  			Expect(MessageWithDiff(largeString, "to equal", smallString)).Should(Equal(expectedTruncatedStartSizeFailureMessage))
   208  			Expect(MessageWithDiff(smallString, "to equal", largeString)).Should(Equal(expectedTruncatedStartSizeSwappedFailureMessage))
   209  		})
   210  
   211  		It("truncates the end of long strings that differ only at their start", func() {
   212  			stringWithB := "baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
   213  			stringWithZ := "zaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
   214  
   215  			Expect(MessageWithDiff(stringWithB, "to equal", stringWithZ)).Should(Equal(expectedTruncatedEndStringFailureMessage))
   216  		})
   217  
   218  		It("handles multi-byte sequences correctly", func() {
   219  			stringA := "• abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz1"
   220  			stringB := "• abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"
   221  
   222  			Expect(MessageWithDiff(stringA, "to equal", stringB)).Should(Equal(expectedTruncatedMultiByteFailureMessage))
   223  		})
   224  
   225  		It("prints special characters", func() {
   226  			stringA := "\n"
   227  			stringB := "something_else"
   228  
   229  			Expect(MessageWithDiff(stringA, "to equal", stringB)).Should(Equal(expectedSpecialCharacterFailureMessage))
   230  		})
   231  
   232  		It("handles negative padding length", func() {
   233  			stringWithB := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
   234  			stringWithZ := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaazaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
   235  			longMessage := "to equal very long message"
   236  
   237  			Expect(MessageWithDiff(stringWithB, longMessage, stringWithZ)).Should(Equal(expectedDiffLongMessage))
   238  		})
   239  
   240  		Context("With truncated diff disabled", func() {
   241  			BeforeEach(func() {
   242  				TruncatedDiff = false
   243  			})
   244  
   245  			AfterEach(func() {
   246  				TruncatedDiff = true
   247  			})
   248  
   249  			It("should show the full diff", func() {
   250  				stringWithB := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
   251  				stringWithZ := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaazaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
   252  
   253  				Expect(MessageWithDiff(stringWithB, "to equal", stringWithZ)).Should(Equal(expectedFullFailureDiff))
   254  			})
   255  		})
   256  
   257  		Context("With alternate diff lengths", func() {
   258  			initialValue := TruncateThreshold // 50 by default
   259  			BeforeEach(func() {
   260  				TruncateThreshold = 10000
   261  			})
   262  
   263  			AfterEach(func() {
   264  				TruncateThreshold = initialValue
   265  			})
   266  
   267  			It("should show the full diff when truncate threshold is increased beyond length of strings", func() {
   268  				stringWithB := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
   269  				stringWithZ := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaazaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
   270  
   271  				Expect(MessageWithDiff(stringWithB, "to equal", stringWithZ)).Should(Equal(expectedFullFailureDiff))
   272  			})
   273  		})
   274  
   275  		Context("with alternative number of characters to include around mismatch", func() {
   276  			initialValue := CharactersAroundMismatchToInclude // 5 by default
   277  			BeforeEach(func() {
   278  				CharactersAroundMismatchToInclude = 10
   279  			})
   280  
   281  			AfterEach(func() {
   282  				CharactersAroundMismatchToInclude = initialValue
   283  			})
   284  
   285  			It("it shows more characters around a line length mismatch", func() {
   286  				smallString := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
   287  				largeString := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
   288  
   289  				Expect(MessageWithDiff(largeString, "to equal", smallString)).Should(Equal(expectedTruncatedStartSizeFailureMessageExtraDiff))
   290  				Expect(MessageWithDiff(smallString, "to equal", largeString)).Should(Equal(expectedTruncatedStartSizeSwappedFailureMessageExtraDiff))
   291  			})
   292  		})
   293  
   294  		Describe("At extremes of configurable values", func() {
   295  			Context("with zero-length threshold", func() {
   296  				initialValue := TruncateThreshold // 50 by default
   297  				BeforeEach(func() {
   298  					TruncateThreshold = 0
   299  				})
   300  
   301  				AfterEach(func() {
   302  					TruncateThreshold = initialValue
   303  				})
   304  
   305  				It("should show the full diff when truncate threshold is increased beyond length of strings", func() {
   306  					stringWithB := "aba"
   307  					stringWithZ := "aza"
   308  					Expect(MessageWithDiff(stringWithB, "to equal", stringWithZ)).Should(Equal(expectedDiffSmallThreshold))
   309  				})
   310  			})
   311  
   312  			Context("with zero characters around mismatch", func() {
   313  				initialValue := CharactersAroundMismatchToInclude // 5 by default
   314  				BeforeEach(func() {
   315  					CharactersAroundMismatchToInclude = 0
   316  				})
   317  
   318  				AfterEach(func() {
   319  					CharactersAroundMismatchToInclude = initialValue
   320  				})
   321  
   322  				It("", func() {
   323  					stringWithB := "aba"
   324  					stringWithZ := "aza"
   325  					Expect(MessageWithDiff(stringWithB, "to equal", stringWithZ)).Should(Equal(expectedDiffZeroMismatch))
   326  				})
   327  			})
   328  
   329  			Context("with zero-length threshold and zero characters around mismatch", func() {
   330  				initialCharactersAroundMismatch := CharactersAroundMismatchToInclude
   331  				initialTruncateThreshold := TruncateThreshold
   332  				BeforeEach(func() {
   333  					CharactersAroundMismatchToInclude = 0
   334  					TruncateThreshold = 0
   335  				})
   336  
   337  				AfterEach(func() {
   338  					CharactersAroundMismatchToInclude = initialCharactersAroundMismatch
   339  					TruncateThreshold = initialTruncateThreshold
   340  				})
   341  
   342  				It("", func() {
   343  					stringWithB := "aba"
   344  					stringWithZ := "aza"
   345  					Expect(MessageWithDiff(stringWithB, "to equal", stringWithZ)).Should(Equal(expectedDiffSmallThresholdZeroMismatch))
   346  				})
   347  			})
   348  		})
   349  	})
   350  
   351  	Describe("IndentString", func() {
   352  		It("should indent the string", func() {
   353  			Expect(IndentString("foo\n  bar\nbaz", 2)).Should(Equal("        foo\n          bar\n        baz"))
   354  		})
   355  	})
   356  
   357  	Describe("Object", func() {
   358  		Describe("formatting boolean values", func() {
   359  			It("should give the type and format values correctly", func() {
   360  				Expect(Object(true, 1)).Should(match("bool", "true"))
   361  				Expect(Object(false, 1)).Should(match("bool", "false"))
   362  			})
   363  		})
   364  
   365  		Describe("formatting numbers", func() {
   366  			It("should give the type and format values correctly", func() {
   367  				Expect(Object(int(3), 1)).Should(match("int", "3"))
   368  				Expect(Object(int8(3), 1)).Should(match("int8", "3"))
   369  				Expect(Object(int16(3), 1)).Should(match("int16", "3"))
   370  				Expect(Object(int32(3), 1)).Should(match("int32", "3"))
   371  				Expect(Object(int64(3), 1)).Should(match("int64", "3"))
   372  
   373  				Expect(Object(uint(3), 1)).Should(match("uint", "3"))
   374  				Expect(Object(uint8(3), 1)).Should(match("uint8", "3"))
   375  				Expect(Object(uint16(3), 1)).Should(match("uint16", "3"))
   376  				Expect(Object(uint32(3), 1)).Should(match("uint32", "3"))
   377  				Expect(Object(uint64(3), 1)).Should(match("uint64", "3"))
   378  			})
   379  
   380  			It("should handle uintptr differently", func() {
   381  				Expect(Object(uintptr(3), 1)).Should(match("uintptr", "0x3"))
   382  			})
   383  		})
   384  
   385  		Describe("formatting channels", func() {
   386  			It("should give the type and format values correctly", func() {
   387  				c := make(chan<- bool, 3)
   388  				c <- true
   389  				c <- false
   390  				Expect(Object(c, 1)).Should(match("chan<- bool | len:2, cap:3", "%v", c))
   391  			})
   392  		})
   393  
   394  		Describe("formatting strings", func() {
   395  			It("should give the type and format values correctly", func() {
   396  				s := "a\nb\nc"
   397  				Expect(Object(s, 1)).Should(match("string", `a
   398      b
   399      c`))
   400  			})
   401  		})
   402  
   403  		Describe("formatting []byte slices", func() {
   404  			When("the slice is made of printable bytes", func() {
   405  				It("should present it as string", func() {
   406  					b := []byte("a b c")
   407  					Expect(Object(b, 1)).Should(matchRegexp(`\[\]uint8 \| len:5, cap:\d+`, `a b c`))
   408  				})
   409  			})
   410  			When("the slice contains non-printable bytes", func() {
   411  				It("should present it as slice", func() {
   412  					b := []byte("a b c\n\x01\x02\x03\xff\x1bH")
   413  					Expect(Object(b, 1)).Should(matchRegexp(`\[\]uint8 \| len:12, cap:\d+`, `\[97, 32, 98, 32, 99, 10, 1, 2, 3, 255, 27, 72\]`))
   414  				})
   415  			})
   416  		})
   417  
   418  		Describe("formatting functions", func() {
   419  			It("should give the type and format values correctly", func() {
   420  				f := func(a string, b []int) ([]byte, error) {
   421  					return []byte("abc"), nil
   422  				}
   423  				Expect(Object(f, 1)).Should(match("func(string, []int) ([]uint8, error)", "%v", f))
   424  			})
   425  		})
   426  
   427  		Describe("formatting pointers", func() {
   428  			It("should give the type and dereference the value to format it correctly", func() {
   429  				a := 3
   430  				Expect(Object(&a, 1)).Should(match(fmt.Sprintf("*int | %p", &a), "3"))
   431  			})
   432  
   433  			When("there are pointers to pointers...", func() {
   434  				It("should recursively deference the pointer until it gets to a value", func() {
   435  					a := 3
   436  					var b *int
   437  					var c **int
   438  					var d ***int
   439  					b = &a
   440  					c = &b
   441  					d = &c
   442  
   443  					Expect(Object(d, 1)).Should(match(fmt.Sprintf("***int | %p", d), "3"))
   444  				})
   445  			})
   446  
   447  			When("the pointer points to nil", func() {
   448  				It("should say nil and not explode", func() {
   449  					var a *AStruct
   450  					Expect(Object(a, 1)).Should(match("*format_test.AStruct | 0x0", "nil"))
   451  				})
   452  			})
   453  		})
   454  
   455  		Describe("formatting arrays", func() {
   456  			It("should give the type and format values correctly", func() {
   457  				w := [3]string{"Jed Bartlet", "Toby Ziegler", "CJ Cregg"}
   458  				Expect(Object(w, 1)).Should(match("[3]string", `["Jed Bartlet", "Toby Ziegler", "CJ Cregg"]`))
   459  			})
   460  
   461  			Context("with byte arrays", func() {
   462  				It("should give the type and format values correctly", func() {
   463  					w := [3]byte{17, 28, 19}
   464  					Expect(Object(w, 1)).Should(match("[3]uint8", `[17, 28, 19]`))
   465  				})
   466  			})
   467  		})
   468  
   469  		Describe("formatting slices", func() {
   470  			It("should include the length and capacity in the type information", func() {
   471  				s := make([]bool, 3, 4)
   472  				Expect(Object(s, 1)).Should(match("[]bool | len:3, cap:4", "[false, false, false]"))
   473  			})
   474  
   475  			When("the slice contains long entries", func() {
   476  				It("should format the entries with newlines", func() {
   477  					w := []string{"Josiah Edward Bartlet", "Toby Ziegler", "CJ Cregg"}
   478  					expected := `[
   479          "Josiah Edward Bartlet",
   480          "Toby Ziegler",
   481          "CJ Cregg",
   482      ]`
   483  					Expect(Object(w, 1)).Should(match("[]string | len:3, cap:3", expected))
   484  				})
   485  			})
   486  		})
   487  
   488  		Describe("formatting maps", func() {
   489  			It("should include the length in the type information", func() {
   490  				m := make(map[int]bool, 5)
   491  				m[3] = true
   492  				m[4] = false
   493  				Expect(Object(m, 1)).Should(matchRegexp(`map\[int\]bool \| len:2`, hashMatchingRegexp("3: true", "4: false")))
   494  			})
   495  
   496  			When("the slice contains long entries", func() {
   497  				It("should format the entries with newlines", func() {
   498  					m := map[string][]byte{}
   499  					m["Josiah Edward Bartlet"] = []byte("Martin Sheen")
   500  					m["Toby Ziegler"] = []byte("Richard Schiff")
   501  					m["CJ Cregg"] = []byte("Allison Janney")
   502  					expected := `{
   503          ("Josiah Edward Bartlet": "Martin Sheen"|"Toby Ziegler": "Richard Schiff"|"CJ Cregg": "Allison Janney"),
   504          ("Josiah Edward Bartlet": "Martin Sheen"|"Toby Ziegler": "Richard Schiff"|"CJ Cregg": "Allison Janney"),
   505          ("Josiah Edward Bartlet": "Martin Sheen"|"Toby Ziegler": "Richard Schiff"|"CJ Cregg": "Allison Janney"),
   506      }`
   507  					Expect(Object(m, 1)).Should(matchRegexp(`map\[string\]\[\]uint8 \| len:3`, expected))
   508  				})
   509  			})
   510  		})
   511  
   512  		Describe("formatting structs", func() {
   513  			It("should include the struct name and the field names", func() {
   514  				s := SimpleStruct{
   515  					Name:        "Oswald",
   516  					Enumeration: 17,
   517  					Veritas:     true,
   518  					Data:        []byte("datum"),
   519  					secret:      1983,
   520  				}
   521  
   522  				Expect(Object(s, 1)).Should(match("format_test.SimpleStruct", `{Name: "Oswald", Enumeration: 17, Veritas: true, Data: "datum", secret: 1983}`))
   523  			})
   524  
   525  			When("the struct contains long entries", func() {
   526  				It("should format the entries with new lines", func() {
   527  					s := &SimpleStruct{
   528  						Name:        "Mithrandir Gandalf Greyhame",
   529  						Enumeration: 2021,
   530  						Veritas:     true,
   531  						Data:        []byte("wizard"),
   532  						secret:      3,
   533  					}
   534  
   535  					Expect(Object(s, 1)).Should(match(fmt.Sprintf("*format_test.SimpleStruct | %p", s), `{
   536          Name: "Mithrandir Gandalf Greyhame",
   537          Enumeration: 2021,
   538          Veritas: true,
   539          Data: "wizard",
   540          secret: 3,
   541      }`))
   542  				})
   543  			})
   544  		})
   545  
   546  		Describe("formatting nil values", func() {
   547  			It("should print out nil", func() {
   548  				Expect(Object(nil, 1)).Should(match("nil", "nil"))
   549  				var typedNil *AStruct
   550  				Expect(Object(typedNil, 1)).Should(match("*format_test.AStruct | 0x0", "nil"))
   551  				var c chan<- bool
   552  				Expect(Object(c, 1)).Should(match("chan<- bool | len:0, cap:0", "nil"))
   553  				var s []string
   554  				Expect(Object(s, 1)).Should(match("[]string | len:0, cap:0", "nil"))
   555  				var m map[string]bool
   556  				Expect(Object(m, 1)).Should(match("map[string]bool | len:0", "nil"))
   557  			})
   558  		})
   559  
   560  		Describe("formatting aliased types", func() {
   561  			It("should print out the correct alias type", func() {
   562  				Expect(Object(StringAlias("alias"), 1)).Should(match("format_test.StringAlias", `alias`))
   563  				Expect(Object(ByteAlias("alias"), 1)).Should(matchRegexp(`format_test\.ByteAlias \| len:5, cap:\d+`, `alias`))
   564  				Expect(Object(IntAlias(3), 1)).Should(match("format_test.IntAlias", "3"))
   565  			})
   566  		})
   567  
   568  		Describe("handling nested things", func() {
   569  			It("should produce a correctly nested representation", func() {
   570  				s := ComplexStruct{
   571  					Strings: []string{"lots", "of", "short", "strings"},
   572  					SimpleThings: []*SimpleStruct{
   573  						{"short", 7, true, []byte("succinct"), 17},
   574  						{"something longer", 427, true, []byte("designed to wrap around nicely"), 30},
   575  					},
   576  					DataMaps: map[int]ByteAlias{
   577  						17:   ByteAlias("some substantially longer chunks of data"),
   578  						1138: ByteAlias("that should make things wrap"),
   579  					},
   580  				}
   581  				expected := `{
   582          Strings: \["lots", "of", "short", "strings"\],
   583          SimpleThings: \[
   584              {Name: "short", Enumeration: 7, Veritas: true, Data: "succinct", secret: 17},
   585              {
   586                  Name: "something longer",
   587                  Enumeration: 427,
   588                  Veritas: true,
   589                  Data: "designed to wrap around nicely",
   590                  secret: 30,
   591              },
   592          \],
   593          DataMaps: {
   594              (17: "some substantially longer chunks of data"|1138: "that should make things wrap"),
   595              (17: "some substantially longer chunks of data"|1138: "that should make things wrap"),
   596          },
   597      }`
   598  				Expect(Object(s, 1)).Should(matchRegexp(`format_test\.ComplexStruct`, expected))
   599  			})
   600  		})
   601  
   602  		Describe("formatting nested interface{} types", func() {
   603  			It("should print out the types of the container and value", func() {
   604  				Expect(Object([]interface{}{"foo"}, 1)).
   605  					To(match("[]interface {} | len:1, cap:1", `[<string>"foo"]`))
   606  
   607  				Expect(Object(map[string]interface{}{"foo": true}, 1)).
   608  					To(match("map[string]interface {} | len:1", `{"foo": <bool>true}`))
   609  
   610  				Expect(Object(struct{ A interface{} }{A: 1}, 1)).
   611  					To(match("struct { A interface {} }", "{A: <int>1}"))
   612  
   613  				v := struct{ A interface{} }{A: struct{ B string }{B: "foo"}}
   614  				Expect(Object(v, 1)).To(match(`struct { A interface {} }`, `{
   615          A: <struct { B string }>{B: "foo"},
   616      }`))
   617  			})
   618  		})
   619  
   620  		Describe("formatting times", func() {
   621  			It("should format time as RFC3339", func() {
   622  				t := time.Date(2016, 10, 31, 9, 57, 23, 12345, time.UTC)
   623  				Expect(Object(t, 1)).Should(match("time.Time", `2016-10-31T09:57:23.000012345Z`))
   624  			})
   625  		})
   626  
   627  		Describe("formatting errors", func() {
   628  			It("should include the error() representation", func() {
   629  				err := fmt.Errorf("whoops: %w", fmt.Errorf("welp: %w", fmt.Errorf("ruh roh")))
   630  				Expect(Object(err, 1)).Should(MatchRegexp(`    \<\*fmt\.wrapError \| 0x[0-9a-f]*\>\: 
   631      whoops\: welp\: ruh roh
   632      \{
   633          msg\: "whoops\: welp\: ruh roh",
   634          err\: \<\*fmt.wrapError \| 0x[0-9a-f]*\>\{
   635              msg\: "welp\: ruh roh",
   636              err\: \<\*errors.errorString \| 0x[0-9a-f]*\>\{s\: "ruh roh"\},
   637          \},
   638      \}`))
   639  			})
   640  
   641  			It("should not panic if the error is a boxed nil", func() {
   642  				var err *CustomError
   643  				Expect(Object(err, 1)).Should(Equal("    <*format_test.CustomError | 0x0>: nil"))
   644  			})
   645  		})
   646  	})
   647  
   648  	Describe("Handling unexported fields in structs", func() {
   649  		It("should handle all the various types correctly", func() {
   650  			a := int(5)
   651  			s := SecretiveStruct{
   652  				boolValue:      true,
   653  				intValue:       3,
   654  				uintValue:      4,
   655  				uintptrValue:   5,
   656  				floatValue:     6.0,
   657  				complexValue:   complex(5.0, 3.0),
   658  				chanValue:      make(chan bool, 2),
   659  				funcValue:      func() {},
   660  				pointerValue:   &a,
   661  				sliceValue:     []string{"string", "slice"},
   662  				byteSliceValue: []byte("bytes"),
   663  				stringValue:    "a string",
   664  				arrValue:       [3]int{11, 12, 13},
   665  				byteArrValue:   [3]byte{17, 20, 32},
   666  				mapValue:       map[string]int{"a key": 20, "b key": 30},
   667  				structValue:    AStruct{"exported"},
   668  				interfaceValue: map[string]int{"a key": 17},
   669  			}
   670  
   671  			expected := fmt.Sprintf(`{
   672          boolValue: true,
   673          intValue: 3,
   674          uintValue: 4,
   675          uintptrValue: 0x5,
   676          floatValue: 6,
   677          complexValue: \(5\+3i\),
   678          chanValue: %p,
   679          funcValue: %p,
   680          pointerValue: 5,
   681          sliceValue: \["string", "slice"\],
   682          byteSliceValue: "bytes",
   683          stringValue: "a string",
   684          arrValue: \[11, 12, 13\],
   685          byteArrValue: \[17, 20, 32\],
   686          mapValue: %s,
   687          structValue: {Exported: "exported"},
   688          interfaceValue: <map\[string\]int \| len:1>{"a key": 17},
   689      }`, s.chanValue, s.funcValue, hashMatchingRegexp(`"a key": 20`, `"b key": 30`))
   690  
   691  			Expect(Object(s, 1)).Should(matchRegexp(`format_test\.SecretiveStruct`, expected))
   692  		})
   693  	})
   694  
   695  	Describe("Handling interfaces", func() {
   696  		It("should unpack the interface", func() {
   697  			outerHash := map[string]interface{}{}
   698  			innerHash := map[string]int{}
   699  
   700  			innerHash["inner"] = 3
   701  			outerHash["integer"] = 2
   702  			outerHash["map"] = innerHash
   703  
   704  			expected := hashMatchingRegexp(`"integer": <int>2`, `"map": <map\[string\]int \| len:1>{"inner": 3}`)
   705  			Expect(Object(outerHash, 1)).Should(matchRegexp(`map\[string\]interface {} \| len:2`, expected))
   706  		})
   707  	})
   708  
   709  	Describe("Handling recursive things", func() {
   710  		It("should not go crazy...", func() {
   711  			m := map[string]interface{}{}
   712  			m["integer"] = 2
   713  			m["map"] = m
   714  			Expect(Object(m, 1)).Should(ContainSubstring("..."))
   715  		})
   716  
   717  		It("really should not go crazy...", func() {
   718  			type complexKey struct {
   719  				Value map[interface{}]int
   720  			}
   721  
   722  			complexObject := complexKey{}
   723  			complexObject.Value = make(map[interface{}]int)
   724  
   725  			complexObject.Value[&complexObject] = 2
   726  			Expect(Object(complexObject, 1)).Should(ContainSubstring("..."))
   727  		})
   728  	})
   729  
   730  	Describe("When instructed to use the Stringer representation", func() {
   731  		BeforeEach(func() {
   732  			UseStringerRepresentation = true
   733  		})
   734  
   735  		AfterEach(func() {
   736  			UseStringerRepresentation = false
   737  		})
   738  
   739  		When("passed a GoStringer", func() {
   740  			It("should use what GoString() returns", func() {
   741  				Expect(Object(GoStringer{}, 1)).Should(ContainSubstring("<format_test.GoStringer>: go-string"))
   742  			})
   743  		})
   744  
   745  		When("passed a stringer", func() {
   746  			It("should use what String() returns", func() {
   747  				Expect(Object(Stringer{}, 1)).Should(ContainSubstring("<format_test.Stringer>: string"))
   748  			})
   749  		})
   750  
   751  		When("passed a GomegaStringer", func() {
   752  			It("should use what GomegaString() returns", func() {
   753  				Expect(Object(gomegaStringer{}, 1)).Should(ContainSubstring("<format_test.gomegaStringer>: gomegastring"))
   754  				UseStringerRepresentation = false
   755  				Expect(Object(gomegaStringer{}, 1)).Should(ContainSubstring("<format_test.gomegaStringer>: gomegastring"))
   756  			})
   757  
   758  			It("should use what GomegaString() returns, disregarding MaxLength", func() {
   759  				Expect(Object(gomegaStringerLong{}, 1)).Should(Equal("    <format_test.gomegaStringerLong>: " + strings.Repeat("s", MaxLength*2)))
   760  				UseStringerRepresentation = false
   761  				Expect(Object(gomegaStringerLong{}, 1)).Should(Equal("    <format_test.gomegaStringerLong>: " + strings.Repeat("s", MaxLength*2)))
   762  			})
   763  
   764  			It("should indent what the GomegaString() returns", func() {
   765  				Expect(Object(gomegaStringerMultiline{}, 1)).Should(Equal("    <format_test.gomegaStringerMultiline>: A\n        B\n        C"))
   766  			})
   767  		})
   768  
   769  		Describe("when used with a registered CustomFormatter", func() {
   770  			It("pases objects through the custom formatter and uses the returned format, if handled", func() {
   771  				cf := CustomFormatted{"bob", 17}
   772  				ncf := NotCustomFormatted{"bob", 17}
   773  				Expect(Object(cf, 0)).To(Equal("<format_test.CustomFormatted>: {Data: bob, Count: 17}"))
   774  				Expect(Object(ncf, 0)).To(Equal("<format_test.NotCustomFormatted>: {Data: bob, Count: 17}"))
   775  
   776  				key := RegisterCustomFormatter(customFormatter)
   777  				Expect(Object(cf, 0)).To(Equal("<format_test.CustomFormatted>: bob (17)"))
   778  				Expect(Object(ncf, 0)).To(Equal("<format_test.NotCustomFormatted>: {Data: bob, Count: 17}"))
   779  
   780  				UnregisterCustomFormatter(key)
   781  				Expect(Object(cf, 0)).To(Equal("<format_test.CustomFormatted>: {Data: bob, Count: 17}"))
   782  				Expect(Object(ncf, 0)).To(Equal("<format_test.NotCustomFormatted>: {Data: bob, Count: 17}"))
   783  			})
   784  
   785  			It("indents CustomFormatter output correctly", func() {
   786  				cf := CustomFormatted{"hey\nbob", 17}
   787  				DeferCleanup(UnregisterCustomFormatter, RegisterCustomFormatter(func(value interface{}) (string, bool) {
   788  					cf, ok := value.(CustomFormatted)
   789  					if !ok {
   790  						return "", false
   791  					}
   792  					return fmt.Sprintf("The Data:\n%s\nThe Count:%d", cf.Data, cf.Count), true
   793  				}))
   794  
   795  				Ω(Object(cf, 1)).Should(Equal("    <format_test.CustomFormatted>: The Data:\n        hey\n        bob\n        The Count:17"))
   796  
   797  				type Wrapped struct {
   798  					MyObject   CustomFormatted
   799  					OuterCount int
   800  				}
   801  				wrapped := Wrapped{
   802  					MyObject:   cf,
   803  					OuterCount: 10,
   804  				}
   805  				Ω(Object(wrapped, 1)).Should(Equal("    <format_test.Wrapped>: {\n        MyObject: The Data:\n            hey\n            bob\n            The Count:17,\n        OuterCount: 10,\n    }"))
   806  
   807  			})
   808  		})
   809  	})
   810  
   811  	Describe("Printing a context.Context field", func() {
   812  		type structWithContext struct {
   813  			Context context.Context
   814  			Value   string
   815  		}
   816  
   817  		objWithContext := structWithContext{Value: "some-value", Context: context.TODO()}
   818  
   819  		It("Suppresses the content by default", func() {
   820  			Expect(Object(objWithContext, 1)).Should(ContainSubstring("<suppressed context>"))
   821  		})
   822  
   823  		It("Doesn't suppress the context if it's the object being printed", func() {
   824  			Expect(Object(context.TODO(), 1)).ShouldNot(MatchRegexp("^.*<suppressed context>$"))
   825  		})
   826  
   827  		Context("PrintContextObjects is set", func() {
   828  			BeforeEach(func() {
   829  				PrintContextObjects = true
   830  			})
   831  
   832  			AfterEach(func() {
   833  				PrintContextObjects = false
   834  			})
   835  
   836  			It("Prints the context", func() {
   837  				Expect(Object(objWithContext, 1)).ShouldNot(ContainSubstring("<suppressed context>"))
   838  			})
   839  		})
   840  	})
   841  })
   842  
   843  var expectedLongStringFailureMessage = strings.TrimSpace(`
   844  Expected
   845      <string>: "...aaaaabaaaaa..."
   846  to equal               |
   847      <string>: "...aaaaazaaaaa..."
   848  `)
   849  var expectedTruncatedEndStringFailureMessage = strings.TrimSpace(`
   850  Expected
   851      <string>: "baaaaa..."
   852  to equal       |
   853      <string>: "zaaaaa..."
   854  `)
   855  var expectedTruncatedStartStringFailureMessage = strings.TrimSpace(`
   856  Expected
   857      <string>: "...aaaaab"
   858  to equal               |
   859      <string>: "...aaaaaz"
   860  `)
   861  var expectedTruncatedStartSizeFailureMessage = strings.TrimSpace(`
   862  Expected
   863      <string>: "...aaaaaa"
   864  to equal               |
   865      <string>: "...aaaaa"
   866  `)
   867  var expectedTruncatedStartSizeFailureMessageExtraDiff = strings.TrimSpace(`
   868  Expected
   869      <string>: "...aaaaaaaaaaa"
   870  to equal                    |
   871      <string>: "...aaaaaaaaaa"
   872  `)
   873  var expectedTruncatedStartSizeSwappedFailureMessage = strings.TrimSpace(`
   874  Expected
   875      <string>: "...aaaa"
   876  to equal              |
   877      <string>: "...aaaaa"
   878  `)
   879  var expectedTruncatedStartSizeSwappedFailureMessageExtraDiff = strings.TrimSpace(`
   880  Expected
   881      <string>: "...aaaaaaaaa"
   882  to equal                   |
   883      <string>: "...aaaaaaaaaa"
   884  `)
   885  var expectedTruncatedMultiByteFailureMessage = strings.TrimSpace(`
   886  Expected
   887      <string>: "...tuvwxyz1"
   888  to equal                 |
   889      <string>: "...tuvwxyz"
   890  `)
   891  var expectedFullFailureDiff = strings.TrimSpace(`
   892  Expected
   893      <string>: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
   894  to equal
   895      <string>: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaazaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
   896  `)
   897  var expectedSpecialCharacterFailureMessage = strings.TrimSpace(`
   898  Expected
   899      <string>: \n
   900  to equal
   901      <string>: something_else
   902  
   903  `)
   904  var expectedDiffSmallThreshold = strings.TrimSpace(`
   905  Expected
   906      <string>: "aba"
   907  to equal        |
   908      <string>: "aza"
   909  `)
   910  var expectedDiffZeroMismatch = strings.TrimSpace(`
   911  Expected
   912      <string>: aba
   913  to equal
   914      <string>: aza
   915  `)
   916  var expectedDiffSmallThresholdZeroMismatch = strings.TrimSpace(`
   917  Expected
   918      <string>: "...b..."
   919  to equal          |
   920      <string>: "...z..."
   921  `)
   922  
   923  var expectedDiffLongMessage = strings.TrimSpace(`
   924  Expected
   925      <string>: "...aaaaabaaaaa..."
   926  to equal very long message
   927      <string>: "...aaaaazaaaaa..."
   928  `)
   929  

View as plain text