forked from thesofproject/sof-test
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcheck-alsa-conformance.sh
More file actions
executable file
·358 lines (294 loc) · 10.6 KB
/
check-alsa-conformance.sh
File metadata and controls
executable file
·358 lines (294 loc) · 10.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
#!/bin/bash
# Copyright(c) 2025 Intel Corporation.
# SPDX-License-Identifier: BSD-3-Clause
##
## Case Name: Execute ALSA conformance tests.
##
## Preconditions:
## - ChromeOS Audio Test package is installed
## https://chromium.googlesource.com/chromiumos/platform/audiotest
##
## Description:
## Run `alsa_conformance_test.py` for the playback devices
## and the capture devices with the test suite paramenters given.
## Compose resulting JSON reports.
##
## To select PCMs use either -d, or -p with or without -c parameters.
## If a PCM id has no device id (e.g. 'hw:sofnocodec' instead of 'hw:sofnocodec,0')
## then all devices on that card will be selected for the test run.
## To select all available PCMs omit any -d, -p, -c parameters.
##
## Pass multiple values of the test parameters -d, -p, -c, -r, -F enclosing them
## in quotes, eg. `-F 'U8 S16_LE'` or `-p 'sofnocodec,1 sofnocodec,2'`
##
## Case steps:
## 0. Set ALSA parameters.
## 1. For each PCM selected:
## 1.1 Try to start `alsa_conformance_test` in device info mode.
## 1.2 Start `alsa conformance_test.py` for playback devices.
## 1.3 Start `alsa conformance_test.py` for capture devices.
## 2. Compose the resulting JSON report.
##
## Expect result:
## ALSA conformance results collected and saved in `test_result.json` file.
## Exit status 0.
## In case of errors this test tries to continue and have its JSON report correctly structured.
##
TESTDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
TESTLIB="${TESTDIR}/case-lib"
# shellcheck source=case-lib/lib.sh
source "${TESTLIB}/lib.sh"
OPT_NAME['d']='device' OPT_DESC['d']='ALSA pcm device for playback and capture. Example: hw:0'
OPT_HAS_ARG['d']=1 OPT_VAL['d']=''
OPT_NAME['p']='pcm_p' OPT_DESC['p']='ALSA pcm device for playback only. Example: hw:soundwire,0'
OPT_HAS_ARG['p']=1 OPT_VAL['p']=''
OPT_NAME['c']='pcm_c' OPT_DESC['c']='ALSA pcm device for capture only. Example: hw:soundwire,1'
OPT_HAS_ARG['c']=1 OPT_VAL['c']=''
OPT_NAME['r']='rates' OPT_DESC['r']='Sample ratis to try. Default: check all available rates.'
OPT_HAS_ARG['r']=1 OPT_VAL['r']=''
OPT_NAME['F']='formats' OPT_DESC['F']='Data formats to try. Default: check all available formats.'
OPT_HAS_ARG['F']=1 OPT_VAL['F']=''
OPT_NAME['s']='sof-logger' OPT_DESC['s']="Open sof-logger trace the data will store at $LOG_ROOT"
OPT_HAS_ARG['s']=0 OPT_VAL['s']=1
OPT_NAME['v']='verbose' OPT_DESC['v']='Verbose logging.'
OPT_HAS_ARG['v']=0 OPT_VAL['v']=0
OPT_NAME['E']='rate-diff' OPT_DESC['E']="ALSA conformance --rate-criteria-diff-pct (difference, %)."
OPT_HAS_ARG['E']=1 OPT_VAL['E']=''
OPT_NAME['e']='rate-err' OPT_DESC['e']="ALSA conformance --rate-err-criteria (max rate error)."
OPT_HAS_ARG['e']=1 OPT_VAL['e']=''
OPT_NAME['a']='avail-delay' OPT_DESC['a']="ALSA conformance --avail-delay"
OPT_HAS_ARG['a']=0 OPT_VAL['a']=0
OPT_NAME['T']='test-suites' OPT_DESC['T']="ALSA conformance --test-suites (Default: all)."
OPT_HAS_ARG['T']=1 OPT_VAL['T']=''
OPT_NAME['t']='timeout' OPT_DESC['t']="ALSA conformance --timeout (Default: none)."
OPT_HAS_ARG['t']=1 OPT_VAL['t']=''
OPT_NAME['A']='allow-channels' OPT_DESC['A']="ALSA conformance --allow-channels (Default: all)."
OPT_HAS_ARG['A']=1 OPT_VAL['A']=''
OPT_NAME['S']='skip-channels' OPT_DESC['S']="ALSA conformance --skip-channels (Default: none skipped)."
OPT_HAS_ARG['S']=1 OPT_VAL['S']=''
func_opt_parse_option "$@"
# Options for the ALSA conformance test script call
CMD_OPTS=()
# Recompose OPT_VAL[$1] option as ALSA test script option $2
add_cmd_option()
{
local opt_val="${OPT_VAL[$1]}"
local prefix=$2
if [ -n "${opt_val}" ]; then
# Split list parameters to separate values
opt_val=("${opt_val//[ ,]/ }")
# shellcheck disable=SC2206
CMD_OPTS+=("${prefix}" ${opt_val[@]})
fi
}
init_globals()
{
add_cmd_option 'r' '--allow-rates'
add_cmd_option 'F' '--allow-formats'
add_cmd_option 'E' '--rate-criteria-diff-pct'
add_cmd_option 'e' '--rate-err-criteria'
add_cmd_option 't' '--timeout'
add_cmd_option 'T' '--test-suites'
add_cmd_option 'A' '--allow-channels'
add_cmd_option 'S' '--skip-channels'
run_verbose=0
if [[ "${OPT_VAL['v']}" -eq 1 ]]; then
run_verbose=1
CMD_OPTS+=("--log-file" "/dev/stdout")
fi
if [[ "${OPT_VAL['a']}" -eq 1 ]]; then
CMD_OPTS+=('--avail-delay')
fi
AUDIOTEST_OUT="${LOG_ROOT}/alsa_conformance"
RESULT_JSON="${LOG_ROOT}/test_result.json"
ALSA_CONFORMANCE_PATH=$([ -n "$ALSA_CONFORMANCE_PATH" ] || realpath "${TESTDIR}/../audiotest")
ALSA_CONFORMANCE_TEST="${ALSA_CONFORMANCE_PATH}/alsa_conformance_test"
}
check_alsa_conformance_suite()
{
if [ -d "${ALSA_CONFORMANCE_PATH}" ]; then
if [ -x "${ALSA_CONFORMANCE_TEST}" ] && [ -x "${ALSA_CONFORMANCE_TEST}.py" ]; then
dlogi "Use ALSA conformance test suite: ${ALSA_CONFORMANCE_TEST}"
return
fi
fi
skip_test "ALSA conformance test suite is missing at: ${ALSA_CONFORMANCE_PATH}"
}
# Returns the PCM's full id if it is found as playback or capture device.
# If only card id is given, then all its devices will be returned.
# Empty output if the device is not found.
get_card_devices()
{
local mode=$1
local arg_pcm=$2
# select all devices by default
[ -z "${arg_pcm}" ] && arg_pcm="[^ ]+"
local alsa_list=''
local res_devs=("${arg_pcm}")
if [ "${mode}" == 'playback' ]; then
alsa_list=('aplay' '-l')
elif [ "${mode}" == 'capture' ]; then
alsa_list=('arecord' '-l')
else
return
fi
if [ -n "${arg_pcm}" ]; then
# check is only card name is given or exact device
if [ "${arg_pcm}" == "${arg_pcm##*,}" ]; then
# strip 'hw:' prefix
arg_pcm="${arg_pcm#*:}"
# shellcheck disable=SC2016
local gawk_script='match($0, /^card [0-9]+: ('"${arg_pcm}"') .+ device ([0-9]+): /, arr) { print "hw:" arr[1] "," arr[2] }'
mapfile -t res_devs < <( "${alsa_list[@]}" | gawk "${gawk_script}" )
fi
printf '%s\n' "${res_devs[@]}"
fi
}
select_PCMs()
{
# Don't quote to split into separate items:
# shellcheck disable=SC2206
alsa_device=(${OPT_VAL['d']//[ ]/ })
# shellcheck disable=SC2206
pcm_p=(${OPT_VAL['p']//[ ]/ })
# shellcheck disable=SC2206
pcm_c=(${OPT_VAL['c']//[ ]/ })
if [ -n "${alsa_device[*]}" ]; then
if [ -n "${pcm_p[*]}" ] || [ -n "${pcm_c[*]}" ]; then
die "Give either an ALSA device (-d), or ALSA playback(-p) and/or capture(-c) PCMs."
fi
# we got only -d
pcm_p=("${alsa_device[@]}")
pcm_c=("${alsa_device[@]}")
elif [ -z "${pcm_p[*]}" ] && [ -z "${pcm_c[*]}" ]; then
dlogi "No ALSA PCM is specified - scan all playback and capture devices"
pcm_p=('')
pcm_c=('')
fi
dlogi "pcm_p=(${pcm_p[*]})"
dlogi "pcm_c=(${pcm_c[*]})"
local p_dev_expanded=()
PLAYBACK_DEVICES=()
for p_dev in "${pcm_p[@]}"
do
mapfile -t p_dev_expanded < <(get_card_devices 'playback' "${p_dev}")
PLAYBACK_DEVICES+=( "${p_dev_expanded[@]}" )
done
dlogi "Playback devices: ${PLAYBACK_DEVICES[*]}"
CAPTURE_DEVICES=()
for c_dev in "${pcm_c[@]}"
do
mapfile -t p_dev_expanded < <(get_card_devices 'capture' "${c_dev}")
CAPTURE_DEVICES+=( "${p_dev_expanded[@]}" )
done
dlogi "Capture devices: ${CAPTURE_DEVICES[*]}"
}
alsa_conformance_device_info()
{
local mode=$1
local device=$2
local opt=()
[ "${mode}" == 'playback' ] && opt=("-P" "${device}")
[ "${mode}" == 'capture' ] && opt=("-C" "${device}")
[ -z "${opt[*]}" ] && die "No ALSA PCM parameter."
local run_cmd=("${ALSA_CONFORMANCE_TEST}" "${opt[@]}" "--dev_info_only")
dlogc "${run_cmd[@]}"
local rc=0
"${run_cmd[@]}" || rc=$?
[[ "${rc}" -ne 0 ]] && dloge "Failed to get device info, rc=${rc}"
}
alsa_conformance_test()
{
local mode=$1
local device=$2
local opt=()
[ "${mode}" == 'playback' ] && opt=("-P" "${device}")
[ "${mode}" == 'capture' ] && opt=("-C" "${device}")
[ -z "${opt[*]}" ] && die "No ALSA PCM parameter."
local run_prefix=("export" "PATH=${ALSA_CONFORMANCE_PATH}:${PATH}")
local run_cmd=()
run_cmd+=("${ALSA_CONFORMANCE_TEST}.py" "${CMD_OPTS[@]}" "${opt[@]}")
run_cmd+=("--json-file" "${AUDIOTEST_OUT}_${mode}.json")
dlogc "${run_cmd[@]}"
local rc=0
"${run_prefix[@]}" && "${run_cmd[@]}" || rc=$?
[[ "${rc}" -ne 0 ]] && dloge "Failed ${mode} tests, rc=${rc}"
}
report_start()
{
dlogi "Compose ${RESULT_JSON}"
printf '{"options":{%s}, "alsa_conformance":[' "$(options2json)" > "${RESULT_JSON}"
}
json_next_sep=""
report_conformance()
{
local report_type=$1
local report_device=$2
local report_file="${AUDIOTEST_OUT}_${report_type}.json"
if [ -s "${report_file}" ]; then
printf '%s{"device":"%s","%s":' \
"${json_next_sep}" "${report_device}" "${report_type}" >> "${RESULT_JSON}"
jq --compact-output . "${report_file}" >> "${RESULT_JSON}" && rm "${report_file}"
printf '}' >> "${RESULT_JSON}"
json_next_sep=","
else
dlogw "No conformance report for ${report_type}"
fi
}
report_end()
{
printf ']}\n' >> "${RESULT_JSON}"
[[ "${run_verbose}" -ne 0 ]] && cat "${RESULT_JSON}"
}
assert_failures()
{
local report_type=$1
[ -z "${report_type}" ] && return
local report_key="alsa_conformance[].${report_type}"
local failures=""
failures=$(jq "[.${report_key}.fail // 0] | add" "${RESULT_JSON}")
if [ -z "${failures}" ] || [ "${failures}" -ne "${failures}" ]; then
die "${report_type} has invalid ${RESULT_JSON}"
fi
if [ "${failures}" -ne 0 ]; then
die "${report_type} has ${failures} failures."
fi
# we must have something reported as passed, even zero
passes=$(jq "[.${report_key}.pass] | add // empty" "${RESULT_JSON}")
if [ -z "${passes}" ] || [ "${passes}" -ne "${passes}" ]; then
die "${report_type} has no results."
fi
}
run_test()
{
local t_mode=$1
local t_dev=$2
dlogi "Test ${t_mode} ${t_dev}"
alsa_conformance_device_info "${t_mode}" "${t_dev}"
alsa_conformance_test "${t_mode}" "${t_dev}"
report_conformance "${t_mode}" "${t_dev}"
}
main()
{
init_globals
start_test
check_alsa_conformance_suite
select_PCMs
logger_disabled || func_lib_start_log_collect
set_alsa
report_start
for p_dev in "${PLAYBACK_DEVICES[@]}"
do
run_test 'playback' "${p_dev}"
done
for c_dev in "${CAPTURE_DEVICES[@]}"
do
run_test 'capture' "${c_dev}"
done
report_end
[ -n "${PLAYBACK_DEVICES[*]}" ] && assert_failures 'playback'
[ -n "${CAPTURE_DEVICES[*]}" ] && assert_failures 'capture'
}
{
main "$@"; exit "$?"
}