前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >golang源码阅读:test2json

golang源码阅读:test2json

作者头像
golangLeetcode
发布于 2023-09-06 11:28:07
发布于 2023-09-06 11:28:07
19200
代码可运行
举报
运行总次数:0
代码可运行

go test加参数-json就能输出 json格式,下面我们用一个简单的例子看下对他进行分析,然后分析下相关源码。对于单测

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package test

import (
  "fmt"
  "testing"
)

func TestAdd(t *testing.T) {
  type args struct {
    a int
    b int
  }
  tests := []struct {
    name string
    args args
    want int
  }{
    // TODO: Add test cases.
    {
      args: args{
        a: 1,
        b: 2,
      },
      want: 3,
    },

    {
      args: args{
        a: 1,
        b: 2,
      },
      want: 4,
    },
  }
  for _, tt := range tests {
    t.Run(tt.name, func(t *testing.T) {
      if got := Add(tt.args.a, tt.args.b); got != tt.want {
        t.Errorf("Add() = %v, want %v", got, tt.want)
      }
    })
  }
}

func TestSub(t *testing.T) {
  type args struct {
    a int
    b int
  }
  tests := []struct {
    name string
    args args
    want int
  }{
    // TODO: Add test cases.
    {
      args: args{
        a: 1,
        b: 2,
      },
      want: -1,
    },
  }
  for _, tt := range tests {
    fmt.Println("before")
    fmt.Print("before not endline")
    t.Run(tt.name, func(t *testing.T) {
      if got := Sub(tt.args.a, tt.args.b); got != tt.want {
        t.Errorf("Sub() = %v, want %v", got, tt.want)
      }
    })
    fmt.Println("after")
    t.Skip("skipping test; 1 not set")
  }
}

执行命令

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
% go test ./test/gotest/test/... -json

它的输出如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{"Time":"2023-05-13T17:27:15.492681+08:00","Action":"run","Package":"learn/test/gotest/test","Test":"TestAdd"}
{"Time":"2023-05-13T17:27:15.493129+08:00","Action":"output","Package":"learn/test/gotest/test","Test":"TestAdd","Output":"=== RUN   TestAdd\n"}
{"Time":"2023-05-13T17:27:15.49316+08:00","Action":"run","Package":"learn/test/gotest/test","Test":"TestAdd/#00"}
{"Time":"2023-05-13T17:27:15.493168+08:00","Action":"output","Package":"learn/test/gotest/test","Test":"TestAdd/#00","Output":"=== RUN   TestAdd/#00\n"}
{"Time":"2023-05-13T17:27:15.493185+08:00","Action":"run","Package":"learn/test/gotest/test","Test":"TestAdd/#01"}
{"Time":"2023-05-13T17:27:15.493191+08:00","Action":"output","Package":"learn/test/gotest/test","Test":"TestAdd/#01","Output":"=== RUN   TestAdd/#01\n"}
{"Time":"2023-05-13T17:27:15.493195+08:00","Action":"output","Package":"learn/test/gotest/test","Test":"TestAdd/#01","Output":"    add_test.go:38: Add() = 3, want 4\n"}
{"Time":"2023-05-13T17:27:15.493444+08:00","Action":"output","Package":"learn/test/gotest/test","Test":"TestAdd","Output":"--- FAIL: TestAdd (0.00s)\n"}
{"Time":"2023-05-13T17:27:15.493471+08:00","Action":"output","Package":"learn/test/gotest/test","Test":"TestAdd/#00","Output":"    --- PASS: TestAdd/#00 (0.00s)\n"}
{"Time":"2023-05-13T17:27:15.493477+08:00","Action":"pass","Package":"learn/test/gotest/test","Test":"TestAdd/#00","Elapsed":0}
{"Time":"2023-05-13T17:27:15.49349+08:00","Action":"output","Package":"learn/test/gotest/test","Test":"TestAdd/#01","Output":"    --- FAIL: TestAdd/#01 (0.00s)\n"}
{"Time":"2023-05-13T17:27:15.493495+08:00","Action":"fail","Package":"learn/test/gotest/test","Test":"TestAdd/#01","Elapsed":0}
{"Time":"2023-05-13T17:27:15.493499+08:00","Action":"fail","Package":"learn/test/gotest/test","Test":"TestAdd","Elapsed":0}
{"Time":"2023-05-13T17:27:15.493503+08:00","Action":"run","Package":"learn/test/gotest/test","Test":"TestSub"}
{"Time":"2023-05-13T17:27:15.493506+08:00","Action":"output","Package":"learn/test/gotest/test","Test":"TestSub","Output":"=== RUN   TestSub\n"}
{"Time":"2023-05-13T17:27:15.49362+08:00","Action":"output","Package":"learn/test/gotest/test","Test":"TestSub","Output":"before\n"}
{"Time":"2023-05-13T17:27:15.493633+08:00","Action":"output","Package":"learn/test/gotest/test","Test":"TestSub","Output":"before not endline=== RUN   TestSub/#00\n"}
{"Time":"2023-05-13T17:27:15.493642+08:00","Action":"output","Package":"learn/test/gotest/test","Test":"TestSub","Output":"after\n"}
{"Time":"2023-05-13T17:27:15.493646+08:00","Action":"cont","Package":"learn/test/gotest/test","Test":"TestSub"}
{"Time":"2023-05-13T17:27:15.49365+08:00","Action":"output","Package":"learn/test/gotest/test","Test":"TestSub","Output":"=== CONT  TestSub\n"}
{"Time":"2023-05-13T17:27:15.493654+08:00","Action":"output","Package":"learn/test/gotest/test","Test":"TestSub","Output":"    add_test.go:72: skipping test; 1 not set\n"}
{"Time":"2023-05-13T17:27:15.493659+08:00","Action":"output","Package":"learn/test/gotest/test","Test":"TestSub","Output":"--- SKIP: TestSub (0.00s)\n"}
{"Time":"2023-05-13T17:27:15.493802+08:00","Action":"output","Package":"learn/test/gotest/test","Test":"TestSub/#00","Output":"    --- PASS: TestSub/#00 (0.00s)\n"}
{"Time":"2023-05-13T17:27:15.493818+08:00","Action":"pass","Package":"learn/test/gotest/test","Test":"TestSub/#00","Elapsed":0}
{"Time":"2023-05-13T17:27:15.493824+08:00","Action":"skip","Package":"learn/test/gotest/test","Test":"TestSub","Elapsed":0}
{"Time":"2023-05-13T17:27:15.493827+08:00","Action":"output","Package":"learn/test/gotest/test","Output":"FAIL\n"}
{"Time":"2023-05-13T17:27:15.493899+08:00","Action":"output","Package":"learn/test/gotest/test","Output":"FAIL\tlearn/test/gotest/test\t0.138s\n"}
{"Time":"2023-05-13T17:27:15.493915+08:00","Action":"fail","Package":"learn/test/gotest/test","Elapsed":0.139}

可以看到,和直直接输出相比,输出格式变成了json,每条输出都是一个json对象,它包含属性有时间,类型、包、测试方法、以及输出等。比不带-json参数多了很多内容

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
--- FAIL: TestAdd (0.00s)
    --- FAIL: TestAdd/#01 (0.00s)
        add_test.go:38: Add() = 3, want 4
before
before not endlineafter
FAIL
FAIL    learn/test/gotest/test  0.176s
FAIL

下面分析下它的源码实现,源码位于src/cmd/internal/test2json/test2json.go输出被定义成了event类型

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
type event struct {
  Time    *time.Time `json:",omitempty"`
  Action  string
  Package string     `json:",omitempty"`
  Test    string     `json:",omitempty"`
  Elapsed *float64   `json:",omitempty"`
  Output  *textBytes `json:",omitempty"`
}

其中Converter负责将gotest的输出转化为event事件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
type Converter struct {
  w        io.Writer  // JSON output stream
  pkg      string     // package to name in events
  mode     Mode       // mode bits
  start    time.Time  // time converter started
  testName string     // name of current test, for output attribution
  report   []*event   // pending test result reports (nested for subtests)
  result   string     // overall test result if seen
  input    lineBuffer // input buffer
  output   lineBuffer // output buffer
}

构造方法如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func NewConverter(w io.Writer, pkg string, mode Mode) *Converter {
  c := new(Converter)
  *c = Converter{
    w:     w,
    pkg:   pkg,
    mode:  mode,
    start: time.Now(),
    input: lineBuffer{
      b:    make([]byte, 0, inBuffer),
      line: c.handleInputLine,
      part: c.output.write,
    },
    output: lineBuffer{
      b:    make([]byte, 0, outBuffer),
      line: c.writeOutputEvent,
      part: c.writeOutputEvent,
    },
  }
  return c
}

退出对应了pass和fail两种action

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func (c *Converter) Exited(err error) {
  if err == nil {
    c.result = "pass"
  } else {
    c.result = "fail"
  }
}

相应的文案定义如下,也就是我们常看到的单测结果

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  updates = [][]byte{
    []byte("=== RUN   "),
    []byte("=== PAUSE "),
    []byte("=== CONT  "),
  }

  reports = [][]byte{
    []byte("--- PASS: "),
    []byte("--- FAIL: "),
    []byte("--- SKIP: "),
    []byte("--- BENCH: "),
  }

根据输入的每一行内容,进行解析,得到event,如果都没有匹配成功,就把前一个测试的名字赋值给当前测试。否则根据冒号截取内容获取Action和Test。最后把原始输入,写入到Output字段里面。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func (c *Converter) handleInputLine(line []byte) {
  // Final PASS or FAIL.
  if bytes.Equal(line, bigPass) || bytes.Equal(line, bigFail) || bytes.HasPrefix(line, bigFailErrorPrefix) {
    c.flushReport(0)
    c.output.write(line)
    if bytes.Equal(line, bigPass) {
      c.result = "pass"
    } else {
      c.result = "fail"
    }
    return
  }
  if bytes.HasPrefix(line, skipLinePrefix) && bytes.HasSuffix(line, skipLineSuffix) && len(c.report) == 0 {
    c.result = "skip"
  }
  
  for _, magic := range reports {
      if bytes.HasPrefix(line, magic) {
        actionColon = true
        ok = true
        break
      }
    }
   
    if indent > 0 && indent <= len(c.report) {
      c.testName = c.report[indent-1].Test
    }
   
     action := strings.ToLower(strings.TrimSuffix(strings.TrimSpace(string(line[4:i])), ":"))
     name := strings.TrimSpace(string(line[i:]))
     
     c.output.write(origLine)

大于当前层级的结果输出

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func (c *Converter) flushReport(depth int) {
  c.testName = ""
  for len(c.report) > depth {
    e := c.report[len(c.report)-1]
    c.report = c.report[:len(c.report)-1]
    c.writeEvent(e)
  }
}

输出output是一个单独的事件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func (c *Converter) writeOutputEvent(out []byte) {
  c.writeEvent(&event{
    Action: "output",
    Output: (*textBytes)(&out),
  })
}

最后来到了writevent函数,把包名字和测试名字赋值,然后序列化。加入换行然后输出。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func (c *Converter) writeEvent(e *event) {
  e.Package = c.pkg
  if c.mode&Timestamp != 0 {
    t := time.Now()
    e.Time = &t
  }
  if e.Test == "" {
    e.Test = c.testName
  }
  js, err := json.Marshal(e)
  js = append(js, '\n')
  c.w.Write(js)

总的来说,就是解析字符串,转化成json的一个过程。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-05-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 golang算法架构leetcode技术php 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
dotnet 读 WPF 源代码笔记 使用 Win32 方法修改窗口的坐标和大小对窗口依赖属性的影响
咱可以使用 Win32 的 SetWindowPos 修改窗口的坐标和大小,此时 WPF 的窗口的 Left 和 Top 和 Width 和 Height 依赖属性也会受到影响,本文将会告诉大家在啥时候会同步更改 WPF 依赖属性的值,而什么时候不会
林德熙
2021/01/12
8660
dotnet 读 WPF 源代码笔记 启动欢迎界面 SplashScreen 的原理
本文是我在读 WPF 源代码做的笔记。在 WPF 中的启动界面,为了能让 WPF 的启动界面显示足够快,需要在应用的 WPF 主机还没有启动完成之前就显示出启动图,此时的启动图需要自己解析图片同时也需要自己创建显示窗口
林德熙
2020/12/22
1.1K0
dotnet 读 WPF 源代码笔记 从 WM_POINTER 消息到 Touch 事件
本文记录我读 WPF 源代码的笔记,在 WPF 底层是如何从 Win32 的消息循环获取到的 WM_POINTER 消息处理转换作为 Touch 事件的参数
林德熙
2024/09/09
3760
dotnet 读 WPF 源代码 聊聊 DispatcherTimer 的实现
本文来告诉大家在 WPF 框架里面,是如何实现 DispatcherTimer 的功能。有小伙伴告诉我,读源代码系列的博客看不动,原因是太底层了。我尝试换一个方式切入逻辑,通过提问题和解决问题的方法,一步步告诉大家 WPF 是如何实现 DispatcherTimer 的功能
林德熙
2021/12/24
7430
WPF 从裸 Win 32 的 WM_Pointer 消息获取触摸点绘制笔迹
本文将告诉大家如何在 WPF 里面,接收裸 Win 32 的 WM_Pointer 消息,从消息里面获取触摸点信息,使用触摸点信息绘制简单的笔迹
林德熙
2024/09/01
2200
WPF 从裸 Win 32 的 WM_Pointer 消息获取触摸点绘制笔迹
Support Horizontal Scrolling of TouchPad in WPF Application
发布于 2017-11-23 14:09 更新于 2018-08-12 08:02
walterlv
2018/09/18
7500
Support Horizontal Scrolling of TouchPad in WPF Application
WPF 编写一个测试 WM_TOUCH 触摸消息延迟的应用
我听说在 Win10 到 Win11 的系统版本左右,微软加上了一大波触摸性能优化,准确来说是 HID 性能优化。我想测试一下在这些系统下,采用从 Windows 消息接收到 WM_TOUCH 触摸消息的延迟将会是多少。本文将告诉大家我编写的测试应
林德熙
2023/04/07
6370
WPF 渲染原理
在 WPF 最主要的就是渲染,因为 WPF 是一个界面框架。想用一篇博客就能告诉大家完整的 WPF 渲染原理是不可能的。本文告诉大家 WPF 从开发者告诉如何画图像到在屏幕显示的过程。本文是从一个很高的地方来看渲染的过程,在本文之后会添加很多博客来告诉大家渲染的细节。
林德熙
2018/09/19
3K0
WPF 渲染原理
wpf 单例
本文告诉大家如何做一个 wpf 单例程序。单例就是用户只能运行这个程序一次,也就是内存只有存在这个程序一个。
林德熙
2018/09/19
1.3K0
wpf 单例
dotnet 读 WPF 源代码 Popup 的 StaysOpen 为 false 将会吃掉其他窗口的首次激活
在 WPF 中,使用 Popup 控件,可以设置 StaysOpen 属性来控制是否在 Popup 失去焦点时,也就是点击界面空白处,自动收起 Popup 控件。但如果有两个窗口,在设置 Popup 控件的 StaysOpen 属性为 false 那么将会吃掉在点击其他窗口的第一次交互,如鼠标点击或触摸点击时将不会让本进程的其他窗口 Activate 激活
林德熙
2021/12/24
6800
dotnet 读 WPF 源代码笔记 插入触摸设备的初始化获取设备信息
在 WPF 触摸应用中,插入触摸设备,即可在应用里面使用上插入的触摸设备。在 WPF 使用触摸设备的触摸时,需要获取到触摸设备的信息,才能实现触摸
林德熙
2021/05/27
7030
dotnet 读 WPF 源代码笔记 渲染收集是如何触发
在 WPF 里面,渲染可以从架构上划分为两层。上层是 WPF 框架的 OnRender 之类的函数,作用是收集应用程序渲染的命令。上层将收集到的应用程序绘制渲染的命令传给下层,下层是 WPF 的 GFX 层,作用是根据收到的渲染的命令绘制出界面。本文所聊的是渲染上层部分,在 WPF 框架是如何做到界面刷新渲染,包括此调用的顺序以及框架逻辑
林德熙
2021/09/18
8600
dotnet 读 WPF 源代码笔记 渲染收集是如何触发
WPF 制作支持点击穿透的高性能的透明背景异形窗口
默认的 WPF 的支持点击穿透的透明背景窗口,是通过 AllowsTransparency 实现的,但是此方法的性能比较低。本文来告诉大家一个高性能的方法,通过此方法制作出来的 WPF 窗口可以获取很高的性能,设置透明和设置窗口不透明之间几乎没有性能差别
林德熙
2021/01/07
2.9K1
WPF 从触摸消息转触摸事件
在 WPF 程序可能因为一些坑让程序触摸失效,如果此时还可以收到系统的触摸消息,那么可以通过从触摸消息转触摸事件解决程序触摸失效但不适合所有触摸失效程序
林德熙
2020/07/07
1.3K0
WPF 使用 RawInput 接收裸数据
在 Windows 提供很底层的方法接收硬件设备的裸数据,通过接收裸数据可以做到性能更高的全局键盘,还能支持多个鼠标。但是用这个方法需要自己解析裸数据,同时会因为接受到很多消息降低性能
林德熙
2022/08/04
9870
dotnet DirectX 做一个简单绘制折线笔迹的 D2D 应用
本文将告诉大家如何从简单的控制台开始,使用 Vortice 辅助调用 Direct2D1 的功能,配合 WM_Pointer 消息,制作一个简单绘制触摸折线笔迹的 D2D 应用
林德熙
2024/10/16
1780
将 WPF 嵌入到 MFC 中,无法响应键盘输入
在 将 WPF 窗口嵌入到 MFC 窗口中 中提到,可以将 WPF 嵌入到 MFC 窗口中, 但遗留了一个没有发现的问题,WPF 界面,无法响应键盘的输入。
jgrass
2024/12/25
2680
WPF 开启Pointer消息存在的坑
启用了Pointer之后,调用Textbox.Focus(),起不来屏幕键盘,必须点在它之上才行,触摸在它之上才行
林德熙
2022/08/12
9070
WPF 开启Pointer消息存在的坑
WPF 插拔触摸设备触摸失效
最近使用 WPF 程序,在不停插拔触摸设备会让 WPF 程序触摸失效。通过分析 WPF 源代码可以找到 WPF 触摸失效的原因。
林德熙
2018/09/19
1.8K0
WPF 插拔触摸设备触摸失效
把WPF Dialog转成WinForm Dialog需要注意的问题续
上一篇讲到将WPF的窗口转为WinForm窗口需要注意的问题,这里给出了另一种解决方案,闲话不说,请看代码: //============================================================================== // File Name : WpfModalDialogFixer.cs // // Copyright (C) 2010 GrapeCity Inc. All rights reserved. // // Distribu
葡萄城控件
2018/01/10
1K0
推荐阅读
相关推荐
dotnet 读 WPF 源代码笔记 使用 Win32 方法修改窗口的坐标和大小对窗口依赖属性的影响
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档