pytestflow 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bootstrap_templates/__init__.py +0 -0
- bootstrap_templates/config.yaml +13 -0
- bootstrap_templates/custom_step_types/__init__.py +0 -0
- bootstrap_templates/custom_step_types/custom_step_template.py +7 -0
- bootstrap_templates/process_models/__init__.py +0 -0
- bootstrap_templates/process_models/reporting/README.md +48 -0
- bootstrap_templates/process_models/reporting/__init__.py +0 -0
- bootstrap_templates/process_models/reporting/default_report.html.j2 +122 -0
- bootstrap_templates/process_models/reporting/html_report.py +331 -0
- bootstrap_templates/process_models/sequential_model.py +141 -0
- bootstrap_templates/test_sequences/__init__.py +0 -0
- bootstrap_templates/test_sequences/basic_sequence.py +62 -0
- bootstrap_templates/test_sequences/message_box_and_flow_control.py +169 -0
- bootstrap_templates/test_sequences/motherboard_test_sequence.py +125 -0
- bootstrap_templates/test_sequences/step_types_quickstart.py +168 -0
- pytestflow/README.md +13 -0
- pytestflow/__init__.py +19 -0
- pytestflow/backend/__init__.py +2 -0
- pytestflow/backend/event_bus.py +27 -0
- pytestflow/backend/frontend/assets/full_logo-D1DRTUt8.svg +21 -0
- pytestflow/backend/frontend/assets/index-480TOyh4.js +2 -0
- pytestflow/backend/frontend/assets/index-qEI3VAQU.css +1 -0
- pytestflow/backend/frontend/index.html +14 -0
- pytestflow/backend/frontend/logo.svg +21 -0
- pytestflow/backend/handlers.py +214 -0
- pytestflow/backend/report_manager.py +15 -0
- pytestflow/backend/sequences_info.py +130 -0
- pytestflow/backend/start_backend.py +118 -0
- pytestflow/backend/uuids_handler.py +67 -0
- pytestflow/backend/websocket_gateway.py +91 -0
- pytestflow/cli.py +183 -0
- pytestflow/config/__init__.py +0 -0
- pytestflow/config/config_manager.py +44 -0
- pytestflow/core/README.md +110 -0
- pytestflow/core/__init__.py +15 -0
- pytestflow/core/context.py +41 -0
- pytestflow/core/core.py +112 -0
- pytestflow/core/pytestflow_states.py +88 -0
- pytestflow/core/runtime_control.py +164 -0
- pytestflow/core/seq_file_runner.py +38 -0
- pytestflow/core/sequence.py +404 -0
- pytestflow/core/utils.py +81 -0
- pytestflow/flow_utils/README.md +6 -0
- pytestflow/flow_utils/__init__.py +0 -0
- pytestflow/flow_utils/conditions.py +0 -0
- pytestflow/flow_utils/transitions.py +0 -0
- pytestflow/starter_here.md +43 -0
- pytestflow/steps/README.md +43 -0
- pytestflow/steps/__init__.py +15 -0
- pytestflow/steps/action_step.py +94 -0
- pytestflow/steps/common.py +51 -0
- pytestflow/steps/df_numeric_limits.py +151 -0
- pytestflow/steps/flow_control.py +86 -0
- pytestflow/steps/message_pop_up.py +76 -0
- pytestflow/steps/numeric_limit.py +109 -0
- pytestflow/steps/pass_fail.py +49 -0
- pytestflow/steps/string_check.py +104 -0
- pytestflow/steps/waveform_limit.py +170 -0
- pytestflow-0.2.0.dist-info/METADATA +73 -0
- pytestflow-0.2.0.dist-info/RECORD +63 -0
- pytestflow-0.2.0.dist-info/WHEEL +5 -0
- pytestflow-0.2.0.dist-info/entry_points.txt +2 -0
- pytestflow-0.2.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from pytestflow.core.sequence import TestSequence
|
|
2
|
+
from pytestflow.steps.numeric_limit import numeric_limit_step
|
|
3
|
+
from pytestflow.steps.pass_fail import pass_fail_step
|
|
4
|
+
|
|
5
|
+
# ------------------------------------------------------------
|
|
6
|
+
# STEP 1: Numeric limit validation example
|
|
7
|
+
# ------------------------------------------------------------
|
|
8
|
+
# This step demonstrates how a numeric_limit_step works.
|
|
9
|
+
#
|
|
10
|
+
# The wrapped function MUST return a numeric value (int or float).
|
|
11
|
+
#
|
|
12
|
+
# The framework will validate the output using the selected comparator:
|
|
13
|
+
# - "ge" → greater or equal
|
|
14
|
+
# - "le" → less or equal
|
|
15
|
+
# - "between" → value must be within [min, max]
|
|
16
|
+
# - "outside" → value must be outside [min, max]
|
|
17
|
+
#
|
|
18
|
+
# This is a generic example and does not represent any specific domain.
|
|
19
|
+
@numeric_limit_step(name="measure_vcore", limit=(1.0, 1.3), mode="between")
|
|
20
|
+
def measure_vcore():
|
|
21
|
+
return 1.15
|
|
22
|
+
|
|
23
|
+
# ------------------------------------------------------------
|
|
24
|
+
# STEP 2: Pass / Fail validation example
|
|
25
|
+
# ------------------------------------------------------------
|
|
26
|
+
# This step demonstrates a simple boolean-based check.
|
|
27
|
+
# The function must return True (pass) or False (fail).
|
|
28
|
+
@pass_fail_step(name="functional_check")
|
|
29
|
+
def functional_check():
|
|
30
|
+
return True
|
|
31
|
+
|
|
32
|
+
# ------------------------------------------------------------
|
|
33
|
+
# TEST SEQUENCE DEFINITION
|
|
34
|
+
# ------------------------------------------------------------
|
|
35
|
+
# A TestSequence represents an executable workflow in PyTestFlow.
|
|
36
|
+
#
|
|
37
|
+
# Structure:
|
|
38
|
+
# - setup_steps → executed before main execution (initialization phase)
|
|
39
|
+
# - main_steps → primary execution steps
|
|
40
|
+
# - cleanup_steps → executed after execution (teardown phase)
|
|
41
|
+
#
|
|
42
|
+
# In this example:
|
|
43
|
+
# - no setup or cleanup steps are defined
|
|
44
|
+
# - two demonstration steps are executed sequentially
|
|
45
|
+
def main_sequence() -> TestSequence:
|
|
46
|
+
return TestSequence(
|
|
47
|
+
name="BasicSequence",
|
|
48
|
+
setup_steps=[],
|
|
49
|
+
main_steps=[measure_vcore, functional_check],
|
|
50
|
+
cleanup_steps=[],
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# ------------------------------------------------------------
|
|
54
|
+
# PROCESS HOOK REGISTRATION
|
|
55
|
+
# ------------------------------------------------------------
|
|
56
|
+
# PROCESS_HOOKS maps a sequence name to its factory function.
|
|
57
|
+
# This enables dynamic discovery and execution of test sequences
|
|
58
|
+
# by the PyTestFlow runtime.
|
|
59
|
+
PROCESS_HOOKS = {
|
|
60
|
+
"main_sequence": main_sequence,
|
|
61
|
+
}
|
|
62
|
+
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
from pytestflow.core.sequence import TestSequence
|
|
2
|
+
from pytestflow.core import ptf_context
|
|
3
|
+
|
|
4
|
+
from pytestflow.steps.message_pop_up import message_pop_up_step
|
|
5
|
+
from pytestflow.steps.numeric_limit import numeric_limit_step
|
|
6
|
+
from pytestflow.steps.pass_fail import pass_fail_step
|
|
7
|
+
from pytestflow.steps.flow_control import flow_control_step
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# ------------------------------------------------------------
|
|
11
|
+
# USER INPUT STEP (message box)
|
|
12
|
+
# ------------------------------------------------------------
|
|
13
|
+
# This step displays a message box to the user and stores the response.
|
|
14
|
+
#
|
|
15
|
+
# The response is saved into ptf_context.locals using `store_as`.
|
|
16
|
+
#
|
|
17
|
+
# Typical use case:
|
|
18
|
+
# - operator confirmation
|
|
19
|
+
# - manual selection of test path
|
|
20
|
+
@message_pop_up_step(
|
|
21
|
+
name="operator_confirmation",
|
|
22
|
+
title="Operator Check",
|
|
23
|
+
msg="Confirm that the setup is ready, then continue.",
|
|
24
|
+
buttons=["Continue", "Cancel"],
|
|
25
|
+
show_response_box=False,
|
|
26
|
+
store_as="operator_confirmation_response",
|
|
27
|
+
)
|
|
28
|
+
def operator_confirmation():
|
|
29
|
+
return "Message box displayed"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# ------------------------------------------------------------
|
|
33
|
+
# FLOW CONTROL STEP (based on user response)
|
|
34
|
+
# ------------------------------------------------------------
|
|
35
|
+
# This step decides the next execution path based on a condition.
|
|
36
|
+
#
|
|
37
|
+
# It returns an index that maps to `next_steps`:
|
|
38
|
+
# 0 → "end"
|
|
39
|
+
# 1 → "next"
|
|
40
|
+
#
|
|
41
|
+
# The decision is based on the stored user response.
|
|
42
|
+
@flow_control_step(
|
|
43
|
+
name="check_operator_confirmation",
|
|
44
|
+
next_steps={0: "end", 1: "next"},
|
|
45
|
+
)
|
|
46
|
+
def check_operator_confirmation():
|
|
47
|
+
value = ptf_context.locals.get("operator_confirmation_response")
|
|
48
|
+
return value["button"] == "Continue"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# ------------------------------------------------------------
|
|
52
|
+
# PASS / FAIL STEP
|
|
53
|
+
# ------------------------------------------------------------
|
|
54
|
+
@pass_fail_step(name="power_good_check")
|
|
55
|
+
def power_good_check():
|
|
56
|
+
return True
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# ------------------------------------------------------------
|
|
60
|
+
# NUMERIC LIMIT STEP
|
|
61
|
+
# ------------------------------------------------------------
|
|
62
|
+
@numeric_limit_step(name="vcore_voltage_check", limit=(1.0, 1.3), mode="between")
|
|
63
|
+
def vcore_voltage_check():
|
|
64
|
+
return 1.15
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# ------------------------------------------------------------
|
|
68
|
+
# USER SELECTION STEP (message box)
|
|
69
|
+
# ------------------------------------------------------------
|
|
70
|
+
# This step allows the operator to select a product/version.
|
|
71
|
+
# The selected value is stored for later flow routing.
|
|
72
|
+
@message_pop_up_step(
|
|
73
|
+
name="operator_product_selection",
|
|
74
|
+
title="Operator Product Selection",
|
|
75
|
+
msg="Select Product Version",
|
|
76
|
+
buttons=["Ver 1", "Ver 2", "Ver 3"],
|
|
77
|
+
show_response_box=False,
|
|
78
|
+
store_as="operator_product_selection_response",
|
|
79
|
+
)
|
|
80
|
+
def operator_product_selection():
|
|
81
|
+
return "Selection step executed"
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# ------------------------------------------------------------
|
|
85
|
+
# FLOW CONTROL BASED ON SELECTION
|
|
86
|
+
# ------------------------------------------------------------
|
|
87
|
+
# Converts the selected button into an execution index.
|
|
88
|
+
# This index is used to dynamically route the sequence.
|
|
89
|
+
@flow_control_step(
|
|
90
|
+
name="check_operator_product_selection",
|
|
91
|
+
next_steps={
|
|
92
|
+
0: "ver1_test",
|
|
93
|
+
1: "ver2_test",
|
|
94
|
+
2: "ver3_test",
|
|
95
|
+
},
|
|
96
|
+
)
|
|
97
|
+
def check_operator_product_selection():
|
|
98
|
+
versions = ["Ver 1", "Ver 2", "Ver 3"]
|
|
99
|
+
|
|
100
|
+
value = ptf_context.locals.get("operator_product_selection_response")
|
|
101
|
+
idx = versions.index(value["button"])
|
|
102
|
+
|
|
103
|
+
return idx
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
# ------------------------------------------------------------
|
|
107
|
+
# VERSION-SPECIFIC TESTS
|
|
108
|
+
# ------------------------------------------------------------
|
|
109
|
+
@numeric_limit_step(name="ver1_test", limit=(4.9, 5.1), mode="between")
|
|
110
|
+
def ver1_test():
|
|
111
|
+
return 5.0
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@numeric_limit_step(name="ver2_test", limit=(11.9, 12.1), mode="between")
|
|
115
|
+
def ver2_test():
|
|
116
|
+
return 12.0
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@numeric_limit_step(name="ver3_test", limit=(23.9, 24.1), mode="between")
|
|
120
|
+
def ver3_test():
|
|
121
|
+
return 24.0
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
# ------------------------------------------------------------
|
|
125
|
+
# SIMPLE FLOW TERMINATION STEPS
|
|
126
|
+
# ------------------------------------------------------------
|
|
127
|
+
# These steps demonstrate explicit flow termination routing.
|
|
128
|
+
@flow_control_step(name="go_to_end_after_ver1", next_steps={0: "end"})
|
|
129
|
+
def go_to_end_after_ver1():
|
|
130
|
+
return 0
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@flow_control_step(name="go_to_end_after_ver2", next_steps={0: "end"})
|
|
134
|
+
def go_to_end_after_ver2():
|
|
135
|
+
return 0
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
# ------------------------------------------------------------
|
|
139
|
+
# SEQUENCE DEFINITION
|
|
140
|
+
# ------------------------------------------------------------
|
|
141
|
+
# Demonstrates conditional execution and branching logic
|
|
142
|
+
# driven by user input and flow control steps.
|
|
143
|
+
def main_sequence() -> TestSequence:
|
|
144
|
+
return TestSequence(
|
|
145
|
+
name="MessageBoxAndFlowControl",
|
|
146
|
+
setup_steps=[],
|
|
147
|
+
main_steps=[
|
|
148
|
+
operator_confirmation,
|
|
149
|
+
check_operator_confirmation,
|
|
150
|
+
power_good_check,
|
|
151
|
+
vcore_voltage_check,
|
|
152
|
+
operator_product_selection,
|
|
153
|
+
check_operator_product_selection,
|
|
154
|
+
ver1_test,
|
|
155
|
+
go_to_end_after_ver1,
|
|
156
|
+
ver2_test,
|
|
157
|
+
go_to_end_after_ver2,
|
|
158
|
+
ver3_test,
|
|
159
|
+
],
|
|
160
|
+
cleanup_steps=[],
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
# ------------------------------------------------------------
|
|
165
|
+
# PROCESS HOOK REGISTRATION
|
|
166
|
+
# ------------------------------------------------------------
|
|
167
|
+
PROCESS_HOOKS = {
|
|
168
|
+
"main_sequence": main_sequence,
|
|
169
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
from pytestflow.core.sequence import TestSequence
|
|
2
|
+
from pytestflow.steps.pass_fail import pass_fail_step
|
|
3
|
+
from pytestflow.steps.numeric_limit import numeric_limit_step
|
|
4
|
+
from pytestflow.steps.df_numeric_limits import df_numeric_limits_step
|
|
5
|
+
from pytestflow.steps.string_check import string_check_step
|
|
6
|
+
from pytestflow.steps.waveform_limit import waveform_limit_step
|
|
7
|
+
from pytestflow.steps.action_step import action_step
|
|
8
|
+
from pytestflow.core import ptf_context
|
|
9
|
+
import pandas as pd
|
|
10
|
+
import time
|
|
11
|
+
|
|
12
|
+
# --------------------
|
|
13
|
+
# STEP DEFINITIONS
|
|
14
|
+
# --------------------
|
|
15
|
+
|
|
16
|
+
# --- Pass/Fail ---
|
|
17
|
+
@pass_fail_step(name="power_good_check")
|
|
18
|
+
def power_good_check():
|
|
19
|
+
return True
|
|
20
|
+
|
|
21
|
+
@pass_fail_step(name="reset_deasserted_check")
|
|
22
|
+
def reset_deasserted_check():
|
|
23
|
+
return False # Let's say this fails
|
|
24
|
+
|
|
25
|
+
# --- Numeric Limits ---
|
|
26
|
+
@numeric_limit_step(name="vcore_voltage_check", limit=1.2, mode="le")
|
|
27
|
+
def vcore_voltage_check():
|
|
28
|
+
time.sleep(1)
|
|
29
|
+
ptf_context.current_step.add_additional_info("added sleep to test purpose - with Murali & Team", 1)
|
|
30
|
+
return 1.1
|
|
31
|
+
|
|
32
|
+
@numeric_limit_step(name="pll_lock_time_check", limit=500, mode="le")
|
|
33
|
+
def pll_lock_time_check():
|
|
34
|
+
return 700 # Fails
|
|
35
|
+
|
|
36
|
+
# --- DataFrame Numeric Limits ---
|
|
37
|
+
df_limits = pd.DataFrame([
|
|
38
|
+
{"name": "Vdd", "limit": 1.0, "mode": "ge"},
|
|
39
|
+
{"name": "Vss", "limit": -0.1, "mode": "le"},
|
|
40
|
+
])
|
|
41
|
+
|
|
42
|
+
@df_numeric_limits_step(limits_df=df_limits, name="supply_margin_check")
|
|
43
|
+
def supply_margin_check():
|
|
44
|
+
return {"Vdd": 1.1, "Vss": -0.2}
|
|
45
|
+
|
|
46
|
+
@df_numeric_limits_step(limits_df=df_limits, name="supply_margin_fail")
|
|
47
|
+
def supply_margin_fail():
|
|
48
|
+
return {"Vdd": 0.9, "Vss": -0.05}
|
|
49
|
+
|
|
50
|
+
# --- String Check ---
|
|
51
|
+
@string_check_step(name="serial_number_check", expected="MB123456", match="exact")
|
|
52
|
+
def serial_number_check():
|
|
53
|
+
return "MB123456"
|
|
54
|
+
|
|
55
|
+
@string_check_step(name="lot_code_check", expected="LOT789", match="exact")
|
|
56
|
+
def lot_code_check():
|
|
57
|
+
return "LOT123" # Fails
|
|
58
|
+
|
|
59
|
+
# --- Waveform Check ---
|
|
60
|
+
lower = [(0, 0.5), (1, 0.5), (2, 0.5)]
|
|
61
|
+
upper = [(0, 2.5), (1, 2.5), (2, 2.5)]
|
|
62
|
+
|
|
63
|
+
@waveform_limit_step(name="clk_rise_check", lower_mask=lower, upper_mask=upper, mode="between")
|
|
64
|
+
def clk_rise_check():
|
|
65
|
+
time.sleep(0.001)
|
|
66
|
+
ptf_context.current_step.add_additional_info("This is a test for additiona info", "This info is addedd at runtime to this step")
|
|
67
|
+
return {"x": [0, 1, 2], "y": [1.0, 1.5, 2.0]}
|
|
68
|
+
|
|
69
|
+
@waveform_limit_step(name="clk_rise_fail", lower_mask=lower, upper_mask=upper, mode="between")
|
|
70
|
+
def clk_rise_fail():
|
|
71
|
+
time.sleep(0.001)
|
|
72
|
+
return {"x": [0, 1, 2], "y": [0.1, 2.7, 2.9]}
|
|
73
|
+
|
|
74
|
+
# --- Action Step ---
|
|
75
|
+
@action_step(name="store_mb_info", store_as="mb_info")
|
|
76
|
+
def store_mb_info():
|
|
77
|
+
return "Motherboard A1"
|
|
78
|
+
|
|
79
|
+
# --------------------
|
|
80
|
+
# SUBSEQUENCE (like a child sequence)
|
|
81
|
+
# --------------------
|
|
82
|
+
def create_power_on_subsequence() -> TestSequence:
|
|
83
|
+
@pass_fail_step(name="power_on_pass")
|
|
84
|
+
def power_on_pass():
|
|
85
|
+
return True
|
|
86
|
+
|
|
87
|
+
@numeric_limit_step(name="power_on_stabilization_time", limit=200, mode="le")
|
|
88
|
+
def power_on_stabilization_time():
|
|
89
|
+
return 150
|
|
90
|
+
|
|
91
|
+
return TestSequence(
|
|
92
|
+
name="PowerOnSequence",
|
|
93
|
+
setup_steps=[],
|
|
94
|
+
main_steps=[power_on_pass, power_on_stabilization_time],
|
|
95
|
+
cleanup_steps=[],
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
# --------------------
|
|
101
|
+
# MAIN MOTHERBOARD TEST SEQUENCE
|
|
102
|
+
# --------------------
|
|
103
|
+
def motherboard_sequence() -> TestSequence:
|
|
104
|
+
return TestSequence(
|
|
105
|
+
name="MotherboardTestSequence",
|
|
106
|
+
setup_steps=[store_mb_info],
|
|
107
|
+
main_steps=[
|
|
108
|
+
serial_number_check,
|
|
109
|
+
lot_code_check,
|
|
110
|
+
vcore_voltage_check,
|
|
111
|
+
pll_lock_time_check,
|
|
112
|
+
supply_margin_check,
|
|
113
|
+
supply_margin_fail,
|
|
114
|
+
clk_rise_check,
|
|
115
|
+
clk_rise_fail,
|
|
116
|
+
reset_deasserted_check,
|
|
117
|
+
power_good_check,
|
|
118
|
+
create_power_on_subsequence(), # This is the subsequence
|
|
119
|
+
],
|
|
120
|
+
cleanup_steps=[],
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
PROCESS_HOOKS = {
|
|
124
|
+
"main_sequence": motherboard_sequence,
|
|
125
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
from pytestflow.core.sequence import TestSequence
|
|
2
|
+
from pytestflow.core import ptf_context
|
|
3
|
+
from pytestflow.steps.action_step import action_step
|
|
4
|
+
from pytestflow.steps.df_numeric_limits import df_numeric_limits_step
|
|
5
|
+
from pytestflow.steps.message_pop_up import message_pop_up_step
|
|
6
|
+
from pytestflow.steps.numeric_limit import numeric_limit_step
|
|
7
|
+
from pytestflow.steps.pass_fail import pass_fail_step
|
|
8
|
+
from pytestflow.steps.string_check import string_check_step
|
|
9
|
+
from pytestflow.steps.waveform_limit import waveform_limit_step
|
|
10
|
+
import pandas as pd
|
|
11
|
+
|
|
12
|
+
# ------------------------------------------------------------
|
|
13
|
+
# USER INTERACTION STEP (message pop-up)
|
|
14
|
+
# ------------------------------------------------------------
|
|
15
|
+
# This step pauses execution and requests user interaction.
|
|
16
|
+
#
|
|
17
|
+
# Typical use case:
|
|
18
|
+
# - operator confirmation
|
|
19
|
+
# - manual validation before continuing a sequence
|
|
20
|
+
@message_pop_up_step(
|
|
21
|
+
name="operator_confirmation",
|
|
22
|
+
title="Operator Check",
|
|
23
|
+
msg="Confirm fixture and DUT are connected, then press Continue.",
|
|
24
|
+
buttons=["Ok", "Other Option 1", "Other Option 2"],
|
|
25
|
+
show_response_box=False,
|
|
26
|
+
)
|
|
27
|
+
def operator_confirmation():
|
|
28
|
+
return "Operator confirmed bench is ready"
|
|
29
|
+
|
|
30
|
+
# ------------------------------------------------------------
|
|
31
|
+
# PASS / FAIL STEP
|
|
32
|
+
# ------------------------------------------------------------
|
|
33
|
+
# Returns a boolean result (True = pass, False = fail).
|
|
34
|
+
#
|
|
35
|
+
# Typical use case:
|
|
36
|
+
# - digital signals
|
|
37
|
+
# - readiness checks
|
|
38
|
+
# - simple validation logic
|
|
39
|
+
@pass_fail_step(name="power_good_check")
|
|
40
|
+
def power_good_check():
|
|
41
|
+
# Typical use case: a digital status pin or a single readiness condition.
|
|
42
|
+
return True
|
|
43
|
+
|
|
44
|
+
# ------------------------------------------------------------
|
|
45
|
+
# NUMERIC LIMIT STEP (single value)
|
|
46
|
+
# ------------------------------------------------------------
|
|
47
|
+
# The function MUST return a numeric value (int or float).
|
|
48
|
+
#
|
|
49
|
+
# Supported comparison modes:
|
|
50
|
+
# - "ge" → greater or equal
|
|
51
|
+
# - "le" → less or equal
|
|
52
|
+
# - "between" → value within [min, max]
|
|
53
|
+
# - "outside" → value outside [min, max]
|
|
54
|
+
#
|
|
55
|
+
# Typical use case:
|
|
56
|
+
# - validating a single measurement against limits
|
|
57
|
+
@numeric_limit_step(name="vcore_voltage_check", limit=(1.0, 1.3), mode="between")
|
|
58
|
+
def vcore_voltage_check():
|
|
59
|
+
# Typical use case: one scalar analog measurement against limits.
|
|
60
|
+
return 1.15
|
|
61
|
+
|
|
62
|
+
# ------------------------------------------------------------
|
|
63
|
+
# DATAFRAME NUMERIC LIMIT STEP (multi-channel)
|
|
64
|
+
# ------------------------------------------------------------
|
|
65
|
+
# Allows validating multiple values using a structured limit table.
|
|
66
|
+
#
|
|
67
|
+
# Each row defines:
|
|
68
|
+
# - name
|
|
69
|
+
# - limit range
|
|
70
|
+
# - comparison mode
|
|
71
|
+
#
|
|
72
|
+
# Typical use case:
|
|
73
|
+
# - validating multiple related signals at once
|
|
74
|
+
limits_df = pd.DataFrame(
|
|
75
|
+
[
|
|
76
|
+
{"name": "rail_1v0", "limit": (0.95, 1.05), "mode": "between"},
|
|
77
|
+
{"name": "rail_3v3", "limit": (3.2, 3.4), "mode": "between"},
|
|
78
|
+
]
|
|
79
|
+
)
|
|
80
|
+
@df_numeric_limits_step(name="multi_rail_check", limits_df=limits_df)
|
|
81
|
+
def multi_rail_check():
|
|
82
|
+
# Typical use case: validate a set of related channels at once.
|
|
83
|
+
return {"rail_1v0": 1.01, "rail_3v3": 3.31}
|
|
84
|
+
|
|
85
|
+
# ------------------------------------------------------------
|
|
86
|
+
# STRING CHECK STEP
|
|
87
|
+
# ------------------------------------------------------------
|
|
88
|
+
# Compares a returned string against an expected value.
|
|
89
|
+
#
|
|
90
|
+
# Supported modes typically include:
|
|
91
|
+
# - "exact"
|
|
92
|
+
#
|
|
93
|
+
# Typical use case:
|
|
94
|
+
# - serial numbers
|
|
95
|
+
# - firmware versions
|
|
96
|
+
# - identifiers
|
|
97
|
+
@string_check_step(name="serial_format_check", expected="MB-2026-A01", match="exact")
|
|
98
|
+
def serial_format_check():
|
|
99
|
+
# Typical use case: exact ID, lot code, firmware tag, or SKU checks.
|
|
100
|
+
return "MB-2026-A01"
|
|
101
|
+
|
|
102
|
+
# ------------------------------------------------------------
|
|
103
|
+
# WAVEFORM LIMIT STEP
|
|
104
|
+
# ------------------------------------------------------------
|
|
105
|
+
# Validates waveform data against upper and lower masks.
|
|
106
|
+
#
|
|
107
|
+
# Typical use case:
|
|
108
|
+
# - oscilloscope captures
|
|
109
|
+
# - signal integrity validation
|
|
110
|
+
lower_mask = [(0, 0.2), (1, 0.2), (2, 0.2), (3, 0.2)]
|
|
111
|
+
upper_mask = [(0, 1.8), (1, 1.8), (2, 1.8), (3, 1.8)]
|
|
112
|
+
@waveform_limit_step(
|
|
113
|
+
name="clk_waveform_mask_check",
|
|
114
|
+
lower_mask=lower_mask,
|
|
115
|
+
upper_mask=upper_mask,
|
|
116
|
+
mode="between",
|
|
117
|
+
)
|
|
118
|
+
def clk_waveform_mask_check():
|
|
119
|
+
ptf_context.current_step.add_additional_info(
|
|
120
|
+
"typical_use_case",
|
|
121
|
+
"Waveform limits are used, for example, in oscilloscope captures or spectrum captures to apply masks.",
|
|
122
|
+
)
|
|
123
|
+
return {"x": [0, 1, 2, 3], "y": [0.6, 0.9, 1.2, 1.1]}
|
|
124
|
+
|
|
125
|
+
# ------------------------------------------------------------
|
|
126
|
+
# ACTION STEP
|
|
127
|
+
# ------------------------------------------------------------
|
|
128
|
+
# Performs an action and optionally stores a value for later use.
|
|
129
|
+
#
|
|
130
|
+
# Typical use case:
|
|
131
|
+
# - logging operator input
|
|
132
|
+
# - saving intermediate results
|
|
133
|
+
@action_step(name="store_operator_note", store_as="operator_note")
|
|
134
|
+
def store_operator_note():
|
|
135
|
+
# Typical use case: save runtime values for later steps/report sections.
|
|
136
|
+
return "Quickstart run completed with one example per step type."
|
|
137
|
+
|
|
138
|
+
# ------------------------------------------------------------
|
|
139
|
+
# SEQUENCE DEFINITION
|
|
140
|
+
# ------------------------------------------------------------
|
|
141
|
+
# A TestSequence defines an ordered execution pipeline.
|
|
142
|
+
#
|
|
143
|
+
# - setup_steps → initialization phase
|
|
144
|
+
# - main_steps → core execution steps
|
|
145
|
+
# - cleanup_steps → teardown phase
|
|
146
|
+
def main_sequence() -> TestSequence:
|
|
147
|
+
return TestSequence(
|
|
148
|
+
name="StepTypesQuickstartSequence",
|
|
149
|
+
setup_steps=[],
|
|
150
|
+
main_steps=[
|
|
151
|
+
operator_confirmation,
|
|
152
|
+
power_good_check,
|
|
153
|
+
vcore_voltage_check,
|
|
154
|
+
multi_rail_check,
|
|
155
|
+
serial_format_check,
|
|
156
|
+
clk_waveform_mask_check,
|
|
157
|
+
store_operator_note,
|
|
158
|
+
],
|
|
159
|
+
cleanup_steps=[],
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
# ------------------------------------------------------------
|
|
164
|
+
# PROCESS HOOK REGISTRATION
|
|
165
|
+
# ------------------------------------------------------------
|
|
166
|
+
PROCESS_HOOKS = {
|
|
167
|
+
"main_sequence": main_sequence,
|
|
168
|
+
}
|
pytestflow/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# PyTestFlow Package
|
|
2
|
+
|
|
3
|
+
This package bundles the runtime building blocks:
|
|
4
|
+
|
|
5
|
+
- `core`: Prefect-integrated step wrapper, execution context, state classes,
|
|
6
|
+
and sequence/process-model execution.
|
|
7
|
+
- `steps`: reusable step decorators built on top of the base `@step` wrapper.
|
|
8
|
+
- `flow_utils`: orchestration helpers for conditions and transitions.
|
|
9
|
+
- `reporting`: utilities that transform `PyTestflowState` data into JSON or HTML.
|
|
10
|
+
|
|
11
|
+
Together these modules implement the same model used throughout the repository:
|
|
12
|
+
steps are tasks, sequences are flows, and `ptf_context` is the shared channel
|
|
13
|
+
for runtime data exchange.
|
pytestflow/__init__.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from pytestflow.core import (
|
|
2
|
+
step,
|
|
3
|
+
ptf_context,
|
|
4
|
+
PyTestflowPassed,
|
|
5
|
+
PyTestflowFailed,
|
|
6
|
+
PyTestflowDone,
|
|
7
|
+
PyTestflowError,
|
|
8
|
+
Sequence,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"step",
|
|
13
|
+
"ptf_context",
|
|
14
|
+
"PyTestflowPassed",
|
|
15
|
+
"PyTestflowFailed",
|
|
16
|
+
"PyTestflowDone",
|
|
17
|
+
"PyTestflowError",
|
|
18
|
+
"Sequence",
|
|
19
|
+
]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# event_bus.py
|
|
2
|
+
import asyncio
|
|
3
|
+
from collections import defaultdict
|
|
4
|
+
|
|
5
|
+
class EventBus:
|
|
6
|
+
def __init__(self):
|
|
7
|
+
self._subscribers = defaultdict(list)
|
|
8
|
+
self._queue = asyncio.Queue()
|
|
9
|
+
|
|
10
|
+
def set_loop(self, loop):
|
|
11
|
+
self.loop = loop
|
|
12
|
+
|
|
13
|
+
def on(self, cmd_name, callback):
|
|
14
|
+
self._subscribers[cmd_name].append(callback)
|
|
15
|
+
|
|
16
|
+
async def emit(self, cmd_name, data=None):
|
|
17
|
+
await self._queue.put((cmd_name, data))
|
|
18
|
+
|
|
19
|
+
async def start(self):
|
|
20
|
+
while True:
|
|
21
|
+
cmd_name, data = await self._queue.get()
|
|
22
|
+
for callback in self._subscribers[cmd_name]:
|
|
23
|
+
asyncio.create_task(callback(data))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
event_bus = EventBus()
|
|
27
|
+
pending_requests = {}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
|
+
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
3
|
+
|
|
4
|
+
<svg
|
|
5
|
+
version="1.1"
|
|
6
|
+
id="svg1"
|
|
7
|
+
width="1823"
|
|
8
|
+
height="1160"
|
|
9
|
+
viewBox="0 0 1823 1160"
|
|
10
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
11
|
+
xmlns:svg="http://www.w3.org/2000/svg">
|
|
12
|
+
<defs
|
|
13
|
+
id="defs1" />
|
|
14
|
+
<g
|
|
15
|
+
id="g1">
|
|
16
|
+
<path
|
|
17
|
+
id="path1"
|
|
18
|
+
style="display:inline;fill:#53686D"
|
|
19
|
+
d="m 1465,14 v 25 25 l -41.25,0.25 -41.25,0.25 -41.75,-0.386719 -41.75,-0.384765 0.018,1.384765 0.02,1.386719 1.543,8.5 1.5429,8.5 -0.6699,9.458984 -0.6719,9.457036 -1.5097,5.54296 -1.5117,5.54102 -0.4961,2.25 -0.4961,2.25 h 109.5371 109.5391 l 8.3281,6.26953 8.3281,6.26953 19,13.89649 19,13.89453 0.5,0.44336 0.5,0.44531 -2.4551,1.64063 -2.457,1.64062 -17.043,13.12305 -17.0449,13.12304 -8.5,6.10938 -8.5,6.10937 -251,0.0176 -251,0.0176 0.2617,253.35156 0.2617,253.34961 -5.9589,3.67969 -5.961,3.67773 -4.54295,5.19336 -4.54102,5.19141 -3.46094,7.0293 -3.45898,7.02734 -1.02539,4.5 -1.02539,4.5 -0.0234,9.06836 -0.0254,9.06836 1.5957,5.43164 1.59766,5.43164 3.5,6.69922 3.50195,6.70117 6.02346,6.08008 6.0253,6.08008 8.127,3.91797 8.1289,3.91796 5,0.93946 5,0.93945 6.5664,0.11328 6.5664,0.11133 5.9043,-1.0918 5.9063,-1.09179 7.5273,-3.5625 7.5293,-3.5625 7.084,-7.09571 7.084,-7.0957 3.6132,-7.5 3.6133,-7.5 1.1856,-6.91602 1.1836,-6.91601 -0.5293,-7.58399 -0.5274,-7.58398 -2.1601,-6.38672 -2.1602,-6.38672 -3.3203,-5.11328 -3.3223,-5.11328 -4.2656,-4.23828 -4.2676,-4.23828 -4.8554,-3.11524 L 1066,707.79102 V 481.42578 255.06055 l 194.7871,-0.041 194.7852,-0.0391 4.7148,0.64453 4.7129,0.64648 V 281.13672 306 l 1.25,-0.0137 1.25,-0.0137 19,-14.25 19,-14.25 23,-17.43946 23,-17.4375 48.1074,-36.04882 48.1074,-36.04688 7.1036,-5 7.1035,-5 -0.012,-1.5 -0.012,-1.5 -7.1992,-5.18555 -7.1992,-5.18554 -24.1348,-17.81446 L 1599.2305,111.5 1565.3652,86.507812 1531.5,61.517578 1521,53.558594 1510.5,45.601562 1489.1113,29.800781 1467.7227,14 h -1.3614 z m -260.9023,15 -5.2989,1.121094 -5.2988,1.121094 -6,2.724609 -6,2.722656 -5.2988,4.21875 -5.3008,4.216797 -4.6738,6.019531 -4.6758,6.021485 -2.0567,3.664062 -2.0546,3.666016 -241.95708,0.002 -241.95508,0.002 -0.30273,1.572266 -0.30273,1.570312 2.78906,1.269531 2.78906,1.269532 9.51758,6.234375 9.51758,6.234375 14.98242,12.078125 14.98242,12.07812 5.4668,5.59766 5.46484,5.5957 h 210.05859 210.05857 l 4.4688,6.13672 4.4687,6.13672 5.3653,4.78125 5.3632,4.78125 5.8438,3.11523 5.8457,3.11524 7.2969,1.91211 7.2988,1.9121 7.5,0.0371 7.5,0.0371 7,-1.47656 7,-1.47461 6,-2.95508 6,-2.95312 5.0645,-4.30274 5.0644,-4.30273 4.1406,-5.05859 4.1426,-5.0586 3.1133,-6.27539 3.1152,-6.27734 1.4121,-6.66602 1.4121,-6.664056 -0.017,-7 -0.018,-7 -1.3164,-6.134766 -1.3184,-6.136718 -3.0586,-6.53711 -3.0586,-6.537109 -4.0175,-5.4375 -4.0196,-5.439453 -4.3203,-3.705078 -4.3203,-3.703125 -5,-2.837891 -5,-2.839844 -6.8555,-2.095703 L 1223.7891,29 h -9.8457 z M 217,56 v 224 224 h 1.39648 1.39454 l 3.25586,-5.07227 3.25585,-5.07421 7.36329,-9.67579 L 241.03125,474.5 253.01562,461.82617 265,449.1543 V 276.54688 103.9375 l 135.75,0.31445 135.75,0.31446 9.10547,1.69726 9.10742,1.69727 12.39258,2.96289 12.39453,2.96094 11,4.08398 11,4.08594 13.10742,6.34961 13.10938,6.34961 11.39062,7.43554 11.39258,7.4336 8.5,6.83007 8.5,6.82813 7.81641,7.60937 7.81836,7.60938 6.8457,8.01953 6.84375,8.01953 3.74609,5.48047 3.74414,5.48047 4.67578,7 4.67579,7 6.09375,11 6.09375,11 3.95898,10 3.95703,10 2.59375,8.23242 2.5918,8.23242 2.49414,9.83594 2.49414,9.83789 2.08398,12.42969 2.08399,12.43164 0.5957,16.34961 0.5957,16.34766 -1.22656,11.65234 -1.22461,11.65039 -1.96875,11.23047 -1.96679,11.23242 -3.45704,12.61719 -3.45703,12.61523 -3.99414,9.90235 -3.99219,9.90234 -5.39843,10.75 -5.39649,10.75 -6.93945,10.5 -6.93945,10.5 -8.04102,10 -8.04101,10 -10.37891,10.07227 -10.38086,10.07226 -7,5.56445 -7,5.56641 -10.33594,6.6875 -10.33789,6.6875 -10.16211,5.36914 -10.16406,5.36914 -11.62305,4.53711 -11.625,4.53516 -15.375,3.98828 -15.37695,3.98828 -11.5,1.65039 -11.5,1.65234 -54.25,0.47852 -54.25,0.48047 V 547.1582 509.11719 l -0.97461,-0.60157 -0.97461,-0.60156 -14.72851,1.09961 -14.72852,1.09961 -7.04687,1.46875 -7.04688,1.46875 -1.75,0.54297 -1.75,0.54102 v 210.76562 210.76563 l 0.72461,0.7246 0.72461,0.72461 2.02539,-0.54296 2.02539,-0.54493 21.75,-0.0137 L 426,936 V 784.625 633.24805 l 51.75,-0.16211 51.75,-0.16406 12,-1.02149 12,-1.02148 10.5,-2.00391 10.5,-2.00586 9.5,-2.38867 9.5,-2.39063 10.58594,-3.43164 10.58789,-3.43359 12.32617,-5.18359 12.32422,-5.1836 6.58789,-3.58008 6.58789,-3.58007 7.5,-4.5 7.5,-4.49805 7,-4.7168 7,-4.71484 8.35742,-6.58985 8.35547,-6.58984 10.44727,-9.79297 10.44726,-9.79492 8.19727,-9.36328 8.19531,-9.36328 6.31641,-8.63672 6.31445,-8.63672 6.84375,-11 6.8418,-11 5.55664,-11 5.55468,-11 5.45704,-14.52344 5.45507,-14.52148 3.57032,-13.97852 3.57031,-13.97656 2.55859,-15 2.55664,-15 0.86328,-18 0.86133,-18 -0.8789,-14.5 -0.88086,-14.5 -1.57617,-10.5 -1.57618,-10.5 -3.04297,-13.5 -3.04296,-13.5 -4.91211,-14.67188 -4.91407,-14.67187 -5.41992,-12.07813 -5.42187,-12.07617 -5.02149,-9.53906 -5.02148,-9.53906 -9.50195,-13.71094 -9.50196,-13.71289 -8.40625,-10 -8.40625,-10 L 708.39844,134.99609 696.5,123.49023 685.8457,114.99609 675.18945,106.5 664.8457,100.03906 654.5,93.580078 l -12,-6.189453 -12,-6.189453 -14,-5.558594 -14,-5.558594 L 590.35742,66.632812 578.21484,63.179688 563.35742,60.615234 548.5,58.048828 537.5,57.025391 526.5,56.001953 371.75,56 Z m 582,145 v 1.30469 1.30469 l 5.54492,13.41015 5.54492,13.4082 4.05274,12.26954 4.05469,12.26953 26.15039,0.26758 L 870.5,255.5 l 0.5,268 0.5,268 2.20703,9.65234 2.20703,9.65235 3.6875,10.84765 3.6875,10.84766 3.67774,8 3.67773,8 6.18164,9.80664 6.17969,9.80859 5.40039,6.69141 5.40039,6.69336 7.8457,7.57617 7.84766,7.57813 5,4.00781 5,4.00781 6.08398,4.08985 6.08204,4.08789 9.41796,5.01953 9.41602,5.01953 8.18555,3.00781 8.18554,3.00781 9.81446,2.6875 9.81445,2.6875 8.5,1.25196 8.4961,1.25195 13,0.0918 13,0.0898 10.4492,-1.48437 10.4512,-1.48633 6.5488,-1.58399 6.5508,-1.58398 8.5801,-2.85938 8.582,-2.86132 7.418,-3.38282 7.4199,-3.38281 8.2187,-5.10156 8.2207,-5.10156 8.2793,-6.29883 8.2813,-6.30078 8.123,-8.19336 8.1231,-8.19336 6.9433,-9.32422 6.9415,-9.32422 2.8828,-5 2.8828,-5 4.4043,-8.91406 4.4062,-8.91406 3.877,-11.8086 3.8789,-11.80859 2.2598,-11.77735 2.2617,-11.77734 0.01,-88.5 0.01,-88.5 4,-0.67578 4,-0.67578 125.2266,-0.0742 125.2265,-0.0742 1.4024,-2.25 1.4004,-2.25 2.9609,-5.5 2.9629,-5.5 3.3496,-6 3.3496,-6 11.1836,-19.5 11.1836,-19.5 4.9785,-9 4.9805,-9 3.8047,-7 3.8066,-7 3.6953,-6.5 3.6973,-6.5 5.1445,-8.82812 5.1465,-8.83008 v -0.38282 -0.38281 l 13.3594,-23.28906 13.3594,-23.28711 2.1406,-4.21094 2.1406,-4.20898 v -1.53711 -1.53711 l -181.1484,-0.25391 -181.1465,-0.25195 0.1191,-54 0.1192,-54 -0.5801,-10.75 -0.5821,-10.75 H 1177.8906 1151 v 91.99609 91.99805 l 163.4199,0.25195 163.4219,0.25391 -5.5859,10 -5.5879,10 -11.5157,20.5 -11.5136,20.5 -5.125,9 -5.1231,9 -141.1972,0.25391 -141.1954,0.2539 V 667.25391 781.5 l -1.4199,8 -1.4179,8 -3.9297,11.12695 -3.9317,11.12696 -4.7597,7.83984 -4.7598,7.83984 -6.0488,7.31446 -6.0508,7.3125 -7.0899,6.04687 -7.0898,6.04492 -7,4.1211 -7,4.11914 -6.5,3.1543 -6.5,3.15234 -6.5,2.125 -6.5,2.12305 -8.8809,1.52734 -8.8808,1.52539 h -8.7578 -8.7598 l -8.3594,-1.54492 -8.3613,-1.54492 -6.5,-2.09571 -6.5,-2.09765 -8,-3.96485 -8,-3.96679 -8,-5.3086 -8,-5.30664 -8.20508,-8.33594 -8.20508,-8.33398 -3.33789,-4.5 -3.33593,-4.5 -3.16993,-5.0293 -3.17187,-5.02929 -3.16211,-6.97071 -3.16406,-6.9707 -2.32032,-7.74609 -2.32031,-7.74805 -1.05469,-7.57617 L 925,777.85547 V 489.42773 201 h -63 z m -422,16 v 90.62695 90.62891 l 4.25,-0.70508 4.25,-0.70312 11.5,-1.39258 11.5,-1.39258 8.75,-0.0312 L 426,394 v -64 -64 h 50.81055 50.80859 l 8.44141,2.12305 8.43945,2.12304 5,2.4961 5,2.49609 6,4.29297 6,4.29297 5.1543,5.33789 5.15429,5.33789 4.45313,7 4.45312,7 2.23243,6 2.23046,6 1.89063,8.5 1.88867,8.5 0.0215,9.12109 0.0215,9.12305 -2.50586,9.87305 -2.50391,9.875 -3.92968,7.74414 -3.92969,7.74609 -4.54297,5.9043 -4.54492,5.90625 -4.77149,4.21484 -4.77148,4.21485 -4.23633,2.92773 -4.23633,2.92773 -6.96875,2.96485 -6.96875,2.9668 -7.29492,1.54296 -7.29492,1.54493 -60.5,0.38476 -60.5,0.38672 -11.66602,1.50781 -11.66406,1.50782 -8.83594,2.07812 -8.83398,2.08008 -8,2.59961 -8,2.59961 -8.5,3.56836 -8.5,3.56836 -6.5,3.45898 -6.5,3.45899 -6.7168,4.10156 -6.71679,4.10156 -8.78321,6.99219 -8.7832,6.99414 -7.77344,7.41211 -7.77344,7.41211 -6.85156,8.09375 -6.84961,8.0957 -5.79297,8.5 -5.79296,8.5 -6.78125,14 -6.78125,14 -3.02149,9.24805 -3.02148,9.24804 -2.01367,8.75196 -2.01172,8.75195 -1.04297,6.79297 -1.04492,6.79297 -1.45899,21.20703 -1.45898,21.20703 -0.0156,97.10938 -0.0137,97.10742 -5.4043,3.58789 -5.40625,3.58984 -3.52539,3.0957 -3.52734,3.09766 -3.52539,5.3125 -3.52735,5.31055 -2.08203,5.2832 -2.08203,5.2832 -1.0664,7.92188 -1.06641,7.92187 1.17187,8.00782 1.16993,8.00781 1.9414,4.68164 1.94336,4.68164 1.64844,3 1.64844,3 4.33203,5.14453 4.33008,5.14258 5.75586,3.82031 5.7539,3.82031 5.95703,2.40625 5.95508,2.4043 7.05274,0.75586 7.05273,0.75391 6,-0.51563 6,-0.51367 4.20508,-1.24805 4.20312,-1.24804 6.69922,-3.7461 6.69922,-3.74609 6.11328,-6.11524 6.11133,-6.11523 2.87695,-6 2.875,-6 1.32618,-4.30273 1.32617,-4.30274 0.63476,-8.69726 0.63477,-8.69727 -1.64453,-7 -1.64453,-7 -2.29102,-5 -2.28906,-5 -5.01758,-6 -5.01563,-6 -7.17968,-4.5 -7.17969,-4.5 0.39844,-109 0.40039,-109 1.60156,-9.26172 1.60156,-9.26367 2.65235,-10.73633 2.65429,-10.73828 3.79102,-9.00586 3.79297,-9.00586 3.73828,-6.99414 3.74023,-6.99414 4.94532,-7.2168 4.94531,-7.21679 6.41406,-7.51758 6.41211,-7.51758 7.73437,-6.6543 7.73438,-6.65625 8,-5.24023 8,-5.24219 8,-3.99219 8,-3.99414 7,-2.5 7,-2.5 9.5,-2.05664 9.5,-2.05469 3,-0.68554 3,-0.6836 68,-0.74218 68,-0.74219 7.36133,-1.70899 7.36328,-1.70703 8.35742,-2.84375 8.35938,-2.84179 9.27929,-4.89844 9.2793,-4.89649 8.67773,-6.71289 8.67579,-6.71289 6.20117,-6.73242 6.19922,-6.73242 3.69726,-5 3.69922,-5 3.48242,-6 3.48242,-6 3.70313,-8.875 3.70312,-8.87695 1.90821,-7.62305 1.90625,-7.625 1.20898,-12.5 1.20703,-12.5 -1.08789,-13.54883 -1.08789,-13.54883 -2,-8.57812 -2,-8.57813 -2.4082,-6.37304 -2.40625,-6.37305 -2.30469,-5 -2.30664,-5 -6.40625,-9.7168 -6.4082,-9.71875 -7.66797,-7.89453 -7.66797,-7.89258 -8.08594,-5.95312 -8.08398,-5.95117 -8.91602,-4.58399 -8.91406,-4.58203 -8.10156,-2.54101 -8.10352,-2.54102 -10.52539,-1.5625 L 527.24414,217 h -75.12305 z m 412.5,747.73828 -9,0.60352 -9,0.60351 -6,1.54297 -6,1.54297 -7.20508,3.51563 -7.20508,3.51757 -5.33789,4.87891 -5.33593,4.87891 -2.61914,4.83789 -2.6211,4.83984 -1.69336,5.873 -1.69531,5.8711 0.4707,10.6289 0.47071,10.627 1.61328,5.0449 1.61328,5.0449 3.1914,4.8184 3.19141,4.8184 4.58203,3.3964 4.58008,3.3965 6.27148,3.0508 6.26954,3.0527 5.73046,1.9981 5.72852,1.998 12.95117,3.4747 12.94922,3.4746 5.70117,2.5586 5.70117,2.5585 2.76368,2.8848 2.76562,2.8848 0.42383,5.2734 0.42187,5.2715 -2.34765,3.5 -2.3457,3.5 -4.45508,2.25 -4.45313,2.25 -8.03711,-0.012 -8.03906,-0.012 -5.5,-1.4551 -5.5,-1.4551 -7.04297,-3.5644 -7.04297,-3.5665 -8.76562,-6.5781 -8.76563,-6.5781 -1.1914,1.5312 -1.19141,1.5313 -9.89258,12.1113 -9.89258,12.1133 2.39258,2.5742 2.39258,2.5762 6,4.2852 6,4.2851 5,2.9199 5,2.92 8,3.3203 8,3.3222 8.5,1.795 8.5,1.7949 11.29297,0.07 11.29101,0.07 7.70899,-1.5723 7.70703,-1.5742 7.25781,-3.4668 7.25781,-3.4687 6.4961,-6.0782 6.49609,-6.0761 3.74414,-7.5782 3.7461,-7.5781 0.35742,-11.2715 0.35742,-11.2734 -2.06445,-6.7813 -2.06446,-6.7812 -2.96875,-4.668 -2.96679,-4.6679 -5.27735,-4.1719 -5.27929,-4.1719 -6.54492,-3.2949 -6.54688,-3.2969 -10,-3.0566 -10,-3.0586 -9.20898,-2.5313 -9.20704,-2.5332 -4.39453,-2.0644 -4.39257,-2.0645 -2.89844,-3.0254 -2.89844,-3.0254 v -5.3613 -5.3613 l 2.05859,-2.8223 2.0586,-2.8242 4.40234,-2.25 4.4043,-2.25 h 8.07812 8.07813 l 5.95898,1.5312 5.96094,1.5333 7.24414,3.416 7.24414,3.416 4.57617,3.1484 4.57617,3.1485 0.28321,-0.3457 0.28515,-0.3477 9.32032,-13.4219 9.32031,-13.42185 -3.42383,-2.90039 -3.42578,-2.90039 -7,-3.92969 -7,-3.93164 -10.52344,-3.42969 -10.52148,-3.42968 -8.97852,-1.19727 z m 611,0.86133 -15.5,0.0527 -15.5,0.0547 -7.9453,2.15625 -7.9473,2.1543 -8.0527,3.95117 -8.0547,3.94922 -6.8555,5.20117 -6.8574,5.19922 -6.4883,7.5918 -6.4902,7.58986 -4.832,10 -4.8321,10 -1.8144,8 -1.8145,8 0.1153,13.5 0.1152,13.5 2.1367,8 2.1367,8 3.8477,8 3.8476,8 4.6895,6.1992 4.6895,6.1992 5.2031,4.6817 5.2031,4.6797 5.5,3.7304 5.5,3.7286 4.0723,2.0703 4.0703,2.0703 6.4297,2.1875 6.4277,2.1875 7,1.3594 7,1.3574 13.5,-0.3809 13.5,-0.3808 8.9512,-2.6485 8.9492,-2.6484 7.7461,-4.0293 7.7441,-4.0293 6.7793,-5.6934 6.7774,-5.6933 5.6621,-6.7403 5.6601,-6.7382 3.5586,-6.7715 3.5567,-6.7715 2.6328,-8.9629 2.6308,-8.9629 0.543,-11.5 0.541,-11.5 -1.5976,-9.1426 -1.5977,-9.1426 -2.7246,-7.3496 -2.7227,-7.3476 -5.7617,-8.6114 -5.7597,-8.61325 -6.3028,-5.89649 -6.3047,-5.89648 -5.7754,-3.83203 -5.7734,-3.83399 -6.2988,-2.85546 -6.2969,-2.85547 -8.4082,-2.26172 z M 118,969 v 32.8516 h 38 v -0.043 l 18.25,0.4062 18.25,0.4043 5.56641,2.1934 5.5664,2.1934 2.57031,2.7461 2.56836,2.748 1.84766,3.5 1.8457,3.5 -0.004,9.5215 -0.002,9.5215 -2.79101,5.705 -2.79102,5.7032 -3.01172,2.1445 -3.00976,2.1445 -4.58594,1.3789 L 191.6815,1057 H 173.8418 156 v 0 h -38 v 80 h 19 19 v -23 -23 l 11.25,-0.016 11.25,-0.016 11.5,-0.9571 11.5,-0.957 8,-2.0274 8,-2.0253 7.26367,-3.5625 7.26367,-3.5625 5.12305,-5.1231 5.12305,-5.123 3.38086,-7.1192 3.38281,-7.1211 1.60547,-8.4453 1.60547,-8.4453 -0.57617,-10.5 -0.57618,-10.5 -1.55664,-5.8281 -1.55468,-5.8301 -2.74219,-5.63672 -2.74219,-5.63672 -5.51172,-5.64648 -5.50976,-5.64649 -5.4668,-2.76953 -5.4668,-2.76953 -5.52148,-1.91602 -5.52344,-1.91601 -9.5,-1.44532 -9.5,-1.44726 -36.25,-0.006 z m 139,0 0.002,0.75 v 0.75 l 28.5,49.873 28.498,49.875 v 33.375 33.377 h 19.5 19.5 v -33.5117 -33.5117 l 28.08203,-49.1192 28.08008,-49.11717 0.52539,-1.37109 L 410.21484,969 h -20.4414 -20.43946 l -16.37109,28.25 -16.37109,28.25 -1.61914,2.7227 -1.61719,2.7226 -17.92774,-30.95311 -17.92773,-30.95313 -20.25,-0.0195 z m 157,0 v 16.5 16.5 h 23.5 23.5 v 67.5 67.5 h 19 19 v -67.5 -67.5 h 24 24 V 985.5 969 h -66.5 z m 160,0 v 84 84 h 62.02344 62.02539 L 697.77344,1120.25 697.5,1103.5 654.75,1103.2402 612,1102.9785 V 1085.9902 1069 h 37.5 37.5 v -16 -16 H 649.5 612 v -17 -17 h 41.5 41.5 v -17 -17 h -60.5 z m 294,0 v 16.5 16.5 h 24 24 v 67.5 67.5 h 19 19 v -67.5 -67.5 h 23.5 23.5 V 985.5 969 h -66.5 z m 160,0 v 84 84 h 19 19 v -32.4883 -32.4863 l 36.75,-0.2637 36.75,-0.2617 0.2734,-16.75 0.2754,-16.75 H 1103.0234 1066 v -17.4883 -17.4883 l 38.75,-0.2617 38.75,-0.2617 0.2734,-16.75 0.2754,-16.75 h -58.0254 z m 147,0 v 84 84 h 55.0234 55.0254 l -0.2754,-16.75 -0.2734,-16.75 -35.75,-0.2617 -35.75,-0.2637 V 1035.9883 969 h -19 z m 305.7676,0 0.4707,1.75 0.4726,1.75 27.2696,78 27.2695,78 1.4688,4.25 1.4687,4.25 h 14.248 14.2481 l 17.9082,-57.3965 17.9082,-57.3945 0.6484,1.1445 0.6485,1.1465 16.6484,53 16.6465,53 1.0312,3.25 1.0313,3.25 H 1654.5781 1669 v -0.3984 -0.4004 l 28.375,-81.3496 28.377,-81.3516 0.6914,-2.25 0.6914,-2.25 h -20.4258 -20.4258 l -16.3926,47.4258 -16.3906,47.4258 -0.6406,-1.1758 -0.6406,-1.1758 -10.6836,-35 -10.6817,-35 -3.4824,-11.25 -3.4824,-11.25 H 1603.9453 1584 v 0.44727 0.44531 l -14.1094,45.80472 -14.1094,45.8027 -0.6484,1.1934 -0.6484,1.1933 -13.4004,-39.1933 -13.4004,-39.1934 -2.9356,-8.25 -2.9375,-8.25 h -20.5214 z m -103.2676,29.66797 8.5,0.002 h 8.5 l 5.5,1.81243 5.5,1.8145 5,3.1855 5,3.1875 5.5,5.9629 5.5,5.961 2.7598,5.6602 2.7597,5.6602 1.6153,6.2929 1.6172,6.2929 -0.5176,10.5 -0.5196,10.5 -2.2558,6 -2.2578,6 -3.211,5 -3.2109,5 -3.6445,3.7051 -3.6446,3.7051 -4.2441,2.8984 -4.2461,2.8965 -5.6523,2.0976 -5.6524,2.0958 -7.3476,0.4902 -7.3477,0.4902 -4.7852,-0.8613 -4.7832,-0.8614 -6.2168,-2.9257 -6.2148,-2.9258 -5.1973,-4.6973 -5.1972,-4.6992 -4.0625,-6.084 -4.0645,-6.084 -2.2012,-7.1211 -2.2031,-7.1191 0.014,-10 0.014,-10 1.791,-5.9707 1.791,-5.9688 2.0215,-4.0312 2.0195,-4.0293 3.9649,-5.3086 3.9628,-5.3086 5.9219,-4.3594 5.9238,-4.3593 6.75,-2.2481 z" />
|
|
20
|
+
</g>
|
|
21
|
+
</svg>
|