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