Ansys Assistant will be unavailable on the Learning Forum starting January 30. An upgraded version is coming soon. We apologize for any inconvenience and appreciate your patience. Stay tuned for updates.
Embedded Software

Embedded Software

Testing Scade One models with Python

    • SolutionSolution
      Participant



      Model-based development is evolving quickly, and Scade One represents the next generation of SCADE technology, designed to make embedded software modeling more intuitive, scalable, and accessible. While Scade One possesses a fully-integrated, model-based test offer, it is also open to interfacing with other testing frameworks as required by user workflows. In particular, the PyScadeOne library connects the product with the full Python ecosystem.

      In this article, we will explore how to leverage pytest to set up a highly efficient workflow for building, verifying, and maintaining high-assurance embedded software.

      PyScadeOne: the Python bridge to your models

      At the heart of integrating Scade One models with Python is PyScadeOne, a Python library that provides APIs to trigger IDE features. This includes browsing, navigating and editing Scade One models, launching jobs such as code generation and FMI 2.0 export, and encapsulating the generated code into a Python-accessible wrapper.

      This library is documented with examples and enables a smooth workflow that brings your model directly into Python for testing, simulation, and automation.

      PyScadeOne is published as free, open source software (MIT license) under the PyAnsys initiative.

      Exporting Scade One models as Python-callable code

      Using the PyScadeOne’s Python wrapper functionality, you can export a Scade One model (in the Swan language) as a Python function. This process wraps C code generated by Scade One so it becomes directly callable from a Python script.

      Once this is done, you may use the rich Python ecosystem to write and run tests using pytest. Alternatively, you may also leverage Python libraries such as NumPy, SciPy, or Jupyter Notebooks for simulation, analysis, and reporting.

      In other words, your model becomes a first-class citizen in the Python ecosystem.

      Enter pytest



      Pytest is one of the most widely used Python testing frameworks, known for its simplicity and power. We will use it in this article, although the workflow we illustrate here would work with any Python-based testing framework.

      Pytest features a simple syntax using plain assert keywords. Test discovery is automatic (test_*.py files are tests by convention), and the library is extensible via plugins.

      For complex test setups, pytest provides rich fixtures as well as parameterization for running multiple test scenarios automatically.

      Finally, pytest can generate detailed HTML or JUnit reports and integrates easily with CI/CD pipelines.

      When paired with PyScadeOne, pytest becomes a seamless testing layer over your Scade One models.

      Workflow overview



      The development and testing loop looks like this:

      • Design and debug your models in Scade One
      • Generate code (via Scade One jobs or command line)
      • Generate a Python wrapper (DLL + Python proxy classes)
      • Write tests using pytest
      • Run tests and analyze logs & reports

      The Python wrapper acts as a proxy to the generated C code: it sets inputs, call the cycle function, and retrieves outputs. Everything is callable directly from Python, enabling fast test iteration.

      In a way, the Python wrapper behaves similarly to a Functional Mock-up Unit (FMU). The Scade One model’s generated code is pre-compiled and wrapped in a neat package that can be called externally.

      Setting up a Scade One + pytest project

      First, prepare the operators you want to test and define job configurations for generating their C code:



      Next, organize your project structure. You may place your tests/ folder next to your model, e.g.:

      📂 myproject/
          📂 assets/
          📂 jobs/
          📂 resources/
          🗎 model.sproj
      📂 tests/
      

      Inside the tests/ folder, create a requirements.txt file containing the following:

      pytest
      pytest-html
      matplotlib
      ansys-scadeone-core
      

      Next, prepare a Python virtual environment (a.k.a. “venv”). Here’s an example using Windows PowerShell:

      python -m venv .venv
      .venv\Scripts\Activate.ps1
      python -m pip install -r tests/requirements.txt
      

      This installs: PyScadeOne, pytest and any additional dependencies (plotting, HTML report generation) in a fresh, contained Python environment. You don’t need to worry about interactions with pre-installed libraries that may have the wrong version or interfere with running the tests.

      Automatically generating Python wrappers

      The next step is to generate C code and Python wrappers from your Scade One operators.

      Wrappers can be generated either by launching Scade One in batch mode from the command line, or directly from the PyScadeOne API:

      # Create a PythonWrapper instance
      wrapper = PythonWrapper(project, job_name, proxy_name, target_path)
      wrapper.generate()
      

      For each operator, the wrapper generates:

      • A class called %operator%_%wrapper%
      • reset and cycle functions
      • Strongly typed input and output structures

      When using pytest, a good practice is to place wrapper generation inside a fixture, so that the DLL and proxy classes are created automatically when tests run. This way, no manual setup is needed and each test has access to the operator under test.

      Below is an example of a pytest fixture that retrieves a Scade One project, generates the code and invokes the Python wrapper. Note that in real-life scenario, you may want to introduce more parameters to control whether the code / DLL need to be regenerated and accelerate test runs.

      def _build_proxy(project_dir: Path, job_name: str,wrapper_name: str,out_dir:str) -> bool:
          s_one_install = Path(r"C:\Program Files\ANSYS Inc\v252\Scade One")
          print("Project path is {} ".format(project_dir))
          app = ScadeOne(install_dir=s_one_install)
          prj = app.load_project(project_dir)
          prj.load_jobs()
          job = prj.get_job(job_name)
          result = job.run()
          assert result.code == 0
          gen = PythonWrapper(project=prj,job=job_name, output=wrapper_name,target_dir=out_dir)
          gen.generate()
          # add the target directory to sys.path
          if os.path.abspath(Path(out_dir+"/"+wrapper_name)) not in sys.path:
              sys.path.append(os.path.abspath(Path(out_dir+"/"+wrapper_name)))
           dll = Path(out_dir+"/"+wrapper_name+"/"+ '%s.dll' % wrapper_name)
          print("Expecting dll in %s " % dll)
          return dll.exists()
      
      @pytest.fixture(scope='session') 
      def check_cc_proxy() -> bool: 
      """Ensure the proxy is built and up-to-date.""" 
          project_dir = Path(file).parent.parent.parent / 'CruiseControl'/ 'CruiseControl.sproj' 
          return _build_proxy(project_dir, "CCCodeGenerationJob","cc","Tests/proxy")
      

      Developing tests with PyTest

      Writing a test procedure is straightforward. Start by creating a tests/test_%something%.py file and instantiate the proxy class representing your operator using a fixture. Within each test, you can freely combine basic operations: resetting the model, setting inputs, running cycles, and asserting outputs.

      A typical flow begins by calling reset() to ensure a clean initial state. From there, set any required inputs to establish the desired starting conditions. Then, apply test-specific inputs and invoke the operator’s logic by calling cycle() as many times as needed. At any point, after cycle(), you can validate behavior by asserting output values with assert:

       def test_controller_integration(check_cc_proxy):
          # TC_CC_I_002
          assert check_cc_proxy
          # check_csm_proxy adds the cc module directory to sys.path
          from cc import Controller
          root = Controller()
          root.reset()
          # set the initial values for the commands
          root.on = False
          root.off = False
          root.resume_ = False
          root.set = False
          root.speed = 0.0
          root.accel = 0.0
          root.brake = 0.0
          root.quickAccel= False
          root.quickDecel= False
      
          # Test Reference: CC_TEST_INT_01
          #
          # Test Case Objectives:
          #    verify the CC is started in STDBY mode when activated
          #    while the driver accelerates.
          #
          # Test Case Acceptance Criteria:
          #    cruiseState is STDBY, throttleCmd = accel,
          #    and cruiseSpeed = speed
          root.speed = 100.0
          root.accel = 25.0
          root.on = True
          root.cycle()
          assert root.cruiseState == CC_STATE.STDBY.value
          assert abs(root.throttleCmd - root.accel) < 5.0e-6
          assert abs(root.cruiseSpeed - root.speed) < 5.0e-6
      

      Running tests

      Pytest allows you to:

      • Run all tests in a folder:

      > pytest tests\
      
      • Run a single test file:

      > pytest .\tests\test_cc.py
      
      • Filter using keywords or markers
      • Run from the CLI or directly from IDEs like Visual Studio Code:



      Running tests from VSCode

      Pytest may generate reports in HTML or JUnit formats, which can easily be imported into test management tools:



      Sample HTML report generated with pytest-html

      Ready to try this out?

      In this article, we saw how to combine Scade One, PyScadeOne, and pytest to unlock a powerful, modern, and highly automated workflow for testing embedded software models. We get:

      • Scade One’s robust modeling and code generation
      • Python’s productivity and tooling ecosystem
      • Scalable, maintainable testing pipelines
      • Rich reporting and easy to export results

      This integration is ideal for cost-critical embedded systems that must meet the highest testing standards.

      If you are a Scade One user and would like to play with this concept, you may download an example model here.

      If you are a SCADE user, you may try out Scade One at no extra cost with your existing licenses.

      About the author



      Romain Andrieux (LinkedIn) is a Product Specialist Engineer at Ansys. He has been working on SCADE Ecosystem and the SCADE Support team development for 2 years.