formatter.go 24 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117
  1. package httpexpect
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "flag"
  6. "fmt"
  7. "math"
  8. "net/http/httputil"
  9. "os"
  10. "path/filepath"
  11. "regexp"
  12. "strconv"
  13. "strings"
  14. "sync"
  15. "testing"
  16. "text/template"
  17. "github.com/fatih/color"
  18. "github.com/mattn/go-isatty"
  19. "github.com/mitchellh/go-wordwrap"
  20. "github.com/sanity-io/litter"
  21. "github.com/yudai/gojsondiff"
  22. "github.com/yudai/gojsondiff/formatter"
  23. )
  24. // Formatter is used to format assertion messages into strings.
  25. type Formatter interface {
  26. FormatSuccess(*AssertionContext) string
  27. FormatFailure(*AssertionContext, *AssertionFailure) string
  28. }
  29. // DefaultFormatter is the default Formatter implementation.
  30. //
  31. // DefaultFormatter gathers values from AssertionContext and AssertionFailure,
  32. // converts them to strings, and creates FormatData struct. Then it passes
  33. // FormatData to the template engine (text/template) to format message.
  34. //
  35. // You can control what is included and what is excluded from messages via
  36. // several public fields.
  37. //
  38. // If desired, you can provide custom templates and function map. This may
  39. // be easier than creating your own formatter from scratch.
  40. type DefaultFormatter struct {
  41. // Exclude test name and request name from failure report.
  42. DisableNames bool
  43. // Exclude assertion path from failure report.
  44. DisablePaths bool
  45. // Exclude aliased assertion path from failure report.
  46. DisableAliases bool
  47. // Exclude diff from failure report.
  48. DisableDiffs bool
  49. // Exclude HTTP request from failure report.
  50. DisableRequests bool
  51. // Exclude HTTP response from failure report.
  52. DisableResponses bool
  53. // Thousand separator.
  54. // Default is DigitSeparatorUnderscore.
  55. DigitSeparator DigitSeparator
  56. // Float printing format.
  57. // Default is FloatFormatAuto.
  58. FloatFormat FloatFormat
  59. // Defines whether to print stacktrace on failure and in what format.
  60. // Default is StacktraceModeDisabled.
  61. StacktraceMode StacktraceMode
  62. // Colorization mode.
  63. // Default is ColorModeAuto.
  64. ColorMode ColorMode
  65. // Wrap text to keep lines below given width.
  66. // Use zero for default width, and negative value to disable wrapping.
  67. LineWidth int
  68. // If not empty, used to format success messages.
  69. // If empty, default template is used.
  70. SuccessTemplate string
  71. // If not empty, used to format failure messages.
  72. // If empty, default template is used.
  73. FailureTemplate string
  74. // When SuccessTemplate or FailureTemplate is set, this field
  75. // defines the function map passed to template engine.
  76. // May be nil.
  77. TemplateFuncs template.FuncMap
  78. }
  79. // FormatSuccess implements Formatter.FormatSuccess.
  80. func (f *DefaultFormatter) FormatSuccess(ctx *AssertionContext) string {
  81. if f.SuccessTemplate != "" {
  82. return f.applyTemplate("SuccessTemplate",
  83. f.SuccessTemplate, f.TemplateFuncs, ctx, nil)
  84. } else {
  85. return f.applyTemplate("SuccessTemplate",
  86. defaultSuccessTemplate, defaultTemplateFuncs, ctx, nil)
  87. }
  88. }
  89. // FormatFailure implements Formatter.FormatFailure.
  90. func (f *DefaultFormatter) FormatFailure(
  91. ctx *AssertionContext, failure *AssertionFailure,
  92. ) string {
  93. if f.FailureTemplate != "" {
  94. return f.applyTemplate("FailureTemplate",
  95. f.FailureTemplate, f.TemplateFuncs, ctx, failure)
  96. } else {
  97. return f.applyTemplate("FailureTemplate",
  98. defaultFailureTemplate, defaultTemplateFuncs, ctx, failure)
  99. }
  100. }
  101. // DigitSeparator defines the separator used to format integers and floats.
  102. type DigitSeparator int
  103. const (
  104. // Separate using underscore
  105. DigitSeparatorUnderscore DigitSeparator = iota
  106. // Separate using comma
  107. DigitSeparatorComma
  108. // Separate using apostrophe
  109. DigitSeparatorApostrophe
  110. // Do not separate
  111. DigitSeparatorNone
  112. )
  113. // FloatFormat defines the format in which all floats are printed.
  114. type FloatFormat int
  115. const (
  116. // Print floats in scientific notation for large exponents,
  117. // otherwise print in decimal notation.
  118. // Precision is the smallest needed to identify the value uniquely.
  119. // Similar to %g format.
  120. FloatFormatAuto FloatFormat = iota
  121. // Always print floats in decimal notation.
  122. // Precision is the smallest needed to identify the value uniquely.
  123. // Similar to %f format.
  124. FloatFormatDecimal
  125. // Always print floats in scientific notation.
  126. // Precision is the smallest needed to identify the value uniquely.
  127. // Similar to %e format.
  128. FloatFormatScientific
  129. )
  130. // StacktraceMode defines the format of stacktrace.
  131. type StacktraceMode int
  132. const (
  133. // Don't print stacktrace.
  134. StacktraceModeDisabled StacktraceMode = iota
  135. // Standard, verbose format.
  136. StacktraceModeStandard
  137. // Compact format.
  138. StacktraceModeCompact
  139. )
  140. // ColorMode defines how the text color is enabled.
  141. type ColorMode int
  142. const (
  143. // Automatically enable colors if ALL of the following is true:
  144. // - stdout is a tty / console
  145. // - AssertionHandler is known to output to testing.T
  146. // - testing.Verbose() is true
  147. //
  148. // Colors are forcibly enabled if FORCE_COLOR environment variable
  149. // is set to a positive integer.
  150. //
  151. // Colors are forcibly disabled if TERM is "dumb" or NO_COLOR
  152. // environment variable is set to non-empty string.
  153. ColorModeAuto ColorMode = iota
  154. // Unconditionally enable colors.
  155. ColorModeAlways
  156. // Unconditionally disable colors.
  157. ColorModeNever
  158. )
  159. // FormatData defines data passed to template engine when DefaultFormatter
  160. // formats assertion. You can use these fields in your custom templates.
  161. type FormatData struct {
  162. TestName string
  163. RequestName string
  164. AssertPath []string
  165. AssertType string
  166. AssertSeverity string
  167. Errors []string
  168. HaveActual bool
  169. Actual string
  170. HaveExpected bool
  171. IsNegation bool
  172. IsComparison bool
  173. ExpectedKind string
  174. Expected []string
  175. HaveReference bool
  176. Reference string
  177. HaveDelta bool
  178. Delta string
  179. HaveDiff bool
  180. Diff string
  181. HaveRequest bool
  182. Request string
  183. HaveResponse bool
  184. Response string
  185. HaveStacktrace bool
  186. Stacktrace []string
  187. EnableColors bool
  188. LineWidth int
  189. }
  190. const (
  191. kindRange = "range"
  192. kindSchema = "schema"
  193. kindPath = "path"
  194. kindRegexp = "regexp"
  195. kindFormat = "format"
  196. kindFormatList = "formats"
  197. kindKey = "key"
  198. kindElement = "element"
  199. kindSubset = "subset"
  200. kindValue = "value"
  201. kindValueList = "values"
  202. )
  203. func (f *DefaultFormatter) applyTemplate(
  204. templateName string,
  205. templateString string,
  206. templateFuncs template.FuncMap,
  207. ctx *AssertionContext,
  208. failure *AssertionFailure,
  209. ) string {
  210. templateData := f.buildFormatData(ctx, failure)
  211. t, err := template.New(templateName).Funcs(templateFuncs).Parse(templateString)
  212. if err != nil {
  213. panic(err)
  214. }
  215. var b bytes.Buffer
  216. err = t.Execute(&b, templateData)
  217. if err != nil {
  218. panic(err)
  219. }
  220. return b.String()
  221. }
  222. func (f *DefaultFormatter) buildFormatData(
  223. ctx *AssertionContext, failure *AssertionFailure,
  224. ) *FormatData {
  225. data := FormatData{}
  226. f.fillGeneral(&data, ctx)
  227. if failure != nil {
  228. data.AssertType = failure.Type.String()
  229. data.AssertSeverity = failure.Severity.String()
  230. f.fillErrors(&data, ctx, failure)
  231. if failure.Actual != nil {
  232. f.fillActual(&data, ctx, failure)
  233. }
  234. if failure.Expected != nil {
  235. f.fillExpected(&data, ctx, failure)
  236. f.fillIsNegation(&data, ctx, failure)
  237. f.fillIsComparison(&data, ctx, failure)
  238. }
  239. if failure.Reference != nil {
  240. f.fillReference(&data, ctx, failure)
  241. }
  242. if failure.Delta != nil {
  243. f.fillDelta(&data, ctx, failure)
  244. }
  245. f.fillRequest(&data, ctx, failure)
  246. f.fillResponse(&data, ctx, failure)
  247. f.fillStacktrace(&data, ctx, failure)
  248. }
  249. return &data
  250. }
  251. func (f *DefaultFormatter) fillGeneral(
  252. data *FormatData, ctx *AssertionContext,
  253. ) {
  254. if !f.DisableNames {
  255. data.TestName = ctx.TestName
  256. data.RequestName = ctx.RequestName
  257. }
  258. if !f.DisablePaths {
  259. if !f.DisableAliases {
  260. data.AssertPath = ctx.AliasedPath
  261. } else {
  262. data.AssertPath = ctx.Path
  263. }
  264. }
  265. switch f.ColorMode {
  266. case ColorModeAuto:
  267. switch colorMode() {
  268. case colorsUnsupported:
  269. data.EnableColors = false
  270. case colorsForced:
  271. data.EnableColors = true
  272. case colorsSupported:
  273. data.EnableColors = ctx.TestingTB && flag.Parsed() && testing.Verbose()
  274. }
  275. case ColorModeAlways:
  276. data.EnableColors = true
  277. case ColorModeNever:
  278. data.EnableColors = false
  279. }
  280. if f.LineWidth != 0 {
  281. data.LineWidth = f.LineWidth
  282. } else {
  283. data.LineWidth = defaultLineWidth
  284. }
  285. }
  286. func (f *DefaultFormatter) fillErrors(
  287. data *FormatData, ctx *AssertionContext, failure *AssertionFailure,
  288. ) {
  289. data.Errors = []string{}
  290. for _, err := range failure.Errors {
  291. if refIsNil(err) {
  292. continue
  293. }
  294. data.Errors = append(data.Errors, err.Error())
  295. }
  296. }
  297. func (f *DefaultFormatter) fillActual(
  298. data *FormatData, ctx *AssertionContext, failure *AssertionFailure,
  299. ) {
  300. switch failure.Type { //nolint
  301. case AssertUsage, AssertOperation:
  302. data.HaveActual = false
  303. case AssertType, AssertNotType:
  304. data.HaveActual = true
  305. data.Actual = f.formatTypedValue(failure.Actual.Value)
  306. default:
  307. data.HaveActual = true
  308. data.Actual = f.formatValue(failure.Actual.Value)
  309. }
  310. }
  311. func (f *DefaultFormatter) fillExpected(
  312. data *FormatData, ctx *AssertionContext, failure *AssertionFailure,
  313. ) {
  314. switch failure.Type {
  315. case AssertUsage, AssertOperation,
  316. AssertType, AssertNotType,
  317. AssertValid, AssertNotValid,
  318. AssertNil, AssertNotNil,
  319. AssertEmpty, AssertNotEmpty,
  320. AssertNotEqual:
  321. data.HaveExpected = false
  322. case AssertEqual:
  323. data.HaveExpected = true
  324. data.ExpectedKind = kindValue
  325. data.Expected = []string{
  326. f.formatValue(failure.Expected.Value),
  327. }
  328. if !f.DisableDiffs && failure.Actual != nil && failure.Expected != nil {
  329. data.Diff, data.HaveDiff = f.formatDiff(
  330. failure.Expected.Value, failure.Actual.Value)
  331. }
  332. case AssertLt, AssertLe, AssertGt, AssertGe:
  333. data.HaveExpected = true
  334. data.ExpectedKind = kindValue
  335. data.Expected = []string{
  336. f.formatValue(failure.Expected.Value),
  337. }
  338. case AssertInRange, AssertNotInRange:
  339. data.HaveExpected = true
  340. data.ExpectedKind = kindRange
  341. data.Expected = f.formatRangeValue(failure.Expected.Value)
  342. case AssertMatchSchema, AssertNotMatchSchema:
  343. data.HaveExpected = true
  344. data.ExpectedKind = kindSchema
  345. data.Expected = []string{
  346. f.formatMatchValue(failure.Expected.Value),
  347. }
  348. case AssertMatchPath, AssertNotMatchPath:
  349. data.HaveExpected = true
  350. data.ExpectedKind = kindPath
  351. data.Expected = []string{
  352. f.formatMatchValue(failure.Expected.Value),
  353. }
  354. case AssertMatchRegexp, AssertNotMatchRegexp:
  355. data.HaveExpected = true
  356. data.ExpectedKind = kindRegexp
  357. data.Expected = []string{
  358. f.formatMatchValue(failure.Expected.Value),
  359. }
  360. case AssertMatchFormat, AssertNotMatchFormat:
  361. data.HaveExpected = true
  362. if extractList(failure.Expected.Value) != nil {
  363. data.ExpectedKind = kindFormatList
  364. } else {
  365. data.ExpectedKind = kindFormat
  366. }
  367. data.Expected = f.formatListValue(failure.Expected.Value)
  368. case AssertContainsKey, AssertNotContainsKey:
  369. data.HaveExpected = true
  370. data.ExpectedKind = kindKey
  371. data.Expected = []string{
  372. f.formatValue(failure.Expected.Value),
  373. }
  374. case AssertContainsElement, AssertNotContainsElement:
  375. data.HaveExpected = true
  376. data.ExpectedKind = kindElement
  377. data.Expected = []string{
  378. f.formatValue(failure.Expected.Value),
  379. }
  380. case AssertContainsSubset, AssertNotContainsSubset:
  381. data.HaveExpected = true
  382. data.ExpectedKind = kindSubset
  383. data.Expected = []string{
  384. f.formatValue(failure.Expected.Value),
  385. }
  386. case AssertBelongs, AssertNotBelongs:
  387. data.HaveExpected = true
  388. data.ExpectedKind = kindValueList
  389. data.Expected = f.formatListValue(failure.Expected.Value)
  390. }
  391. }
  392. func (f *DefaultFormatter) fillIsNegation(
  393. data *FormatData, ctx *AssertionContext, failure *AssertionFailure,
  394. ) {
  395. switch failure.Type {
  396. case AssertUsage, AssertOperation,
  397. AssertType,
  398. AssertValid,
  399. AssertNil,
  400. AssertEmpty,
  401. AssertEqual,
  402. AssertLt, AssertLe, AssertGt, AssertGe,
  403. AssertInRange,
  404. AssertMatchSchema,
  405. AssertMatchPath,
  406. AssertMatchRegexp,
  407. AssertMatchFormat,
  408. AssertContainsKey,
  409. AssertContainsElement,
  410. AssertContainsSubset,
  411. AssertBelongs:
  412. break
  413. case AssertNotType,
  414. AssertNotValid,
  415. AssertNotNil,
  416. AssertNotEmpty,
  417. AssertNotEqual,
  418. AssertNotInRange,
  419. AssertNotMatchSchema,
  420. AssertNotMatchPath,
  421. AssertNotMatchRegexp,
  422. AssertNotMatchFormat,
  423. AssertNotContainsKey,
  424. AssertNotContainsElement,
  425. AssertNotContainsSubset,
  426. AssertNotBelongs:
  427. data.IsNegation = true
  428. }
  429. }
  430. func (f *DefaultFormatter) fillIsComparison(
  431. data *FormatData, ctx *AssertionContext, failure *AssertionFailure,
  432. ) {
  433. switch failure.Type { //nolint
  434. case AssertLt, AssertLe, AssertGt, AssertGe:
  435. data.IsComparison = true
  436. }
  437. }
  438. func (f *DefaultFormatter) fillReference(
  439. data *FormatData, ctx *AssertionContext, failure *AssertionFailure,
  440. ) {
  441. data.HaveReference = true
  442. data.Reference = f.formatValue(failure.Reference.Value)
  443. }
  444. func (f *DefaultFormatter) fillDelta(
  445. data *FormatData, ctx *AssertionContext, failure *AssertionFailure,
  446. ) {
  447. data.HaveDelta = true
  448. data.Delta = f.formatValue(failure.Delta.Value)
  449. }
  450. func (f *DefaultFormatter) fillRequest(
  451. data *FormatData, ctx *AssertionContext, failure *AssertionFailure,
  452. ) {
  453. if !f.DisableRequests && ctx.Request != nil && ctx.Request.httpReq != nil {
  454. dump, err := httputil.DumpRequest(ctx.Request.httpReq, false)
  455. if err != nil {
  456. return
  457. }
  458. data.HaveRequest = true
  459. data.Request = string(dump)
  460. }
  461. }
  462. func (f *DefaultFormatter) fillResponse(
  463. data *FormatData, ctx *AssertionContext, failure *AssertionFailure,
  464. ) {
  465. if !f.DisableResponses && ctx.Response != nil && ctx.Response.httpResp != nil {
  466. dump, err := httputil.DumpResponse(ctx.Response.httpResp, false)
  467. if err != nil {
  468. return
  469. }
  470. text := strings.Replace(string(dump), "\r\n", "\n", -1)
  471. lines := strings.SplitN(text, "\n", 2)
  472. data.HaveResponse = true
  473. data.Response = fmt.Sprintf("%s %s\n%s", lines[0], ctx.Response.rtt, lines[1])
  474. }
  475. }
  476. func (f *DefaultFormatter) fillStacktrace(
  477. data *FormatData, ctx *AssertionContext, failure *AssertionFailure,
  478. ) {
  479. data.Stacktrace = []string{}
  480. switch f.StacktraceMode {
  481. case StacktraceModeDisabled:
  482. break
  483. case StacktraceModeStandard:
  484. for _, entry := range failure.Stacktrace {
  485. data.HaveStacktrace = true
  486. data.Stacktrace = append(data.Stacktrace,
  487. fmt.Sprintf("%s()\n\t%s:%d +0x%x",
  488. entry.Func.Name(), entry.File, entry.Line, entry.FuncOffset))
  489. }
  490. case StacktraceModeCompact:
  491. for _, entry := range failure.Stacktrace {
  492. if entry.IsEntrypoint {
  493. break
  494. }
  495. data.HaveStacktrace = true
  496. data.Stacktrace = append(data.Stacktrace,
  497. fmt.Sprintf("%s() at %s:%d (%s)",
  498. entry.FuncName, filepath.Base(entry.File), entry.Line, entry.FuncPackage))
  499. }
  500. }
  501. }
  502. func (f *DefaultFormatter) formatValue(value interface{}) string {
  503. if flt := extractFloat32(value); flt != nil {
  504. return f.reformatNumber(f.formatFloatValue(*flt, 32))
  505. }
  506. if flt := extractFloat64(value); flt != nil {
  507. return f.reformatNumber(f.formatFloatValue(*flt, 64))
  508. }
  509. if refIsNum(value) {
  510. return f.reformatNumber(fmt.Sprintf("%v", value))
  511. }
  512. if !refIsNil(value) && !refIsHTTP(value) {
  513. if s, _ := value.(fmt.Stringer); s != nil {
  514. if ss := s.String(); strings.TrimSpace(ss) != "" {
  515. return ss
  516. }
  517. }
  518. if b, err := json.MarshalIndent(value, "", defaultIndent); err == nil {
  519. return string(b)
  520. }
  521. }
  522. sq := litter.Options{
  523. Separator: defaultIndent,
  524. }
  525. return sq.Sdump(value)
  526. }
  527. func (f *DefaultFormatter) formatFloatValue(value float64, bits int) string {
  528. switch f.FloatFormat {
  529. case FloatFormatAuto:
  530. if _, frac := math.Modf(value); frac != 0 {
  531. return strconv.FormatFloat(value, 'g', -1, bits)
  532. } else {
  533. return strconv.FormatFloat(value, 'f', -1, bits)
  534. }
  535. case FloatFormatDecimal:
  536. return strconv.FormatFloat(value, 'f', -1, bits)
  537. case FloatFormatScientific:
  538. return strconv.FormatFloat(value, 'e', -1, bits)
  539. default:
  540. return fmt.Sprintf("%v", value)
  541. }
  542. }
  543. func (f *DefaultFormatter) formatTypedValue(value interface{}) string {
  544. if refIsNum(value) {
  545. return fmt.Sprintf("%T(%v)", value, f.formatValue(value))
  546. }
  547. return fmt.Sprintf("%T(%#v)", value, value)
  548. }
  549. func (f *DefaultFormatter) formatMatchValue(value interface{}) string {
  550. if str := extractString(value); str != nil {
  551. return *str
  552. }
  553. return f.formatValue(value)
  554. }
  555. func (f *DefaultFormatter) formatRangeValue(value interface{}) []string {
  556. if rng := exctractRange(value); rng != nil {
  557. if refIsNum(rng.Min) && refIsNum(rng.Max) {
  558. return []string{
  559. fmt.Sprintf("[%v; %v]", f.formatValue(rng.Min), f.formatValue(rng.Max)),
  560. }
  561. } else {
  562. return []string{
  563. fmt.Sprintf("%v", rng.Min),
  564. fmt.Sprintf("%v", rng.Max),
  565. }
  566. }
  567. } else {
  568. return []string{
  569. f.formatValue(value),
  570. }
  571. }
  572. }
  573. func (f *DefaultFormatter) formatListValue(value interface{}) []string {
  574. if lst := extractList(value); lst != nil {
  575. s := make([]string, 0, len(*lst))
  576. for _, e := range *lst {
  577. s = append(s, f.formatValue(e))
  578. }
  579. return s
  580. } else {
  581. return []string{
  582. f.formatValue(value),
  583. }
  584. }
  585. }
  586. func (f *DefaultFormatter) formatDiff(expected, actual interface{}) (string, bool) {
  587. differ := gojsondiff.New()
  588. var diff gojsondiff.Diff
  589. if ve, ok := expected.(map[string]interface{}); ok {
  590. if va, ok := actual.(map[string]interface{}); ok {
  591. diff = differ.CompareObjects(ve, va)
  592. } else {
  593. return "", false
  594. }
  595. } else if ve, ok := expected.([]interface{}); ok {
  596. if va, ok := actual.([]interface{}); ok {
  597. diff = differ.CompareArrays(ve, va)
  598. } else {
  599. return "", false
  600. }
  601. } else {
  602. return "", false
  603. }
  604. if !diff.Modified() {
  605. return "", false
  606. }
  607. config := formatter.AsciiFormatterConfig{
  608. ShowArrayIndex: true,
  609. }
  610. fa := formatter.NewAsciiFormatter(expected, config)
  611. str, err := fa.Format(diff)
  612. if err != nil {
  613. return "", false
  614. }
  615. diffText := "--- expected\n+++ actual\n" + str
  616. return diffText, true
  617. }
  618. func (f *DefaultFormatter) reformatNumber(numStr string) string {
  619. signPart, intPart, fracPart, expPart := f.decomposeNumber(numStr)
  620. if intPart == "" {
  621. return numStr
  622. }
  623. var sb strings.Builder
  624. sb.WriteString(signPart)
  625. sb.WriteString(f.applySeparator(intPart, -1))
  626. if fracPart != "" {
  627. sb.WriteString(".")
  628. sb.WriteString(f.applySeparator(fracPart, +1))
  629. }
  630. if expPart != "" {
  631. sb.WriteString("e")
  632. sb.WriteString(expPart)
  633. }
  634. return sb.String()
  635. }
  636. var (
  637. decomposeRegexp = regexp.MustCompile(`^([+-])?(\d+)([.](\d+))?([eE]([+-]?\d+))?$`)
  638. )
  639. func (f *DefaultFormatter) decomposeNumber(numStr string) (
  640. signPart, intPart, fracPart, expPart string,
  641. ) {
  642. parts := decomposeRegexp.FindStringSubmatch(numStr)
  643. if len(parts) > 1 {
  644. signPart = parts[1]
  645. }
  646. if len(parts) > 2 {
  647. intPart = parts[2]
  648. }
  649. if len(parts) > 4 {
  650. fracPart = parts[4]
  651. }
  652. if len(parts) > 6 {
  653. expPart = parts[6]
  654. }
  655. return
  656. }
  657. func (f *DefaultFormatter) applySeparator(numStr string, dir int) string {
  658. var separator string
  659. switch f.DigitSeparator {
  660. case DigitSeparatorUnderscore:
  661. separator = "_"
  662. break
  663. case DigitSeparatorApostrophe:
  664. separator = "'"
  665. break
  666. case DigitSeparatorComma:
  667. separator = ","
  668. break
  669. case DigitSeparatorNone:
  670. default:
  671. return numStr
  672. }
  673. var sb strings.Builder
  674. cnt := 0
  675. if dir < 0 {
  676. cnt = len(numStr)
  677. }
  678. for i := 0; i != len(numStr); i++ {
  679. sb.WriteByte(numStr[i])
  680. cnt += dir
  681. if cnt%3 == 0 && i != len(numStr)-1 {
  682. sb.WriteString(separator)
  683. }
  684. }
  685. return sb.String()
  686. }
  687. func extractString(value interface{}) *string {
  688. switch s := value.(type) {
  689. case string:
  690. return &s
  691. default:
  692. return nil
  693. }
  694. }
  695. func extractFloat32(value interface{}) *float64 {
  696. switch f := value.(type) {
  697. case float32:
  698. ff := float64(f)
  699. return &ff
  700. default:
  701. return nil
  702. }
  703. }
  704. func extractFloat64(value interface{}) *float64 {
  705. switch f := value.(type) {
  706. case float64:
  707. return &f
  708. default:
  709. return nil
  710. }
  711. }
  712. func exctractRange(value interface{}) *AssertionRange {
  713. switch rng := value.(type) {
  714. case AssertionRange:
  715. return &rng
  716. case *AssertionRange: // invalid, but we handle it
  717. return rng
  718. default:
  719. return nil
  720. }
  721. }
  722. func extractList(value interface{}) *AssertionList {
  723. switch lst := value.(type) {
  724. case AssertionList:
  725. return &lst
  726. case *AssertionList: // invalid, but we handle it
  727. return lst
  728. default:
  729. return nil
  730. }
  731. }
  732. var (
  733. colorsSupportedOnce sync.Once
  734. colorsSupportedMode int
  735. )
  736. const (
  737. colorsUnsupported = iota
  738. colorsSupported
  739. colorsForced
  740. )
  741. func colorMode() int {
  742. colorsSupportedOnce.Do(func() {
  743. if s := os.Getenv("FORCE_COLOR"); len(s) != 0 {
  744. if n, err := strconv.Atoi(s); err == nil && n > 0 {
  745. colorsSupportedMode = colorsForced
  746. return
  747. }
  748. }
  749. if (isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd())) &&
  750. len(os.Getenv("NO_COLOR")) == 0 &&
  751. !strings.HasPrefix(os.Getenv("TERM"), "dumb") {
  752. colorsSupportedMode = colorsSupported
  753. return
  754. }
  755. colorsSupportedMode = colorsUnsupported
  756. return
  757. })
  758. return colorsSupportedMode
  759. }
  760. const (
  761. defaultIndent = " "
  762. defaultLineWidth = 60
  763. )
  764. var defaultColors = map[string]color.Attribute{
  765. // regular
  766. "Black": color.FgBlack,
  767. "Red": color.FgRed,
  768. "Green": color.FgGreen,
  769. "Yellow": color.FgYellow,
  770. "Magenta": color.FgMagenta,
  771. "Cyan": color.FgCyan,
  772. "White": color.FgWhite,
  773. // bright
  774. "HiBlack": color.FgHiBlack,
  775. "HiRed": color.FgHiRed,
  776. "HiGreen": color.FgHiGreen,
  777. "HiYellow": color.FgHiYellow,
  778. "HiMagenta": color.FgHiMagenta,
  779. "HiCyan": color.FgHiCyan,
  780. "HiWhite": color.FgHiWhite,
  781. }
  782. var defaultTemplateFuncs = template.FuncMap{
  783. "trim": func(input string) string {
  784. return strings.TrimSpace(input)
  785. },
  786. "indent": func(input string) string {
  787. var sb strings.Builder
  788. for _, s := range strings.Split(input, "\n") {
  789. if sb.Len() != 0 {
  790. sb.WriteString("\n")
  791. }
  792. sb.WriteString(defaultIndent)
  793. sb.WriteString(s)
  794. }
  795. return sb.String()
  796. },
  797. "wrap": func(width int, input string) string {
  798. input = strings.TrimSpace(input)
  799. width -= len(defaultIndent)
  800. if width <= 0 {
  801. return input
  802. }
  803. return wordwrap.WrapString(input, uint(width))
  804. },
  805. "join": func(width int, tokenList []string) string {
  806. width -= len(defaultIndent)
  807. if width <= 0 {
  808. return strings.Join(tokenList, ".")
  809. }
  810. var sb strings.Builder
  811. lineLen := 0
  812. lineNum := 0
  813. write := func(s string) {
  814. sb.WriteString(s)
  815. lineLen += len(s)
  816. }
  817. for n, token := range tokenList {
  818. if lineLen+len(token)+1 > width {
  819. write("\n")
  820. lineLen = 0
  821. if lineNum < 2 {
  822. lineNum++
  823. }
  824. }
  825. if lineLen == 0 {
  826. for l := 0; l < lineNum; l++ {
  827. write(defaultIndent)
  828. }
  829. }
  830. write(token)
  831. if n != len(tokenList)-1 {
  832. write(".")
  833. }
  834. }
  835. return sb.String()
  836. },
  837. "color": func(enable bool, colorName, input string) string {
  838. if !enable {
  839. return input
  840. }
  841. colorAttr := color.Reset
  842. if ca, ok := defaultColors[colorName]; ok {
  843. colorAttr = ca
  844. }
  845. return color.New(colorAttr).Sprint(input)
  846. },
  847. "colordiff": func(enable bool, input string) string {
  848. if !enable {
  849. return input
  850. }
  851. prefixColor := []struct {
  852. prefix string
  853. color color.Attribute
  854. }{
  855. {"---", color.FgWhite},
  856. {"+++", color.FgWhite},
  857. {"-", color.FgRed},
  858. {"+", color.FgGreen},
  859. }
  860. lineColor := func(s string) color.Attribute {
  861. for _, pc := range prefixColor {
  862. if strings.HasPrefix(s, pc.prefix) {
  863. return pc.color
  864. }
  865. }
  866. return color.Reset
  867. }
  868. var sb strings.Builder
  869. for _, line := range strings.Split(input, "\n") {
  870. if sb.Len() != 0 {
  871. sb.WriteString("\n")
  872. }
  873. sb.WriteString(color.New(lineColor(line)).Sprint(line))
  874. }
  875. return sb.String()
  876. },
  877. }
  878. var defaultSuccessTemplate = `[OK] {{ join .LineWidth .AssertPath }}`
  879. var defaultFailureTemplate = `
  880. {{- range $n, $err := .Errors }}
  881. {{ if eq $n 0 -}}
  882. {{ $err | wrap $.LineWidth | color $.EnableColors "Red" }}
  883. {{- else -}}
  884. {{ $err | wrap $.LineWidth | indent | color $.EnableColors "Red" }}
  885. {{- end -}}
  886. {{- end -}}
  887. {{- if .TestName }}
  888. test name: {{ .TestName | color $.EnableColors "Cyan" }}
  889. {{- end -}}
  890. {{- if .RequestName }}
  891. request name: {{ .RequestName | color $.EnableColors "Cyan" }}
  892. {{- end -}}
  893. {{- if .HaveRequest }}
  894. request: {{ .Request | indent | trim | color $.EnableColors "HiMagenta" }}
  895. {{- end -}}
  896. {{- if .HaveResponse }}
  897. response: {{ .Response | indent | trim | color $.EnableColors "HiMagenta" }}
  898. {{- end -}}
  899. {{- if .HaveStacktrace }}
  900. trace:
  901. {{- range $n, $call := .Stacktrace }}
  902. {{ $call | indent }}
  903. {{- end -}}
  904. {{- end -}}
  905. {{- if .AssertPath }}
  906. assertion:
  907. {{ join .LineWidth .AssertPath | indent | color .EnableColors "Yellow" }}
  908. {{- end -}}
  909. {{- if .HaveExpected }}
  910. {{ if .IsNegation }}denied
  911. {{- else if .IsComparison }}compared
  912. {{- else }}expected
  913. {{- end }} {{ .ExpectedKind }}:
  914. {{- range $n, $exp := .Expected }}
  915. {{ $exp | indent | color $.EnableColors "HiMagenta" }}
  916. {{- end -}}
  917. {{- end -}}
  918. {{- if .HaveActual }}
  919. actual value:
  920. {{ .Actual | indent | color .EnableColors "HiMagenta" }}
  921. {{- end -}}
  922. {{- if .HaveReference }}
  923. reference value:
  924. {{ .Reference | indent | color .EnableColors "HiMagenta" }}
  925. {{- end -}}
  926. {{- if .HaveDelta }}
  927. allowed delta:
  928. {{ .Delta | indent | color .EnableColors "HiMagenta" }}
  929. {{- end -}}
  930. {{- if .HaveDiff }}
  931. diff:
  932. {{ .Diff | colordiff .EnableColors | indent }}
  933. {{- end -}}
  934. `