xref: /xnu-11215.61.5/tools/tests/kernpost_test_report/kernpost_test_report.lua (revision 4f1223e81cd707a65cc109d0b8ad6653699da3c4)
1*4f1223e8SApple OSS Distributions#!/usr/local/bin/recon
2*4f1223e8SApple OSS Distributions
3*4f1223e8SApple OSS Distributionslocal argparse = require 'argparse'
4*4f1223e8SApple OSS Distributionslocal darwin = require 'darwin'
5*4f1223e8SApple OSS Distributionslocal plist = require 'plist'
6*4f1223e8SApple OSS Distributionslocal kcdata = require 'kcdata'
7*4f1223e8SApple OSS Distributionslocal lfs = require 'lfs'
8*4f1223e8SApple OSS Distributionslocal sysctl = require 'sysctl'
9*4f1223e8SApple OSS Distributions
10*4f1223e8SApple OSS Distributionsrequire 'strict'
11*4f1223e8SApple OSS Distributions
12*4f1223e8SApple OSS Distributionslocal parser = argparse(){
13*4f1223e8SApple OSS Distributions  name = 'kernpost_report',
14*4f1223e8SApple OSS Distributions}
15*4f1223e8SApple OSS Distributions
16*4f1223e8SApple OSS Distributionslocal FMT_RAW <const> = 'raw'
17*4f1223e8SApple OSS Distributionslocal FMT_PLIST <const> = 'plist'
18*4f1223e8SApple OSS Distributionslocal FMT_BUNDLE <const> = 'resultbundle'
19*4f1223e8SApple OSS Distributions
20*4f1223e8SApple OSS Distributionslocal export = parser:command('export')
21*4f1223e8SApple OSS Distributionsexport:option{
22*4f1223e8SApple OSS Distributions  name = '-f --format',
23*4f1223e8SApple OSS Distributions  description = 'the format to export the tests in',
24*4f1223e8SApple OSS Distributions  choices = { FMT_RAW, FMT_PLIST, FMT_BUNDLE, },
25*4f1223e8SApple OSS Distributions  count = '?',
26*4f1223e8SApple OSS Distributions}
27*4f1223e8SApple OSS Distributionsexport:option{
28*4f1223e8SApple OSS Distributions  name = '-o --output',
29*4f1223e8SApple OSS Distributions  description = 'where to write the report, must be a directory',
30*4f1223e8SApple OSS Distributions  count = 1,
31*4f1223e8SApple OSS Distributions}
32*4f1223e8SApple OSS Distributions
33*4f1223e8SApple OSS Distributionslocal function write_file(path, data)
34*4f1223e8SApple OSS Distributions  local f, err = io.open(path, 'w')
35*4f1223e8SApple OSS Distributions  if not f then
36*4f1223e8SApple OSS Distributions    io.stderr:write(path, ': failed to open for writing: ', err, '\n')
37*4f1223e8SApple OSS Distributions    os.exit(1)
38*4f1223e8SApple OSS Distributions  end
39*4f1223e8SApple OSS Distributions  f:write(data)
40*4f1223e8SApple OSS Distributions  f:close()
41*4f1223e8SApple OSS Distributionsend
42*4f1223e8SApple OSS Distributions
43*4f1223e8SApple OSS Distributionslocal function default_test_info(config)
44*4f1223e8SApple OSS Distributions  return {
45*4f1223e8SApple OSS Distributions    version = 2,
46*4f1223e8SApple OSS Distributions    test_category = 'unittest',
47*4f1223e8SApple OSS Distributions    Project = 'xnu',
48*4f1223e8SApple OSS Distributions    ['boot-args'] = assert(config.boot_args),
49*4f1223e8SApple OSS Distributions    osVersion = assert(config.osversion),
50*4f1223e8SApple OSS Distributions    mach_timebase_info = config.mach_timebase_info,
51*4f1223e8SApple OSS Distributions  }
52*4f1223e8SApple OSS Distributionsend
53*4f1223e8SApple OSS Distributions
54*4f1223e8SApple OSS Distributionslocal function current_timezone_offset()
55*4f1223e8SApple OSS Distributions  local now = os.time()
56*4f1223e8SApple OSS Distributions  local timezone = os.difftime(now, os.time(os.date("!*t", now)))
57*4f1223e8SApple OSS Distributions  local h, m = math.modf(timezone / 3600)
58*4f1223e8SApple OSS Distributions  return ("%+.2d:%.2d"):format(h, 60 * m)
59*4f1223e8SApple OSS Distributionsend
60*4f1223e8SApple OSS Distributionslocal timezone_offset = current_timezone_offset()
61*4f1223e8SApple OSS Distributions
62*4f1223e8SApple OSS Distributionslocal function convert_time(raw_time, tb)
63*4f1223e8SApple OSS Distributions  local time_secs = raw_time * tb.numer / tb.denom / 1e9
64*4f1223e8SApple OSS Distributions  local boottime_secs = darwin.mach_boottime_usec() / 1e6
65*4f1223e8SApple OSS Distributions  local walltime_secs = math.modf(boottime_secs + time_secs)
66*4f1223e8SApple OSS Distributions  local time_str = os.date('%Y-%m-%dT%H:%M:%S', walltime_secs)
67*4f1223e8SApple OSS Distributions  local time_ms_str = time_str .. '.' .. tostring(
68*4f1223e8SApple OSS Distributions      math.floor(walltime_secs / 1e6))
69*4f1223e8SApple OSS Distributions  return time_ms_str .. timezone_offset
70*4f1223e8SApple OSS Distributionsend
71*4f1223e8SApple OSS Distributions
72*4f1223e8SApple OSS Distributionslocal function write_test_result(config, test_data, dir)
73*4f1223e8SApple OSS Distributions  local info_tbl = default_test_info(config)
74*4f1223e8SApple OSS Distributions
75*4f1223e8SApple OSS Distributions  local name = test_data.test_name
76*4f1223e8SApple OSS Distributions  local test_dir = dir .. '/test_' .. name
77*4f1223e8SApple OSS Distributions  lfs.mkdir(test_dir)
78*4f1223e8SApple OSS Distributions
79*4f1223e8SApple OSS Distributions  info_tbl.test_id = name
80*4f1223e8SApple OSS Distributions
81*4f1223e8SApple OSS Distributions  local test_pass = test_data.retval ~= nil and
82*4f1223e8SApple OSS Distributions      test_data.retval == test_data.expected_retval
83*4f1223e8SApple OSS Distributions  info_tbl.result_code = test_pass and 200 or 400
84*4f1223e8SApple OSS Distributions
85*4f1223e8SApple OSS Distributions  local tb = config.mach_timebase_info
86*4f1223e8SApple OSS Distributions  info_tbl.result_started = convert_time(test_data.begin_time, tb)
87*4f1223e8SApple OSS Distributions  info_tbl.beginTimeRaw = test_data.begin_time
88*4f1223e8SApple OSS Distributions
89*4f1223e8SApple OSS Distributions  info_tbl.result_finished = convert_time(test_data.end_time, tb)
90*4f1223e8SApple OSS Distributions  info_tbl.endTimeRaw = test_data.end_time
91*4f1223e8SApple OSS Distributions
92*4f1223e8SApple OSS Distributions  local info_path <const> = test_dir .. '/Info.plist'
93*4f1223e8SApple OSS Distributions  local info_plist, err = plist.encode(info_tbl)
94*4f1223e8SApple OSS Distributions  if not info_plist then
95*4f1223e8SApple OSS Distributions    io.stderr:write('error: failed to serialize test Info.plist: ', err, '\n')
96*4f1223e8SApple OSS Distributions    os.exit(1)
97*4f1223e8SApple OSS Distributions  end
98*4f1223e8SApple OSS Distributions  write_file(info_path, info_plist)
99*4f1223e8SApple OSS Distributions
100*4f1223e8SApple OSS Distributions  lfs.mkdir(test_dir .. '/Attachments')
101*4f1223e8SApple OSS Distributions  lfs.mkdir(test_dir .. '/Diagnostics')
102*4f1223e8SApple OSS Distributions
103*4f1223e8SApple OSS Distributions  local status_path = test_dir .. '/' .. (test_pass and 'PASS' or 'FAIL') ..
104*4f1223e8SApple OSS Distributions      '.status'
105*4f1223e8SApple OSS Distributions  write_file(status_path, '')
106*4f1223e8SApple OSS Distributionsend
107*4f1223e8SApple OSS Distributions
108*4f1223e8SApple OSS Distributionslocal function write_result_bundle(data, dir)
109*4f1223e8SApple OSS Distributions  lfs.mkdir(dir)
110*4f1223e8SApple OSS Distributions  local config = data.xnupost_testconfig
111*4f1223e8SApple OSS Distributions  for _, test in ipairs(config.xnupost_test_config) do
112*4f1223e8SApple OSS Distributions    write_test_result(config, test, dir)
113*4f1223e8SApple OSS Distributions  end
114*4f1223e8SApple OSS Distributionsend
115*4f1223e8SApple OSS Distributions
116*4f1223e8SApple OSS Distributionsexport:action(function (args)
117*4f1223e8SApple OSS Distributions  local dir = args.output
118*4f1223e8SApple OSS Distributions  if lfs.attributes(dir, 'mode') ~= 'directory' then
119*4f1223e8SApple OSS Distributions    io.stderr:write(dir, ': output path must be a directory\n')
120*4f1223e8SApple OSS Distributions    os.exit(1)
121*4f1223e8SApple OSS Distributions  end
122*4f1223e8SApple OSS Distributions
123*4f1223e8SApple OSS Distributions  local raw_data, err = sysctl('debug.xnupost_get_tests')
124*4f1223e8SApple OSS Distributions  if not raw_data then
125*4f1223e8SApple OSS Distributions    io.stderr:write('error: failed to retrieve test data from kernel: ', err,
126*4f1223e8SApple OSS Distributions        '\n')
127*4f1223e8SApple OSS Distributions    os.exit(1)
128*4f1223e8SApple OSS Distributions  end
129*4f1223e8SApple OSS Distributions
130*4f1223e8SApple OSS Distributions  if args.format == FMT_RAW then
131*4f1223e8SApple OSS Distributions    write_file(dir .. '/xnupost.kcdata', raw_data)
132*4f1223e8SApple OSS Distributions  elseif args.format == FMT_PLIST then
133*4f1223e8SApple OSS Distributions    local tbl_data
134*4f1223e8SApple OSS Distributions    tbl_data, err = kcdata.decode(raw_data)
135*4f1223e8SApple OSS Distributions    if not tbl_data then
136*4f1223e8SApple OSS Distributions      io.stderr:write('error: failed to deserialize kernel data: ', err, '\n')
137*4f1223e8SApple OSS Distributions      os.exit(1)
138*4f1223e8SApple OSS Distributions    end
139*4f1223e8SApple OSS Distributions    local data
140*4f1223e8SApple OSS Distributions    data, err = plist.encode(tbl_data, 'xml')
141*4f1223e8SApple OSS Distributions    if not data then
142*4f1223e8SApple OSS Distributions      io.stderr:write('error: failed to serialize kernel data to plist: ', err,
143*4f1223e8SApple OSS Distributions          '\n')
144*4f1223e8SApple OSS Distributions      os.exit(1)
145*4f1223e8SApple OSS Distributions    end
146*4f1223e8SApple OSS Distributions    write_file(dir .. '/xnupost.plist', data)
147*4f1223e8SApple OSS Distributions  elseif args.format == FMT_BUNDLE then
148*4f1223e8SApple OSS Distributions    local tbl_data
149*4f1223e8SApple OSS Distributions    tbl_data, err = kcdata.decode(raw_data)
150*4f1223e8SApple OSS Distributions    if not tbl_data then
151*4f1223e8SApple OSS Distributions      io.stderr:write('error: failed to deserialize kernel data: ', err, '\n')
152*4f1223e8SApple OSS Distributions      os.exit(1)
153*4f1223e8SApple OSS Distributions    end
154*4f1223e8SApple OSS Distributions    write_result_bundle(tbl_data, dir .. '/xnupost')
155*4f1223e8SApple OSS Distributions  end
156*4f1223e8SApple OSS Distributionsend)
157*4f1223e8SApple OSS Distributions
158*4f1223e8SApple OSS Distributionsparser:parse(arg)
159