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