Project restart

This commit is contained in:
2025-12-29 10:46:05 +01:00
commit 59d19cb48c
388 changed files with 48020 additions and 0 deletions

28
.gitignore vendored Normal file
View File

@@ -0,0 +1,28 @@
*__pycache__
build
dist
*.egg-info/
.env
/.project
/.pydevproject
/.vscode
/.venv
crash.tx*
report_test.tx*
*.autosave
*.swp
.buildinfo
_autosummary/
test/venv
test/tmp/*
test/validation/tmp
!test/tmp/.gitkeep
package/appimage/appimage-build*
package/appimage/*.zsync
package/appimage/*.AppImage
package/appimage/src
package/appimage/*.py
AppDir
doc/manual/doxygen
doc/manual/sphinx/build/*
doc/manual/sphinx/source/_build/*

247
README.md Normal file
View File

@@ -0,0 +1,247 @@
# Documentation
[See here](doc/manual/testium_manual.pdf).
# Installation
## Installation from local pypi repository
### Virtualenv
It is strongly recommended to create a python virtual environment to be able to install testium with pip.
This method is also required for git sources install and debug.
#### Virtualenv setup
Creation of the python virtual environment:
python3 -m venv <my_venv_dir>/<my_python_venv>
Each time it is needed to enter the virtual environment, just execute:
source <my_venv_dir>/<my_python_venv>/bin/activate
this line can also be inserted in the `.bashrc` to be automatically called in a linux terminal.
It is possible to configure the *code* IDE to use this virtual environment by setting it
in the preferences: "File->Settings", search "venv", then setup the virtual env.
And when properly set, you can select the interpreter from your newly created venv.
### install testium
From the python virtual environment run:
pip install testium
all the dependencies are automatically installed in the virtual env.
### run testium
From the python virtual environment just run:
python -m testium
or simply
testium
## Installation from sources
The python virtual environment should be installed first (see above).
### Requirements
In the virtual environment, the following modules must be installed:
* pyside6
* pyserial
* pyyaml
* pexpect
* gitpython
* jinja2
* colorama
* matplotlib
* junit-xml
* lxml
A `requirements.txt` file is also available in the git repository in the path `testium/src/`.
### Git repository
Clone testium from the company's git repository.
### Tagged version
In the case testium must be executed at a given release, the tagged version
is expected.
To know the tags which exist for the software, just execute the following command in the `testium` directory:
$ git tag --list
Then the list of tags is displayed.
To switch to the considered tag, execute the following commands:
$ git checkout <tag_name>
If you want to be sure that you're on the right tag, just execute:
$ git status
And the console may return:
HEAD detached at <tag_name>
nothing to commit, working tree clean
$
### Execution from sources
**Windows**
$ python.exe <path_to_testium>\src\testium
**Linux**
$ python <path_to_testium>/src/testium
# Documentation generation
This section describes how to generate the documentation.
The testium's user's manual is genearted with the help of the sphinx
framework.
## Install sphinx
pip install sphinx
## Generate the doc
Execute
doc/manual/sphinx/./build_doc.sh
This command works if texlive package has been installed on the system. It can be done by invoking the following command.
sudo apt install texlive-full
# QT GUI
## QT GUI modification
Open the ".ui" file with `qtcreator` and modify the gui. Then regenerate the python code.
On linux, a helper script has been created:
scripts/./qt_generate.sh
# Debugging
In order to debug testium or your python script executed within testium.
## In VSCODE
This is the prefered method :
1. Create a debug configuration like the following:
```
"configurations": [
{
"name": "Python : testium",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/src/testium",
"console": "integratedTerminal",
"args": ["-g"],
"justMyCode": true
},
]
```
2. Install debugpy module in python
python -m pip install debugpy
3. Then got to the "RUN AND DEBUG" tab and press the play button.
4. A testium window will pops up ; start execution of your tum.
5. Do not forget to put breakpoints where you want to investigate.
## Icons
Icons are coming from the following site: https://github.com/free-icons/free-icons.git
# testium Release
## Pre-requisite
A `python` virtual environment must have been set as described above.
### Install appimage-builder
Install `appimage-builder` package using pip.
### Install pyinstaller
Install `pyinstaller` package using pip.
## Generate the binary package
The procedure for a binary release is as follows:
1. update the `release_note.txt` file
2. modify the version in `src/VERSION` file
3. be sure that the documentation is up to date, and if not execute `doc/manual/sphinx/build_doc.sh` script
4. push modifications and create a tag with the new version on the git repository
5. generate an appimage by calling `package/appimage/./build.sh`
6. generate an executable file by calling `package/pyinstaller/./build.sh`
7. run the complete validation test for each generated binary
8. check that all the validation results are OK
9. On artifactory add the following files to a new testium version:
* release note
* testium binary(ies)
* testium user's manual
* validation results
# Troubleshooting
## The testium exe crashes `wl_proxy_marshal_flags`
### Error message
/testium: symbol lookup error: /tmp/_MEIOhDCPF/libQt6WaylandClient.so.6: undefined symbol: wl_proxy_marshal_flags
### Solution
Set the appropriate environment variable
export QT_QPA_PLATFORM=xcb
testium
## xcb plugin missing
### Error message
qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found.
### Solution
A package is missing
sudo apt install libxcb-cursor0
sudo apt-get install libicu-dev
sudo apt-get install libxcb-cursor-dev
## The testium appimage crashes when opening a file
This is usually because wayland is defined as the default X server.
To change it :
* Disable Wayland by uncommenting WaylandEnable=false in the `/etc/gdm3/daemon.conf`
* Add `QT_QPA_PLATFORM=xcb` in `/etc/environment`
* After a reboot, check that the environment variable value returns `x11`:
$ echo $XDG_SESSION_TYPE
x11

View File

@@ -0,0 +1,4 @@
- unittest_file:
name: Test 5
test_file: dummy.py

View File

@@ -0,0 +1,16 @@
sequence: &endurance_test
!include endurance.tum
sequence:
- unittest_file:
name: Test 3
test_file: dummy.py
test_method: test_01_pass
- loop:
iterator: 10
steps:
*endurance_test
- unittest_file:
name: Test 4
test_file: dummy.py

View File

@@ -0,0 +1,10 @@
def setTemperature(param):
print('Tempe set : %s'%param)
def temperatureAtteinte(param):
if int(param) > 50:
return True
else:
return False

View File

@@ -0,0 +1,40 @@
import unittest
from time import sleep
def donothing():
return 0
class DummyTests(unittest.TestCase):
@unittest.skip("test skipped")
def test_00_skipped(self):
''' Test 00 is skipped
'''
sleep(0.5)
def test_01_pass(self):
''' Test 01 is passed and adds a report key
'''
self.reported_values['key reported']= 'value_reported'
sleep(0.5)
def test_02_pass(self):
''' Test 02 is passed and adds a report key
'''
self.reported_values['key reported']= 'toto'
sleep(0.5)
def test_03_fail(self):
''' Test 03 is fail by unittest method
'''
sleep(0.5)
self.fail(msg='Fail message')
def test_04_disabled(self):
''' Test 04 is disabled
'''
sleep(0.5)
def test_05_crash(self):
''' Test 05 crashes
'''
print(crash)

View File

@@ -0,0 +1,51 @@
sequence: &temperature_step_sequence
!include tum/temperature.tum
main:
name: Test Sample number one
version: 0.1
steps:
- dialog_references:
name: Ask for reference
question: Please give the reference of the product
reference:
- $(reference_1)
- $(reference_2)
report_show_success: true
- unittest_file:
name: Test 1
test_file: dummy.py
doc: |
Ceci est le test n°1
Voilà...
- sleep:
{name: Sleep between one and two, timeout: 10, dialog: true}
- unittest_file:
{name: Test 2, test_file: dummy.py,execute_on_stop: true}
- loop:
name: Cycle Temperature
iterator : [10,20]
steps:
- py_func:
name: set temperature
file: cycle_temperature.py
func_name: setTemperature
param : $(loop_param)
- *temperature_step_sequence
- py_func:
name: temperature reached
file: cycle_temperature.py
func_name: temperatureAtteinte
param : $(loop_param)
####### REPORT ######
report:
module: test_report_text.py
class: TestReportTxt
title: My Dummy Tests
headers: False
banners: False

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" ?>
<root>
<parameter name="func_param" value="param"/>
</root>

View File

@@ -0,0 +1,13 @@
import random
import libs.testium as tm
from libs.testium import FunctionItem
def random_value():
return random.random()
class LastValues(FunctionItem):
def exec(self, name):
res = tm.last_plot_value(name)
self.reportValue('my_reported_value', res)
print("Last plot value: {}".format(res))
return res

View File

@@ -0,0 +1,10 @@
import libs.testium as tm
def post_exec():
print('Success !!!!')
print(str(tm.gd('test_outputs')))
def post_exec_fail():
print('Failure :(')
print(str(tm.gd('test_outputs')))

View File

@@ -0,0 +1,18 @@
import unittest
class TestStringMethods(unittest.TestCase):
def test_upper(self):
self.assertEqual('foo'.upper(), 'FOO')
def test_isupper(self):
self.assertTrue('FOO'.isupper())
self.assertFalse('Foo'.isupper())
def test_split(self):
s = 'hello world'
self.assertEqual(s.split(), ['hello', 'world'])
# check that s.split fails when the separator is not a string
with self.assertRaises(TypeError):
s.split(2)

View File

@@ -0,0 +1,110 @@
main:
name: Test conditionals
version: 0.1
stop_on_failure: False
steps:
- group:
name: Set test variables depending on the OS
steps:
- let:
name: Set test variables for Linux
condition: "'$(os)' == 'Linux'"
values:
- terminal_prompt: $(linux_prompt)
- let:
name: Set test variables for Windows
condition: "'$(os)' == 'Windows'"
values:
- terminal_prompt: $(windows_prompt)
# This loop illustrate the way to exit the loop upon operator answer
- loop:
doc: This loop illustrate the way to exit the loop upon operator answer.
name: Infine loop with conditional exit
stop_on_failure: False
steps:
- sleep:
name: sleep item
timeout: 5
- dialog_question:
name: Question to continue
question: Answer yes to exit the loop
no_fail: True
exit_condition:
value: "'$(last_test_result)' == 'PASS'"
- let:
name: let
eval:
- conditional_exec: "random.randint(1, 4)"
- console:
name: Console creation
condition: "$(conditional_exec) == 1"
console_name: consname
doc: Opening the console
steps:
- open:
protocol: terminal
terminal_path: $(test_directory)
- console:
name: Console read_until with timeout
condition: "$(conditional_exec) == 1"
console_name: consname
steps:
- read_until: {expected: "$(terminal_prompt)", timeout: 10}
- console:
name: Console write
condition: "$(conditional_exec) == 1"
console_name: consname
steps:
- writeln: echo 0
- sleep:
name: sleep item
condition: "$(conditional_exec) == 1"
timeout: 5
- console:
name: Console read_until immediate
condition: "$(conditional_exec) == 1"
console_name: consname
steps:
- read_until: {expected: "0", timeout: 0}
- console:
name: Console read_until immediate (2)
condition: "$(conditional_exec) == 1"
console_name: consname
steps:
- read_until: {expected: "$(terminal_prompt)", timeout: 0}
- console:
name: Console closure
condition: "$(conditional_exec) == 1"
console_name: consname
steps:
- close: consname
- sleep:
name: sleep item
condition: "$(conditional_exec) == 2"
timeout: 5
- dialog_image:
name: dialog image item
condition: "$(conditional_exec) == 3"
question: click ok if you see the image
filename: image.jpg
- dialog_value:
name: dialog_value item
condition: "$(conditional_exec) == 4"
question: enter something and click ok

View File

@@ -0,0 +1,278 @@
#This suite make the simplest call to each item.
#
main:
name: Test Sample number one
version: 0.1
stop_on_failure: False
steps:
# This loop contains a failing step and exits.
- loop:
doc: This loop contains a failing step and exits.
name: Infine loop step fails
key: report-key-2
stop_on_failure: True
steps:
- unittest_file:
name: unittest item
doc: |
The purpose of this unittest test item is to demonstrate
its various features.
test_file: dummy/dummy.py
test_method: test_01_pass
- py_func:
name: function test item
doc: The purpose of this step is to demonstrate func test item
file: utils.py
func_name: funcToBeExecuted
param:
- 123
- py_func:
name: function test item
doc: |
The purpose of this step is to demonstrate func test item
and the report key feature.
key: report-key-1
file: utils.py
func_name: funcToBeExecuted
param:
- 123
- unittest_file:
name: Unittest item
test_file: dummy/dummy.py
test_method:
- test_04_disabled
- test_03_fail
exit_condition:
file: utils.py
func_name: dummy_exit
# This loop contains a crashing function and exits.
- loop:
doc: This loop contains a crashing function and exits.
name: Infinite loop func crashes
stop_on_failure: True
steps:
- py_func:
name: function crash
file: utils.py
func_name: funcToBeExecuted2
param:
- 123
# This function crashes and does not fail because of expected result.
- py_func:
name: function crash but no fail
file: utils.py
func_name: funcToBeExecuted2
expected_result: FAIL
param:
- 123
- report:
name: Intermediate report
doc: |
The purpose of this step is to demonstrate the report test item
and its various export features.
export:
- junit:
path: $(home)/reports/report-key-1.junit
pattern:
- Unittest%
key: report-key-1
- text:
file_name: report-key-1.txt
path: $(home)/reports
key:
- report-key-1
# This loop contains a crashing unittest step and exits.
- loop:
doc: This loop contains a crashing unittest step and exits.
name: Infine loop unittest step crashes
stop_on_failure: True
steps:
- unittest_file:
name: Unittest item
test_file: dummy/dummy.py
test_method:
- test_05_crash
# This loop is an example of simple iterator.
- !include seq_cycle.tum
# This loop is an example of time exit condition.
- loop:
doc: This loop is an example of time exit condition.
name: Elapsed time exit condition
steps:
- py_func:
name: function test item
file: utils.py
func_name: funcToBeExecuted
param:
- $(loop_param)
- sleep:
name: Sleep
timeout: 0.2
dialog: false
exit_condition:
time: 0.2
# This loop is an example of value exit condition.
- loop:
doc: This loop is an example of value exit condition
name: Loop exits on a condition
steps:
- let:
name: Set a variable to the loop index
values:
- variable: $(loop_index)
- sleep:
name: Sleep
timeout: 0.2
dialog: false
exit_condition:
value: "$(variable) >= 10"
# This loop must fail du to an exception in exit condition.
- loop:
doc: This loop must fail du to an exception in exit condition.
name: Loop exit_condition crash
steps:
- py_func:
name: function test item
file: utils.py
func_name: funcToBeExecuted
param:
- 123
exit_condition:
file: utils.py
func_name: exit_exc
# This loops include complex iterator composed by global variables
- loop:
doc: This loop includes a complex iterator composed by global variables
name: Loop complex iterator
iterator:
- $(global_loop_param_txt)
- $(global_loop_param_num)
- $(global_loop_param_list)
steps:
- loop:
doc: Loop which print the iterators
name: Simple loop
iterator: $(loop_param)
steps:
- py_func:
name: function test item
file: utils.py
func_name: funcToBeExecuted
param:
- $(loop_param)
- sleep:
name: Sleep be
timeout: 0.5
dialog: false
# Loop with function having a number element of a list as expected result
- loop:
doc: Loop with function having a number element of a list as expected result
name: num list loop
iterator: $(global_loop_param_num)
steps:
- py_func:
name: function test item
file: utils.py
func_name: funcToBeExecuted
param:
- $(loop_param)
expected_result: ($(global_loop_param_num))[$(loop_index)]
- sleep:
name: Sleep be
timeout: 0.5
dialog: false
# Loop with function having a text element of a list as expected result
- loop:
doc: Loop with function having a text element of a list as expected result
name: text list loop
iterator: $(global_loop_param_txt)
steps:
- py_func:
name: function test item
file: utils.py
func_name: funcToBeExecuted
param:
- $(loop_param)
expected_result: ($(global_loop_param_txt))[$(loop_index)]
- sleep:
name: Sleep be
timeout: 0.5
dialog: false
# Loop with function having a list element of a list as expected result
- loop:
doc: Loop with function having a list element of a list as expected result
name: list of list loop
iterator: $(global_loop_param_list)
steps:
- py_func:
name: function test item
file: utils.py
func_name: funcToBeExecuted
param:
- $(loop_param)
expected_result: ($(global_loop_param_list))[$(loop_index)]
- sleep:
name: Sleep be
key: report-key-3
timeout: 0.5
dialog: false
# This is an infinite loop to be stopped manually
- loop:
doc: This is an infinite loop to be stopped manually
name: Infinite loop
skipped: True
steps:
- unittest_file:
name: Unittest item
test_file: dummy/dummy.py
test_method: test_01_pass
- sleep:
name: Sleep between one and two
timeout: 0.5
dialog: false
report:
enabled: True
# log_stored: True
export:
- sqlite:
file_name: $(test_name).sqlite
path: $(home)/reports
# - junit:
# file_name: $(test_name)_report-key-1.junit
# path: $(home)/reports
# pattern:
# - function%
# - Unittest%
# key: report-key-1
# - text:
# file_name: $(test_name)_report-key-2_3.txt
# path: $(home)/reports
# pattern: function%
# key:
# - report-key-2
# - report-key-3

View File

@@ -0,0 +1,190 @@
#This suite make the simplest call to each item.
#
config_file:
- param.xml
- param.yaml
- param.json
- $(test_directory)/dummy/param_func.xml
main:
name: Test Sample number one
version: 0.1
steps:
- unittest_file:
name: Unittest item
test_file: dummy/unittest_str.py
doc: Unittest test
- group:
name: Set test variables for Linux
condition: "'$(os)' == 'Linux'"
steps:
- let:
name: Set test variables for Linux
values:
- terminal_prompt: $(linux_prompt)
- group:
name: Set test variables for Windows
condition: "'$(os)' == 'Windows'"
steps:
- let:
name: Set test variables for Windows
values:
- terminal_prompt: $(windows_prompt)
- group:
name: Console test group
steps:
- console:
name: Console creation
console_name: consname
doc: Opening the console
steps:
- open:
protocol: terminal
terminal_path: $(test_directory)
- console:
name: Console read_until with timeout
console_name: consname
steps:
- read_until: {expected: "$(terminal_prompt)", timeout: 10}
- console:
name: Console write
console_name: consname
steps:
- writeln: echo 0
- sleep:
name: sleep item
timeout: 5
- console:
name: Console read_until immediate
console_name: consname
steps:
- read_until: {expected: "0", timeout: 0}
- console:
name: Console read_until immediate (2)
console_name: consname
steps:
- read_until: {expected: "$(terminal_prompt)", timeout: 0}
- console:
name: Console closure
console_name: consname
steps:
- close: consname
- loop:
name: cycle item
iterator : 3
steps:
- unittest_file:
name: Unittest item
test_file: dummy/dummy.py
test_method: test_01_pass
- sleep:
name: sleep item
timeout: 1
- loop:
name: cycle item
iterator : 3
steps:
- unittest_file:
name: Unittest item
test_file: dummy/dummy.py
test_method: test_01_pass
- sleep:
name: sleep item
timeout: 1
- py_func:
name: func item
file: dummy/dummy.py
func_name: donothing
- dialog_choices:
name: Choices
question: Select the items you want
icon: $(test_directory)/../../src/testium/main_win/resources/black/document.png
choices:
- name: choice 1
description: My first choice description
icon: $(test_directory)/../../src/testium/main_win/resources/black/document-save.png
choices:
- name: choice 1.1
description: My choice 1 first subchoice description
icon: $(test_directory)/../../src/testium/main_win/resources/black/Label.png
- name: choice 1.2
description: My choice 1 first subchoice description
- name: choice 2
description: My second choice description
icon: $(test_directory)/../../src/testium/main_win/resources/black/image.png
- name: choice 3
description: My third choice description
icon: $(test_directory)/../../src/testium/main_win/resources/black/image.png
choices:
- name: choice 3.1
description: My choice 3 first subchoice description
- name: choice 3.2
description: My choice 3 second subchoice description
icon: $(test_directory)/../../src/testium/main_win/resources/black/Label.png
- dialog_image:
name: dialog image item
question: click ok if you see the image
filename: image.jpg
- sleep:
name: sleep item
timeout: 3
dialog: true
- git:
name: Testium repo
repo: $(test_directory)
- group:
name: group item
steps:
- sleep:
name: sleep item
timeout: 1
- sleep:
name: sleep item
timeout: 1
- dialog_references:
name: dialog_reference item
question: click ok
# reference:
# - ref 1
# - ref 2/rev
# - ref 3/rev/01
- dialog_value:
name: dialog_value item
question: enter something and click ok
- dialog_message:
name: dialog_message item
question: click ok
- dialog_question:
name: dialog_question item
question: click yes
report:
enabled: True
file_name: $(test_name).rep
path: $(home)/reports
pattern: "Console%"
log_stored: False

View File

@@ -0,0 +1,87 @@
config_file:
- param.yaml
main:
name: Example of plot item usage
version: 0.1
steps:
- console:
name: Creation of the log dir
console_name: console
steps:
- open:
protocol: terminal
terminal_path: $(test_directory)
- writeln: mkdir -p "$(plot_log_path)" && echo Fini
- read_until: {expected: Fini, timeout: 5}
- console:
name: Console closure
execute_on_stop: true
console_name: console
steps:
- close:
- plot:
name: Open the plot
plot_name: Mon Plot
steps:
- open:
log_path: $(plot_log_path)
- plot:
name: Add periodic to the plot
plot_name: Mon Plot
steps:
- periodic:
period: 1
file: $(test_directory)/dummy/plot.py
func_name: random_value
eval: '{"periodic": $(result)}'
- sleep:
name: sleep
dialog: true
timeout: 3
- loop:
name: Add of other data in the plot
iterator: 10
steps:
- plot:
name: Add to the plot
plot_name: Mon Plot
steps:
- add:
value1: $(loop_index)
value2: $(loop_index)+2
- sleep:
name: sleep between values
timeout: 1
- py_func:
name: last plot values
file: $(test_directory)/dummy/plot.py
func_name: LastValues
param:
- Mon Plot
- plot:
name: Export
execute_on_stop: True
plot_name: Mon Plot
steps:
- export: $(plot_log_path)/plot_export.pdf
- export: $(plot_log_path)/plot_export.csv
- plot:
name: Close the plot
execute_on_stop: True
plot_name: Mon Plot
steps:
- close:
wait_dialog_exit: True
timeout: 60

View File

@@ -0,0 +1,11 @@
main:
name: Test run Item
steps:
- run:
name: Execute TUM
tum_file: example_cycle.tum
python_path: python3
testium_path: /home/francois/Projets/testium/src/testium
log_file: $(home)/reports/test.log
report_file: $(home)/reports/test.rep

View File

@@ -0,0 +1,27 @@
#This suite use a sequence as a macro to repeat items
#
sequence: &seq_sleep
!include $(sequence)
main:
name: Test Sample number one
version: 0.1
stop_on_failure: False
steps:
# This loop is an example of value exit condition.
- loop:
doc: This loop is an example of value exit condition
name: Loop exits on a condition
steps:
- let:
name: Set a variable to the loop index
values:
- variable: $(loop_index)
- sleep_timeout: $(loop_index)
- *seq_sleep
exit_condition:
value: "$(variable) >= 3"
- !include {file: seq2.tum, is_dialog: True, sleep_timeout: 12, func_para: truc}

View File

@@ -0,0 +1,18 @@
main:
name: Test conditionals
version: 0.1
stop_on_failure: False
steps:
- py_func:
name: function test item
doc: The purpose of this step is to demonstrate func test item
file: utils.py
func_name: funcToBeExecuted
param:
- 123
- sleep:
name: sleep item
dialog: True
timeout: 3600

View File

@@ -0,0 +1,40 @@
main:
name: Test ssh
version: 0.1
stop_on_failure: False
steps:
- console:
name: Console creation
console_name: consname
doc: Opening the console
steps:
- open:
protocol: ssh
ssh_host: "my_hostname"
ssh_user: "francois"
- console:
name: Console read
console_name: consname
doc: Opening the console
steps:
- writeln: rm -f /tmp/toto.txt && echo done
- read_until: {expected: "done", timeout: 10}
- read_until: {expected: "$ ", timeout: 1}
- writeln: touch /tmp/toto.txt && echo done
- read_until: {expected: "done", timeout: 10}
- read_until: {expected: "$ ", timeout: 1}
- writeln: echo "blablabla" >> /tmp/toto.txt && echo done
- read_until: {expected: "done", timeout: 10}
- read_until: {expected: "$ ", timeout: 1}
- writeln: cat /tmp/toto.txt && echo done
- read_until: {expected: "done", timeout: 10}
- read_until: {expected: "$ ", timeout: 1}
- console:
name: Console deletion
console_name: consname
doc: Closing the console
steps:
- close: consname

View File

@@ -0,0 +1,29 @@
main:
name: Test illustrating string manipulation
version: 0.1
stop_on_failure: False
steps:
- let:
name: Set string value
values:
- rand_text: |
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 002 Device 005: ID 0bda:0487 Realtek Semiconductor Corp. Dell dock
Bus 002 Device 006: ID 0bda:0413 Realtek Semiconductor Corp. Dell dock
Bus 002 Device 007: ID 0bda:8153 Realtek Semiconductor Corp. RTL8153 Gigabit Ethernet Adapter
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 003 Device 003: ID 04f2:b684 Chicony Electronics Co., Ltd Chicony USB2.0 Camera
Bus 003 Device 004: ID 8087:0029 Intel Corp. AX200 Bluetooth
- text_searched: "0bda:8153"
- let:
name: Extract data
eval:
- text_extract: "[l for l in '''$(rand_text)'''.splitlines() if '$(text_searched)' in l][0]"
- dialog_message:
condition: len('$(text_extract)') > 0
name: dialog value test item
question: Tataaaaa !

BIN
doc/examples/image.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

4
doc/examples/param.json Normal file
View File

@@ -0,0 +1,4 @@
{
"global_loop_param_list": [["one", "two", "three"],[1, 2, 3]]
}

9
doc/examples/param.xml Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0" ?>
<root>
<!-- ****************************************************************************
Test configuration
**************************************************************************** -->
<parameter name="windows_prompt" value=">"/>
<parameter name="linux_prompt" value="$"/>
<parameter name="sequence" value="tm.tum"/>
</root>

10
doc/examples/param.yaml Normal file
View File

@@ -0,0 +1,10 @@
#****************************************************************************
# Test configuration
#**************************************************************************** -->
# loops parameters
global_loop_param_txt: ['one', 'two', 'three']
global_loop_param_num: [1, 2, 3]
# Plot parameters
plot_log_path: /tmp/testium_plot/$(testrun_date)/$(testrun_time)/

View File

@@ -0,0 +1,7 @@
import py_func.tm as tm
def post_exec():
print("post_exec PASS")
def post_exec_fail():
print("post_exec_fail PASS")

17
doc/examples/seq.tum Normal file
View File

@@ -0,0 +1,17 @@
# This sequence uses a parameter defined at the upper level in the file including this sequence
- sequence:
- sleep:
name: Sleep
timeout: $(sleep_timeout)
dialog: false
- dialog_message:
name: dialog_message item
question: value is $(sleep_timeout)
- py_func:
name: function test item
file: utils.py
func_name: funcToBeExecuted
param:
- $(loop_param)

16
doc/examples/seq2.tum Normal file
View File

@@ -0,0 +1,16 @@
- sleep:
name: Sleep {{ sleep_timeout }} sec
timeout: {{ sleep_timeout }}
dialog: {{ is_dialog }}
- dialog_message:
name: dialog_message item
question: value is {{ sleep_timeout }}
- py_func:
name: function test item
file: utils.py
func_name: funcToBeExecuted
param:
- {{ func_param }}

View File

@@ -0,0 +1,14 @@
# This loop is an example of simple iterator.
- loop:
doc: This loop is an example of simple iterator.
name: Simple iterator
iterator: 25
steps:
- py_func:
name: function test item
file: utils.py
func_name: funcToBeExecuted
param:
- $(loop_param)
expected_result: $(loop_param)

13
doc/examples/utils.py Normal file
View File

@@ -0,0 +1,13 @@
def dummy_exit(useless1, useless2):
return True
def exit_exc(useless1, useless2):
raise Exception('Crash of exit function')
def funcToBeExecuted (bla):
print(bla)
return bla
def funcToBeExecuted2 (bla):
print(bla)
return blo

View File

@@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

15
doc/manual/sphinx/build_doc.sh Executable file
View File

@@ -0,0 +1,15 @@
#! /bin/env sh
SCRIPT_DIR=$(realpath $( dirname "$0"))
ver_file="$(realpath $SCRIPT_DIR/../../../src/VERSION)"
ver=$(echo "$(cat $ver_file)" | cut -d "_" -f 1)
echo "Version of the manual: $ver"
export APP_VERSION=$ver
rm -r $SCRIPT_DIR/build
mkdir $SCRIPT_DIR/build
make -C $SCRIPT_DIR latexpdf || exit
cp -vf $SCRIPT_DIR/build/latex/testium.pdf $SCRIPT_DIR/../testium_manual.pdf

View File

@@ -0,0 +1,35 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=source
set BUILDDIR=build
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.https://www.sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd

View File

@@ -0,0 +1,128 @@
Command Line Interface
======================
.. code-block:: text
usage: testium.pyw [-h] [--version] [-b] [-m] [-c CONFIG_FILE [CONFIG_FILE ...]] [-r] [-l LOG_FILE]
[-d DEFINE [DEFINE ...]] [-p REPORT_FILE] [-t {sqlite,json,junit,html,text}]
[-n REPORT_PATTERN [REPORT_PATTERN ...]] [-i INCLUDE_PATH [INCLUDE_PATH ...]] [-o] [-g]
[test_file]
positional arguments:
test_file the test script file
optional arguments:
-h, --help show this help message and exit
--version Returns the version of testium
-b, --batch-execution
Executes the test in batch mode
-m, --terminal Starts terminal mode
-c CONFIG_FILE [CONFIG_FILE ...], --config-file CONFIG_FILE [CONFIG_FILE ...]
-o, --no-color Deactivates stdout colors in batch and terminal mode
Configuration file
-r, --run-and-close Runs the test then closes the application
-l LOG_FILE, --log-file LOG_FILE
log file name
-d DEFINE [DEFINE ...], --define DEFINE [DEFINE ...]
Configuration passed to the executed tests.
-p REPORT_FILE, --report-file REPORT_FILE
report file name
-t {sqlite,json,junit,html,text}, --report-type {sqlite,json,junit,html,text}
report file type
-n REPORT_PATTERN [REPORT_PATTERN ...], --report-pattern REPORT_PATTERN [REPORT_PATTERN ...]
report file pattern
-i INCLUDE_PATH [INCLUDE_PATH ...], --include-path INCLUDE_PATH [INCLUDE_PATH ...]
Python modules search path
-g, --debug GUI debug mode
``-h, --help``
--------------
Returns what's in the previous section.
``-b, --batch-execution``
-------------------------
Executes the test in text mode. No need to have QT installed in that case.
``-m, --terminal``
------------------
Starts a testium interactive console. It allows to run commands and sub-tests manually
in a console.
``-o, --no-color``
------------------
Switch allowing to disable the colored output in terminal or batch modes.
``-c, --config-file``
---------------------
This option allows to provide configuration file(s) from the command line.
The configuration files format and content is detailed in the :ref:`config files<sec_configuration_files>` section.
If this parameter is not given while calling *testium*, the default configuration files will be used.
``-r, --run-and-close``
-----------------------
This parameter makes testium to close immediately after running the ``test_file`` argument passed during its call.
If there is no ``test_file`` argument passed, this option is ignored.
``-l, --log-file``
------------------
Path of the log file where to store the log of the test execution.
Goes in a temporary folder if not provided.
.. _sec_option_define:
``-d, --define``
------------------------------------
Defines one or more variables in the form ``VARIABLE1=value1 VARIABLE2=value2 ..."``.
Then, these variables are available from the test scripts, using the :ref:`global variables<sec_global_variables>`
*testium* feature.
.. _sec_p_param:
``-p, --report-file``
----------------------
Path of the report file, stored during the test execution.
This option is only useful in :ref:`batch mode<sec_batch_mode>`.
``-t, --report-type``
---------------------
This option is used in conjuction with option :ref:`-p<sec_p_param>` and is defining
the type of report to be generated.
Please read the :ref:`reports<sec_reports>` section for more details on
the possible types of report.
``-n, --report-pattern``
-------------------------
This option is used in conjuction with option :ref:`-p<sec_p_param>` and is defining
the report parttern(s) used to filter the report results which will be
included in the report file.
More details in :ref:`reports<sec_reports>` section.
``-i, --include-path``
----------------------
Addtional python paths. These paths are appended to the
`sys.path <https://docs.python.org/3/library/sys.html?highlight=sys%20path#sys.path/>`_ python
variable.
``-g, --debug``
---------------
This option is only usefull while debugging *testium* in ``vscode`` in :ref:`graphical mode<sec_graphical_mode>`.

View File

@@ -0,0 +1,84 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import sys
sys.path.insert(0, os.path.abspath('../../../../src/testium/'))
# -- Project information -----------------------------------------------------
project = "testium"
copyright = "2025, François Dausseur"
author = "François Dausseur"
# The full version, including alpha/beta/rc tags
try:
release = os.environ["APP_VERSION"]
version = release
except:
raise Exception("APP_VERSION not defined in environment !")
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
master_doc = "index"
extensions = [
"sphinx.ext.duration",
"sphinx.ext.doctest",
"sphinx.ext.autodoc",
"sphinx.ext.autosummary",
'linuxdoc.rstFlatTable',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ["includes.rst", "templates.rst", "other_features.rst", "reports.rst"]
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
# html_theme = "alabaster"
html_theme = "classic"
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]
numfig = True
latex_engine = "xelatex"
latex_elements = {
"papersize": "a4paper",
'fontpkg': r'''
\setmainfont{DejaVu Sans}
\setsansfont{DejaVu Sans}
\setmonofont{DejaVu Sans Mono}
'''
}
latex_show_urls = "footnote"
pdf_stylesheets = ["style_code_font_size"]
add_module_names = False # Remove namespaces from class/method signatures

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

View File

@@ -0,0 +1,75 @@
.. _sec_python_helper_library:
Python helper library
======================
A python library including helper function for python modules called from
testium.
To include the support of this library in a python script, the following
line must be included in the script header:
.. code-block:: python
:caption: testium helper library import
import py_func.tm as tm
.. _sec_global_variables_helpers:
Global variables helper functions
----------------------------------
To manage values in the global variables dataset, the following testium library API
must be used:
.. automodule:: interpreter.utils.globdict
:members: gd, setgd, delgd
:undoc-members:
:no-index:
Console helper functions
------------------------
Every opened console instance is added to a list with the
key ``console_instances`` of the global variables.
The instance is removed from the list on close step of the ``console`` test item.
To manage consoles from within ``py_func`` python functions,
the following testium library API can be used:
.. automodule:: libs.testium
:members: add_console, remove_console, console
:undoc-members:
:no-index:
Plot helper functions
------------------------
Every opened plot window instance is added to a list with the
key ``plot_instances`` of the global variables.
The instance is removed from the list on close step of the ``plot`` test item.
To manage plots from within ``py_func`` python functions,
the following testium library API can be used:
.. automodule:: libs.testium
:members: add_plot, remove_plot, plot, add_plot_values, last_plot_value
:undoc-members:
:no-index:
Other helper functions
------------------------
.. automodule:: libs.testium
:members: OS, get_main_dir, timestamp, timestamp_as_sec
:undoc-members:
:no-index:
Debug mode
------------------------
.. automodule:: libs.testium
:members: debug_enabled, enable_debug, print_debug, print_info, print_warn
:undoc-members:
:no-index:

View File

@@ -0,0 +1,37 @@
Includes
---------
It is possible to include TUM files from another one by using the ``!include`` tag before the included file.
This feature is a testium specific implementation and is not part of the YAML language,
although it is based on the tagging feature of the language and the customization possibility
offered by the python pyYaml package.
Here is a basic example of file inclusion:
.. code-block:: yaml
:caption: included_file.tum
- test_item:
name: test_2
- test_item:
name: test_3
.. code-block:: yaml
:caption: main.tum
#include with the sub-sequence reference mechanism
sequence: &included_sequence
!include included_file.tum
main:
name: Test example
steps:
- test_item1:
name: test_1
- *included_sequence
#include can also be inserted directly within the steps list
- !include included_file.tum

View File

@@ -0,0 +1,12 @@
testium's User's Manual
=====================================
.. toctree::
:maxdepth: 3
:caption: Contents:
overview.rst
cdl_interface.rst
modes.rst
tum_syntax.rst
helper_lib.rst

View File

@@ -0,0 +1,45 @@
Modes of operation
====================
.. _sec_graphical_mode:
Graphical mode
---------------
*testium* tool has been initially designed to have Graphical User's interface.
The way to call it is simply by executing the ``testium`` command. It is the normal mode.
.. _sec_batch_mode:
Batch mode
----------
The batch mode allows to execute a test in text mode. In this mode, the test does not start any
graphical interface.
.. code-block:: text
:caption: call a test in batch mode
testium -b test/my_test/main.tum
Terminal mode
-------------
The terminal mode starts *testium* in interactive mode. From this console, some tests and
sequences of tests can be called interactively.
.. code-block:: text
:caption: call a test in terminal mode
$ testium -m
Configuration file loaded: /my/execution/path/param.xml
[...]
================================================================================
====== Test configuration
================================================================================
Test executed with testium : 2.4.0 (binary release)
(testium)~

View File

@@ -0,0 +1,96 @@
Test outputs
----------------
A list of test result outputs is automatically updated by *testium*.
This is a member of global variables dataset which key is ``test_outputs``.
This global_dict member contains the log file path and, if configured,
the report path as a list.
Other custom logged files may be added by user updated this global variables entry.
Post execution
------------------
A post execution script can be run for example to copy the output files.
For that, a ``post_execution`` element can be defined in the .tum file.
If the test set execution succeeded the ``post_exec`` function of file_name module is run else the ``post_exec_fail`` is run.
If the post_execution element is not defined, the post_execution.py file in the test directory is used by default if existing.
.. code-block:: yaml
:caption: custom post execution python file
post_execution:
file_name: test_report_text.py
Sub-sequence references
-------------------------
It is possible to alias any part of the TUM description file (typically a sequence of steps to be executed) to be inserted within another sequence.
This feature uses the anchor/alias mechanism of the ``YAML`` `language <https://yaml.org/>`_.
Here is an implementation example of a reference to a sub-sequence in a TUM file:
.. code-block:: yaml
:caption: sub-sequences call
sequence: &temperature_step_sequence
- test_item:
name: test_2
- test_item:
name: test_3
main:
name: Test example
steps:
- test_item1:
name: test_1
- *temperature_step_sequence
.. note::
The entry before the alias (``sequence``: in the example above) is needed
mandatorily by YAML language syntax. Nevertheless, its value is not
used by *testium* and thus can be any value.
Test documentation
--------------------
It is possible to display some explicative text user in the GUI.
The ``doc`` attribute of test items is used for that purpose and is displayed as
a tooltip on the test row.
.. code-block:: yaml
:caption: tests documentation
main:
name: Test example
steps:
- unittest_file:
name: unittest item
doc: |
The purpose of this unittest test item is to demonstrate
its various features.
test_file: dummy/dummy.py
test_method: test_01_pass
See illustration in :numref:`Figure %s<doc-illustration>`.
.. figure:: doc_illustration.png
:name: doc-illustration
Illustration of the ``doc`` attribute effect in the GUI.
Unittest
^^^^^^^^^
For ``unittest_file`` type test items, the python docstring of the test method is used as documentation.

View File

@@ -0,0 +1,32 @@
Overview
========
*testium* is an automated test framework developed in python by François Dausseur.
This software is developed in python and it implements the Qt6 graphical framework.
It has been developed since 2013 with production and development testing in mind.
It's function is to automate the execution of tests. It can be invoked either as command line terminal application or as a graphical interface application.
Tests reports generation and customization are also in this tool's scope.
Its main features are:
* YAML test description,
* Test configuration files in YAML, JSON or XML,
* Full range of pre-existing Test items,
* Test steps, loops,
* Dynamic variables expansion at test runtime,
* Conditional test step execution,
* Modularity of tests (reusable test sequences),
* etc.
All these features give the ability to the test engineer to perform efficient and robust testings.
.. figure:: testium_snapshot.png
testium
Each test is described with the help of a `YAML <https://yaml.org/>`_ file having .tum as extension.
This file is analyzed and then displayed as a tree in a graphical way in the
GUI (see Figure above).

View File

@@ -0,0 +1,47 @@
.. _sec_reports:
Reports
---------
If a report is required (in addition to the log), the ``report`` YAML element
must be added at the root of the TUM main test file.
The ``report`` YAML element has the following form:
.. code-block:: yaml
:caption: reports global settings
report:
enabled: True
file_name: $(test_name).rep
path: $(home)/reports
pattern: "Console%"
export: junit
log_stored: False
.. table:: report attributes
:widths: 20, 30, 50
+-----------------+-----------------------+-------------------------------------------+
| Attribute | default value | Description |
+-----------------+-----------------------+-------------------------------------------+
| ``enabled`` | ``True`` | Report activated |
+-----------------+-----------------------+-------------------------------------------+
| ``file_name`` | / | Report file name |
+-----------------+-----------------------+-------------------------------------------+
| ``path`` | ``$(report_path)`` | Report storage path By default, it uses |
| | | the default one set in the |
| | | preferences. |
+-----------------+-----------------------+-------------------------------------------+
| ``pattern`` | / | The pattern in SQL wildachars syntax |
| | | to be applied on test names to |
| | | selected reported tests. |
+-----------------+-----------------------+-------------------------------------------+
| ``export`` | / | The type of export. For exemple junit. |
| | | By default, the sqlite format is |
| | | used to generate reports. |
+-----------------+-----------------------+-------------------------------------------+
| ``log_stored`` | / | Defines if the output log of each |
| | | test is accessible to generate the |
| | | report export. |
+-----------------+-----------------------+-------------------------------------------+

View File

@@ -0,0 +1,4 @@
styles:
code:
parent: literal
fontSize: 8

View File

@@ -0,0 +1,51 @@
Templates
---------------------------
*testium* embeds the `jinja2 <hhttps://jinja.palletsprojects.com>`_ template engine. It allows a great customization of the
test files, and enforces reusability of test scripts.
In the main test file
^^^^^^^^^^^^^^^^^^^^^^^
The *testium* main test files are systematically passed through the jinja template engine.
The parameters passed to jinja are all the variables contained into the
:ref:`configuration files<sec_configuration_files>` plus the
:ref:`built-in values<sec_global_variables_builtin>`.
In ``!include`` directive
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Along with the basic inclusion capability, there is the possibility to use file inclusion parameters.
Theses parameters are replacing corresponding keywords in bracket in the included file.
See examples below.
.. code-block:: yaml
:caption: including a template
main:
name: Test example
steps:
- test_item1:
name: test_1
#include can also be inserted directly within the steps list
- !include
file: included_template_file.tum
inclusion_parameter_1: param1
inclusion_parameter_2: param2
.. code-block:: yaml
:caption: included template
- test_item:
name: {{ inclusion_parameter_1 }}
- {{ inclusion_parameter_2 }}:
name: test_3
# The following construction is not allowed and will fail to load:
- test_item:
name: {{ $(inclusion)_parameter_3 }}

View File

@@ -0,0 +1,12 @@
**check** test item
============================================================
The ``check`` test item returns the result of a python string evaluation:
.. code-block:: yaml
:caption: example of ``check`` test item usage
- check:
name: check test item example
values:
- '"tictactoe" in "$(my_global_string)"'

View File

@@ -0,0 +1,136 @@
.. _sec_console_test_item:
**console** test item
============================================================
The console test item is of the form:
.. code-block:: yaml
:caption: example of ``console`` test item usage
- console:
name: test name in GUI
console_name: console name in dict
steps:
- open:
protocol: telnet
telnet_host: $(target_ip)
telnet_port: $(target_port)
- writeln: reset
- read_until: {expected: U-Boot, timeout: 50}
- write: $(boot_vxworks_1)
- writeln: $(boot_vxworks_2)
- read_until:
expected: U-Boot
timeout: 15
- read_until:
expected: Something that will never occurs
timeout: 5
no_fail: True
mute: True
- close:
Attributes
-----------------------
Beside common test items attributes, console test item has specific attributes:
* ``console_name``: console instance name
* ``write_delay``: optional parameter giving the delay to wait in
milliseconds between each character sent.
* ``steps``: a sequence of actions to be applied to the console, as listed above.
The console test item steps accept the parameters and configurations defined in the next sections.
All the following actions support the ``name`` attribute. The ``name`` is concatenated with
the step type in the *testium* GUI, and recalled in the test log and reports.
``open`` action
-------------------------
The ``open`` action initializes the console with the attributes defined as described below.
The console instance is then added to the ``console_instances`` entry of the global
variables (cf :ref:`global variables<sec_global_variables>`).
Open step accepts the following attribute:
* ``protocol``: Setting of the console protocol, supported protocol are listed
in table below
* Other attributes are dependent of the protocol in used and are listed
in table below
.. table:: console protocols
:widths: 20, 30, 50
+---------------+------------------------+-------------------------------------------+
| **Protocol** | **protocol parameter** | **Description** |
+---------------+------------------------+-------------------------------------------+
|``telnet`` | ``telnet_host`` | hostname of the target. |
| +------------------------+-------------------------------------------+
| | ``telnet_port`` | port of the telnet server of the target. |
+---------------+------------------------+-------------------------------------------+
|``ssh`` | ``ssh_host`` | Hostname or IP address of the target. |
| +------------------------+-------------------------------------------+
| | ``ssh_user`` | port of the telnet server of the target. |
| +------------------------+-------------------------------------------+
| | ``ssh_pwd`` | Password (optional). |
+---------------+------------------------+-------------------------------------------+
|``serial`` | ``serial_port`` | Serial port to the target. |
| +------------------------+-------------------------------------------+
| | ``serial_baudrate`` | Baud rate of the serial connection. |
| +------------------------+-------------------------------------------+
| | ``buffered`` | Optinal boolean parameter. If ``False``, |
| | | it forces the |
| | | console to read directly the device. |
| | | Default: ``True``. |
+---------------+------------------------+-------------------------------------------+
|``rawtcp`` | ``tcp_host`` | hostname of the target. |
| +------------------------+-------------------------------------------+
| | ``tcp_port`` | port of the rawtcp server of the target. |
+---------------+------------------------+-------------------------------------------+
|``terminal`` | ``terminal_path`` | Path of the terminal console. |
+ +------------------------+-------------------------------------------+
| | ``shell`` | Shell to execute in the terminal |
| | | Default: /usr/bin/env bash |
+---------------+------------------------+-------------------------------------------+
* ``log``: is available only for Telnet and Serial console and is a path to a folder or a file, where the log will be stored.
``close`` action
---------------------------
The ``close`` action closes the console devices and removes its instance from
the ``console_instances`` list accessible in the global variables
(cf :ref:`global variables<sec_global_variables>`).
No parameters required for this action.
``write`` action
---------------------------
``write`` action takes as parameter the string to be written on the console.
``writeln`` action
-------------------------
writeln function is similar to the write function except that a '\n' (newline) character is sent at the end of the string to be written.
``read_until`` action
----------------------------
The ``read_until`` action is waiting for a string pattern from the console,
its parameter are listed below
* ``expected``: Character string to wait for
* ``timeout``: Timeout setting for the action (in seconds)
* ``no_fail``: Boolean value (``True`` or ``False``) leading to no error reported
if the expected input is not read
* ``mute``: Boolean value (``True`` or ``False``) does not log any readen data
The text read by the ``read_until`` action is stored in the global
variable named ``cn_<test_name>`` (See :ref:`global variables<sec_global_variables>`
for more detail on accessing global variables from test items and scripts).
In the example above, the global variable ``$(cn_test name in GUI)``
would be created at the end of the step. It would contain the resulting
data of the read.

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

View File

@@ -0,0 +1,109 @@
.. _sec_dialog_choices_test_item:
**dialog_choices** test item
============================================================
This test item displays a dialog asking a question and waiting for
a selection to be done among defined list of items.
These selectable items can be passed as a tree.
The :numref:`Figure %s<choices-dialog>` displays an example of this item.
.. figure:: dialog_choices.png
:name: choices-dialog
:figwidth: 50 %
:width: 50 %
:align: center
choices dialog
The item parameters corresponding to :numref:`Figure %s<choices-dialog>`
is shown below.
.. code-block:: yaml
:caption: example of ``choices_dialog`` test item usage
- dialog_choices:
name: Choices
question: Select the items you want
icon: $(test_directory)/document.png
choices:
- name: choice 1
description: My first choice description
icon: $(test_directory)/document-save.png
choices:
- name: choice 1.1
description: My choice 1 first subchoice description
icon: $(test_directory)/Label.png
- name: choice 1.2
description: My choice 1 first subchoice description
- name: choice 2
description: My second choice description
icon: $(test_directory)/image.png
- name: choice 3
description: My third choice description
icon: $(test_directory)/image.png
choices:
- name: choice 3.1
description: My choice 3 first subchoice description
- name: choice 3.2
description: My choice 3 second subchoice description
icon: $(test_directory)/Label.png
Attributes
---------------
The supported attributes of the ``dialog_choices`` test item are:
* ``question``: Question to be displayed in the dialog box.
* ``choices``: List of the choicies presented to the user.
* ``icon``: Optional. Path of the icon used in the
selection tree, for all the items by default.
``Choices`` attribute content
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Each choice element is a dictionary which can have the following attributes
* ``name``: name of the choice to be done.
* ``description``: description of the choice to be done.
* ``icon``: Optional. Path of the icon displayed in the
selection tree in front of the corresponding choice.
* ``choices``: List of sub-choicies presented to the user (recursive).
Feature
------------------
The dialog references test item creates the ``cs_<name of test item>`` entry in the
global dictionary.
In the example above, the global variable name containing the
result of the test item would be ``cs_Choices``, and it would contain an
object of this form:
.. code-block::
:caption: example of result of the ``dialog_choices`` test item
[
{'name': 'choice 1',
'checked': True,
'choices': [ {'name': 'choice 1.1', 'checked': True},
{'name': 'choice 1.2', 'checked': False} ]
},
{ 'name': 'choice 2',
'checked': False},
{ 'name': 'choice 3',
'checked': True,
'choices': [ {'name': 'choice 3.1', 'checked': False},
{'name': 'choice 3.2', 'checked': True} ]
}
]
See :ref:`global variables<sec_global_variables>` for more detail
on how to access to global variables from test items and scripts.

View File

@@ -0,0 +1,27 @@
**dialog_image** test item
============================================================
This test item displays an image within a dialog box.
``dialog_image`` test item has the following description format
.. code-block:: yaml
:caption: example of ``dialog_image`` test item usage
- dialog_image:
name: dialog image test item
question: operator question
filename: imageToBeDisplayed.jpg
Attributes
----------------------
``dialog_image`` has the following specific attributes:
* ``question``: Question to be displayed in the dialog box
* ``filename``: File name of the image to be displayed in the dialog box.
Feature
----------------------
The test returns a ``FAIL`` if the answer is No and ``PASS`` if yes.

View File

@@ -0,0 +1,24 @@
**dialog_message** test item
============================================================
This test item displays a simple dialog asking a question and returning the entered value.
dialog_message test item has the following description format
.. code-block:: yaml
:caption: example of ``dialog_message`` test item usage
- dialog_message:
name: dialog value test item
question: operator question
Attributes
---------------------
``dialog_message`` has the following specific attribute:
* ``question``: Sentence to be displayed in the dialog box
Feature
---------------------
Just display the message.

View File

@@ -0,0 +1,25 @@
**dialog_note** test items
============================================================
This test item displays a simple dialog allowing to enter some text and printing the entered value in logs.
``dialog_note`` test item has the following description format
.. code-block:: yaml
:caption: example of ``dialog_note`` test item usage
- dialog_note:
name: dialog value test item
question: operator question
Attributes
-----------------
``dialog_note`` has the following specific attribute:
* ``question``: Question to be displayed in the dialog box
Feature
--------------------
Prints the entered text in the log.

View File

@@ -0,0 +1,26 @@
**dialog_question** test item
============================================================
This test item displays a simple dialog asking a question and returning
the entered value.
``dialog_question`` test item has the following description format
.. code-block:: yaml
:caption: example of ``dialog_question`` test item usage
- dialog_question:
name: dialog value test item
question: operator question
Attributes
--------------------
``dialog_question`` has the following specific attribute:
* ``question``: Question to be asked in the dialog box
Feature
----------------------
The test returns a ``FAIL`` if the answer is No and ``PASS`` if yes.

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -0,0 +1,54 @@
.. _sec_dialog_references_test_item:
**dialog_references** test item
============================================================
This test item displays a dialog asking a question and waiting for
references of the devices under test.
This test item has the following format:
.. code-block:: yaml
:caption: example of ``dialog_references`` test item usage
- dialog_references:
name: ask for a reference
question: Please give the reference of the product
reference:
- ref 1
- ref 2/rev
The example above displays the dialog box in :numref:`Figure %s<dialog-reference>`.
.. figure:: dialog_reference.png
:name: dialog-reference
:figwidth: 50 %
:width: 50 %
:align: center
dialog reference
Attributes
---------------
All the following attributes are mandatory.
* ``question``: Question to be displayed in the dialog box.
* ``reference``: For each of this parameter in the test correspond to a
row to fill in the dialog.
Every field for a reference can be pre-filled using separating
each filed with an '/' (cf :numref:`Figure %s<dialog-reference>`).
Feature
------------------
The dialog references test item creates the ``tested_items`` entry in the
global_dict global variable. This entry is a list of dictionaries of
this form:
.. code-block:: text
:caption: example of ``tested_items`` global variable result of ``dialog_reference``
test item
[{'reference': 'XXXXX', 'revision': 'YYYYY', 'serial': 'ZZZZZ'}, …]

View File

@@ -0,0 +1,29 @@
.. _sec_dialog_value_test_item:
**dialog_value** test items
============================================================
This test item displays a simple dialog asking a question and returning the entered value.
``dialog_value`` test item has the following description format
.. code-block:: yaml
:caption: example of ``dialog_value`` test item usage
- dialog_value:
name: dialog value test item
question: operator question
Attributes
-------------------
``dialog_value`` has the following specific attribute:
* ``question``: Question to be displayed in the dialog box
* ``default``: default value to place in the dialog form (optional)
Feature
----------------------
The returned value is added in the global variable entry with the key being the
``dialog_value`` test item name.

View File

@@ -0,0 +1,108 @@
.. _sec_func_item:
**py_func** test item
============================================================
The ``py_func`` test item is used to execute custom python scripts with the given
input parameters.
There are two modes for executing a ``py_func`` item. The class mode and the function mode.
class py_func item
-------------------------
This is the normal way of calling some custom python code.
A class must be defined and derived from ``FunctionItem`` from the ``libs.testium`` module.
From this class it is possible to define some custom reported values with the following API
* ``reportValue(key, value)``: This ``FunctionItem`` method is adding a value added to the report,
* ``reportedValues()``: This ``FunctionItem`` method is retrieving the current report values.
.. code-block:: python
:caption: ``py_func`` test item implementation example
:name: scriptFunctionItem.py
import py_func.tm as tm
class TestItemFunc(tm.FunctionItem)
def exec(param1, param2, param4, param4):
...
self.reportValue('my_reported_value', reported_value)
print(self.reportedValues())
return 10
The ``exec`` method of the ``FunctionItem`` derived class is executed while running the ``py_func`` test item.
.. code-block:: yaml
:caption: legacy ``py_func`` test item implementation
- py_func:
name: function test item
file: scriptTestFile.py
func_name: TestItemFunc
param:
- 123
- 0.123
- True
- $(global_dict_key)
expected_result: 10
**legacy py_func**
The legacy py_func test item is of the form:
.. code-block:: python
:caption: legacy ``py_func`` python function example
:name: scriptTestFile.py
def dummy_func(param1, param2, param4, param4):
...
return 10
There is no possibility to access the report features in that mode.
.. code-block:: yaml
:caption: corresponding ``py_func`` tum extract
- py_func:
name: function test item
file: scriptTestFile.py
func_name: funcToBeExecuted
param:
- 123
- 0.123
- True
- $(global_dict_key)
expected_result: 10
**Attributes**
Beside common test items attributes, py_func item has specific attribute, some of which being mandatory.
* ``file``: the script file name that contains the function to be executed.
Only python script format is supported.
* ``func_name``: The function name to be executed.
* ``param``: This is a list of parameters that are passed to the function
in the order they are presented in the script. These parameters are not
mandatory and are highly dependent of the function prototype.
.. code-block:: yaml
:caption: ``py_func`` test item example of usage
- py_func:
file: script_name.py
func_name: methodName
param:
- $(my_param)
The result of the function (after eventual post treatment) is stored in the global
variable named ``fn_<func_name>``
(See :ref:`global variables<sec_global_variables>` for more detail
on how to access to global variables from test items and scripts).
In the example above, the global variable ``$(fn_function test item)``
would be created at the end of the item execution. It would contain the resulting
value of the funcToBeExecuted python function.

View File

@@ -0,0 +1,17 @@
**git** test item
============================================================
Git test item allows
this item has the following description format
.. code-block:: yaml
:caption: ``git`` test item usage example
- git:
name: git test item
repo: [$(test_directory), "/path_to/another/repo"]
Attributes
-----------------
* ``repo``: a string or list of string path to the root of the git repositery(ies) to follow.

View File

@@ -0,0 +1,26 @@
**group** test item
============================================================
This element is of the following form:
.. code-block:: yaml
:caption: ``group`` test item usage example
- group:
name: Group Item
condition: "'$(OS)' == 'Linux'"
steps:
- unittest_file:
test_file: test_prod_rio6_8093.py
test_method:
...
- sleep:
timeout: 10
The ``group`` element is used to manage a sequence of item as a group.
Attributes
--------------------
* The ``steps`` list describes the sequence executed in the group.
It is a list of any of the testium test items,

View File

@@ -0,0 +1,222 @@
.. _sec_item_common:
Items common attributes
============================================================
All test items have common attributes independently of their types, which are
listed in next table, those are all optional parameters and their default value
if not provided is given in the table as well.
.. table:: test items common attributes
:widths: 25, 15, 60
+-----------------------+-------------------+-------------------------------------------------------+
| **Parameter name** | **Default value** | **Description** |
+-----------------------+-------------------+-------------------------------------------------------+
|``name`` | test item type | This is the test item name as displayed |
| | | in the test tree window of the |
| | | testium. |
| | | |
| | | This attribute is also supported by actions of |
| | | ``console``, ``jsonrpc`` or ``plot`` test items. |
| | | |
| | | Default value, if not provided, is the |
| | | test item type. |
+-----------------------+-------------------+-------------------------------------------------------+
|``stop_on_failure`` |``False`` | If ``stop_on_failure`` is set to |
| | | ``True``, the test sequence execution |
| | | stops on test tem failure and no |
| | | further test items are executed, |
| | | except those withexecute_on_stop |
| | | attribute set (see below) |
| | | |
| | | It depends on the test item to take it |
| | | into account or not. |
| | | For example it makes sense to use it |
| | | for ``unittest_file`` test type |
| | | because it can contain many sub-tests, |
| | | but not for sleep test type. |
| | | In cycles, it means that the child |
| | | sequence execution is stopped at first |
| | | failure. It also means that the |
| | | remaining loops are not executed. |
+-----------------------+-------------------+-------------------------------------------------------+
|``execute_on_stop`` |``False`` | When this attribute is set to True, the |
| | | test item is always run, even on test |
| | | failure of any test before. |
| | | This feature is useful, to end the |
| | | test |
| | | sequence properly on test failure |
| | | (switch off power supplies, climatic |
| | | chamber temperature set to ambient |
| | | temperature….) |
+-----------------------+-------------------+-------------------------------------------------------+
|``skipped`` |``False`` | The test item execution is to be |
| | | skipped during test sequence execution |
| | | if set to ``True``. It will be |
| | | displayed as failed in the report. |
+-----------------------+-------------------+-------------------------------------------------------+
| ``no_fail`` | ``False`` | The result of the test step is forced to PASS if this |
| | | attribute is set to ``true``. |
+-----------------------+-------------------+-------------------------------------------------------+
|``doc`` |``""`` | Documentation for the test item that |
| | | appears in the test doc field and the |
| | | contextual text window in the testium |
| | | GUI. |
+-----------------------+-------------------+-------------------------------------------------------+
|``Key`` | / | This attribute defines a key which |
| | | will be attached to the test result and |
| | | which will allow to be filtered during |
| | | the report generation. |
+-----------------------+-------------------+-------------------------------------------------------+
|``report`` | / | This attribute defines values (a dictionary) which |
| | | will be added in the ``data`` field of the report. |
+-----------------------+-------------------+-------------------------------------------------------+
| ``condition`` | / | The test item is not executed if its |
| | | ``condition`` attribute content is |
| | | evaluated as ``False``. |
| | | see :ref:`Conditional |
| | | execution<sec_conditional_execution>`. |
+-----------------------+-------------------+-------------------------------------------------------+
| ``process_result`` | / | Process an evaluation of the process_result |
| | | and store it in the result |
| | | see :ref:`Process result<sec_process_result>` |
| | | for details. |
+-----------------------+-------------------+-------------------------------------------------------+
| ``expected_result`` | / | Expected result value or string. |
| | | see :ref:`Expected result<sec_expected_result>` |
| | | for details. |
+-----------------------+-------------------+-------------------------------------------------------+
last test result
-----------------------------------------------
The global variable ``last_test_result`` is automatically set at the end of a test item execution.
If the corresponding test item does not return any acutal, the content of the ``last_test_result``
variable will be the test success (``PASS``, ``FAIL`` or ``SKIP``).
It the test item returns a value, the ``last_test_result`` variable will contain the returned value.
The main test items returning a value are:
* :ref:`console<sec_console_test_item>` test item,
* :ref:`jsonrpc<sec_jsonrpc_test_item>` test item,
* :ref:`dialog references<sec_dialog_references_test_item>` test item,
* :ref:`dialog value<sec_dialog_value_test_item>` test item.
Test timings
-----------------------------------------------
After the execution of a test step, the following global variables are set :
* ``ts_start_<item_name>``
* ``ts_end_<item_name>``
and
* duration: ``ts_duration_<item_name>``
See :ref:`global variables<sec_global_variables>` for more detail on how to access to global variables from
test items and scripts.
Skipped test items
-----------------------------------------------
A variable named ``skipped_test_item`` can be defined in the global variable entries
or in configuration file (see :ref:`config files<sec_configuration_files>`) as a list of item to be skipped.
.. _sec_conditional_execution:
Conditional execution
-----------------------------------------------
The ``condition`` attribute content is evaluated as a python string.
.. _sec_process_result:
Process result
-----------------------------------------------
The ``process_result`` attribute can be applied to all the test items. However, it's behavior is different
depending if the test item is returning a value or not.
The ``process_result`` attribute content is evaluated as a python line.
The special ``$(result)`` variable is replaced in the ``process_result`` attribute content with the test result value.
The process result is done before the ``expected_result``
If the result of the evaluation is a boolean, the test will be *PASSED* if ``True``, and *FAIL* otherwise.
.. _sec_expected_result:
Expected result
-----------------------------------------------
The ``expected_result`` attribute can be applied to all the test items. However, it's behavior is different
depending if the test item is returning a value or not.
The test items returning a value are:
* :ref:`dialog_references test item<sec_dialog_references_test_item>`
* :ref:`dialog_value test items<sec_dialog_value_test_item>`
* :ref:`py_func test item<sec_func_item>`
* :ref:`dialog_choices test item<sec_dialog_choices_test_item>`
* :ref:`json_rpc test item<sec_jsonrpc_test_item>`
For test items which don't return a value, the ``expected_result`` attribute content is
compared to ``PASS`` or ``FAIL``.
The ``expected_result`` attribute content is a simple comparison with ``$(result)``.
If the result and the expected_result is equal, the test will be *PASSED* if ``True``, and *FAIL* otherwise.
The special ``$(result)`` variable is replaced in the ``expected_result`` attribute content with the test result value.
Export attribute
-----------------------------------------------
.. code-block:: yaml
:caption: Example of ``export`` common attribute usage
- check:
name: Example of result specific to the step 001
values:
- $(last_test_result) == PASS
key:
- GID-1510554_step_1
report:
reported_list: <@ random.sample(range(0,20), k=10) @>
reported_float: <@ math.sqrt(float(1)) @>
reported_str: This is my reported sentence
.. _sec_item_default_folded:
Container items GUI default folding
============================================================
The container items are items which are the parent of other test items. For example loops and groups
are container test items.
In the GUI, if the user wants that a container test item is folded when he opens a test, the ``.``
character has to be place before the test item declaration.
See an example below:
.. code-block:: yaml
:caption: example of ``loop`` folded by default in the GUI
- .loop:
doc: An example loop
name: An example loop
...

View File

@@ -0,0 +1,178 @@
.. _sec_jsonrpc_test_item:
**jsonrpc** test item
============================================================
The `jsonrpc` test item is used to access jsonrpc servers, by sending queries and analysing the
answers.
It supports JSONRPC `v1.0 <https://www.jsonrpc.org/specification_v1>`_ or
`v2.0 <https://www.jsonrpc.org/specification>`_.
This test item can access the jsonrpc server by using an existing
:ref:`console<sec_console_test_item>` or directly using a UDP protocol.
Two low level *adapters* can be then chosen: ``udp`` or ``console``.
Example of ``jsonrpc`` test item with the console adapter:
.. code-block:: yaml
:caption: ``json_rpc`` test item usage example
- json_rpc:
name: JSONRPC console Query
doc: JSONRPC console Query not waiting (only send)
console:
name : jsonrpc_server
prompt: "@@>"
timeout: 1
version: "2.0"
steps:
- query:
method: echo
params:
a: Hello world
b: [0, 1, 2, 3]
id: 3095372
no_wait: True
- [...]
- json_rpc:
name: JSONRPC console Reception
doc: JSONRPC console reception of the previous request
console: {name : jsonrpc_server}
timeout: 1
steps:
- receive:
name: console reception
id: 3095372
timeout: 0.5
Attributes
-----------------------
the jsonrpc attributes are:
* ``timeout``: global communication timeout in seconds. It is a floating point number.
* ``version``: "1.0" or "2.0" (as a string) depending on the version of the JSONRPC
standard which is supported.
* ``mute``: a boolean giving the verbosity of the jsonrpc exchanges on the log output.
* An :ref:`Adapter<sec_jsonrpc_adapters>` is to be chosen between:
* Console,
* UDP,
* ``steps``: a sequence of actions as described in the sections below.
.. _sec_jsonrpc_adapters:
Steps
-----------------------
the jsonrpc steps can be of the following:
* ``open``: used by UDP to open the socket explicitely,
* ``close``: used by UDP adapter to close the socket explicitely,
* ``query``: performs a complete or partial JSONRPC call,
* ``receive``: used to receive the JSONRPC result of call previously
done by the ``query`` action.
If no ``expected_value`` attribute is defined for ``query`` or ``receive`` actions,
the success of the step will depend on the value returned by the JSONRPC frame.
Indeed, this protocol defines a mean to notify if the remote procedure has succeeded
or failed.
All the actions support the ``name`` attribute. The ``name`` is concatenated with
the action type in the *testium* GUI, and recalled in the test log and reports.
adapter attributes
^^^^^^^^^^^^^^^^^^^^^^
The adapters attributes are listed in the table below.
.. flat-table:: jsonrpc adapters
:header-rows: 2
:stub-columns: 1
:widths: 10 20 15 10 10 10
* - :rspan:`1` adapter
- :cspan:`2` attribute
- :cspan:`1` Description
* - attribute
- *type*
* - :rspan:`2` Console
- ``console``
- *dictionary*
- The console adapter configuration
* - ``console.name``
- *string*
- The name of the console which will be retrieved from
the :ref:`global variables<sec_global_variables>`. See also
the :ref:`console test item<sec_console_test_item>`.
* - ``console.prompt``
- *string*
- the eventual enclosing suffix of the jsonrpc frame.
* - :rspan:`4` UDP
- ``udp``
- *dictionary*
- The UDP adapter configuration
* - ``udp.server``
- *string*
- UDP server hostname or IP address.
* - ``udp.snd_port``
- *integer*
- UDP server listening port
* - ``udp.rcv_port``
- *integer*
- UDP answer reception port (on client side)
* - ``bufsize``
- *integer*
- the maximum expected size of the buffer received while waiting for
a jsonrpc frame.
``open`` action
-------------------------
The ``open`` jsonrpc action is only used with the
`UDP adapter<sec_jsonrpc_adapters>` but is mandatory before any ``query`` action.
No parameter is required.
``close`` action
---------------------------
The ``close`` jsonrpc action is only used with the
`UDP adapter<sec_jsonrpc_adapters>` but is mandatory after JSONRPC transfers are finished.
No parameter is required.
``query`` action
---------------------------
The ``query`` jsonrpc action has the following attributes:
* ``method``: JSONRPC method to be called,
* ``params``: JSONRPC param (must be conforming to the version defined above), by default it is an empty list.
* ``id``: JSONRPC id. If not defined or starts with ``rand``, it is chosen randomly.
Otherwise it must be an integer value,
* ``timeout``: reception timeout in seconds. It is a floating point number.
It is by default the jsonrpc timeout.
* ``no_wait``: Optional boolean. False by default. This attribute defines if
the reception is performed in this step (reception can be done appart, in the
``receive`` action described below),
``receive`` action
---------------------------
The ``receive`` jsonrpc action has the following attributes:
* ``id``: JSONRPC id as an integer value,
* ``timeout``: reception timeout in seconds. It is a floating point number,
It is by default the jsonrpc timeout.

View File

@@ -0,0 +1,25 @@
**let** test item
============================================================
This element is of the following form:
.. code-block:: yaml
:caption: ``let`` test item usage example
- let:
name: Let Item
values:
key1: value1
key2: value2
eval:
key3: $(variable)[$(loop_index)]
The ``let`` element is used to set values in the global directory.
Attributes
----------------
* The values list gives the {<key>, <value>} couples to set in the
global directory,
* The eval list gives the strings to evaluate prior to its storage into
the <key> of global directory.

View File

@@ -0,0 +1,95 @@
.. _sec_loop_item:
**loop** test items
============================================================
This element is of the following form:
.. code-block:: yaml
:caption: ``loop`` test item usage example
- loop:
name: Cycle Temperature
iterator: 10
steps:
- unittest_file:
test_file: test_prod_rio6_8093.py
- py_func:
name: function test item
file: scriptTestFile.py
func_name: funcToBeExecuted
param:
- $(loop_param)
exit_condition:
file: script_name.py
func_name: methodName
The loop element executes repeatedly the ``steps`` sequence of items.
The configuration of the iteration process is done according to the iterator
cycle sub-item. As described later in this chapter the iterator is
configurable per cycle and allows to call a python function at each
cycle loop.
Attributes
----------------
Below are described loop test item specific attributes.
* ``Iterator``: giving the number of loop iteration (see dedicated chapter below).
* ``steps``: describes the sequence executed at each cycle; it is
a list of any of the testium test items.
* ``exit_condition``: allows to exit the loop. If False is returned, loop continues
else, it breaks. exit_condition attributes are:
* ``time``: the loop stops after the time (in minutes) is elapsed (optional)
* ``value``: the loop stops when the content of the value attribute is
evaluated as True (optional)
* ``file``: the loop the script file name that contains a function to be
executed on each loop. Only python script format is supported (optional
if another exit_condition attribute is defined)
* ``func_name``: the function to execute on each loop when the file attribute
is defined. The function referenced by the ``func_name`` attribute must
have two parameters: the current loop iterator value and the report,
even if they are not used. This attribute is mandatory if the file
attribute is defined.
* ``eval``: optional parameter allowing post treatment of the function result.
It is a python evaluable string in which the ``$(result)`` keywork
is replaced by the actual function call result (see exemple below).
.. code-block:: yaml
:caption: ``loop`` exit condition
- loop:
...
exit_condition:
file: script_name.py
func_name: methodName
eval: $(result) < 2
**Iterator**
The iterator attribute can be of the following types:
* An integer giving the cycle loop number,
* A list. The number of elements of the list gives the loop number, and
the list member are the consecutive loop parameters,
* Undefined. Then cycle loops until the exit condition is reached.
**Loop variables**
The following loop variables are automatically defined:
* ``$(loop_param)``: parameter of the loop. It contains the iterator value.
* ``$(loop_index)``: index of the loop, starting with 0 and incremented at each cycle.
* ``$(loop_index_inverse)``: inverse of index of the loop, starting from cycle length
-1 and decremented at each cycle.
* ``$(loop_count)``: loop total iteration number. If the number of loops is undefined
its value is the python ``inf``.
When these variables are found in a parameter, an attribute, etc, a
loop is searched recursively in the test hierarchy. And the variable value
is replaced by the corresponding loop value.
If more than one loop exists in the test item hierarchy, the lowest level
loop iterator is used.

View File

@@ -0,0 +1,180 @@
**plot** test item
============================================================
This test item is used to display runtime values of tests variables or any evolving value in
a independent external window.
The plot window is defined using the ``plot`` test item:
.. code-block:: yaml
:caption: ``plot`` test item usage example
- plot:
name: test name in GUI
plot_name: plot identifier
steps:
- open:
- add:
...
Attributes
----------------------
In addition to common test items attributes, console test item has specific attributes:
* ``plot_name``: plot window instance name.
* ``steps``: a sequence of actions to be applied to the plot window. More than one action can be
executed in a ``plot`` item.
The plot test item can accept the actions described in further sections.
All the following actions support the ``name`` attribute. The ``name`` is concatenated with
the action type in the *testium* GUI, and recalled in the test log and reports.
``open`` action
----------------------
This action initializes and opens the plot window with the corresponding attributes as defined below.
This action accepts one optional ``path`` parameter defining a path where are stored the
plot lines values in csv format.
.. code-block:: yaml
:caption: ``plot`` ``open`` action
- plot:
name: Open the plot window
plot_name: plot identifier
steps:
- open:
name: open the plot
log_path: $(test_directory)/tmp
``close`` action
----------------------
The ``close`` action closes the plot window and removes its from the managed instances of *testium*.
This action does not have mandatory parameters. However, ``close`` optional action parameters are:
* ``wait_dialog_exit``: Boolean value. If set to True, the window
is kept opened until the user closes it manually.
* ``timeout``: Value expressed in seconds. It is active if the ``wait_dialog_exit`` is set to True.
If this parameter is defined, and if not closed manually, the dialog window is kept opened until
the timeout elapses.
.. code-block:: yaml
:caption: ``plot`` ``close`` action
- plot:
name: Closes the plot
plot_name: plot identifier
steps:
- close:
wait_dialog_exit: True
timeout: 600
.. note::
When the ``close`` action is entered, the ``periodic`` plots are stopped.
``add`` action
----------------------
The ``add`` action is used to add a single data to the ``plot`` window.
.. code-block:: yaml
:caption: ``plot`` ``add`` action
- plot:
name: Add to the plot
plot_name: plot identifier
steps:
- add:
name: add value 1 & 2
value1: $(loop_index)
value2: $(loop_index)+2
The parameter of the ``add`` action is a dictionnary of (*key*, *values*) pairs where the *key* is the plot line name and
*value* is the numeric value to add to the plot line.
The *value* content is evaluated as a python statement if not a number, but a string.
``periodic`` action
----------------------
This action allows to specify a python function to be called and which result is used to update the
plot.
``periodic`` plots are updated automatically and don't require further steps in a test sequence, once executed.
``periodic`` action parameters are:
* ``period``: period of the automatic value update.
* ``file``: python file containing the function to call.
* ``func_name``: the name of the python function to be periodicaly called.
* ``eval``: optional parameter allowing post treatment of the function result.
The ``eval`` parameter of the periodic action is a python evaluable string in which the $(result) keywork
is replaced by the actual function call result.
The result of the action must be a dictionnary of (*key*, *values*) pairs where the *key* is the plot line name and
*value* is the numeric value to add to the plot line.
.. code-block:: yaml
:caption: ``plot`` ``periodic`` action
- plot:
name: Add periodic to the plot
plot_name: plot identifier
steps:
- periodic:
period: 1
file: $(test_path)$(psep)plot.py
func_name: random_value
eval: '{"periodic": $(result)}'
``last_value`` action
----------------------
The ``last_value`` action returns the last values added to the plot (periodicaly or not) into
the global variables entries.
``last_value`` action parameters are:
* ``name``: Optional parameter giving the list of measures to be returned. If
it is not defined, all the measures are returned.
.. code-block:: yaml
:caption: ``plot`` ``last_value`` action
- plot:
name: Plot measure_1 value
plot_name: plot identifier
steps:
- last_value:
name: [measure_1]
The result of the action is stored in the global
variable named ``plv_<item_name>`` in the example above, it would be
``$(plv_Plot measure_1 value)``. See :ref:`global variables<sec_global_variables>` for more detail
on how to access to global variables from test items and scripts.
``export`` action
----------------------
The ``export`` action saves the plot window data in various formats to the filesystem.
.. code-block:: yaml
:caption: ``plot`` ``export`` action
- plot:
name: Plot export
plot_name: plot identifier
steps:
- export: $(my_custom_path)/plot_export.pdf
- export: $(my_custom_path)/plot_export.csv
At the time of writing of this documentation, ``.pdf`` and ``.csv`` files are supported.

View File

@@ -0,0 +1,51 @@
**report** test item
============================================================
This test item exports a report file.
To have this functionality activated, a ``report section`` must be defined at the root of the test file.
The root report section is described in :ref:`report<sec_reports>` section.
report test item has the following description format
.. code-block:: yaml
:caption: ``report`` test item usage example
- report:
name: Intermediate report
export:
- junit:
path: $(home)/reports/report-key-1.junit
pattern:
- Unittest%
key: report-key-1
- text:
file_name: report-key-1.txt
path: $(home)/reports
key:
- report-key-1Attributes
This item is useful to generate intermediate reports in any format other than ``sqlite``. Nevertheless,
if ``sqlite`` export is defined, It won't generate anything.
Attributes
---------------------
``report`` test item has the following specific attributes:
* ``export``: reports to be exported. It is a list of the reports exports to be executed.
The supported exports are:
* ``junit``
* ``json``
* ``html``
* text
The export sub-attributes (see example above) may contain the following attributes.
* ``path``: path of the report files directory,
* ``filename``: report file name,
* ``Pattern``: list of the patterns (applied on test names) used to select the
tests to exportinto the report,
* ``Key``: list of selected keys which are used to select the tests to export
into the report.

View File

@@ -0,0 +1,30 @@
**run** test item
============================================================
This test item executes a new instance of testium.
.. code-block:: yaml
:caption: ``run`` test item usage example
- run:
name: Execute TUM
tum_fime: example_cycle.tum
python_path: python3
testium_path: /home/francois/projets/testium-new-report/testium.pyw
log_file: $(home)/reports/test.log
report_file: $(home)/reports/test.rep
Attributes
---------------------
run test item has the following specific attributes:
* ``tum_fime``: mandatory the path of the file to execute, it can be relative to current execution folder,
* ``param_file`` (optional) the path of the parameter file to use, otherwise default parameter file is used.
* ``python_path`` (optional) the path of a specific python to run your scripts,
* ``testium_path`` (optional) the path of a specific testium to run your scripts,
* ``log_file`` (optional) the path of log file to register, if not provided a file is created with timestamp at the location of TUM file.
* ``report_file`` (optional), the path of report file to create
* ``start_time`` (optional), start time for the script execution, in HH:MM format.
* ``end_time`` (optional), end time for an execution within a time frame, in HH:MM format.
* ``wait_for_exec`` (optional). True or False, wait to be in the execution window defined by start_time and end_time to run the script.

View File

@@ -0,0 +1,18 @@
**sleep** test item
============================================================
sleep test item has the following description format
.. code-block:: yaml
:caption: ``sleep`` test item example usage
- sleep:
name: sleep test item
timeout: 10
dialog: True
Attributes
---------------
* ``timeout``: sleep duration in second or in relative date format like "2d 5h 31m 3s", which translate into 2 days, 5 hours, 31 minutes and 3 seconds.
* ``dialog``: If set to True, a window showing the remaining time to wait is displayed (optional parameter set to ``False`` by default)

View File

@@ -0,0 +1,81 @@
**unittest_file** test item
============================================================
unittest_file test item allows the execution of unittest test script which
is part of python standard libraries.
The tum file prototype is as followed:
.. code-block:: yaml
:caption: ``unittest_file`` test item usage example
- unittest_file:
name: unitTest test item
test_file: unitTestScript.py
test_method:
- test_1
- test_2
Attributes
------------------
Beside common test items attributes, unittest test item has specific attribute, some of which being mandatory.
* ``test_file``: it is the name (and eventually path) of the unittest file
to be processed.
* ``test_method``: it is an optional unittest_file test sub-item. If one or more
elements are present, the unittest python script file is parsed and only
the corresponding methods are included in the test tree. Otherwise, all
the test methods are included in the test tree.
Access to global variables entries
----------------------------------
``unittest`` file tests instances have access to the testium global variables
by using the :ref:`helper's library<sec_python_helper_library>`.
Report value from unittest
----------------------------------
Value can be added to the test report from unitTest test at runtime.
.. code-block:: python
:caption: example of ``unittest`` test item python function
from unittest import (TestCase)
class DummyTests(TestCase):
def test_01_report(self):
self.reported_values['key reported']= 'value_reported'
Console use example with unittest item
-----------------------------------------
Here is an example how to use the console module from python ``unittest``.
.. code-block:: python
:caption: example of a *testium* console usage from a ``unittest`` python function
from unittest import (TestCase)
import console
class DummyTests(TestCase):
@classmethod
def setUpClass(cls):
cls.consA0= console.TelnetConsole('cons name','192.168.98.123',7001)
cls.consA0.open()
cls.promptA0 = 'test-computer>'
def test_01_console(self):
self.consA0.write('config')
self.assertEqual(self.consA0.read_until(self.promptA0, 10), 0)
self.consA0.write('lsusb && echo "Done."\n')
status, read_data = self.consA0.read_until('Done.',
10, return_data=True)
self.assertEqual(status, 0)
if read_data.find('ID 04f2:b684 Chicony Electronics Co.')!=-1:
index=0
@classmethod
def tearDownClass(cls):
cls.consA0.close()

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

View File

@@ -0,0 +1,254 @@
TUM file syntax
================
*testium* is a python-based tool which uses a ``YAML`` based description file to operate tests: the TUM file.
The description of tests is based on the definition of test sequences. There is a main ``YAML`` element which is the *testium* tool entry point.
All other steps are listed in the step list of the main. Some steps are themselves list of steps as well, such as loop or console items.
``YAML`` is an indented language and parameter encapsulation is defined by their indentation.
``YAML`` language auto detects data type so that it is not necessary to cast the element type explicitly. See `YAML home page <https://yaml.org/>`_ for further information on the language.
The example below shows a basic implementation of the TUM description file:
.. code-block:: yaml
:caption: main test file
main:
name: Test example
steps:
- test_item1:
name: test_1
- loop :
name: test cyle
iterator: 5
steps:
- test_item:
name: test_2
- test_item:
name: test_3
.. _sec_configuration_files:
Configuration files
--------------------
A configuration file can be specified in the .tum file or by the command line.
This configuration file is optional.
It can be of three different syntax:
* XML
* YAML
* JSON
The type of file is recognized by the file name extension (.xml, .yaml, .json).
During the test script loading process, the values defined in these configuration files
are added to the global variables and are then accessible from the test items and scripts
(cf. :ref:`global variables<sec_global_variables>`).
The parameter file can be specified in the .tum file root:
.. code-block:: yaml
:caption: configuration files definition
config_file:
- myparam.xml
- config1.json
- config2.yaml
main:
name: Test example
[...]
If nothing is specified, the ``param.xml``, ``param.yaml`` and ``param.json``
are automatically loaded, if present in the test directory.
Files loading
^^^^^^^^^^^^^^^^^^
The ``JSON`` and ``YAML`` configuration files variables are evaluated directly.
The XML files content is evaluated as follows.
.. code-block:: xml
:name: param.xml
<?xml version="1.0" ?>
<root>
<parameter name="param1" value="['abc', 'bcd']"/>
<parameter name="param2" value="0x123454"/>
<parameter name="param3" str="def"/>
</root>
If the ``parameter`` XML item defines:
* ``value`` argument: its content is parsed for variable substitution
(see :ref:`variables expansion<sec_variable_expansion>`) and then evaluated as a python statement,
* ``str`` argument: its content is not evaluated and is kept as a string.
.. _sec_global_variables:
Global variables
-------------------
Global variables feature is adding the possibility for test items and test scripts to access a common
and global variables database.
The global variables dataset is populated from various sources:
* the :ref:`command line<sec_option_define>`,
* :ref:`built-in values<sec_global_variables_builtin>`,
* the :ref:`configuration files<sec_configuration_files>`,
* some test items results,
* the :ref:`helper library API<sec_global_variables_helpers>`, accessible from python scripts.
Theses global variables are used for variable expansion in scripts (see :ref:`variables expansion<sec_variable_expansion>`).
Another possible usage of the global variables is to share persistent data between test steps.
A library allowing python functions to access global variables is available from the
python scripts. See details in section :ref:`helper library<sec_python_helper_library>`.
Apart from the value obtained from the param.xml file, the global varibles entries
contains also built-in specific value, and test item specific values.
.. _sec_global_variables_builtin:
Built-in values
^^^^^^^^^^^^^^^^^^^
The following keys are automatically accessible through the testium
library API (see :ref:`helper library<sec_python_helper_library>`)
* ``test_directory``: the absolute path of the directory of the main .tum file,
* ``test_main_file``: the main .tum file,
* ``os``: the name of the platform which is used. Can be Linux or Windows,
* ``host_name``: The name of the host on which testium is running,
* ``home``: home directory of the current user,
* ``testrun_date``: The date when the test has started (as a string) in
format YYYY-MM-DD,
* ``testrun_time``: The time when the test has started (as a string) in
format HH:MM:SS,
* ``test_name``: The name of the file being executed without extension,
* ``home``: the path of the current user's home directory,
* ``test_outputs``: list of the paths of the test log and test report (if any),
* ``last_test_result``: test result of the last step (see :ref:`sec_item_common`),
* ``ts_start_<item_name>``: timestamp at the start of test item execution (see :ref:`sec_item_common`),
* ``ts_end_<item_name>``: timestamp at the end of test item execution (see :ref:`sec_item_common`),
* ``ts_duration_<item_name>``: duration of test item execution in seconds (see :ref:`sec_item_common`),
* ``cn_<test_name>``: console test item result (see section :ref:`sec_console_test_item`),
* ``fn_<func_name>``: py_func test item result (see section :ref:`sec_func_item`),
* ``cs_<test_name>``: dialog_choices test item result (see section :ref:`sec_dialog_choices_test_item`),
* ``loop_param``: loop iterator (available from within a loop item,
see :ref:`sec_loop_item`),
* ``loop_index``: loop index (available from within a loop item, see
:ref:`sec_loop_item`),
* ``loop_count``: loop number (available from within a loop item, see
:ref:`sec_loop_item`). If the loop number its value is the python constant
``inf``.
Test items entries
^^^^^^^^^^^^^^^^^^^^
All test items attributes can be global variable entry;
when using the entry ``$(<global>)`` before a key value, the corresponding
key entry is searched within the global variables dataset.
References
^^^^^^^^^^^^^^
If the ``dialog_references`` test item has been included (see
:ref:`dialog_reference test item<sec_dialog_references_test_item>`), the global
dict will contain the result of this test item in the key ``tested_items``.
Dialog values
^^^^^^^^^^^^^^^^
All dialog returned values are inserted in the global variables entries with the
key value being the test item name attribute (see :ref:`dialog_value test item<sec_dialog_value_test_item>`).
.. _sec_variable_expansion:
Paramers passing, variable expansion and evaluations
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
One of the most useful functionalities for scalability and flexibility of the
.tum files is the ability to expanse variables at test runtime.
It is done by replacing any occurrence of ``$(my_global)`` with the content of
the variable in the global variables entries (see :ref:`global variables<sec_global_variables>`).
The variable substitution is recursive and checks all the occurrences of the
``$(x)`` pattern in a string.
It is also possible to perform evaluation of python substrings during parameters passing.
It is done by using the ``<@ expr @>`` pattern in a string.
`expr` may then be a correct python expression.
Below are illustrated simple and more complicated cases of expansion and evaluation depending on
their pattern.
.. code-block:: yaml
:caption: variables expansion and evaluation
- let:
name: Dynamic variables expansion
key: $(test)_PASS
values:
- expanse_select: <@"$(expanse_select)".replace("o", "a")@>
- expanse_index: $(expanse_index_$(expanse_select))
- expanse_table: $(expanse_table_$(expanse_select))
- expanse_eval: <@$(expanse_index) == 1@>
Test Items
--------------------
All *testium* steps are described in sequence as test items in the step
list of the main test item (and eventually of the loop test item).
TUM file ``main`` item is itself a variant of test items with a name and an
step list attributes.
.. toctree::
:maxdepth: 3
:caption: Contents:
test_items/items_common_attributes.rst
test_items/check_test_item.rst
test_items/console_test_item.rst
test_items/dialog_choices_test_item.rst
test_items/dialog_image_test_item.rst
test_items/dialog_message_test_item.rst
test_items/dialog_note_test_item.rst
test_items/dialog_question_test_item.rst
test_items/dialog_reference_test_item.rst
test_items/dialog_value_test_item.rst
test_items/func_test_item.rst
test_items/git_test_item.rst
test_items/group_test_item.rst
test_items/json-rpc_test_item.rst
test_items/let_test_item.rst
test_items/loop_test_item.rst
test_items/plot_test_item.rst
test_items/report_test_item.rst
test_items/run_test_item.rst
test_items/sleep_test_item.rst
test_items/unittest_file_test_item.rst
.. include:: includes.rst
.. include:: templates.rst
.. include:: reports.rst
.. include:: other_features.rst

Binary file not shown.

7
package/Testium.desktop Normal file
View File

@@ -0,0 +1,7 @@
[Desktop Entry]
Type=Application
Name=Testium
Exec=testium
Icon=testium
Terminal=false
Categories=Utility;Automated test

View File

@@ -0,0 +1,85 @@
# appimage-builder recipe see https://appimage-builder.readthedocs.io for details
version: 1
script:
- echo "APP_VERSION={{APP_VERSION}}" >> $BUILDER_ENV
- rm -rf AppDir | true
# Make usr and icons dirs
- mkdir -p AppDir/usr/lib/python
- mkdir -p AppDir/usr/share/icons
- mkdir -p AppDir/usr/bin
# Copy the icon
- cp ../testium.png AppDir/usr/share/icons/
AppDir:
path: AppDir/
app_info:
id: testium
name: Testium
icon: testium
version: "{{APP_VERSION}}"
exec: usr/bin/python3
exec_args: -m testium $@
runtime:
env:
SEQUENCER_REV: '{{APP_VERSION}}'
PYTHONPATH: $APPDIR/usr/lib/python3.11/site-packages:$APPDIR/usr/lib/python3.11
QT_QPA_PLATFORM: xcb
path_mappings:
- /usr/share/matplotlib/mpl-data/matplotlibrc:$APPDIR/etc/matplotlibrc
apt:
arch: [amd64]
allow_unauthenticated: true
sources:
- sourceline: deb http://ftp.fr.debian.org/debian bookworm main non-free contrib
- sourceline: deb http://security.debian.org/debian-security bookworm-security
main contrib non-free
- sourceline: deb http://ftp.fr.debian.org/debian bookworm-updates main contrib
non-free
include:
- libxcb-cursor0
- python3
- python3-distutils
- python3-pkg-resources
exclude: []
files:
include: []
exclude:
- usr/share/man
- usr/share/doc/*/README.*
- usr/share/doc/*/changelog.*
- usr/share/doc/*/NEWS.*
- usr/share/doc/*/TODO.*
after_bundle: |
# Set python 3.11 env
export PYTHONHOME=$TARGET_APPDIR/usr
export PYTHONPATH=$TARGET_APPDIR/usr/lib/python3.11/site-packages:$TARGET_APPDIR/usr/lib/python3.11
export PATH=$TARGET_APPDIR/usr/bin:$PATH
# Set python 3.11 as default
ln -fs python3.11 $TARGET_APPDIR/usr/bin/python3
# Install pip
if [ ! -f "get-pip.py" ]; then curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py; fi
python3.11 get-pip.py --break-system-packages
# Install application dependencies in AppDir
python3.11 -m pip install --break-system-packages --upgrade --isolated --no-input --ignore-installed --prefix=$TARGET_APPDIR/usr -r requirements.txt
export PIP_CONFIG_FILE=$HOME/.pip/pip.conf
python3.11 -m pip install --break-system-packages --upgrade --isolated --no-input --ignore-installed --prefix=$TARGET_APPDIR/usr ../../src/dist/testium-{{APP_VERSION}}-py3-none-any.whl
AppImage:
arch: x86_64
update-information: guess

12
package/appimage/build.sh Executable file
View File

@@ -0,0 +1,12 @@
#!/usr/bin/bash
export APP_VERSION=$(<../../src/VERSION)
appimage-builder --recipe AppImageBuilder.yml
RESULT=$?
if [ -n "$1" ] && [ "$1" = "install" ]; then
if [ $RESULT -eq 0 ]; then
install -v "testium-${APP_VERSION}-x86_64.AppImage" "${HOME}/.local/bin/testium"
fi
fi

View File

@@ -0,0 +1,3 @@
tables
pandas
scapy

16
package/pyinstaller/build.sh Executable file
View File

@@ -0,0 +1,16 @@
#! /bin/env sh
SCRIPT_DIR=$(realpath $( dirname "$0"))
rm -r "${SCRIPT_DIR}/build" "${SCRIPT_DIR}/dist"
pwd=$(pwd)
cd ${SCRIPT_DIR}
pyinstaller testium.spec
RESULT=$?
if [ -n "$1" ] && [ "$1" = "install" ]; then
if [ $RESULT -eq 0 ]; then
install -v "dist/testium" "${HOME}/.local/bin/"
fi
fi
cd $pwd

View File

@@ -0,0 +1,58 @@
# -*- mode: python ; coding: utf-8 -*-
a = Analysis(
['../../src/testium/__main__.py'],
pathex=['../../src/testium',
'../../src/testium/main_win/resources'],
binaries=[],
datas=[ ('../../src/VERSION', '.')],
hiddenimports=["git",
"interpreter",
"main_win",
"libs",
"libs.console",
"libs.termconsole",
"libs.console_ssh",
"libs.raw_tcp_console",
"libs.runtime_plot",
"matplotlib.backends.backend_pdf",
"telnetlib3",
"serial",
"yaml",
"pexpect",
"jinja2",
"colorama",
"matplotlib",
"junit_xml",
"lxml",
"tables",
"pandas",],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name='testium',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)

BIN
package/testium.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

3
release_note.txt Normal file
View File

@@ -0,0 +1,3 @@
version 0.1
==============
- Start of the project

15
run.sh Executable file
View File

@@ -0,0 +1,15 @@
#!/bin/bash
#set -x
SCRIPT_PATH="$(readlink -f "$0")"
SCRIPT_DIR=$(realpath $( dirname "$SCRIPT_PATH"))
export PY_VENV_NAME=".venv"
export PY_VENV_DIR="$SCRIPT_DIR/test/tmp/$PY_VENV_NAME"
export REQ_PATH="$SCRIPT_DIR/src/requirements.txt"
bash $SCRIPT_DIR/scripts/build_env.sh
source $SCRIPT_DIR/scripts/set_env.sh
python3 "$SCRIPT_DIR/src/testium" $@

30
scripts/build_env.sh Executable file
View File

@@ -0,0 +1,30 @@
#!/bin/bash
if [ -z $PY_VENV_DIR ]; then
echo "PY_VENV_DIR must be defined"
exit -1
fi
if [ -z $REQ_PATH ]; then
echo "REQ_PATH must be defined"
exit -1
fi
if [ "$1" == "clean" ]; then
rm -Rf "$PY_VENV_DIR"
fi
# Check if venv is installed
python3 -c "import venv"
if [ "$?" -ne 0 ]; then
echo "venv must be installed on the host distribution."
exit -1
fi
# Install the virtual environment if needed
if [ ! -d "$PY_VENV_DIR" ]; then
echo "Creation of the virtual environment"
python3 -m venv "$PY_VENV_DIR"
source "$PY_VENV_DIR/bin/activate"
pip install --extra-index-url https://pypi.python.org/pypi -r $REQ_PATH
fi

32
scripts/qt_generate.sh Executable file
View File

@@ -0,0 +1,32 @@
#!/bin/bash
SCRIPT_DIR=$(realpath $( dirname "$0"))
MAIN_DIR=${SCRIPT_DIR}/../src/testium
EXE_UI=pyside6-uic
EXE_RCC=pyside6-rcc
UIFILES="main_win/testium_core_win.ui"
UIFILES+=" main_win/about_win/about_win.ui"
UIFILES+=" main_win/preference_win/preference_core_win.ui"
UIFILES+=" main_win/f1_win/f1_win_core.ui"
UIFILES+=" interpreter/test_items/dialog_choices_files/choices_dialog_win.ui"
UIFILES+=" interpreter/test_items/dialog_image_files/dialog_image_win.ui"
UIFILES+=" interpreter/test_items/dialog_note_files/dialog_note_win.ui"
UIFILES+=" interpreter/test_items/dialog_sleep_files/dialog_sleep_win.ui"
UIFILES+=" interpreter/test_items/dialog_value_files/dialog_value_win.ui"
UIFILES+=" interpreter/test_items/tested_references_files/tested_refs_win.ui"
RCFILES="main_win/resources/testium_core_win.qrc"
RCFILES+=" main_win/resources/about_win.qrc"
RCFILES+=" main_win/resources/f1_win.qrc"
for f in ${UIFILES}
do
${EXE_UI} "${MAIN_DIR}/$f" > "${MAIN_DIR}/${f%.*}.py"
done
for f in ${RCFILES}
do
${EXE_RCC} "${MAIN_DIR}/$f" > "${MAIN_DIR}/${f%.*}_rc.py"
done

8
scripts/set_env.sh Executable file
View File

@@ -0,0 +1,8 @@
#!/bin/bash
if [ -z $PY_VENV_DIR ]; then
echo "PY_VENV_NAME must be defined"
exit -1
fi
source "$PY_VENV_DIR/bin/activate"

1
src/VERSION Normal file
View File

@@ -0,0 +1 @@
0.1

38
src/pyproject.toml Normal file
View File

@@ -0,0 +1,38 @@
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name="testium"
requires-python = ">=3.11"
authors = [
{name = "François Dausseur", email = "francois@beafrancois.fr"},
]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Programming Language :: Python"
]
dependencies = [
"setuptools",
"pyside6",
"pyyaml",
"pyserial",
"colorama",
"matplotlib",
"telnetlib3",
"jinja2",
"pexpect",
"gitpython",
"junit-xml",
"lxml",
]
dynamic = ["version"]
[project.scripts]
testium = "testium:main"
[tool.setuptools.package-data]
docpkg = ["*.pdf"]
[tool.setuptools.dynamic]
version = {file = ["VERSION"]}

12
src/requirements.txt Normal file
View File

@@ -0,0 +1,12 @@
setuptools
pyside6
pyserial
telnetlib3
pyyaml
pexpect
gitpython
jinja2
colorama
matplotlib
junit-xml
lxml

140
src/testium/__init__.py Executable file
View File

@@ -0,0 +1,140 @@
#!/usr/bin/env python
import sys
import os
import multiprocessing
from pathlib import Path
ourpath = Path(__file__)
ourpath = ourpath.resolve()
sys.path.append(os.path.abspath(ourpath.parent))
from interpreter.utils.eval import evaluate
import interpreter.utils.constants as cst
def main():
# This line sets the method for the "Process" function. It is required for Linux
# support of the test dialogs.
multiprocessing.set_start_method('spawn')
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--version",
help="Returns the version of testium", action='store_true')
parser.add_argument("-b", "--batch-execution",
help="Executes the test in batch mode", action='store_true')
parser.add_argument("-m", "--terminal",
help="Starts terminal mode", action='store_true')
parser.add_argument("-o", "--no-color",
help="Deactivates stdout colors in batch and terminal mode", action='store_true')
parser.add_argument("-c", "--config-file", help="Configuration file",
nargs='+',
default=[])
parser.add_argument("-r", "--run-and-close", action='store_true',
help="Runs the test then closes the application",
required=False)
parser.add_argument("-l", "--log-file", help="log file name", default='')
parser.add_argument("-d", "--define",
help="Configuration passed to the executed tests.",
nargs='+',
type=str,
action='append',
default=[])
parser.add_argument("-p", "--report-file",
help="report file name", default='')
parser.add_argument("-t", "--report-type", help="report file type",
choices=cst.REP_TYPES,
default='')
parser.add_argument("-n", "--report-pattern", help="report file pattern",
nargs='+',
default=[])
parser.add_argument("-i", "--include-path",
help="Python modules search path",
nargs='+',
default=[])
parser.add_argument("-g", "--debug", action='store_true',
help="GUI debug mode",
required=False)
parser.add_argument(
'test_file', help='the test script file', nargs='?', default='')
args = parser.parse_args()
if len(args.include_path)>0:
for p in args.include_path:
sys.path.append(p)
defines = {}
defs = []
for define in args.define:
defs += define
for define in defs:
d = define.split('=', 1)
if d[0].strip() != '':
if len(d) > 1:
_, edef = evaluate(d[1])
defines.update({d[0].strip(): edef})
else:
defines.update({d[0].strip(): True})
cf = []
for c in args.config_file:
conf = c.strip('\"').strip("\'")
if not os.path.isabs(conf):
conf = os.path.join(os.getcwd(), conf)
cf.append(conf)
tf = args.test_file.strip('\"').strip("\'")
rf = args.report_file.strip('\"').strip("\'")
lf = args.log_file.strip('\"').strip("\'")
pn = []
for p in args.report_pattern:
pn.append(p.strip('\"').strip("\'"))
if args.version:
# initilization of the settings (used to know if git supported)
import interpreter.utils.settings as prefs
prefs.init()
from interpreter.utils.version import get_testium_version
print(get_testium_version())
elif args.terminal:
import select
from interpreter.terminal import Terminal
if (lf != '') or (rf != '') or (tf != '') or (pn != []):
print('"-l", "-p", "-t", "-n" options are not supported in this mode.')
t = Terminal(os.getcwd(), cf, defines, args.no_color)
loop = 1
while loop:
try:
loop = 0
t.cmdloop()
except KeyboardInterrupt:
print("\n<ctrl-c>")
loop = 1
except Exception as exc:
if str(exc) == 'quit':
break
print(exc)
loop = 1
elif args.batch_execution:
if (lf != ''):
print('"-l" option is not supported in this mode.')
from interpreter.batch import Batch
b = Batch(tf, cf, defines, rf, args.report_type, pn, args.no_color)
else:
from main_win.testium_win import MainWin
MainWin(tf, config_files=cf,
run=args.run_and_close,
log_file=lf,
defines=defines,
report=rf,
report_type=args.report_type,
report_pattern=pn,
debug=args.debug)

25
src/testium/__main__.py Normal file
View File

@@ -0,0 +1,25 @@
import os, sys
import logging
import traceback
logging.basicConfig(
level=logging.ERROR,
filename=os.path.join(os.path.normpath(os.getcwd()), "crash.txt"),
format="%(asctime)s - %(levelname)s - %(message)s"
)
def exception_handler(typ_exc, value, trbk):
"""Testium Exception handling"""
logging.error("An unmanaged exception occured", exc_info=(typ_exc, value, trbk))
print(f"Critical failure : '{value}'.")
tb = traceback.format_exception(typ_exc, value, trbk)
print("".join(tb[-4:]))
sys.excepthook = exception_handler
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..'))
from testium import main
if __name__ == '__main__':
main()

View File

View File

@@ -0,0 +1,98 @@
import os
import sys
import platform
from time import sleep
from signal import signal, SIGINT
from queue import Empty
from multiprocessing import Queue
from interpreter.process import TestProcess
from interpreter.utils.test_ctrl import TestSetController
from interpreter.utils.tum_except import ETUMFileError
from interpreter.utils.stdout_redirect import stdio_redir
class Batch:
def __init__(
self,
test_file,
config_files,
defines,
report_file,
report_type,
report_pattern,
no_color,
):
try:
try:
file_name = os.path.abspath(test_file)
initial_dir = os.path.dirname(file_name)
if not os.path.isdir(initial_dir):
raise ETUMFileError("Could not find %s directory" % (initial_dir))
if not os.path.isfile(file_name):
raise ETUMFileError("Could not find %s file" % (file_name))
if not file_name:
raise ETUMFileError("No file to load")
outstream = sys.stdout
if "Linux" in platform.system() and not no_color:
try:
from interpreter.utils.termlog import TermLog
outstream = TermLog(sys.stdout)
stdio_redir.redirect(outstream)
except ModuleNotFoundError:
print(
"Colored console not supported by the system."
+ " If you want it, please install colorama module"
)
signal(SIGINT, self.sigint_handler)
msg_queue = Queue()
self.tst_ctrl = TestSetController()
tst_proc = TestProcess(
file_name,
msg_queue,
self.tst_ctrl,
config_files,
defines,
)
tst_proc.start()
while not self.tst_ctrl.control("loaded"):
sleep(0.1)
self.tst_ctrl.control(
"report",
rep_path=report_file,
rep_type=report_type,
pattern=report_pattern,
)
# Start test execution
self.tst_ctrl.control("execute")
# Wait for the "finished" signal
while True:
try:
m = msg_queue.get(timeout=0.2)
if m.get("id", None) is None:
# No id -> finished
break
except Empty:
continue
# Close the process and wait for termination
self.tst_ctrl.control("close")
tst_proc.join()
except Exception as e:
print("Exception encountered:")
print(str(e))
finally:
stdio_redir.restore()
def sigint_handler(self, signal_received, frame):
self.tst_ctrl.control("stop")

View File

@@ -0,0 +1,230 @@
import os
import sys
from multiprocessing import Process, Queue, Pipe
from queue import Empty
from threading import Thread
from time import sleep
import traceback
import libs.testium as tm
from interpreter.utils.params import expanse
from interpreter.utils.string_queue import StringQueue
from interpreter.utils.test_ctrl import TestSetController
from interpreter.utils.test_init import (
env_init,
load_test,
test_run_init,
test_run_header,
locate_report_file,
backup_gd,
restore_gd,
)
from interpreter.test_set import TestSet
from interpreter.utils.stdout_redirect import stdio_redir
from interpreter.utils.tum_except import print_exception
from interpreter.utils.func_exec import func_call_init
from interpreter.utils.api_srv import api_request
class TestProcess(Process):
def __init__(
self,
file_name,
status_queue: Queue,
tst_control: TestSetController,
config_files,
defines,
) -> None:
super().__init__()
self.__fname = file_name
self.__squeue = status_queue
self.__tctrl = tst_control
self.__cfgf = config_files
self.__defs = defines
self.__exec = False
self.__loaded = False
self.__closed = False
self.__pconn = self.redirect_stdout()
def run(self):
try:
try:
# Thread for stdout redirection
in_stream = StringQueue()
self.redir = Thread(target=self.send_stdout, args=[in_stream])
self.redir.daemon = True
stdio_redir.redirect(in_stream)
self.redir.start()
test_dir = os.path.dirname(os.path.abspath(self.__fname))
env_init()
# Load the test file
test_dict, cfg_files = load_test(
self.__fname, test_dir, self.__cfgf, self.__defs)
# Backup the global dict in case of restart of the test
gdict = backup_gd()
# The path of the test file is included in PYTHONPATH
sys.path.append(os.path.dirname(self.__fname))
# Now create the test structure and objects
test_set = TestSet(self.__fname, test_dict, self.__squeue)
# Thread for incoming control commands
self.init_commands(test_set)
self.cmd_th = Thread(
target=self.process_control_commands, args=[self.__tctrl])
self.cmd_th.daemon = True
self.cmd_th.start()
test_set.report_path = locate_report_file(test_set.report_path)
# Python functions call subprocess initialization
fproc = func_call_init(tm.gd("python_path", ""), api_request)
self.__loaded = True
while True:
# waiting for a control command
while (not self.__exec) and (not self.__closed):
sleep(0.2)
# if close is required
if self.__closed:
break
# Test is started
try:
try:
try:
test_run_init()
print(test_run_header())
fproc.start()
fproc.wait_ready()
test_set.execute()
finally:
if test_set.success():
print("Test run success.")
else:
print("Test run failed.")
test_set.run_post_exec()
finally:
# Stop function execution process
fproc.stop()
fproc.join()
self.__exec = False
# Sends signal to the GUI
self.send_finished()
restore_gd(gdict)
except Exception as e:
print_exception(e)
except Exception as e:
print_exception(e)
finally:
self.exit()
def init_commands(self, test_set: TestSet):
self.__cmds = {
"pause": test_set.pause,
"cont": test_set.cont,
"tree": test_set.tree,
"report": test_set.set_report,
"stop": test_set.stop,
"loaded": self.loaded,
"execute": self.execute,
"add_breakpoint": test_set.addBreakpoint,
"del_breakpoint": test_set.delBreakpoint,
"skipped_state": test_set.getSkippedState,
"enabled_state": test_set.getEnabledState,
"process_param": self.process_param,
"set_test_outputs": self.set_test_outputs,
"set_enabled_state": test_set.setEnabledState,
"check_uncheck_all": test_set.checkUncheckAll,
"get_folded": test_set.getFolded,
"close": self.close,
}
def exit(self):
self.__closed = True
if hasattr(self, "cmd_th"):
self.cmd_th.join()
self.redir.join()
stdio_redir.restore()
stdio_redir.stop()
def send_finished(self):
status = {'id': None,
'name': "test_process",
'status': 'finished'}
self.__squeue.put(status)
def execute(self):
self.__exec = True
def loaded(self):
return self.__loaded
def close(self):
self.__closed = True
def process_param(self, param):
return expanse(param)
def set_test_outputs(self, outputs: list):
tm.setgd("test_outputs", outputs)
def process_control_commands(self, tctrl):
term = False
while (not term) and (not self.__closed):
cmd = ""
res = None
args = {}
try:
qcontent = tctrl.ctrl.get(timeout=0.2)
try:
cmd = list(qcontent.keys())[0]
args = qcontent[cmd]
if cmd == "exit":
term = True
break
try:
if isinstance(args, dict):
res = {cmd: self.__cmds[cmd](**args)}
elif args is None:
res = {cmd: self.__cmds[cmd]()}
except:
res = (None, "function unknown or call failed")
except:
res = (None, "Malformed command")
tctrl.resp.put(res)
except Empty:
continue
def redirect_stdout(self):
pipe = pconn, cconn = Pipe()
redir = Thread(target=self.capture_stdout, args=(cconn,))
redir.daemon = True
redir.start()
return pconn
def send_stdout(self, stream):
while not self.__closed:
try:
data = stream.read(block=True, timeout=0.2)
if data != "":
self.__pconn.send(data)
except RuntimeError:
continue
def capture_stdout(self, cconn):
while True:
try:
# read the pipe data
data = cconn.recv()
print(data, end="")
except EOFError:
# exit the loop is the pipe is closed
break

View File

@@ -0,0 +1,243 @@
try:
import readline
except:
pass
from cmd import Cmd
import os
import sys
from yaml import load, Loader
import functools
import platform
import types
import inspect
# test modules
from interpreter.utils.test_init import (
env_init, prepare_global, set_standard_gd_keys,
update_global, test_run_init, test_run_header, load_test)
from interpreter.utils.globdict import (global_dict)
import libs.testium as tm
from interpreter.utils.constants import TestItemType as cst
from interpreter.test_report.test_report import TestReport
class FakeQueue:
def put(self, arg):
pass
def func(self, args):
if not args.startswith("{"):
args = "{"+args+"}"
y = load(args, Loader)
obj = self.current_item(y, status_queue=FakeQueue())
obj.report = self.report
res = obj.execute()
if not (res.value is None):
print('result : {}'.format(res.value))
print(res.test_result)
class Terminal(Cmd):
SUPPORTED_TESTS = [
cst.TYPE_SLEEP,
cst.TYPE_LET,
cst.TYPE_FUNCTION,
cst.TYPE_CONSOLE,
cst.TYPE_IMAGE_DLG,
cst.TYPE_MESSAGE_DLG,
cst.TYPE_QUESTION_DLG,
cst.TYPE_VALUE_DLG,
]
SUPPORTED_GROUPS = [
cst.TYPE_GROUP,
cst.TYPE_CYCLE
]
def __init__(self, working_dir, config_files, defines, no_color):
super().__init__()
self.working_dir = working_dir
self.config_files = config_files
self.current_item = None
report = TestReport(None)
self.report = report
env_init()
prepare_global()
# Define the builtin variables
set_standard_gd_keys("Unnamed", self.working_dir, '', config_files)
update_global([], defines)
# creation of the functions
for tst in self.SUPPORTED_TESTS:
meth_name = "do_" + tst.item_cmd
# copy of the function
f = types.FunctionType(func.__code__, func.__globals__, name=meth_name,
argdefs=func.__defaults__,
closure=func.__closure__)
f = functools.update_wrapper(f, func)
f.__kwdefaults__ = func.__kwdefaults__
f.__doc__ = tst.item_class.__doc__
setattr(self, meth_name, types.MethodType(f, self))
test_run_init()
self.prompt = "(testium)~ "
# display header
print(test_run_header())
# redirect output
if 'Linux' in platform.system() and not no_color:
from interpreter.utils.stdout_redirect import stdio_redir
try:
from interpreter.utils.termlog import TermLog
stdio_redir.redirect(TermLog(sys.stdout))
except ModuleNotFoundError:
tm.print_info('Colored console not supported by the system.' +
' If you want it, please install colorama module')
def precmd(self, line: str) -> str:
c = line.split(" ", 1)[0].strip()
self.current_item = None
for tst in self.SUPPORTED_TESTS:
if c == tst.item_cmd:
self.current_item = tst.item_class
break
return line
def load_test_recursively(self, tree_parent, parent_seq, status_queue):
try:
parent_seq_name = parent_seq['name']
except KeyError:
parent_seq['name'] = "sequence"
except TypeError:
raise Exception("Syntax error in an item of type {} which is a child of {}".format(
tree_parent.type(), tree_parent.parent().name()))
try:
parent_seq_actions = parent_seq['steps']
except KeyError:
raise Exception(' No action list found for "%s" sequence'
% (parent_seq_name))
# if action is a dictionary , we assume it is a single action
# that has not been nested in a list, so do it
if isinstance(parent_seq_actions, (dict)):
parent_seq_actions = [parent_seq_actions]
if not isinstance(parent_seq_actions, (list, tuple)):
raise Exception('Actions list not valid.')
# first we merged to the same level 'sequence dict entries and list within the list
counter = 0
test_dir = tm.gd('test_directory')
while (counter < len(parent_seq_actions)):
action = parent_seq_actions[counter]
# if action is a list raise up to the the same level,
# ie insert action element into the parent_seq_actions
if isinstance(action, (list, tuple)):
parent_seq_actions[counter:counter+1] = action
continue
# if action is a NoneType skip and continue
# (when pointing to an unused alias for instance)
if action is None:
counter += 1
continue
# if action is a sequence we insert its entry into the action list
if 'sequence' in action:
parent_seq_actions[counter:counter+1] = action['sequence']
continue
else:
executed = False
for it in [*self.SUPPORTED_TESTS, *self.SUPPORTED_GROUPS]:
if it.item_cmd in action:
executed = True
item = (it.item_class)(action[it.item_cmd],
tree_parent,
status_queue)
# check for sequence type:
if it.item_cmd == cst.TYPE_UNITTEST_FILE.item_cmd:
item.setTestDir(test_dir)
item.load()
elif ((it.item_cmd == cst.TYPE_CYCLE.item_cmd) or
(it.item_cmd == cst.TYPE_GROUP.item_cmd)):
self.load_test_recursively(
item, action[it.item_cmd], status_queue)
if not executed:
raise Exception('action type is not known "{}"'.format(
list(action.keys())[0]))
counter += 1
def __setReportRecursively(self, parent):
for i in range(parent.childCount()):
parent.child(i).report = self.report
self.__setReportRecursively(parent.child(i))
def setReport(self, root_item):
root_item.report = self.report
self.__setReportRecursively(root_item)
def get_names(self):
memb = inspect.getmembers(self)
return [n[0] for n in memb if (inspect.ismethod(n[1]) and n[0].startswith("do_"))]
def do_load(self, args):
"""load function.
This function loads and executes a testium sub-script.
The loaded sequence can't be a main testium script ("testium -b" option is
defined for such a usage).
Accepted files are with extension "*.tum".
usage:
load path/to/my/sequence.tum
"""
file = args.strip()
suff = file[-4:]
if not suff in ['.tum']:
raise Exception('Wrong input file extension')
if not (os.path.exists(file) and os.path.isfile(file)):
raise Exception(
'"{}" does not exist or is not a file.'.format(file))
d, _ = load_test(file)
if not isinstance(d, list):
raise Exception(
"The file root object must be a list. A \"main\" tum can't be loaded from here (use batch mode instead).")
if (len(d) == 1) and isinstance(d[0], dict) and (not d[0].get('sequence', None) is None):
d = d[0]['sequence']
sq = FakeQueue()
root_item = (cst.TYPE_ROOT.item_class)(
dict_item={'steps': d}, status_queue=sq)
self.load_test_recursively(root_item, {'steps': d}, sq)
self.setReport(root_item)
res = root_item.execute()
if not (res.value is None):
print('"{}" execution overall result: {}'.format(file, res.value))
print(res.test_result)
def do_gd(self, args):
"""Variables lists and values.
usage:
gd
gd home
"""
if args != '':
res = tm.gd(args, None)
if res is None:
raise Exception(
'the variable: "{}" has not been found.'.format(args))
print(res)
return
for k in global_dict.keys():
print('{}: {}'.format(str(k), str(global_dict[k])))
def do_quit(self, args):
'''Quit the application.'''
raise Exception('quit')

View File

@@ -0,0 +1,254 @@
import sys
import os
from multiprocessing import freeze_support
from itertools import chain
from PySide6.QtGui import QIcon, QPixmap
from PySide6.QtWidgets import QApplication, QDialog, QDialogButtonBox
from PySide6.QtCore import Qt, QSettings, QSize
from PySide6.QtGui import QFont, QFontInfo
from PySide6.QtWidgets import QTreeWidgetItem
# try:
from interpreter.test_items.dialog_choices_files import choices_dialog_win
# except:
# import choices_dialog_win
def __iter__QTreeWidgetItem(self):
for item in chain(*map(iter, self.children())):
yield item
yield self
def childrenQTreeWidgetItem(self):
return [self.child(i) for i in range(self.childCount())]
QTreeWidgetItem.name = ""
QTreeWidgetItem.__iter__ = __iter__QTreeWidgetItem
QTreeWidgetItem.children = childrenQTreeWidgetItem
class ChoicesTreeItem(QTreeWidgetItem):
def __init__(self, parent, dic, default_icon):
super().__init__()
self.name = dic.get("name", "")
self.setFlags(self.flags() | Qt.ItemIsUserCheckable)
self.setCheckState(0, Qt.Checked)
parent.addChild(self)
self._default_icon = default_icon
self.setRowIcon(dic.get("icon", ""))
def setRowIcon(self, icon_path):
icon = None
if icon_path != "":
if os.path.exists(icon_path):
try:
pmap = QPixmap(icon_path)
icon = QIcon(pmap)
self.setIcon(0, icon)
except:
# we don't want to crash for an icon
print(f"WARN Impossible to load '{icon_path}' icon.")
if (icon is None) and (self._default_icon is not None):
self.setIcon(0, self._default_icon)
class ChoicesDialog(QDialog, choices_dialog_win.Ui_Dialog):
def __init__(self):
super().__init__()
self._default_icon = None
self.setupUi(self)
self.choicesView.setColumnCount(2)
self.choicesView.setAlternatingRowColors(True)
self.choicesView.setIconSize(QSize(24, 24))
font = QFont()
font.setPointSize(12)
self.choicesView.setFont(font)
self.choicesView.setAlternatingRowColors(True)
self.choicesView.header().setVisible(True)
self.choicesView.header().setDefaultSectionSize(50)
self.choicesView.header().setMinimumSectionSize(50)
self.choicesView.header().setStretchLastSection(False)
self.choicesView.headerItem().setText(0, "name")
self.choicesView.setColumnWidth(0, 300)
self.choicesView.headerItem().setText(1, "description")
self.choicesView.setColumnWidth(1, 800)
self.root = self.choicesView.invisibleRootItem()
def connect_checked(self):
self.choicesView.itemChanged.connect(self.on_testChecked)
def apply_default_icon(self, path):
if (path is not None) and os.path.exists(path):
try:
pmap = QPixmap(path)
self._default_icon = QIcon(pmap)
except:
# we don't want to crash for an icon
print(f"WARN Impossible to load '{path}' icon.")
elif path is not None:
print("Icon not loaded since it is not a valid path.")
def populate_tree(self, parent, choices):
if not isinstance(choices, list):
return
for choice in choices:
name = choice.get("name", "")
desc = choice.get("description", "")
if name == "":
continue
tree_item = ChoicesTreeItem(parent, choice, self._default_icon)
tree_item.setText(0, name)
tree_item.setText(1, desc)
sub_choices = choice.get("choices", None)
if sub_choices is not None:
self.populate_tree(tree_item, sub_choices)
def __foldRecursively(self, tree_item, is_fold):
for i in range(tree_item.childCount()):
if tree_item.child(i).childCount() > 0:
tree_item.child(i).setExpanded(not is_fold)
self.__foldRecursively(tree_item.child(i), is_fold)
def foldAll(self, is_fold):
self.__foldRecursively(self.root, is_fold)
def on_testChecked(self, item, index):
self.updateTreeCheckState(item, Qt.Checked == item.checkState(0))
def updateTreeCheckState(self, tree_item, is_checked):
# treat the case of the invisible root
if tree_item is self.root:
for i in range(self.root.childCount()):
self.updateTreeCheckState(self.root.child(i), is_checked)
else:
if is_checked:
tree_item.setCheckState(0, Qt.Checked)
else:
tree_item.setCheckState(0, Qt.Unchecked)
for i in range(tree_item.childCount()):
self.updateTreeCheckState(tree_item.child(i), is_checked)
def checked_state(self, parent=None):
if parent is None:
return self.checked_state(self.root)
sub_choices = []
for i in range(parent.childCount()):
sub_choices.append(self.checked_state(parent.child(i)))
if parent is self.root:
res = sub_choices
else:
res = {
"name": parent.name,
"checked": Qt.Checked == parent.checkState(0),
}
if len(sub_choices) > 0:
res.update({"choices": sub_choices})
return res
def apply_checked(self, choice, parent=None):
if parent is None:
self.apply_checked(choice, self.root)
return
if not isinstance(choice, list):
return
if len(choice) != parent.childCount():
return
for i in range(parent.childCount()):
if not isinstance(choice[i], dict):
return
if choice[i].get("checked", True) == True:
parent.child(i).setCheckState(0, Qt.Checked)
else:
parent.child(i).setCheckState(0, Qt.Unchecked)
sub_choices = choice[i].get("choices", None)
if sub_choices is not None:
self.apply_checked(sub_choices, parent.child(i))
def main(args, conn=None):
SettingsCompagny = "Testium"
SettingsApplication = "testium_choices_dlg_" + args[0]
SettingsLastChoices = "last_choice"
success = True
app = QApplication()
d = ChoicesDialog()
d.setFixedSize(800, 600)
d.setWindowFlags(Qt.WindowStaysOnTopHint)
d.setWindowTitle(args[0])
d.labelDialog.setText(args[1])
d.labelDialog.setAlignment(Qt.AlignCenter)
d.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok)
d.apply_default_icon(args[3])
d.populate_tree(d.root, args[2])
d.foldAll(False)
settings = QSettings(SettingsCompagny, SettingsApplication)
last_choice = settings.value(SettingsLastChoices, "")
d.apply_checked(last_choice)
d.connect_checked()
d.choicesView.setFocus()
dres = d.exec()
if dres == QDialog.Rejected:
success = False
# build the answer:
result = d.checked_state()
if conn:
settings.setValue(SettingsLastChoices, result)
conn.send([result, success])
conn.close()
else:
print(result, end="")
if hasattr(sys, "frozen"):
# all standard streams are replaced by dummy one to avoid cx_freeze flushing bug.
class dummyStream:
"""dummyStream behaves like a stream but does nothing."""
def __init__(self):
pass
def write(self, data):
pass
def read(self, data):
pass
def flush(self):
pass
def close(self):
pass
# and now redirect all default streams to this dummyStream:
sys.stdout = dummyStream()
sys.stderr = dummyStream()
sys.stdin = dummyStream()
sys.__stdout__ = dummyStream()
sys.__stderr__ = dummyStream()
sys.__stdin__ = dummyStream()
if __name__ == "__main__":
main(sys.argv[1:])

View File

@@ -0,0 +1,66 @@
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'choices_dialog_win.ui'
##
## Created by: Qt User Interface Compiler version 6.10.1
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
QMetaObject, QObject, QPoint, QRect,
QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QAbstractButton, QApplication, QDialog, QDialogButtonBox,
QHeaderView, QLabel, QSizePolicy, QTreeWidget,
QTreeWidgetItem, QVBoxLayout, QWidget)
class Ui_Dialog(object):
def setupUi(self, Dialog):
if not Dialog.objectName():
Dialog.setObjectName(u"Dialog")
Dialog.resize(481, 386)
Dialog.setModal(True)
self.verticalLayout_2 = QVBoxLayout(Dialog)
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.verticalLayout = QVBoxLayout()
self.verticalLayout.setObjectName(u"verticalLayout")
self.labelDialog = QLabel(Dialog)
self.labelDialog.setObjectName(u"labelDialog")
font = QFont()
font.setPointSize(22)
self.labelDialog.setFont(font)
self.verticalLayout.addWidget(self.labelDialog)
self.choicesView = QTreeWidget(Dialog)
self.choicesView.setObjectName(u"choicesView")
self.choicesView.setColumnCount(0)
self.verticalLayout.addWidget(self.choicesView)
self.buttonBox = QDialogButtonBox(Dialog)
self.buttonBox.setObjectName(u"buttonBox")
self.verticalLayout.addWidget(self.buttonBox)
self.verticalLayout_2.addLayout(self.verticalLayout)
self.retranslateUi(Dialog)
self.buttonBox.accepted.connect(Dialog.accept)
self.buttonBox.rejected.connect(Dialog.reject)
QMetaObject.connectSlotsByName(Dialog)
# setupUi
def retranslateUi(self, Dialog):
Dialog.setWindowTitle(QCoreApplication.translate("Dialog", u"Dialog", None))
self.labelDialog.setText(QCoreApplication.translate("Dialog", u"TextLabel", None))
# retranslateUi

View File

@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>481</width>
<height>386</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="labelDialog">
<property name="font">
<font>
<pointsize>22</pointsize>
</font>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<widget class="QTreeWidget" name="choicesView">
<property name="columnCount">
<number>0</number>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox"/>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>240</x>
<y>362</y>
</hint>
<hint type="destinationlabel">
<x>240</x>
<y>192</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>240</x>
<y>362</y>
</hint>
<hint type="destinationlabel">
<x>240</x>
<y>192</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -0,0 +1,72 @@
import sys
import os
from PySide6.QtCore import (Qt)
from PySide6.QtWidgets import (QApplication, QDialog)
from PySide6 import (QtGui)
try:
from interpreter.test_items.dialog_image_files import dialog_image_win
except:
import dialog_image_win
from multiprocessing import freeze_support
class TestDialogWindow(QDialog, dialog_image_win.Ui_Dialog):
def __init__(self):
super().__init__()
self.setupUi(self)
def main(args, conn):
success = True
app = QApplication(args)
d = TestDialogWindow()
d.setFixedSize(700,600)
d.setWindowFlags(Qt.WindowStaysOnTopHint)
d.setWindowTitle(args[0])
d.labelDialog.setText(args[1])
image = QtGui.QImage(args[2])
if image.isNull():
print('Image %s could not be loaded...' % (args[2]))
success = False
else:
image2 = image.scaled(d.labelImage.width(), d.labelImage.height(),
aspectMode=Qt.KeepAspectRatio)
d.labelImage.setPixmap(QtGui.QPixmap.fromImage(image2))
dres = d.exec()
if dres == QDialog.Rejected:
success = False
if conn is not None:
conn.send(success)
conn.close()
if hasattr(sys, "frozen"):
#all standard streams are replaced by dummy one to avoid cx_freeze flushing bug.
class dummyStream:
''' dummyStream behaves like a stream but does nothing. '''
def __init__(self): pass
def write(self,data): pass
def read(self,data): pass
def flush(self): pass
def close(self): pass
# and now redirect all default streams to this dummyStream:
sys.stdout = dummyStream()
sys.stderr = dummyStream()
sys.stdin = dummyStream()
sys.__stdout__ = dummyStream()
sys.__stderr__ = dummyStream()
sys.__stdin__ = dummyStream()
if __name__ == '__main__':
main(sys.argv[1:], None)

View File

@@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'dialog_image_win.ui'
##
## Created by: Qt User Interface Compiler version 6.10.1
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
QMetaObject, QObject, QPoint, QRect,
QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QFont, QFontDatabase, QGradient, QIcon,
QImage, QKeySequence, QLinearGradient, QPainter,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QAbstractButton, QApplication, QDialog, QDialogButtonBox,
QLabel, QSizePolicy, QWidget)
class Ui_Dialog(object):
def setupUi(self, Dialog):
if not Dialog.objectName():
Dialog.setObjectName(u"Dialog")
Dialog.setWindowModality(Qt.WindowModal)
Dialog.resize(700, 600)
Dialog.setSizeGripEnabled(False)
Dialog.setModal(True)
self.buttonBox = QDialogButtonBox(Dialog)
self.buttonBox.setObjectName(u"buttonBox")
self.buttonBox.setGeometry(QRect(10, 560, 681, 32))
self.buttonBox.setOrientation(Qt.Horizontal)
self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok)
self.labelDialog = QLabel(Dialog)
self.labelDialog.setObjectName(u"labelDialog")
self.labelDialog.setGeometry(QRect(10, 10, 681, 71))
font = QFont()
font.setPointSize(20)
self.labelDialog.setFont(font)
self.labelDialog.setAlignment(Qt.AlignCenter)
self.labelDialog.setWordWrap(True)
self.labelImage = QLabel(Dialog)
self.labelImage.setObjectName(u"labelImage")
self.labelImage.setGeometry(QRect(10, 80, 681, 471))
self.labelImage.setAlignment(Qt.AlignCenter)
self.retranslateUi(Dialog)
self.buttonBox.accepted.connect(Dialog.accept)
self.buttonBox.rejected.connect(Dialog.reject)
QMetaObject.connectSlotsByName(Dialog)
# setupUi
def retranslateUi(self, Dialog):
Dialog.setWindowTitle(QCoreApplication.translate("Dialog", u"Dialog", None))
self.labelDialog.setText(QCoreApplication.translate("Dialog", u"TextLabel", None))
self.labelImage.setText("")
# retranslateUi

View File

@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="windowModality">
<enum>Qt::WindowModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>700</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<property name="sizeGripEnabled">
<bool>false</bool>
</property>
<property name="modal">
<bool>true</bool>
</property>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="geometry">
<rect>
<x>10</x>
<y>560</y>
<width>681</width>
<height>32</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
<widget class="QLabel" name="labelDialog">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>681</width>
<height>71</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>20</pointsize>
</font>
</property>
<property name="text">
<string>TextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
<widget class="QLabel" name="labelImage">
<property name="geometry">
<rect>
<x>10</x>
<y>80</y>
<width>681</width>
<height>471</height>
</rect>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

Some files were not shown because too many files have changed in this diff Show More