diff --git a/case-lib/lib.sh b/case-lib/lib.sh index de53bc2c..d2e7d00a 100644 --- a/case-lib/lib.sh +++ b/case-lib/lib.sh @@ -252,6 +252,14 @@ func_lib_get_tplg_path() return 0 } +func_lib_check_pa() +{ + pactl stat &>/dev/null || { + dloge "pactl stat failed" + return 1 + } +} + die() { dloge "$@" diff --git a/test-case/check-userspace-cardinfo.sh b/test-case/check-userspace-cardinfo.sh new file mode 100755 index 00000000..b74753fc --- /dev/null +++ b/test-case/check-userspace-cardinfo.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +## +## Case Name: check-userspace-cardinfo +## Preconditions: +## N/A +## Description: +## Get the card name from pactl info +## check if there is an available card to do the following tests +## Case step: +## 1. run pactl to get the cards info +## 2. check the card name +## Expect result: +## There is at least one available card for test +## + +set -e + +# shellcheck source=case-lib/lib.sh +source "$(dirname "${BASH_SOURCE[0]}")"/../case-lib/lib.sh + +# check pulseaudio runs properly or not +func_lib_check_pa || die "Please check whether pulseaudio runs correctly or not" + +func_opt_parse_option "$@" + +OLDIFS=$IFS +dlogc "pactl list cards short" +cardlist=$(pactl list cards short) +: $((available_card=0)) +IFS=$'\n' +for card in $cardlist; do + # pactl list cards short format should be like: + # 0 alsa_card.pci-0000_00_1f.3-platform-sof_sdw module-alsa-card.c + dlogi "found card: $(echo "$card" | awk '{print $2}')" + echo "$card" |grep -i "usb" &>/dev/null || : $((available_card++)) +done +IFS=$OLDIFS + +if [ "$available_card" == "0" ]; then + # TODO: do more check to give hint why there is no available card + die "no available card for test" +fi diff --git a/test-case/check-userspace-paplay.sh b/test-case/check-userspace-paplay.sh new file mode 100755 index 00000000..ed354644 --- /dev/null +++ b/test-case/check-userspace-paplay.sh @@ -0,0 +1,113 @@ +#!/bin/bash + +## +## Case Name: check-userspace-paplay +## Preconditions: +## N/A +## Description: +## Go through all the sinks and paplay on the sinks whose +## active port is available (jack connected) or unknown (speaker) +## Case step: +## 1. go through all the sinks +## 2. check the sink's active port is available or not +## 3. paplay on the active port which is available or unknown +## Expect result: +## paplay on the sinks successfully +## + +set -e + +# shellcheck source=case-lib/lib.sh +source "$(dirname "${BASH_SOURCE[0]}")"/../case-lib/lib.sh + +# check pulseaudio runs properly or not +func_lib_check_pa || die "Please check whether pulseaudio runs correctly or not" + +OPT_OPT_lst['r']='round' OPT_DESC_lst['r']='round count' +OPT_PARM_lst['r']=1 OPT_VALUE_lst['r']=3 + +OPT_OPT_lst['d']='duration' OPT_DESC_lst['d']='paplay duration in second' +OPT_PARM_lst['d']=1 OPT_VALUE_lst['d']=8 + +OPT_OPT_lst['f']='file' OPT_DESC_lst['f']='source file path' +OPT_PARM_lst['f']=1 OPT_VALUE_lst['f']='/dev/zero' + +OPT_OPT_lst['F']='format' OPT_DESC_lst['F']='sample format' +OPT_PARM_lst['F']=1 OPT_VALUE_lst['F']=s16le + +OPT_OPT_lst['R']='rate' OPT_DESC_lst['R']='sample rate' +OPT_PARM_lst['R']=1 OPT_VALUE_lst['R']=44100 + +OPT_OPT_lst['C']='channels' OPT_DESC_lst['C']='channels' +OPT_PARM_lst['C']=1 OPT_VALUE_lst['C']=2 + +func_opt_parse_option "$@" + +round_cnt=${OPT_VALUE_lst['r']} +duration=${OPT_VALUE_lst['d']} +file=${OPT_VALUE_lst['f']} +format=${OPT_VALUE_lst['F']} +rate=${OPT_VALUE_lst['R']} +channel=${OPT_VALUE_lst['C']} + +[[ -e $file ]] || { dlogw "$file does not exist, use /dev/zero as dummy playback source" && file=/dev/zero; } + +# TODO: check the parameter is valid or not + +# go through all the sinks +# get all the sinks name +sinkkeys=$(pactlinfo.py --showsinks) +for round in $(seq 1 $round_cnt); do + for i in $sinkkeys; do + sinkcard=$(pactlinfo.py --getsinkcardname "$i") || { + dlogw "failed to get sink $i card_name" + continue + } + + # Let's skip testing on USB card + # TODO: add a list for other skipped cards such as HDA + if echo "$sinkcard" |grep -i "usb" &>/dev/null; then + continue + fi + + # get the sink's active port + actport=$(pactlinfo.py --getsinkactport "$i") || { + dlogw "failed to get sink $i active port" + continue + } + + # get the active port's information + actportvalue=$(pactlinfo.py --getsinkportinfo "$actport") || { + dlogw "failed to get sink $i active port $actport info" + continue + } + + # check the active port is available or not from the port's information + portavailable=$(echo "$actportvalue" |grep "not available") || true + if [ -z "$portavailable" ]; then + # now prepare to paplay on this sink as the active port is not "not available" + # get the sink's name + sinkname=$(pactlinfo.py --getsinkname "$i") + sinkname=$(eval echo "$sinkname") #formalize the sink name + dlogi "===== Testing: (Round: $round/$round_cnt) (sink: $sinkname.$actport) =====" + dlogc "paplay -v --device=$sinkname --raw --rate=$rate --format=$format --channels=$channel $file" + paplay -v --device="$sinkname" --raw --rate=$rate --format=$format --channels=$channel $file & + pid=$! + sleep $duration + # check whether process is still running + count=$(ps -A| grep -c $pid) || true + if [[ $count -eq 0 ]]; then + if wait $pid; then #checks if process executed successfully or not + dlogi "paplay has stopped successfully" + else + die "paplay on $sinkname failed (returned $?)" + fi + else + dlogi "paplay runs successfully" + # kill all paplay process + pkill -9 paplay + sleep 0.5 + fi + fi + done +done diff --git a/test-case/check-userspace-parecord.sh b/test-case/check-userspace-parecord.sh new file mode 100755 index 00000000..e07b7a23 --- /dev/null +++ b/test-case/check-userspace-parecord.sh @@ -0,0 +1,121 @@ +#!/bin/bash + +## +## Case Name: check-userspace-parecord +## Preconditions: +## N/A +## Description: +## Go through all the sources and parecord on the sources whose +## active port is available (jack connected) or unknown (DMIC) +## Case step: +## 1. go through all the sources +## 2. check the source's active port is available or not +## 3. parecord on the active port which is available or unknown +## Expect result: +## parecord on the source successfully +## + +set -e + +# shellcheck source=case-lib/lib.sh +source "$(dirname "${BASH_SOURCE[0]}")"/../case-lib/lib.sh + +# check pulseaudio runs properly or not +func_lib_check_pa || die "Please check whether pulseaudio runs correctly or not" + +OPT_OPT_lst['r']='round' OPT_DESC_lst['r']='round count' +OPT_PARM_lst['r']=1 OPT_VALUE_lst['r']=3 + +OPT_OPT_lst['d']='duration' OPT_DESC_lst['d']='parecord duration in second' +OPT_PARM_lst['d']=1 OPT_VALUE_lst['d']=8 + +OPT_OPT_lst['f']='file' OPT_DESC_lst['f']='source file path' +OPT_PARM_lst['f']=1 OPT_VALUE_lst['f']='/dev/zero' + +OPT_OPT_lst['F']='format' OPT_DESC_lst['F']='sample format' +OPT_PARM_lst['F']=1 OPT_VALUE_lst['F']=s16le + +OPT_OPT_lst['R']='rate' OPT_DESC_lst['R']='sample rate' +OPT_PARM_lst['R']=1 OPT_VALUE_lst['R']=44100 + +OPT_OPT_lst['C']='channels' OPT_DESC_lst['C']='channels' +OPT_PARM_lst['C']=1 OPT_VALUE_lst['C']=2 + +func_opt_parse_option "$@" + +round_cnt=${OPT_VALUE_lst['r']} +duration=${OPT_VALUE_lst['d']} +file=${OPT_VALUE_lst['f']} +format=${OPT_VALUE_lst['F']} +rate=${OPT_VALUE_lst['R']} +channel=${OPT_VALUE_lst['C']} + +[[ -e $file ]] || { dlogw "$file does not exist, use /dev/zero as dummy playback source" && file=/dev/null; } + +# TODO: check the parameter is valid or not + +# go through all the sources +# get all the sources name +sourcekeys=$(pactlinfo.py --showsources) +for round in $(seq 1 $round_cnt); do + for i in $sourcekeys; do + sourcecard=$(pactlinfo.py --getsourcecardname "$i") || { + dlogw "failed to get source $i card_name" + continue + } + + # Let's skip testing on USB card + # TODO: add a list for other skipped cards such as HDA + if echo "$sourcecard" |grep -i "usb" &>/dev/null; then + continue + fi + + sourceclass=$(pactlinfo.py --getsourcedeviceclass "$i") || { + dlogw "failed to get source $i device class" + continue + } + # Let's skip the monitor sources + if echo "$sourceclass" |grep "monitor" &>/dev/null; then + continue + fi + + # get the source's active port + actport=$(pactlinfo.py --getsourceactport "$i") || { + dlogw "failed to get source $i active port" + continue + } + # get the active port's information + actportvalue=$(pactlinfo.py --getsourceportinfo "$actport") || { + dlogw "failed to get source $i active port $actport info" + continue + } + # check the active port is available or not from the port's information + portavailable=$(echo "$actportvalue" |grep "not available") || true + if [ -z "$portavailable" ]; then + # now prepare to parecord on this source as the active port is not "not available" + # get the source's name + sourcename=$(pactlinfo.py --getsourcename "$i") + sourcename=$(eval echo "$sourcename") + dlogi "===== Testing: (Round: $round/$round_cnt) (source: $sourcename.$actport) =====" + dlogc "parecord -v --device=$sourcename --raw --rate=$rate --format=$format --channels=$channel $file" + parecord -v --device="$sourcename" --raw --rate=$rate --format=$format --channels=$channel $file & + pid=$! + sleep $duration + # check whether process is still running + count=$(ps -A| grep -c $pid) || true + if [[ $count -eq 0 ]]; then + if wait $pid; then #checks if process executed successfully or not + die "parecord has stopped successfully, which is abnormal" + else + die "parecord on $sourcename failed (returned $?)" + fi + else + dlogi "parecord runs successfully" + # kill all parecord processes + pkill -9 parecord >/dev/null + sleep 0.5 + fi + fi + + done +done diff --git a/tools/pactlinfo.py b/tools/pactlinfo.py new file mode 100755 index 00000000..128ba811 --- /dev/null +++ b/tools/pactlinfo.py @@ -0,0 +1,153 @@ +#!/usr/bin/python3 + +import re +import os +import argparse + +# get the specified sink information from "pactl list sink" +def get_sink(sinkname): + sinks = os.popen("pactl list sinks").read() + sink="" + start = 0 + for line in sinks.splitlines(): + # sink information starts with "Sink #n" + m = re.match('^Sink #(\d+)', line) + if m: + m1 = re.match('^Sink #'+sinkname, line) + if m1: + start = 1 + else: + start = 0 + else: + if start == 1: + sink += line+'\n' + return sink + +# get the specified source information from "pactl list source" +def get_source(sourcename): + sources = os.popen("pactl list sources").read() + source="" + start = 0 + for line in sources.splitlines(): + # source information starts with "Source #n" + m = re.match('^Source #(\d+)', line) + if m: + m1 = re.match('^Source #'+sourcename, line) + if m1: + start = 1 + else: + start = 0 + else: + if start == 1: + source += line+'\n' + return source + +# get the value of the key in the bulk +# For example, the bulk is the information of a sink, which +# includes "State", "Name", "Driver" and etc information. +# get_value(bulk, "Name: ") will get the name information. +# The "Name: " is the key word format, please refer 'pactl list' for +# more keys format. +# TODO: some informations are splitted into multiple lines. Need to handle +# this situation if necessary. +def get_value(bulk, key): + # refine the key to handle the special characters + schar='\[@_!#$%^&*()<>?/|}{~]' + for char in schar: + key=key.replace(char, "\\"+char) + + res = re.findall(key+".*", bulk) + if res: + print (re.match("("+key+")(.*)", res[0]).groups(1)[1]) + return(0) + else: + return(1) + +'''test Main''' +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='parse pactl list information', + add_help=True, formatter_class=argparse.RawTextHelpFormatter) + parser.add_argument('--showsinks', action='store_true', default=False, help='show all sinks') + parser.add_argument('--showsources', action='store_true', default=False, help='show all sources') + parser.add_argument('--getsinkname', type=int, help='get the appointed sink Name') + parser.add_argument('--getsourcename', type=int, help='get the appointed source Name') + parser.add_argument('--getsinkcardname', type=int, help='get the card_name of appointed sink') + parser.add_argument('--getsourcecardname', type=int, help='get the card_name of appointed source') + parser.add_argument('--getsinkdeviceclass', type=int, help='get the appointed sink device.class') + parser.add_argument('--getsourcedeviceclass', type=int, help='get the appointed source device.class') + parser.add_argument('--getsinkactport', type=int, help='get the appointed sink active port name') + parser.add_argument('--getsourceactport', type=int, help='get the appointed source active port name') + parser.add_argument('--getsinkportinfo', help='get the appointed sink port information') + parser.add_argument('--getsourceportinfo', help='get the appointed source port information') + parser.add_argument('--version', action='version', version='%(prog)s 1.0') + + ret_args = vars(parser.parse_args()) + + if ret_args['showsinks'] is True: + sinks = os.popen("pactl list short sinks").read() + for line in sinks.splitlines(): + print (re.match("(\d+)(.*)", line).groups(1)[0]) + exit(0) + + if ret_args['showsources'] is True: + sources = os.popen("pactl list short sources").read() + for line in sources.splitlines(): + print (re.match("(\d+)(.*)", line).groups(1)[0]) + exit(0) + + if ret_args.get('getsinkname') is not None: + sink = ret_args['getsinkname'] + sinkinfo = get_sink(str(sink)) + exit (get_value(sinkinfo, "Name: ")) + + if ret_args.get('getsourcename') is not None: + source = ret_args['getsourcename'] + sourceinfo = get_source(str(source)) + exit (get_value(sourceinfo, "Name: ")) + + if ret_args.get('getsinkcardname') is not None: + sink = ret_args['getsinkcardname'] + sinkinfo = get_sink(str(sink)) + exit (get_value(sinkinfo, "alsa.card_name = ")) + + if ret_args.get('getsourcecardname') is not None: + source = ret_args['getsourcecardname'] + sourceinfo = get_source(str(source)) + exit (get_value(sourceinfo, "alsa.card_name = ")) + + if ret_args.get('getsinkdeviceclass') is not None: + sink = ret_args['getsinkdeviceclass'] + sinkinfo = get_sink(str(sink)) + exit (get_value(sinkinfo, "device.class = ")) + + if ret_args.get('getsourcedeviceclass') is not None: + source = ret_args['getsourcedeviceclass'] + sourceinfo = get_source(str(source)) + exit (get_value(sourceinfo, "device.class = ")) + + if ret_args.get('getsinkactport') is not None: + sink = ret_args['getsinkactport'] + sinkinfo = get_sink(str(sink)) + exit (get_value(sinkinfo, "Active Port: ")) + + if ret_args.get('getsourceactport') is not None: + source = ret_args['getsourceactport'] + sourceinfo = get_source(str(source)) + exit (get_value(sourceinfo, "Active Port: ")) + + if ret_args.get('getsinkportinfo') is not None: + port = ret_args['getsinkportinfo'] + # get all the sinks + # TODO: skip the sink ports which belong to unsupported card + sinkinfo = get_sink(".*") + exit (get_value(sinkinfo, port+": ")) + + + if ret_args.get('getsourceportinfo') is not None: + port = ret_args['getsourceportinfo'] + # get all the sources + # TODO: skip the source ports which belong to unsupported card + sourceinfo = get_source(".*") + exit (get_value(sourceinfo, port+": ")) + + exit(0)